diff --git a/QuantumInfo.lean b/QuantumInfo.lean index 6fa9744d9..a2e6b4271 100644 --- a/QuantumInfo.lean +++ b/QuantumInfo.lean @@ -17,7 +17,10 @@ public import QuantumInfo.ForMathlib.MatrixNorm.TraceNorm public import QuantumInfo.ForMathlib.Matrix public import QuantumInfo.ForMathlib.Minimax public import QuantumInfo.ForMathlib.Misc +public import QuantumInfo.ForMathlib.Superadditive +public import QuantumInfo.ForMathlib.ULift public import QuantumInfo.ForMathlib.Unitary +public import QuantumInfo.ForMathlib.HayataGroup.TraceInequality.JensenOperatorInequalityIVtoV --Code public import QuantumInfo.Channels.DegradableOrder @@ -29,8 +32,12 @@ public import QuantumInfo.Channels.Unbundled public import QuantumInfo.States.Mixed.Fidelity public import QuantumInfo.States.Mixed.TraceDistance public import QuantumInfo.States.Pure.Qubit +public import QuantumInfo.States.Pure.BlochSphere +public import QuantumInfo.States.Pure.BargmannInvariant +public import QuantumInfo.StdBasisState public import QuantumInfo.ResourceTheory.FreeState public import QuantumInfo.ResourceTheory.SteinsLemma +public import QuantumInfo.ResourceTheory.ResourceTheory public import QuantumInfo.States.Pure.Braket public import QuantumInfo.Capacity.Capacity public import QuantumInfo.States.Ensemble @@ -39,17 +46,19 @@ public import QuantumInfo.Entropy.VonNeumann public import QuantumInfo.Entropy.SSA public import QuantumInfo.Entropy.Relative public import QuantumInfo.Entropy.DPI +public import QuantumInfo.Entropy.Axiomatized.Renyi public import QuantumInfo.States.Mixed.MState public import QuantumInfo.Channels.Pinching public import QuantumInfo.Measurements.POVM public import QuantumInfo.Operators.Unitary +public import QuantumInfo.Regularized --Documentation without code public import QuantumInfo.Capacity.Capacity_doc --Classical information theory --- import QuantumInfo.ClassicalInfo.Capacity --- import QuantumInfo.ClassicalInfo.Channel +public import QuantumInfo.ClassicalInfo.Capacity +public import QuantumInfo.ClassicalInfo.Channel public import QuantumInfo.ClassicalInfo.Distribution public import QuantumInfo.ClassicalInfo.Entropy public import QuantumInfo.ClassicalInfo.Prob diff --git a/QuantumInfo/Capacity/Capacity.lean b/QuantumInfo/Capacity/Capacity.lean index d35e563d8..2a31c2d5d 100644 --- a/QuantumInfo/Capacity/Capacity.lean +++ b/QuantumInfo/Capacity/Capacity.lean @@ -101,7 +101,7 @@ And other important theorems like superdense coding, nonadditivity, superactivat @[expose] public section -namespace CPTPMap +namespace CPTPOp variable {d₁ d₂ d₃ d₄ d₅ d₆ : Type*} variable [Fintype d₁] [Fintype d₂] [Fintype d₃] [Fintype d₄] [Fintype d₅] [Fintype d₆] @@ -125,7 +125,7 @@ A channel A `εApproximates` channel B of the same dimensions if for every state fidelity F(A(ρ), B(ρ)) is at least 1-ε. -/ def εApproximates (A B : CPTPMap d₁ d₂) (ε : ℝ) : Prop := - ∀ (ρ : MState d₁), (A ρ).fidelity (B ρ) ≥ 1-ε + ∀ (ρ : MState d₁), DensityOp.fidelity (A ρ) (B ρ) ≥ 1-ε /-- A channel A `AchievesRate` R:ℝ if for every ε>0, some n copies of A emulates a channel B such @@ -134,9 +134,9 @@ that log2(dimout(B))/n ≥ R, and that B εApproximates the identity channel. def AchievesRate (A : CPTPMap d₁ d₂) (R : ℝ) : Prop := ∀ ε : ℝ, ε > 0 → ∃ n > 0, ∃ (dimB : ℕ) (B : CPTPMap (Fin dimB) (Fin dimB)), - (CPTPMap.piProd (fun (_ : Fin n) ↦ A)).Emulates B ∧ + (CPTPOp.piProd (fun (_ : Fin n) ↦ A)).Emulates B ∧ Real.logb 2 dimB ≥ R*n ∧ - B.εApproximates CPTPMap.id ε + B.εApproximates CPTPOp.id ε /-- The quantum capacity of a channel A: the supremum of all rates R such that `A.AchievesRate R`, i.e. the maximum asymptotic rate at which quantum information can be @@ -156,7 +156,7 @@ variable [DecidableEq d₃] [DecidableEq d₄] [DecidableEq d₅] /-- Every quantum channel emulates itself. -/ @[refl] theorem emulates_self (Λ : CPTPMap d₁ d₂) : Λ.Emulates Λ := - ⟨CPTPMap.id, CPTPMap.id, by simp⟩ + ⟨CPTPOp.id, CPTPOp.id, by simp⟩ /-- If a quantum channel A emulates B, and B emulates C, then A emulates C. -/ @[trans] @@ -176,10 +176,12 @@ end emulates section εApproximates +omit [DecidableEq d₂] in /-- Every quantum channel perfectly approximates itself, that is, `εApproximates` with `ε = 0`. -/ theorem εApproximates_self (Λ : CPTPMap d₁ d₂) : Λ.εApproximates Λ 0 := - fun ρ ↦ ((Λ ρ).fidelity_self_eq_one.trans (sub_zero 1).symm).ge + fun ρ ↦ ((DensityOp.fidelity_self_eq_one (Λ ρ)).trans (sub_zero 1).symm).ge +omit [DecidableEq d₂] in /-- If a quantum channel A approximates B with ε₀, it also approximates B with all larger ε₁. -/ theorem εApproximates_monotone {A B : CPTPMap d₁ d₂} {ε₀ : ℝ} (h : A.εApproximates B ε₀) {ε₁ : ℝ} (h₂ : ε₀ ≤ ε₁) : A.εApproximates B ε₁ := @@ -195,12 +197,15 @@ end εApproximates section AchievesRate -/-- Every quantum channel out of a nonempty space achieves at least a rate of zero. -/ -theorem achievesRate_0 (Λ : CPTPMap d₁ d₂) [Nonempty d₁] : Λ.AchievesRate 0 := fun ε hε => by - have : Nonempty d₂ := Λ.toPTPMap.nonemptyOut - refine ⟨1, one_pos, 1, default, ⟨default, default, Subsingleton.elim _ _⟩, by norm_num, ?_⟩ - simpa [show (default : CPTPMap (Fin 1) (Fin 1)) = id from Subsingleton.elim _ _] using - εApproximates_monotone (εApproximates_self (id (dIn := Fin 1))) hε.le +/-- Every quantum channel on a nonempty space achieves a rate of zero. (The hypothesis is needed: +a channel out of an empty space into a nonempty one emulates nothing at all, since an emulation +would need both a channel into the empty space and one out of the nonempty one.) -/ +theorem achievesRate_0 [Nonempty d₁] (Λ : CPTPMap d₁ d₂) : Λ.AchievesRate 0 := by + have : Nonempty d₂ := MState.nonempty (Λ default) + intro ε hε + refine ⟨1, zero_lt_one, 1, default, ⟨default, default, Unique.eq_default _⟩, by norm_num, ?_⟩ + rw [Unique.eq_default (id : CPTPMap (Fin 1) (Fin 1))] + exact εApproximates_monotone (εApproximates_self default) hε.le /-- The identity channel on D dimensional space achieves a rate of log2(D). -/ theorem id_achievesRate_log_dim : @@ -208,46 +213,139 @@ theorem id_achievesRate_log_dim : intro ε hε use 1, zero_lt_one, Fintype.card d₁, id constructor - · --piProd of id's is id, then use emulates_self up to equivalence - rw [show (fun (_ : Fin 1) ↦ id (dIn := d₁)) = (fun _ ↦ id) from rfl, piProd_id] - exact let σ := Fintype.equivFinOfCardEq (by simp +decide : - Fintype.card (Fin 1 → d₁) = Fintype.card d₁) - ⟨ofEquiv σ.symm, ofEquiv σ, by ext1; simp⟩ + · --they are equivalent up to permutation + -- TODO: Instead this proof should be `@[simp] piProd (fun x => id) = id` and `emulates_self` + refine' ⟨ _, _, _ ⟩; + exact CPTPOp.ofEquiv ( Fintype.equivFinOfCardEq ( by simp +decide ) ).symm; + exact CPTPOp.ofEquiv ( Fintype.equivFinOfCardEq ( by simp +decide ) ); + apply CPTPOp.ext_map + simp only [compose_map, piProd_map, id_map, ofEquiv_map] + rw [show (fun _ : Fin 1 ↦ (LinearMap.id : MatrixMap d₁ d₁ ℂ)) = + (fun i : Fin 1 ↦ MatrixMap.id ((fun _ : Fin 1 ↦ d₁) i) ℂ) from rfl] + simp [MatrixMap.id] constructor · norm_num · exact εApproximates_monotone (εApproximates_self id) hε.le -/-- A channel cannot achieve a rate greater than log2(D), where D is the input dimension. -/ -@[sorryful] -theorem not_achievesRate_gt_log_dim_in (Λ : CPTPMap d₁ d₂) {R : ℝ} - (hR : Real.logb 2 (Fintype.card d₁) < R) : ¬Λ.AchievesRate R := by - sorry - -noncomputable section AristotleLemmas - -end AristotleLemmas +/-- A channel `D ∘ E` that factors through `d₂` can only approximate the identity channel on `d₁` if +`d₂` is nearly as large as `d₁`: each of the `card d₁` basis states `∣i⟩` must come back out of `D` +with probability at least `(1-ε)²`, and these probabilities are the expectation values of a POVM +whose total trace is only `card d₂`. -/ +theorem card_mul_le_card_of_εApproximates_id (E : CPTPMap d₁ d₂) (D : CPTPMap d₂ d₁) {ε : ℝ} + (hε : ε ≤ 1) (h : (D.compose E).εApproximates CPTPOp.id ε) : + (Fintype.card d₁ : ℝ) * (1 - ε)^2 ≤ Fintype.card d₂ := by + have key : ∀ i : d₁, (1 - ε)^2 ≤ (D.dual (MState.pure (Ket.basis i)).M).trace := by + intro i + have h1 := h (MState.pure (Ket.basis i)) + rw [ge_iff_le, compose_eq, id_MState, DensityOp.fidelity_symm, DensityOp.fidelity_pure] at h1 + have hnn : 0 ≤ MState.exp_val (D (E (MState.pure (Ket.basis i)))) + (MState.pure (Ket.basis i)).M := + MState.exp_val_nonneg _ (MState.pure (Ket.basis i)).nonneg + have h2 := mul_self_le_mul_self (by linarith : (0:ℝ) ≤ 1 - ε) h1 + rw [Real.mul_self_sqrt hnn, ← sq] at h2 + calc (1 - ε)^2 + ≤ MState.exp_val (D (E (MState.pure (Ket.basis i)))) (MState.pure (Ket.basis i)).M := h2 + _ = MState.exp_val (E (MState.pure (Ket.basis i))) (D.dual (MState.pure (Ket.basis i)).M) := + exp_val_Dual D _ _ + _ ≤ MState.exp_val (E (MState.pure (Ket.basis i))) + ((D.dual (MState.pure (Ket.basis i)).M).trace • 1) := + MState.exp_val_le_exp_val _ + (HermitianMat.le_trace_smul_one (D.dual_pos (MState.pure (Ket.basis i)).nonneg)) + _ = (D.dual (MState.pure (Ket.basis i)).M).trace := by + rw [MState.exp_val_smul, MState.exp_val_one, mul_one] + calc (Fintype.card d₁ : ℝ) * (1 - ε)^2 = ∑ _i : d₁, (1 - ε)^2 := by + rw [Finset.sum_const, Finset.card_univ, nsmul_eq_mul] + _ ≤ ∑ i : d₁, (D.dual (MState.pure (Ket.basis i)).M).trace := + Finset.sum_le_sum fun i _ ↦ key i + _ = (D.dual (∑ i : d₁, (MState.pure (Ket.basis i)).M)).trace := by + rw [map_sum, HermitianMat.trace_sum] + _ = Fintype.card d₂ := by rw [MState.sum_pure_basis, map_one, HermitianMat.trace_one] + +/-- A channel cannot achieve a rate greater than log2(D), where D is the input dimension. + +If `Λ` achieved a rate `R` above `L = log₂(D)`, then for every `ε` there would be a channel `B` on +`2^(Rn)` dimensions, `ε`-approximating the identity, that factors through the `Dⁿ`-dimensional +input of `Λ⊗ⁿ`. By `card_mul_le_card_of_εApproximates_id` that forces `2^(Rn) (1-ε)² ≤ 2^(Ln)`, +that is, `(1-ε)² ≤ 2^((L-R)n) ≤ 2^(L-R) < 1`, which fails once `ε` is small enough. -/ +theorem not_achievesRate_gt_log_dim_in (Λ : CPTPMap d₁ d₂) {R : ℝ} (hR : Real.logb 2 (Fintype.card d₁) < R) : + ¬Λ.AchievesRate R := by + intro hach + set L := Real.logb 2 (Fintype.card d₁) with hL + have hL0 : 0 ≤ L := by + rcases Nat.eq_zero_or_pos (Fintype.card d₁) with h | h + · simp [hL, h] + · exact Real.logb_nonneg one_lt_two (by exact_mod_cast h) + have hR0 : 0 < R := lt_of_le_of_lt hL0 hR + have hpow : (0:ℝ) < 2 ^ (L - R) := Real.rpow_pos_of_pos two_pos _ + set t := Real.sqrt (2 ^ (L - R)) with ht + have ht0 : 0 < t := Real.sqrt_pos.mpr hpow + have ht1 : t < 1 := by + rw [ht, show (1:ℝ) = Real.sqrt 1 from Real.sqrt_one.symm] + refine Real.sqrt_lt_sqrt hpow.le ?_ + calc (2:ℝ) ^ (L - R) < 2 ^ (0:ℝ) := + (Real.rpow_lt_rpow_left_iff one_lt_two).mpr (by linarith) + _ = 1 := Real.rpow_zero 2 + obtain ⟨n, hn, dimB, B, ⟨E', D', hED⟩, hRate, hApprox⟩ := hach ((1 - t)/2) (by linarith) + have hnR : (0:ℝ) < n := by exact_mod_cast hn + have hRn : 0 < R * n := mul_pos hR0 hnR + -- Regroup the emulation `B = D' ∘ Λ⊗ⁿ ∘ E'` as a factorization through `Fin n → d₁`. + have hApprox' : ((D'.compose (CPTPOp.piProd fun _ : Fin n ↦ Λ)).compose E').εApproximates + CPTPOp.id ((1 - t)/2) := by + rw [compose_assoc, hED] + exact hApprox + have hcount : (dimB : ℝ) * (1 - (1 - t)/2)^2 ≤ ((Fintype.card d₁ : ℝ)) ^ n := by + have h := card_mul_le_card_of_εApproximates_id E' (D'.compose (piProd fun _ : Fin n ↦ Λ)) + (by linarith) hApprox' + rwa [Fintype.card_fin, Fintype.card_fun, Fintype.card_fin, Nat.cast_pow] at h + have hd0 : 0 < dimB := by + rcases Nat.eq_zero_or_pos dimB with h | h + · rw [h] at hRate + simp only [Nat.cast_zero, Real.logb_zero, ge_iff_le] at hRate + linarith + · exact h + have hdimB : (2:ℝ) ^ (R * n) ≤ dimB := + calc (2:ℝ) ^ (R * n) ≤ 2 ^ Real.logb 2 dimB := + (Real.rpow_le_rpow_left_iff one_lt_two).mpr hRate + _ = dimB := Real.rpow_logb two_pos (by norm_num) (by exact_mod_cast hd0) + have hcard : ((Fintype.card d₁ : ℝ)) ^ n ≤ (2:ℝ) ^ (L * n) := by + rcases Nat.eq_zero_or_pos (Fintype.card d₁) with h | h + · rw [h, Nat.cast_zero, zero_pow hn.ne'] + positivity + · have hc : ((Fintype.card d₁ : ℝ)) = 2 ^ L := + (Real.rpow_logb two_pos (by norm_num) (by exact_mod_cast h)).symm + rw [hc, ← Real.rpow_natCast (2 ^ L) n, ← Real.rpow_mul zero_le_two] + have hsq : (1 - (1 - t)/2)^2 ≤ t^2 := by + rw [ht, Real.sq_sqrt hpow.le] + have h1 : (2:ℝ) ^ (R * n) * (1 - (1 - t)/2)^2 ≤ 2 ^ (L * n) := + le_trans (mul_le_mul_of_nonneg_right hdimB (by positivity)) (hcount.trans hcard) + have h2 : (2:ℝ) ^ (L * n) ≤ 2 ^ (R * n) * 2 ^ (L - R) := by + rw [← Real.rpow_add two_pos] + refine (Real.rpow_le_rpow_left_iff one_lt_two).mpr ?_ + have hn1 : (1:ℝ) ≤ n := by exact_mod_cast hn + nlinarith [mul_le_mul_of_nonneg_left hn1 (sub_pos.mpr hR).le] + exact le_of_mul_le_mul_left (h1.trans h2) (Real.rpow_pos_of_pos two_pos _) + nlinarith [mul_pos (sub_pos.mpr ht1) (show (0:ℝ) < 1 + 3*t by linarith)] /-- A channel cannot achieve a rate greater than log2(D), where D is the output dimension. -/ @[sorryful] theorem not_achievesRate_gt_log_dim_out (Λ : CPTPMap d₁ d₂) {R : ℝ} (hR : Real.logb 2 (Fintype.card d₂) < R) : ¬Λ.AchievesRate R := by intro h; - -- We show that the identity channel on the output space `d₂` emulates `Λ`. Since capacity - -- is monotonic under emulation, `Q(Λ) ≤ Q(id_{d₂})`. - have h_emulate : (CPTPMap.id (dIn := d₂)).Emulates Λ := by - exact ⟨Λ, CPTPMap.id, by simp⟩ - -- If `Λ` achieves rate `R`, then `id_{d₂}` achieves rate `R`. This follows because if - -- `Λ^{\otimes n}` emulates `B`, and `id^{\otimes n}` emulates `Λ^{\otimes n}` (by - -- functoriality of tensor product), then `id^{\otimes n}` emulates `B`. - have h_id_achieves : (CPTPMap.id (dIn := d₂)).AchievesRate R := by + -- We show that the identity channel on the output space `d₂` emulates `Λ`. Since capacity is monotonic under emulation, `Q(Λ) ≤ Q(id_{d₂})`. + have h_emulate : (CPTPOp.id (dIn := d₂)).Emulates Λ := by + exact ⟨Λ, CPTPOp.id, by simp⟩ + -- If `Λ` achieves rate `R`, then `id_{d₂}` achieves rate `R`. This follows because if `Λ^{\otimes n}` emulates `B`, and `id^{\otimes n}` emulates `Λ^{\otimes n}` (by functoriality of tensor product), then `id^{\otimes n}` emulates `B`. + have h_id_achieves : (CPTPOp.id (dIn := d₂)).AchievesRate R := by intro ε hε_pos obtain ⟨n, hn, dimB, B, hB_emulate, hB_rate, hB_approx⟩ := h ε hε_pos - have h_id_emulate : - (CPTPMap.piProd (fun (_ : Fin n) => CPTPMap.id (dIn := d₂))).Emulates B := by - rw [piProd_id] - obtain ⟨E, D, hD⟩ := h_emulate - exact emulates_trans _ _ _ ⟨piProd fun _ => E, piProd fun _ => D, - by simp [← hD, ← CPTPMap.piProd_comp]⟩ hB_emulate + have h_id_emulate : (CPTPOp.piProd (fun (_ : Fin n) => CPTPOp.id (dIn := d₂))).Emulates B := by + -- Since `id_{d₂}` emulates `Λ`, we can use the fact that the tensor product of emulations is an emulation. + have h_tensor_emulate : ∀ (n : ℕ), (CPTPOp.piProd (fun (_ : Fin n) => CPTPOp.id (dIn := d₂))).Emulates (CPTPOp.piProd (fun (_ : Fin n) => Λ)) := by + intro n + obtain ⟨E, D, hD⟩ := h_emulate + use CPTPOp.piProd (fun (_ : Fin n) => E), CPTPOp.piProd (fun (_ : Fin n) => D); + simp [ ← hD, ← CPTPOp.piProd_comp]; + exact emulates_trans _ _ _ ( h_tensor_emulate n ) hB_emulate; exact ⟨ n, hn, dimB, B, h_id_emulate, hB_rate, hB_approx ⟩; refine not_le_of_gt hR <| not_lt.mp fun h => ?_ exact not_lt_of_ge ( le_of_not_gt fun h' => not_achievesRate_gt_log_dim_in _ h' h_id_achieves ) h @@ -270,10 +368,8 @@ end AchievesRate section capacity -/-- Quantum channel capacity is nonnegative for channels out of a nonempty space. -/ -@[sorryful] -theorem zero_le_quantumCapacity (Λ : CPTPMap d₁ d₂) [Nonempty d₁] : - 0 ≤ Λ.quantumCapacity := +/-- Quantum channel capacity is nonnegative. -/ +theorem zero_le_quantumCapacity [Nonempty d₁] (Λ : CPTPMap d₁ d₂) : 0 ≤ Λ.quantumCapacity := le_csSup (bddAbove_achievesRate Λ) (achievesRate_0 Λ) /-- Quantum channel capacity is at most log2(D), where D is the input dimension. -/ @@ -302,7 +398,7 @@ theorem coherentInfo_le_quantumCapacity (Λ : CPTPMap d₁ d₂) (ρ : MState d /-- The quantum capacity is the limit of the coherent information of n-copy uses of the channel. -/ @[sorryful] theorem quantumCapacity_eq_piProd_coherentInfo (Λ : CPTPMap d₁ d₂) : Λ.quantumCapacity = - sSup { r : ℝ | ∃ n ρ, r = coherentInfo ρ (CPTPMap.piProd (fun (_ : Fin n) ↦ Λ))} := by + sSup { r : ℝ | ∃ n ρ, r = coherentInfo ρ (CPTPOp.piProd (fun (_ : Fin n) ↦ Λ))} := by sorry end capacity diff --git a/QuantumInfo/Channels/Bundled.lean b/QuantumInfo/Channels/Bundled.lean index 220ac9a17..c7b7fdb07 100644 --- a/QuantumInfo/Channels/Bundled.lean +++ b/QuantumInfo/Channels/Bundled.lean @@ -5,105 +5,171 @@ Authors: Alex Meiburg -/ module -public import QuantumInfo.Channels.Unbundled +public import QuantumInfo.Channels.OpMap public import QuantumInfo.States.Mixed.MState public import Mathlib.Topology.Order.Hom.Basic -/-! # Classes of Matrix Maps +/-! # Classes of operator maps -The bundled `MatrixMap`s: `HPMap`, `UnitalMap`, `TPMap`, `PMap`, and `CPMap`. -These are defined over the bare minimum rings (`Semiring` or `RCLike`, respectively). +The bundled `OpMap`s: `HPOp` (Hermitian preserving), `UnitalOp`, `TPOp` (trace preserving), +`POp` (positive), and `CPOp` (completely positive), together with the combinations `PTPOp`, +`PUOp`, `CPTPOp`, and `CPUOp`. -The combinations `PTPMap` (positive trace-preserving), `CPTPMap`, and `CPUMap` -(CP unital maps) take ℂ as the default class. +These are all maps between operators on complex Hilbert spaces, and so are independent of any +choice of basis. Given preferred orthonormal bases -- that is, `StdBasis ℂ E ι` and +`StdBasis ℂ F κ` instances -- `HPOp.map` is the corresponding `MatrixMap`, and each defining +property has a matrix analogue (`HPOp.map_HP`, `TPOp.map_TP`, and so on). The abbreviations +`HPMap dIn dOut`, ..., `CPTPMap dIn dOut` are the special case of `EuclideanSpace`s, where the +preferred bases are the computational ones. -The majority of quantum theory revolves around `CPTPMap`s, so those are explored more +The majority of quantum theory revolves around `CPTPOp`s, so those are explored more thoroughly in their file CPTP.lean. -/ @[expose] public section +noncomputable section -variable (dIn dOut R : Type*) (𝕜 : Type := ℂ) -variable [Semiring R] [RCLike 𝕜] +open scoped ComplexOrder -/-- Hermitian-preserving linear maps. -/ -structure HPMap extends MatrixMap dIn dOut 𝕜 where - HP : MatrixMap.IsHermitianPreserving toLinearMap +section Defs -/-- Unital linear maps. -/ -structure UnitalMap [DecidableEq dIn] [DecidableEq dOut] - extends MatrixMap dIn dOut R where - unital : MatrixMap.Unital toLinearMap +variable (E F : Type*) +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] -/-- Trace-preserving linear maps. -/ -structure TPMap [Fintype dIn] [Fintype dOut] extends MatrixMap dIn dOut R where - TP : MatrixMap.IsTracePreserving toLinearMap +/-- Hermitian-preserving linear maps of operators. -/ +structure HPOp extends OpMap E F where + HP : OpMap.IsHermitianPreserving toLinearMap ---Mark this as [simp] so that simp lemmas requiring `IsTracePreserving` can pick it up. ---In theory this could be making "IsTracePreserving" a typeclass ... or more realistically, ---defining a `TracePreservingClass` similar to `AddHomClass` -attribute [simp] TPMap.TP +/-- Unital linear maps of operators. -/ +structure UnitalOp extends OpMap E F where + unital : OpMap.Unital toLinearMap -/-- Positive linear maps. -/ -structure PMap [Fintype dIn] [Fintype dOut] - extends HPMap dIn dOut 𝕜 where - pos : MatrixMap.IsPositive toLinearMap - HP := pos.IsHermitianPreserving +/-- Trace-preserving linear maps of operators. -/ +structure TPOp extends OpMap E F where + TP : OpMap.IsTracePreserving toLinearMap -/-- Completely positive linear maps. -/ -structure CPMap [Fintype dIn] [Fintype dOut] [DecidableEq dIn] - extends PMap dIn dOut 𝕜 where - cp : MatrixMap.IsCompletelyPositive toLinearMap - pos := cp.IsPositive +/-- Positive linear maps of operators. -/ +structure POp extends HPOp E F where + pos : OpMap.IsPositive toLinearMap + HP := pos.isHermitianPreserving -/-- Positive trace-preserving linear maps. These includes all channels, but aren't - necessarily *completely* positive, see `CPTPMap`. -/ -structure PTPMap [Fintype dIn] [Fintype dOut] - extends PMap dIn dOut 𝕜, TPMap dIn dOut 𝕜 +/-- Completely positive linear maps of operators. -/ +structure CPOp extends POp E F where + cp : OpMap.IsCompletelyPositive toLinearMap + pos := cp.isPositive -/-- Positive unital maps. These are important because they are the - dual to `PTPMap`: they are the most general way to map *observables*. -/ -structure PUMap [Fintype dIn] [Fintype dOut] [DecidableEq dIn] [DecidableEq dOut] - extends PMap dIn dOut 𝕜, UnitalMap dIn dOut 𝕜 +/-- Positive trace-preserving linear maps. These include all channels, but aren't + necessarily *completely* positive, see `CPTPOp`. -/ +structure PTPOp extends POp E F, TPOp E F -attribute [simp] PTPMap.TP +/-- Positive unital maps. These are important because they are the + dual to `PTPOp`: they are the most general way to map *observables*. -/ +structure PUOp extends POp E F, UnitalOp E F /-- Completely positive trace-preserving linear maps. This is the most common meaning of "channel", often described as "the most general physically realizable quantum operation". -/ -structure CPTPMap [Fintype dIn] [Fintype dOut] [DecidableEq dIn] - extends PTPMap dIn dOut (𝕜 := 𝕜), CPMap dIn dOut 𝕜 where +structure CPTPOp extends PTPOp E F, CPOp E F /-- Completely positive unital maps. These are important because they are the - dual to `CPTPMap`: they are the physically realizable ways to map *observables*. -/ -structure CPUMap [Fintype dIn] [Fintype dOut] [DecidableEq dIn] [DecidableEq dOut] - extends CPMap dIn dOut 𝕜, PUMap dIn dOut 𝕜 + dual to `CPTPOp`: they are the physically realizable ways to map *observables*. -/ +structure CPUOp extends CPOp E F, PUOp E F -variable {dIn dOut R} {𝕜 : Type} [RCLike 𝕜] +end Defs -/-! +section Euclidean -## Hermitian-preserving maps +variable (dIn dOut : Type*) [Fintype dIn] [Fintype dOut] --/ +/-- Hermitian-preserving maps between systems with computational bases. -/ +abbrev HPMap := HPOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) + +/-- Unital maps between systems with computational bases. -/ +abbrev UnitalMap := UnitalOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) + +/-- Trace-preserving maps between systems with computational bases. -/ +abbrev TPMap := TPOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) + +/-- Positive maps between systems with computational bases. -/ +abbrev PMap := POp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) + +/-- Completely positive maps between systems with computational bases. -/ +abbrev CPMap := CPOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) ---Hermitian-presering maps: continuous linear maps on HermitianMats. -namespace HPMap -variable {Λ₁ Λ₂ : HPMap dIn dOut 𝕜} -variable {CΛ₁ CΛ₂ : HPMap dIn dOut ℂ} +/-- Positive trace-preserving maps between systems with computational bases. -/ +abbrev PTPMap := PTPOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) -abbrev map (M : HPMap dIn dOut 𝕜) : MatrixMap dIn dOut 𝕜 := M.toLinearMap +/-- Positive unital maps between systems with computational bases. -/ +abbrev PUMap := PUOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) + +/-- Quantum channels between systems with computational bases. -/ +abbrev CPTPMap := CPTPOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) + +/-- Completely positive unital maps between systems with computational bases. -/ +abbrev CPUMap := CPUOp (EuclideanSpace ℂ dIn) (EuclideanSpace ℂ dOut) + +end Euclidean + +variable {E F ι κ : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] + +--Hermitian-preserving maps: continuous linear maps on HermitianMats. +namespace HPOp + +variable {Λ₁ Λ₂ : HPOp E F} @[ext] -theorem ext (h : Λ₁.map = Λ₂.map) : Λ₁ = Λ₂ := by - rwa [HPMap.mk.injEq] +theorem ext (h : Λ₁.toLinearMap = Λ₂.toLinearMap) : Λ₁ = Λ₂ := by + rwa [HPOp.mk.injEq] + +/-- A Hermitian-preserving map acting on self-adjoint operators. This is the basis-free form of +`HPOp.instFunLike`; `HPOp.toMat_opApply` is its matrix analogue. -/ +def opApply (Λ : HPOp E F) (A : HermitianOp E) : HermitianOp F := + ⟨Λ.toLinearMap A.op, Λ.HP A.H⟩ + +@[simp] +theorem op_opApply (Λ : HPOp E F) (A : HermitianOp E) : + (Λ.opApply A).op = Λ.toLinearMap A.op := + rfl + +section StdBasis + +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] + +/-- **Matrix analogue of a Hermitian-preserving map**: its matrix in the preferred bases. -/ +def map (Λ : HPOp E F) : MatrixMap ι κ ℂ := + OpMap.toMat Λ.toLinearMap + +theorem map_eq (Λ : HPOp E F) : Λ.map (ι := ι) (κ := κ) = OpMap.toMat Λ.toLinearMap := + rfl + +/-- Two maps with the same matrix are equal. -/ +theorem ext_map (h : Λ₁.map (ι := ι) (κ := κ) = Λ₂.map) : Λ₁ = Λ₂ := + ext (OpMap.toMat_injective h) + +@[simp] +theorem map_HP (Λ : HPOp E F) : (Λ.map (ι := ι) (κ := κ)).IsHermitianPreserving := + (OpMap.isHermitianPreserving_toMat_iff _).mpr Λ.HP + +/-- The Hermitian-preserving map with a given Hermitian-preserving matrix. -/ +def ofMat (M : MatrixMap ι κ ℂ) (hHP : M.IsHermitianPreserving) : HPOp E F where + toLinearMap := OpMap.ofMat E F M + HP := (OpMap.isHermitianPreserving_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hHP) + +@[simp] +theorem map_ofMat (M : MatrixMap ι κ ℂ) (hHP : M.IsHermitianPreserving) : + (ofMat (E := E) (F := F) M hHP).map = M := + OpMap.toMat_ofMat M /-- Two maps are equal if they agree on all Hermitian inputs. -/ -theorem funext_hermitian (h : ∀ M : HermitianMat dIn ℂ, CΛ₁.map M = CΛ₂.map M) : - CΛ₁ = CΛ₂ := by - ext M : 2 +theorem funext_hermitian (h : ∀ M : HermitianMat ι ℂ, Λ₁.map (κ := κ) M = Λ₂.map M) : + Λ₁ = Λ₂ := by + refine ext_map (ι := ι) (κ := κ) ?_ + ext M : 1 have hH := h (realPart M) have hA := h (imaginaryPart M) convert congr($hH + Complex.I • $hA) @@ -111,20 +177,20 @@ theorem funext_hermitian (h : ∀ M : HermitianMat dIn ℂ, CΛ₁.map M = CΛ <;> rfl /-- Two maps are equal if they agree on all positive inputs. -/ -theorem funext_pos [Fintype dIn] (h : ∀ M : HermitianMat dIn ℂ, 0 ≤ M → CΛ₁.map M = CΛ₂.map M) : - CΛ₁ = CΛ₂ := by +theorem funext_pos (h : ∀ M : HermitianMat ι ℂ, 0 ≤ M → Λ₁.map (κ := κ) M = Λ₂.map M) : + Λ₁ = Λ₂ := by classical open scoped HermitianMat in - apply funext_hermitian + apply funext_hermitian (κ := κ) intro M rw [← M.posPart_add_negPart] simp [HermitianMat.posPart_nonneg, HermitianMat.negPart_nonneg, h] /-- Two maps are equal if they agree on all positive inputs with trace one -/ -theorem funext_pos_trace [Fintype dIn] - (h : ∀ M : HermitianMat dIn ℂ, 0 ≤ M → M.trace = 1 → CΛ₁.map M = CΛ₂.map M) : - CΛ₁ = CΛ₂ := by - apply funext_pos +theorem funext_pos_trace + (h : ∀ M : HermitianMat ι ℂ, 0 ≤ M → M.trace = 1 → Λ₁.map (κ := κ) M = Λ₂.map M) : + Λ₁ = Λ₂ := by + apply funext_pos (κ := κ) intro M hM' rcases hM'.eq_or_lt with rfl | hM · simp @@ -141,339 +207,536 @@ theorem funext_pos_trace [Fintype dIn] · apply smul_nonneg (by positivity) hM' · simp [field] -/-- Two maps are equal if they agree on all `MState`s. -/ -theorem funext_mstate [Fintype dIn] [DecidableEq dIn] {Λ₁ Λ₂ : HPMap dIn dOut ℂ} - (h : ∀ ρ : MState dIn, Λ₁.map ρ.m = Λ₂.map ρ.m) : +/-- Two maps are equal if they agree on all states. -/ +theorem funext_mstate (h : ∀ ρ : DensityOp E, Λ₁.map (κ := κ) (ρ.m (ι := ι)) = Λ₂.map ρ.m) : Λ₁ = Λ₂ := - funext_pos_trace fun M hM_pos hM_tr ↦ h ⟨M, hM_pos, hM_tr⟩ + funext_pos_trace (κ := κ) fun M hM_pos hM_tr ↦ by + simpa using h (DensityOp.ofMat M hM_pos hM_tr) /-- Hermitian-preserving maps are functions from `HermitianMat`s to `HermitianMat`s. -/ -noncomputable instance instFunLike : FunLike (HPMap dIn dOut ℂ) (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - coe Λ ρ := ⟨Λ.map ρ.1, Λ.HP ρ.2⟩ +instance instFunLike : FunLike (HPOp E F) (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + coe Λ ρ := ⟨Λ.map ρ.1, Λ.map_HP ρ.2⟩ coe_injective x y h := funext_hermitian fun M ↦ by simpa using congrFun h M -lemma apply_hermitianMat_eq (Λ : HPMap dIn dOut ℂ) (ρ : HermitianMat dIn ℂ) : - Λ ρ = ⟨Λ.map ρ.1, Λ.HP ρ.2⟩ := rfl +/-- **Matrix analogue of applying a Hermitian-preserving map**: the underlying matrix of `Λ T` is +the image of the underlying matrix of `T`. -/ +@[simp] +theorem mat_apply (Λ : HPOp E F) (T : HermitianMat ι ℂ) : + (Λ T : HermitianMat κ ℂ).mat = Λ.map T.mat := + rfl -set_option backward.isDefEq.respectTransparency false in -instance [Fintype dIn] : ContinuousLinearMapClass - (HPMap dIn dOut ℂ) ℝ (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_add f x y := HermitianMat.ext <| LinearMap.map_add f.toLinearMap x y - map_smulₛₗ f c x := HermitianMat.ext <| by simp [apply_hermitianMat_eq] +/-- **Matrix analogue of `HPOp.opApply`**: the matrix of `Λ.opApply A` is the image of the matrix +of `A`. Not a `simp` lemma: the index type of `E`'s preferred basis appears only on the +right-hand side, so `simp` cannot infer it. -/ +theorem toMat_opApply (Λ : HPOp E F) (A : HermitianOp E) : + ((Λ.opApply A).toMat : HermitianMat κ ℂ) = Λ (A.toMat : HermitianMat ι ℂ) := by + refine HermitianMat.ext ?_ + rw [mat_apply, HermitianOp.toMat_mat, HermitianOp.toMat_mat, op_opApply, map_eq, + OpMap.toMat_apply_toMat] + +instance : ContinuousLinearMapClass + (HPOp E F) ℝ (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_add f x y := HermitianMat.ext <| LinearMap.map_add f.map x y + map_smulₛₗ f c x := HermitianMat.ext <| by simp map_continuous f := .subtype_mk (by fun_prop) _ -end HPMap - -variable [Fintype dIn] [Fintype dOut] +end StdBasis -/-! - -## Positive-preserving maps - --/ +end HPOp --Positive-preserving maps: continuous linear order-preserving maps on HermitianMats. -namespace PMap +namespace POp @[ext] -theorem ext {Λ₁ Λ₂ : PMap dIn dOut 𝕜} (h : Λ₁.map = Λ₂.map) : Λ₁ = Λ₂ := by - rw [PMap.mk.injEq] - exact HPMap.ext h +theorem ext {Λ₁ Λ₂ : POp E F} (h : Λ₁.toLinearMap = Λ₂.toLinearMap) : Λ₁ = Λ₂ := by + rw [POp.mk.injEq] + exact HPOp.ext h -theorem injective_toHPMap : (PMap.toHPMap (dIn := dIn) (dOut := dOut) (𝕜 := 𝕜)).Injective := +theorem injective_toHPOp : (POp.toHPOp (E := E) (F := F)).Injective := fun _ _ ↦ (mk.injEq _ _ _ _).mpr +/-- A positive map sends nonnegative operators to nonnegative operators. -/ +theorem opApply_nonneg (Λ : POp E F) {A : HermitianOp E} (h : 0 ≤ A) : 0 ≤ Λ.toHPOp.opApply A := + Λ.pos h + +section StdBasis + +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] + +/-- **Matrix analogue of positivity**: the matrix map is positive. -/ +@[simp] +theorem map_pos (Λ : POp E F) : (Λ.map (ι := ι) (κ := κ)).IsPositive := + (OpMap.isPositive_toMat_iff _).mpr Λ.pos + +/-- The positive map with a given positive matrix. -/ +def ofMat (M : MatrixMap ι κ ℂ) (hpos : M.IsPositive) : POp E F where + toLinearMap := OpMap.ofMat E F M + pos := (OpMap.isPositive_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hpos) + +@[simp] +theorem map_ofMat (M : MatrixMap ι κ ℂ) (hpos : M.IsPositive) : + (ofMat (E := E) (F := F) M hpos).map = M := + OpMap.toMat_ofMat M + /-- Positive maps are functions from `HermitianMat`s to `HermitianMat`s. -/ -noncomputable instance instFunLike : FunLike (PMap dIn dOut ℂ) (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - coe := DFunLike.coe ∘ toHPMap - coe_injective := DFunLike.coe_injective.comp injective_toHPMap +instance instFunLike : FunLike (POp E F) (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + coe := DFunLike.coe ∘ toHPOp + coe_injective := DFunLike.coe_injective.comp injective_toHPOp -lemma apply_hermitianMat_eq (Λ : PMap dIn dOut ℂ) (ρ : HermitianMat dIn ℂ) : - Λ ρ = ⟨Λ.map ρ.1, Λ.HP ρ.2⟩ := rfl +/-- **Matrix analogue of applying a positive map**: the underlying matrix of `Λ T` is +the image of the underlying matrix of `T`. -/ +@[simp] +theorem mat_apply (Λ : POp E F) (T : HermitianMat ι ℂ) : + (Λ T : HermitianMat κ ℂ).mat = Λ.map T.mat := + rfl -set_option backward.isDefEq.respectTransparency false in set_option synthInstance.maxHeartbeats 40000 in -instance instLinearMapClass : LinearMapClass (PMap dIn dOut ℂ) ℝ (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_add f x y := HermitianMat.ext <| LinearMap.map_add f.toLinearMap x y - map_smulₛₗ f c x := HermitianMat.ext <| by simp [apply_hermitianMat_eq] - -instance instContinuousOrderHomClass : ContinuousOrderHomClass (PMap dIn dOut ℂ) - (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_continuous f := ContinuousMapClass.map_continuous f.toHPMap +instance instLinearMapClass : + LinearMapClass (POp E F) ℝ (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_add f x y := HermitianMat.ext <| LinearMap.map_add f.map x y + map_smulₛₗ f c x := HermitianMat.ext <| by simp + +instance instContinuousOrderHomClass : ContinuousOrderHomClass (POp E F) + (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_continuous f := ContinuousMapClass.map_continuous f.toHPOp map_monotone f x y h := by - have h1 := f.pos h - simp_all only [HermitianMat.val_eq_coe, map_sub, ge_iff_le] - exact h1 + rw [HermitianMat.le_iff] at h ⊢ + have hpos := f.map_pos (ι := ι) (κ := κ) h + rwa [HermitianMat.mat_sub, mat_apply, mat_apply, ← map_sub, ← HermitianMat.mat_sub] -/-- Positive-presering maps also preserve positivity on, specifically, Hermitian matrices. -/ +/-- Positive maps also preserve positivity on, specifically, Hermitian matrices. -/ @[simp] -theorem pos_Hermitian (M : PMap dIn dOut ℂ) {x : HermitianMat dIn ℂ} (h : 0 ≤ x) : 0 ≤ M x := by +theorem pos_Hermitian (M : POp E F) {x : HermitianMat ι ℂ} (h : 0 ≤ x) : + 0 ≤ (M x : HermitianMat κ ℂ) := by simpa only [map_zero] using ContinuousOrderHomClass.map_monotone M h -end PMap +end StdBasis -namespace CPMap +end POp -def of_kraus_CPMap {κ : Type*} [Fintype κ] [DecidableEq dIn] (M : κ → Matrix dOut dIn 𝕜) : CPMap dIn dOut 𝕜 where - toLinearMap := MatrixMap.of_kraus M M - cp := MatrixMap.of_kraus_isCompletelyPositive M +namespace CPOp -end CPMap +@[ext] +theorem ext {Λ₁ Λ₂ : CPOp E F} (h : Λ₁.toLinearMap = Λ₂.toLinearMap) : Λ₁ = Λ₂ := by + rw [CPOp.mk.injEq] + exact POp.ext h -/-! +section StdBasis -## Positive trace-preserving maps +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] + +/-- **Matrix analogue of complete positivity**: the matrix map is completely positive. -/ +@[simp] +theorem map_cp (Λ : CPOp E F) : (Λ.map (ι := ι) (κ := κ)).IsCompletelyPositive := + (OpMap.isCompletelyPositive_toMat_iff _).mpr Λ.cp + +/-- The completely positive map with a given completely positive matrix. -/ +def ofMat (M : MatrixMap ι κ ℂ) (hcp : M.IsCompletelyPositive) : CPOp E F where + toLinearMap := OpMap.ofMat E F M + cp := (OpMap.isCompletelyPositive_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hcp) + +@[simp] +theorem map_ofMat (M : MatrixMap ι κ ℂ) (hcp : M.IsCompletelyPositive) : + (ofMat (E := E) (F := F) M hcp).map = M := + OpMap.toMat_ofMat M + +/-- The completely positive map with the given Kraus operators. -/ +def of_kraus_CPMap {ν : Type*} [Fintype ν] (M : ν → Matrix κ ι ℂ) : CPOp E F := + ofMat (MatrixMap.of_kraus M M) (MatrixMap.of_kraus_isCompletelyPositive M) + +/-- **Matrix analogue of `CPOp.of_kraus_CPMap`**: its matrix is the Kraus-operator sum. -/ +@[simp] +theorem map_of_kraus_CPMap {ν : Type*} [Fintype ν] (M : ν → Matrix κ ι ℂ) : + (of_kraus_CPMap (E := E) (F := F) M).map = MatrixMap.of_kraus M M := + map_ofMat _ _ + +end StdBasis + +end CPOp --/ --Positive trace-preserving maps: -- * Continuous linear order-preserving maps on HermitianMats. --- * Continuous maps on MStates. -namespace PTPMap +-- * Continuous maps on states. +namespace PTPOp @[ext] -theorem ext {Λ₁ Λ₂ : PTPMap dIn dOut 𝕜} (h : Λ₁.map = Λ₂.map) : Λ₁ = Λ₂ := by - rw [PTPMap.mk.injEq] - exact PMap.ext h +theorem ext {Λ₁ Λ₂ : PTPOp E F} (h : Λ₁.toLinearMap = Λ₂.toLinearMap) : Λ₁ = Λ₂ := by + rw [PTPOp.mk.injEq] + exact POp.ext h -theorem injective_toPMap : (PTPMap.toPMap (dIn := dIn) (dOut := dOut) (𝕜 := 𝕜)).Injective := +theorem injective_toPOp : (PTPOp.toPOp (E := E) (F := F)).Injective := fun _ _ ↦ (mk.injEq _ _ _ _).mpr +/-- A trace-preserving map preserves the trace of a self-adjoint operator. -/ +theorem trace_opApply (Λ : PTPOp E F) (A : HermitianOp E) : + (Λ.toHPOp.opApply A).trace = A.trace := + congrArg RCLike.re (Λ.TP A.op) + +section StdBasis + +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] + +/-- **Matrix analogue of trace preservation**: the matrix map is trace-preserving. -/ +@[simp] +theorem map_TP (Λ : PTPOp E F) : (Λ.map (ι := ι) (κ := κ)).IsTracePreserving := + (OpMap.isTracePreserving_toMat_iff _).mpr Λ.TP + +/-- The positive trace-preserving map with a given positive, trace-preserving matrix. -/ +def ofMat (M : MatrixMap ι κ ℂ) (hpos : M.IsPositive) (hTP : M.IsTracePreserving) : PTPOp E F where + toLinearMap := OpMap.ofMat E F M + pos := (OpMap.isPositive_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hpos) + TP := (OpMap.isTracePreserving_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hTP) + +@[simp] +theorem map_ofMat (M : MatrixMap ι κ ℂ) (hpos : M.IsPositive) (hTP : M.IsTracePreserving) : + (ofMat (E := E) (F := F) M hpos hTP).map = M := + OpMap.toMat_ofMat M + /-- Positive trace-preserving maps are functions from `HermitianMat`s to `HermitianMat`s. -/ -noncomputable instance instFunLike : FunLike (PTPMap dIn dOut ℂ) (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - coe := DFunLike.coe ∘ toPMap - coe_injective := DFunLike.coe_injective.comp injective_toPMap +instance instFunLike : FunLike (PTPOp E F) (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + coe := DFunLike.coe ∘ toPOp + coe_injective := DFunLike.coe_injective.comp injective_toPOp -lemma apply_hermitianMat_eq_toPMap (Λ : PTPMap dIn dOut ℂ) (ρ : HermitianMat dIn ℂ) : - Λ ρ = Λ.toPMap ρ := rfl +/-- **Matrix analogue of applying a positive trace-preserving map**: the underlying matrix of `Λ T` is +the image of the underlying matrix of `T`. -/ +@[simp] +theorem mat_apply (Λ : PTPOp E F) (T : HermitianMat ι ℂ) : + (Λ T : HermitianMat κ ℂ).mat = Λ.map T.mat := + rfl -instance instLinearMapClass : LinearMapClass (PTPMap dIn dOut ℂ) ℝ (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_add f x y := by simp [apply_hermitianMat_eq_toPMap] - map_smulₛₗ f c x := by simp [apply_hermitianMat_eq_toPMap] +instance instLinearMapClass : + LinearMapClass (PTPOp E F) ℝ (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_add f x y := HermitianMat.ext <| LinearMap.map_add f.map x y + map_smulₛₗ f c x := HermitianMat.ext <| by simp -instance instHContinuousOrderHomClass : ContinuousOrderHomClass (PTPMap dIn dOut ℂ) - (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_continuous f := ContinuousMapClass.map_continuous f.toPMap +instance instHContinuousOrderHomClass : ContinuousOrderHomClass (PTPOp E F) + (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_continuous f := ContinuousMapClass.map_continuous f.toPOp map_monotone f x y h := by - have := f.pos h - simp_all only [HermitianMat.val_eq_coe, map_sub, ge_iff_le] - exact this + rw [HermitianMat.le_iff] at h ⊢ + have hpos := f.map_pos (ι := ι) (κ := κ) h + rwa [HermitianMat.mat_sub, mat_apply, mat_apply, ← map_sub, ← HermitianMat.mat_sub] /-- PTP maps also preserve positivity on Hermitian matrices. -/ @[simp] -theorem pos_Hermitian (M : PTPMap dIn dOut ℂ) {x : HermitianMat dIn ℂ} (h : 0 ≤ x) : 0 ≤ M x := by +theorem pos_Hermitian (M : PTPOp E F) {x : HermitianMat ι ℂ} (h : 0 ≤ x) : + 0 ≤ (M x : HermitianMat κ ℂ) := by simpa only [map_zero] using ContinuousOrderHomClass.map_monotone M h -/-- `PTPMap`s are functions from `MState`s to `MState`s. -/ -noncomputable instance instMFunLike [DecidableEq dIn] [DecidableEq dOut] : - FunLike (PTPMap dIn dOut) (MState dIn) (MState dOut) where - coe Λ ρ := MState.mk - (Λ.toHPMap ρ.M) (HermitianMat.zero_le_iff.mpr (Λ.pos ρ.psd)) (by - rw [HermitianMat.trace_eq_one_iff, ← ρ.tr'] - exact Λ.TP ρ) - coe_injective x y h := injective_toPMap <| PMap.injective_toHPMap <| - HPMap.funext_mstate fun ρ ↦ by - have := congr($h ρ); - rwa [MState.ext_iff, HermitianMat.ext_iff] at this - -lemma apply_mstate_eq [DecidableEq dIn] [DecidableEq dOut] (Λ : PTPMap dIn dOut ℂ) (ρ : MState dIn) : - Λ ρ = MState.mk - (Λ.toHPMap ρ.M) (HermitianMat.zero_le_iff.mpr (Λ.pos ρ.psd)) (by - rw [HermitianMat.trace_eq_one_iff, ← ρ.tr'] - exact Λ.TP ρ) := rfl - -instance instMContinuousMapClass [DecidableEq dIn] [DecidableEq dOut] : - ContinuousMapClass (PTPMap dIn dOut) (MState dIn) (MState dOut) where - map_continuous f := by - rw [continuous_induced_rng] - exact (map_continuous f.toHPMap).comp MState.Continuous_HermitianMat - --- @[norm_cast] -theorem val_apply_MState [DecidableEq dIn] (M : PTPMap dIn dOut) (ρ : MState dIn) : - (M ρ : HermitianMat dOut ℂ) = (instFunLike.coe M) ρ := by +end StdBasis + +/-- The action of a positive trace-preserving map on states: it carries a positive unit-trace +operator to another one, with no choice of basis anywhere. `PTPOp.instMFunLike` installs this as +the `FunLike` coercion, so it is normally written `Λ ρ`. -/ +def applyState (Λ : PTPOp E F) (ρ : DensityOp E) : DensityOp F where + op := Λ.toHPOp.opApply ρ.op + op_nonneg := POp.opApply_nonneg Λ.toPOp ρ.op_nonneg + op_trace := (Λ.trace_opApply ρ.op).trans ρ.op_trace + +@[simp] +theorem op_applyState (Λ : PTPOp E F) (ρ : DensityOp E) : + (Λ.applyState ρ).op = Λ.toHPOp.opApply ρ.op := rfl +/-- **Matrix analogue of `PTPOp.applyState`**: the density matrix of `Λ.applyState ρ` is the image +of the density matrix of `ρ`. Not a `simp` lemma, for the same reason as +`HPOp.toMat_opApply`. -/ +theorem M_applyState [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] + [StdBasis ℂ F κ] (Λ : PTPOp E F) (ρ : DensityOp E) : + ((Λ.applyState ρ).M : HermitianMat κ ℂ) = instFunLike.coe Λ (ρ.M : HermitianMat ι ℂ) := + HPOp.toMat_opApply Λ.toHPOp ρ.op + +/-- `PTPOp`s are functions from states to states, through `PTPOp.applyState`. -/ +instance (priority := 1100) instMFunLike : FunLike (PTPOp E F) (DensityOp E) (DensityOp F) where + coe := applyState + coe_injective x y h := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + refine injective_toPOp (POp.injective_toHPOp (HPOp.funext_mstate fun ρ ↦ ?_)) + have h₂ := congrArg (fun σ : DensityOp F ↦ σ.M) (congr($h ρ)) + rw [M_applyState, M_applyState] at h₂ + simpa using congrArg HermitianMat.mat h₂ + +@[simp] +theorem op_apply_MState (Λ : PTPOp E F) (ρ : DensityOp E) : + (Λ ρ : DensityOp F).op = Λ.toHPOp.opApply ρ.op := + rfl + +/-- Two positive trace-preserving maps are equal exactly when they agree on every state. -/ +theorem funext_iff {Λ₁ Λ₂ : PTPOp E F} : Λ₁ = Λ₂ ↔ ∀ ρ : DensityOp E, Λ₁ ρ = Λ₂ ρ := + DFunLike.ext_iff + +/-- **Matrix analogue of applying a positive trace-preserving map to a state**: the density matrix +of `Λ ρ` is the image of the density matrix of `ρ`. Not a `simp` lemma, for the same reason as +`HPOp.toMat_opApply`. -/ +theorem M_apply_MState [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] + [StdBasis ℂ F κ] (Λ : PTPOp E F) (ρ : DensityOp E) : + ((Λ ρ : DensityOp F).M : HermitianMat κ ℂ) = + instFunLike.coe Λ (ρ.M : HermitianMat ι ℂ) := + M_applyState Λ ρ + --If we have a PTPMap, the input and output dimensions are always both nonempty (otherwise --we can't preserve trace) - or they're both empty. So `[Nonempty dIn]` will always suffice. -- This would be nice as an `instance` but that would leave `dIn` as a metavariable. -theorem nonemptyOut (Λ : PTPMap dIn dOut) [hIn : Nonempty dIn] [DecidableEq dIn] : Nonempty dOut := by +theorem nonemptyOut {dIn dOut : Type*} [Fintype dIn] [DecidableEq dIn] [Fintype dOut] + [DecidableEq dOut] (Λ : PTPMap dIn dOut) [hIn : Nonempty dIn] : Nonempty dOut := by by_contra h simp only [not_nonempty_iff] at h let M := (1 : Matrix dIn dIn ℂ) have := calc (Finset.univ.card (α := dIn) : ℂ) _ = M.trace := by simp [Matrix.trace, M] - _ = (Λ.map M).trace := (Λ.TP M).symm + _ = (Λ.map M).trace := (Λ.map_TP M).symm _ = 0 := by simp only [Matrix.trace_eq_zero_of_isEmpty] norm_num [Finset.univ_eq_empty_iff] at this -end PTPMap - -/-! - -## Completely positive trace-preserving linear maps +end PTPOp --/ - -namespace CPTPMap -variable [DecidableEq dIn] +namespace CPTPOp -/-- Two `CPTPMap`s are equal if their `MatrixMap`s are equal. -/ +/-- Two `CPTPOp`s are equal if their `OpMap`s are equal. -/ @[ext] -theorem ext {Λ₁ Λ₂ : CPTPMap dIn dOut 𝕜} (h : Λ₁.map = Λ₂.map) : Λ₁ = Λ₂ := by - rw [CPTPMap.mk.injEq] - exact PTPMap.ext h +theorem ext {Λ₁ Λ₂ : CPTPOp E F} (h : Λ₁.toLinearMap = Λ₂.toLinearMap) : Λ₁ = Λ₂ := by + rw [CPTPOp.mk.injEq] + exact PTPOp.ext h -theorem injective_toPTPMap : (CPTPMap.toPTPMap (dIn := dIn) (dOut := dOut) (𝕜 := 𝕜)).Injective := +theorem injective_toPTPOp : (CPTPOp.toPTPOp (E := E) (F := F)).Injective := fun _ _ ↦ (mk.injEq _ _ _ _).mpr --- /-- Positive trace-preserving maps are functions from `HermitianMat`s to `HermitianMat`s. -/ --- instance instFunLike : FunLike (CPTPMap dIn dOut 𝕜) (HermitianMat dIn 𝕜) (HermitianMat dOut 𝕜) where --- coe := DFunLike.coe ∘ toPTPMap --- coe_injective' := DFunLike.coe_injective'.comp injective_toPTPMap +/-- `CPTPOp`s are functions from states to states. -/ +instance (priority := 1100) instMFunLike : + FunLike (CPTPOp E F) (DensityOp E) (DensityOp F) where + coe := DFunLike.coe ∘ toPTPOp + coe_injective := DFunLike.coe_injective.comp injective_toPTPOp + +theorem apply_eq_toPTPOp (Λ : CPTPOp E F) (ρ : DensityOp E) : + (Λ ρ : DensityOp F) = Λ.toPTPOp ρ := + rfl --- set_option synthInstance.maxHeartbeats 40000 in --- instance instLinearMapClass : LinearMapClass (CPTPMap dIn dOut 𝕜) ℝ (HermitianMat dIn 𝕜) (HermitianMat dOut 𝕜) where --- map_add f x y := by simp [instFunLike] --- map_smulₛₗ f c x := by simp [instFunLike] +section StdBasis --- instance instContinuousOrderHomClass : ContinuousOrderHomClass (CPTPMap dIn dOut 𝕜) --- (HermitianMat dIn 𝕜) (HermitianMat dOut 𝕜) where --- map_continuous f := ContinuousMapClass.map_continuous f.toPMap --- map_monotone f x y h := by - -- simpa using f.pos h +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] --- /-- PTP maps also preserve positivity on Hermitian matrices. -/ --- @[simp] --- theorem pos_Hermitian (M : CPTPMap dIn dOut 𝕜) {x : HermitianMat dIn 𝕜} (h : 0 ≤ x) : 0 ≤ M x := by --- simpa only [map_zero] using ContinuousOrderHomClass.map_monotone M h +/-- Two maps with the same matrix are equal. -/ +theorem ext_map {Λ₁ Λ₂ : CPTPOp E F} (h : Λ₁.map (ι := ι) (κ := κ) = Λ₂.map) : Λ₁ = Λ₂ := + ext (OpMap.toMat_injective h) -/-- `CPTPMap`s are functions from `MState`s to `MState`s. -/ -noncomputable instance instMFunLike [DecidableEq dOut] : FunLike (CPTPMap dIn dOut) (MState dIn) (MState dOut) where - coe := DFunLike.coe ∘ toPTPMap - coe_injective := DFunLike.coe_injective.comp injective_toPTPMap +/-- Two channels are equal exactly when they agree on every state. -/ +theorem funext_iff {Λ₁ Λ₂ : CPTPOp E F} : Λ₁ = Λ₂ ↔ ∀ ρ : DensityOp E, Λ₁ ρ = Λ₂ ρ := + DFunLike.ext_iff -lemma apply_mState_eq_toPTPMap [DecidableEq dOut] (Λ : CPTPMap dIn dOut) (ρ : MState dIn) : - Λ ρ = Λ.toPTPMap ρ := rfl +@[simp] +theorem IsTracePreserving (Λ : CPTPOp E F) : (Λ.map (ι := ι) (κ := κ)).IsTracePreserving := + Λ.map_TP --- @[norm_cast] --- theorem val_apply_MState [DecidableEq dOut] (M : CPTPMap dIn dOut) (ρ : MState dIn) : --- (M ρ : HermitianMat dOut ℂ) = (instFunLike.coe M) ρ := by --- rfl +/-- The channel with a given completely positive, trace-preserving matrix. -/ +def ofMat (M : MatrixMap ι κ ℂ) (hcp : M.IsCompletelyPositive) (hTP : M.IsTracePreserving) : + CPTPOp E F where + toLinearMap := OpMap.ofMat E F M + cp := (OpMap.isCompletelyPositive_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hcp) + TP := (OpMap.isTracePreserving_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hTP) @[simp] -theorem IsTracePreserving (Λ : CPTPMap dIn dOut 𝕜) : Λ.map.IsTracePreserving := - Λ.TP +theorem map_ofMat (M : MatrixMap ι κ ℂ) (hcp : M.IsCompletelyPositive) + (hTP : M.IsTracePreserving) : (ofMat (E := E) (F := F) M hcp hTP).map = M := + OpMap.toMat_ofMat M + +variable (ι κ) in +/-- Read a channel between `E` and `F` as a channel between the Euclidean spaces indexed by their +preferred bases. The matrix of the channel is unchanged (`map_transport`), and it acts on +transported states in the transported way (`transport_apply`). -/ +def transport (Λ : CPTPOp E F) : CPTPMap ι κ := + ofMat Λ.map ((OpMap.isCompletelyPositive_toMat_iff _).mpr Λ.cp) Λ.map_TP + +/-- **Matrix analogue of `CPTPOp.transport`**: the matrix of the channel is unchanged. -/ +@[simp] +theorem map_transport (Λ : CPTPOp E F) : (Λ.transport ι κ).map = Λ.map (ι := ι) (κ := κ) := + map_ofMat _ _ _ -def of_kraus_CPTPMap {κ : Type*} [Fintype κ] - (M : κ → Matrix dOut dIn 𝕜) - (hTP : (∑ k, (M k).conjTranspose * (M k)) = 1) : CPTPMap dIn dOut 𝕜 where - toLinearMap := MatrixMap.of_kraus M M - cp := MatrixMap.of_kraus_isCompletelyPositive M - TP := MatrixMap.IsTracePreserving.of_kraus_isTracePreserving M M hTP +/-- Transporting a channel to the Euclidean spaces of its preferred bases commutes with +transporting the states it acts on. -/ +@[simp] +theorem transport_apply (Λ : CPTPOp E F) (ρ : MState ι) : + Λ.transport ι κ ρ = (Λ (ρ.transport E)).transport (EuclideanSpace ℂ κ) := by + refine DensityOp.ext (ι := κ) ?_ + rw [DensityOp.M_transport, apply_eq_toPTPOp, apply_eq_toPTPOp, PTPOp.M_apply_MState, + PTPOp.M_apply_MState, DensityOp.M_transport] + refine HermitianMat.ext ?_ + rw [PTPOp.mat_apply, PTPOp.mat_apply, map_transport] + +/-- The channel with the given Kraus operators. -/ +def of_kraus_CPTPMap {ν : Type*} [Fintype ν] (M : ν → Matrix κ ι ℂ) + (hTP : (∑ k, (M k).conjTranspose * (M k)) = 1) : CPTPOp E F := + ofMat (MatrixMap.of_kraus M M) (MatrixMap.of_kraus_isCompletelyPositive M) + (MatrixMap.IsTracePreserving.of_kraus_isTracePreserving M M hTP) + +/-- **Matrix analogue of `CPTPOp.of_kraus_CPTPMap`**: its matrix is the Kraus-operator sum. -/ +@[simp] +theorem map_of_kraus_CPTPMap {ν : Type*} [Fintype ν] (M : ν → Matrix κ ι ℂ) + (hTP : (∑ k, (M k).conjTranspose * (M k)) = 1) : + (of_kraus_CPTPMap (E := E) (F := F) M hTP).map = MatrixMap.of_kraus M M := + map_ofMat _ _ _ + +end StdBasis -end CPTPMap +end CPTPOp -namespace PUMap -variable [DecidableEq dIn] [DecidableEq dOut] +namespace PUOp @[ext] -theorem ext {Λ₁ Λ₂ : PUMap dIn dOut 𝕜} (h : Λ₁.map = Λ₂.map) : Λ₁ = Λ₂ := by - rw [PUMap.mk.injEq] - exact PMap.ext h +theorem ext {Λ₁ Λ₂ : PUOp E F} (h : Λ₁.toLinearMap = Λ₂.toLinearMap) : Λ₁ = Λ₂ := by + rw [PUOp.mk.injEq] + exact POp.ext h -theorem injective_toPMap : (PUMap.toPMap (dIn := dIn) (dOut := dOut) (𝕜 := 𝕜)).Injective := by +theorem injective_toPOp : (PUOp.toPOp (E := E) (F := F)).Injective := by intro _ _ _ - rwa [PUMap.mk.injEq] + rwa [PUOp.mk.injEq] + +section StdBasis -/-- `PUMap`s are functions from `HermitianMat`s to `HermitianMat`s. -/ -noncomputable instance instFunLike : FunLike (PUMap dIn dOut ℂ) (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - coe Λ := Λ.toPMap - coe_injective := (DFunLike.coe_injective (F := PMap dIn dOut ℂ)).comp injective_toPMap +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] -lemma apply_hermitianMat_eq_toPMap (Λ : PUMap dIn dOut ℂ) (ρ : HermitianMat dIn ℂ) : - Λ ρ = Λ.toPMap ρ := rfl +/-- **Matrix analogue of unitality**: the matrix map is unital. -/ +@[simp] +theorem map_unital (Λ : PUOp E F) : (Λ.map (ι := ι) (κ := κ)).Unital := + (OpMap.unital_toMat_iff _).mpr Λ.unital + +/-- The positive unital map with a given positive, unital matrix. -/ +def ofMat (M : MatrixMap ι κ ℂ) (hpos : M.IsPositive) (hu : M.Unital) : PUOp E F where + toLinearMap := OpMap.ofMat E F M + pos := (OpMap.isPositive_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hpos) + unital := (OpMap.unital_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hu) + +@[simp] +theorem map_ofMat (M : MatrixMap ι κ ℂ) (hpos : M.IsPositive) (hu : M.Unital) : + (ofMat (E := E) (F := F) M hpos hu).map = M := + OpMap.toMat_ofMat M -instance instLinearMapClass : LinearMapClass (PUMap dIn dOut ℂ) ℝ (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_add f x y := HermitianMat.ext <| LinearMap.map_add f.toLinearMap x y - map_smulₛₗ f c x := HermitianMat.ext <| by simp [apply_hermitianMat_eq_toPMap] +/-- `PUOp`s are functions from `HermitianMat`s to `HermitianMat`s. -/ +instance instFunLike : FunLike (PUOp E F) (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + coe Λ := Λ.toPOp + coe_injective := (DFunLike.coe_injective (F := POp E F)).comp injective_toPOp + +/-- **Matrix analogue of applying a positive unital map**: the underlying matrix of `Λ T` is +the image of the underlying matrix of `T`. -/ +@[simp] +theorem mat_apply (Λ : PUOp E F) (T : HermitianMat ι ℂ) : + (Λ T : HermitianMat κ ℂ).mat = Λ.map T.mat := + rfl -instance instHContinuousOrderHomClass : ContinuousOrderHomClass (PUMap dIn dOut ℂ) - (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_continuous f := ContinuousMapClass.map_continuous f.toPMap +instance instLinearMapClass : + LinearMapClass (PUOp E F) ℝ (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_add f x y := HermitianMat.ext <| LinearMap.map_add f.map x y + map_smulₛₗ f c x := HermitianMat.ext <| by simp + +instance instHContinuousOrderHomClass : ContinuousOrderHomClass (PUOp E F) + (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_continuous f := ContinuousMapClass.map_continuous f.toPOp map_monotone f x y h := by - have := f.pos h - simp_all only [HermitianMat.val_eq_coe, map_sub, ge_iff_le] - exact this + rw [HermitianMat.le_iff] at h ⊢ + have hpos := f.map_pos (ι := ι) (κ := κ) h + rwa [HermitianMat.mat_sub, mat_apply, mat_apply, ← map_sub, ← HermitianMat.mat_sub] -instance instOneHomClass : OneHomClass (PUMap dIn dOut ℂ) - (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_one f := HermitianMat.ext (f.unital) +instance instOneHomClass : OneHomClass (PUOp E F) + (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_one f := HermitianMat.ext (f.map_unital (ι := ι) (κ := κ)) -/-- CPTP maps also preserve positivity on Hermitian matrices. -/ +/-- Positive unital maps also preserve positivity on Hermitian matrices. -/ @[simp] -theorem pos_Hermitian (M : PUMap dIn dOut ℂ) {x : HermitianMat dIn ℂ} (h : 0 ≤ x) : 0 ≤ M x := by +theorem pos_Hermitian (M : PUOp E F) {x : HermitianMat ι ℂ} (h : 0 ≤ x) : + 0 ≤ (M x : HermitianMat κ ℂ) := by simpa only [map_zero] using ContinuousOrderHomClass.map_monotone M h -end PUMap +end StdBasis + +end PUOp -namespace CPUMap -variable [DecidableEq dIn] [DecidableEq dOut] +namespace CPUOp @[ext] -theorem ext {Λ₁ Λ₂ : CPUMap dIn dOut 𝕜} (h : Λ₁.map = Λ₂.map) : Λ₁ = Λ₂ := by - rw [CPUMap.mk.injEq, CPMap.mk.injEq] - exact PMap.ext h +theorem ext {Λ₁ Λ₂ : CPUOp E F} (h : Λ₁.toLinearMap = Λ₂.toLinearMap) : Λ₁ = Λ₂ := by + rw [CPUOp.mk.injEq, CPOp.mk.injEq] + exact POp.ext h -theorem injective_toPMap : (CPMap.toPMap ∘ CPUMap.toCPMap (dIn := dIn) (dOut := dOut) (𝕜 := 𝕜)).Injective := by +theorem injective_toPOp : (CPOp.toPOp ∘ CPUOp.toCPOp (E := E) (F := F)).Injective := by intro _ _ _ - rwa [CPUMap.mk.injEq, CPMap.mk.injEq] + rwa [CPUOp.mk.injEq, CPOp.mk.injEq] -/-- `CPUMap`s are functions from `HermitianMat`s to `HermitianMat`s. -/ -noncomputable instance instFunLike : FunLike (CPUMap dIn dOut ℂ) (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - coe Λ := Λ.toPMap - coe_injective := (DFunLike.coe_injective (F := PMap dIn dOut ℂ)).comp injective_toPMap +section StdBasis -lemma apply_hermitianMat_eq_toPMap (Λ : CPUMap dIn dOut ℂ) (ρ : HermitianMat dIn ℂ) : - Λ ρ = Λ.toPMap ρ := rfl +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] + +/-- **Matrix analogue of unitality**: the matrix map is unital. -/ +@[simp] +theorem map_unital (Λ : CPUOp E F) : (Λ.map (ι := ι) (κ := κ)).Unital := + (OpMap.unital_toMat_iff _).mpr Λ.unital + +/-- The completely positive unital map with a given completely positive, unital matrix. -/ +def ofMat (M : MatrixMap ι κ ℂ) (hcp : M.IsCompletelyPositive) (hu : M.Unital) : CPUOp E F where + toLinearMap := OpMap.ofMat E F M + cp := (OpMap.isCompletelyPositive_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hcp) + unital := (OpMap.unital_toMat_iff (ι := ι) (κ := κ) _).mp (by simpa using hu) + +@[simp] +theorem map_ofMat (M : MatrixMap ι κ ℂ) (hcp : M.IsCompletelyPositive) (hu : M.Unital) : + (ofMat (E := E) (F := F) M hcp hu).map = M := + OpMap.toMat_ofMat M -instance instLinearMapClass : LinearMapClass (CPUMap dIn dOut ℂ) ℝ (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_add f x y := HermitianMat.ext <| LinearMap.map_add f.toLinearMap x y - map_smulₛₗ f c x := HermitianMat.ext <| by simp [apply_hermitianMat_eq_toPMap] +/-- `CPUOp`s are functions from `HermitianMat`s to `HermitianMat`s. -/ +instance instFunLike : FunLike (CPUOp E F) (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + coe Λ := Λ.toPOp + coe_injective := (DFunLike.coe_injective (F := POp E F)).comp injective_toPOp -instance instHContinuousOrderHomClass : ContinuousOrderHomClass (CPUMap dIn dOut ℂ) - (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_continuous f := ContinuousMapClass.map_continuous f.toPMap +/-- **Matrix analogue of applying a completely positive unital map**: the underlying matrix of `Λ T` is +the image of the underlying matrix of `T`. -/ +@[simp] +theorem mat_apply (Λ : CPUOp E F) (T : HermitianMat ι ℂ) : + (Λ T : HermitianMat κ ℂ).mat = Λ.map T.mat := + rfl + +instance instLinearMapClass : + LinearMapClass (CPUOp E F) ℝ (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_add f x y := HermitianMat.ext <| LinearMap.map_add f.map x y + map_smulₛₗ f c x := HermitianMat.ext <| by simp + +instance instHContinuousOrderHomClass : ContinuousOrderHomClass (CPUOp E F) + (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_continuous f := ContinuousMapClass.map_continuous f.toPOp map_monotone f x y h := by - have := f.pos h - simp_all only [HermitianMat.val_eq_coe, map_sub, ge_iff_le] - exact this + rw [HermitianMat.le_iff] at h ⊢ + have hpos := f.map_pos (ι := ι) (κ := κ) h + rwa [HermitianMat.mat_sub, mat_apply, mat_apply, ← map_sub, ← HermitianMat.mat_sub] -instance instOneHomClass : OneHomClass (CPUMap dIn dOut ℂ) - (HermitianMat dIn ℂ) (HermitianMat dOut ℂ) where - map_one f := HermitianMat.ext (f.unital) +instance instOneHomClass : OneHomClass (CPUOp E F) + (HermitianMat ι ℂ) (HermitianMat κ ℂ) where + map_one f := HermitianMat.ext (f.map_unital (ι := ι) (κ := κ)) -/-- CPTP maps also preserve positivity on Hermitian matrices. -/ +/-- Completely positive unital maps also preserve positivity on Hermitian matrices. -/ @[simp] -theorem pos_Hermitian (M : CPUMap dIn dOut ℂ) {x : HermitianMat dIn ℂ} (h : 0 ≤ x) : 0 ≤ M x := by +theorem pos_Hermitian (M : CPUOp E F) {x : HermitianMat ι ℂ} (h : 0 ≤ x) : + 0 ≤ (M x : HermitianMat κ ℂ) := by simpa only [map_zero] using ContinuousOrderHomClass.map_monotone M h -end CPUMap +end StdBasis + +end CPUOp --Tests to make sure that our `simp`s and classes are all working like we want them too section test -variable [DecidableEq dIn] [DecidableEq dOut] + +variable {dIn dOut : Type*} [Fintype dIn] [DecidableEq dIn] [Fintype dOut] [DecidableEq dOut] #guard_msgs in -example (M : HPMap dIn dOut ℂ) : (M (Real.pi • 1)) = Real.pi • M 1 := by simp +example (M : HPMap dIn dOut) : (M (Real.pi • 1)) = Real.pi • M 1 := by simp #guard_msgs in -example (M : PTPMap dIn dOut ℂ) : (M.toHPMap (Real.pi • 1)) = Real.pi • M.toHPMap 1 := by simp +example (M : PTPMap dIn dOut) : (M.toHPOp (Real.pi • 1)) = Real.pi • M.toHPOp 1 := by simp #guard_msgs in -example (M : CPTPMap dIn dOut 𝕜) (ρ : Matrix dIn dIn 𝕜) : (M.map ρ).trace = ρ.trace := by simp +example (M : CPTPMap dIn dOut) (ρ : Matrix dIn dIn ℂ) : (M.map ρ).trace = ρ.trace := by simp #guard_msgs in -example (M : CPUMap dIn dOut ℂ) (T : HermitianMat dIn ℂ) : M (1 + 2 • T) = 1 + 2 • M T := by simp +example (M : CPUMap dIn dOut) (T : HermitianMat dIn ℂ) : M (1 + 2 • T) = 1 + 2 • M T := by simp end test diff --git a/QuantumInfo/Channels/CPTP.lean b/QuantumInfo/Channels/CPTP.lean index 27578e118..55e7118b7 100644 --- a/QuantumInfo/Channels/CPTP.lean +++ b/QuantumInfo/Channels/CPTP.lean @@ -10,11 +10,10 @@ public import QuantumInfo.Operators.Unitary /-! # Completely Positive Trace Preserving maps -A `CPTPMap` is a `ℂ`-linear map between matrices (`MatrixMap` is an alias), bundled with the facts -that it `IsCompletelyPositive` and `IsTracePreserving`. CPTP maps are typically regarded as the -"most general quantum operation", as they map density matrices (`MState`s) to density matrices. The -type `PTPMap`, for maps that are positive (but not necessarily completely positive) is also -declared. +A `CPTPMap` is a `ℂ`-linear map between matrices (`MatrixMap` is an alias), bundled with the facts that it +`IsCompletelyPositive` and `IsTracePreserving`. CPTP maps are typically regarded as the "most general +quantum operation", as they map density matrices (`MState`s) to density matrices. The type `PTPMap`, +for maps that are positive (but not necessarily completely positive) is also declared. A large portion of the theory is in terms of the Choi matrix (`MatrixMap.choi_matrix`), as the positive-definiteness of this matrix corresponds to being a CP map. This is @@ -33,93 +32,109 @@ This file also defines several important examples of, classes of, and operations * `IsEntanglementBreaking`, `IsDegradable`, `IsAntidegradable`: Entanglement breaking, degradable and antidegradable channels. * `SWAP`, `assoc`, `assoc'`, `traceLeft`, `traceRight`: The CPTP maps corresponding to important - operations on states. These correspond directly to `MState.SWAP`, `MState.assoc`, - `MState.assoc'`, `MState.traceLeft`, and `MState.traceRight`. + operations on states. These correspond directly to `MState.SWAP`, `MState.assoc`, `MState.assoc'`, + `MState.traceLeft`, and `MState.traceRight`. -/ @[expose] public section +set_option backward.isDefEq.respectTransparency false + variable {dIn dOut dOut₂ : Type*} [Fintype dIn] [Fintype dOut] [Fintype dOut₂] +variable [DecidableEq dIn] [DecidableEq dOut] [DecidableEq dOut₂] -namespace CPTPMap +namespace CPTPOp noncomputable section open scoped Matrix ComplexOrder -variable [DecidableEq dIn] - variable {dM : Type*} [Fintype dM] [DecidableEq dM] variable {dM₂ : Type*} [Fintype dM₂] [DecidableEq dM₂] variable (Λ : CPTPMap dIn dOut) -/-- The Choi matrix of a CPTPMap. -/ +/-- The Choi matrix of a CPTPOp. -/ @[reducible] def choi := Λ.map.choi_matrix /-- Two CPTPMaps are equal if their Choi matrices are equal. -/ -theorem choi_ext {Λ₁ Λ₂ : CPTPMap dIn dOut} (h : Λ₁.choi = Λ₂.choi) : Λ₁ = Λ₂ := by - ext1 - exact MatrixMap.choi_equiv.injective h +theorem choi_ext {Λ₁ Λ₂ : CPTPMap dIn dOut} (h : Λ₁.choi = Λ₂.choi) : Λ₁ = Λ₂ := + ext_map (MatrixMap.choi_equiv.injective h) /-- The Choi matrix of a channel is PSD. -/ theorem choi_PSD_of_CPTP : Λ.map.choi_matrix.PosSemidef := - Λ.map.choi_PSD_iff_CP_map.1 Λ.cp + Λ.map.choi_PSD_iff_CP_map.1 Λ.map_cp /-- The trace of a Choi matrix of a CPTP map is the cardinality of the input space. -/ @[simp] theorem Tr_of_choi_of_CPTP : Λ.choi.trace = (Finset.univ (α := dIn)).card := - Λ.TP.trace_choi + Λ.map_TP.trace_choi /-- Construct a CPTP map from a PSD Choi matrix with correct partial trace. -/ def CPTP_of_choi_PSD_Tr {M : Matrix (dOut × dIn) (dOut × dIn) ℂ} (h₁ : M.PosSemidef) - (h₂ : M.traceLeft = 1) : CPTPMap dIn dOut where - toLinearMap := MatrixMap.of_choi_matrix M - cp := (MatrixMap.choi_PSD_iff_CP_map (MatrixMap.of_choi_matrix M)).2 - ((MatrixMap.map_choi_inv M).symm ▸ h₁) - TP := (MatrixMap.of_choi_matrix M).IsTracePreserving_iff_trace_choi.2 - ((MatrixMap.map_choi_inv M).symm ▸ h₂) + (h₂ : M.traceLeft = 1) : CPTPMap dIn dOut := + ofMat (MatrixMap.of_choi_matrix M) + ((MatrixMap.choi_PSD_iff_CP_map (MatrixMap.of_choi_matrix M)).2 + ((MatrixMap.map_choi_inv M).symm ▸ h₁)) + ((MatrixMap.of_choi_matrix M).IsTracePreserving_iff_trace_choi.2 + ((MatrixMap.map_choi_inv M).symm ▸ h₂)) + +@[simp] +theorem map_CPTP_of_choi_PSD_Tr {M : Matrix (dOut × dIn) (dOut × dIn) ℂ} {h₁} {h₂} : + (CPTP_of_choi_PSD_Tr (M := M) h₁ h₂).map = MatrixMap.of_choi_matrix M := + map_ofMat _ _ _ @[simp] theorem choi_of_CPTP_of_choi (M : Matrix (dOut × dIn) (dOut × dIn) ℂ) {h₁} {h₂} : (CPTP_of_choi_PSD_Tr (M := M) h₁ h₂).choi = M := by - simp only [choi, CPTP_of_choi_PSD_Tr] + simp only [choi, map_CPTP_of_choi_PSD_Tr] rw [MatrixMap.map_choi_inv] -theorem mat_coe_eq_apply_mat [DecidableEq dOut] (ρ : MState dIn) : (Λ ρ).m = Λ.map ρ.m := - rfl +theorem mat_coe_eq_apply_mat (ρ : MState dIn) : (Λ ρ).m = Λ.map ρ.m := + congrArg HermitianMat.mat (PTPOp.M_apply_MState Λ.toPTPOp ρ) +omit [DecidableEq dIn] [DecidableEq dOut] in @[ext] -theorem funext [DecidableEq dOut] {Λ₁ Λ₂ : CPTPMap dIn dOut} (h : ∀ ρ, Λ₁ ρ = Λ₂ ρ) : Λ₁ = Λ₂ := +theorem funext {Λ₁ Λ₂ : CPTPMap dIn dOut} (h : ∀ ρ, Λ₁ ρ = Λ₂ ρ) : Λ₁ = Λ₂ := DFunLike.ext _ _ h -/-- The composition of CPTPMaps, as a CPTPMap. -/ +/-- The composition of CPTPMaps, as a CPTPOp. -/ def compose (Λ₂ : CPTPMap dM dOut) (Λ₁ : CPTPMap dIn dM) : CPTPMap dIn dOut where - toLinearMap := Λ₂.map ∘ₗ Λ₁.map + toLinearMap := Λ₂.toLinearMap ∘ₗ Λ₁.toLinearMap cp := Λ₁.cp.comp Λ₂.cp TP := Λ₁.TP.comp Λ₂.TP -infixl:75 "∘ₘ" => CPTPMap.compose +infixl:75 "∘ₘ" => CPTPOp.compose + +/-- **Matrix analogue of composition**: the matrix of a composition is the composition of the +matrices. -/ +@[simp] +theorem compose_map (Λ₂ : CPTPMap dM dOut) (Λ₁ : CPTPMap dIn dM) : + (Λ₂ ∘ₘ Λ₁).map = Λ₂.map ∘ₗ Λ₁.map := + OpMap.toMat_comp _ _ -/-- Composition of CPTPMaps by `CPTPMap.compose` is compatible with the `instFunLike` action. -/ +/-- Composition of CPTPMaps by `CPTPOp.compose` is compatible with the `instFunLike` action. -/ @[simp] -theorem compose_eq [DecidableEq dOut] {Λ₁ : CPTPMap dIn dM} {Λ₂ : CPTPMap dM dOut} : ∀ρ, (Λ₂ ∘ₘ Λ₁) ρ = Λ₂ (Λ₁ ρ) := - fun _ ↦ rfl +theorem compose_eq {Λ₁ : CPTPMap dIn dM} {Λ₂ : CPTPMap dM dOut} : + ∀ ρ, (Λ₂ ∘ₘ Λ₁) ρ = Λ₂ (Λ₁ ρ) := fun ρ ↦ by + apply DensityOp.ext_m + rw [mat_coe_eq_apply_mat, mat_coe_eq_apply_mat, mat_coe_eq_apply_mat, compose_map] + rfl /-- Composition of CPTPMaps is associative. -/ -theorem compose_assoc [DecidableEq dOut] (Λ₃ : CPTPMap dM₂ dOut) (Λ₂ : CPTPMap dM dM₂) +theorem compose_assoc (Λ₃ : CPTPMap dM₂ dOut) (Λ₂ : CPTPMap dM dM₂) (Λ₁ : CPTPMap dIn dM) : (Λ₃ ∘ₘ Λ₂) ∘ₘ Λ₁ = Λ₃ ∘ₘ (Λ₂ ∘ₘ Λ₁) := by ext1 ρ simp /-- CPTPMaps have a convex structure from their Choi matrices. -/ instance instMixable : Mixable (Matrix (dOut × dIn) (dOut × dIn) ℂ) (CPTPMap dIn dOut) where - to_U := CPTPMap.choi + to_U := CPTPOp.choi to_U_inj := choi_ext mkT {u} h := ⟨CPTP_of_choi_PSD_Tr (M := u) (Exists.recOn h fun t ht => ht ▸ t.choi_PSD_of_CPTP) (Exists.recOn h fun t ht => (by rw [← ht, ← MatrixMap.IsTracePreserving_iff_trace_choi] - exact t.TP)), + exact t.map_TP)), by apply choi_of_CPTP_of_choi⟩ convex := by have h_convex : ∀ (M₁ M₂ : Matrix (dOut × dIn) (dOut × dIn) ℂ), M₁.PosSemidef → M₂.PosSemidef → ∀ (t : ℝ), 0 ≤ t → t ≤ 1 → (t • M₁ + (1 - t) • M₂).PosSemidef := by @@ -131,11 +146,11 @@ instance instMixable : Mixable (Matrix (dOut × dIn) (dOut × dIn) ℂ) (CPTPMap obtain ⟨Λ, hΛ⟩ : ∃ Λ : MatrixMap dIn dOut ℂ, (a • M + b • N).traceLeft = 1 ∧ (a • M + b • N).PosSemidef ∧ Λ = MatrixMap.of_choi_matrix (a • M + b • N) := by refine ⟨_, ?_, ?_, rfl⟩ · have h_trace_M : M.traceLeft = 1 := by - convert Λ₁.TP using 1; + convert Λ₁.map_TP using 1; rw [ ← hΛ₁, MatrixMap.IsTracePreserving_iff_trace_choi ] have h_trace_N : N.traceLeft = 1 := by - convert! Λ₂.map.IsTracePreserving_iff_trace_choi.1 Λ₂.TP; - exact hΛ₂.symm; + convert Λ₂.map_TP using 1; + rw [ ← hΛ₂, MatrixMap.IsTracePreserving_iff_trace_choi ] convert congr_arg₂ ( fun x y : Matrix dIn dIn ℂ => a • x + b • y ) h_trace_M h_trace_N using 1; · ext i j simp [ Matrix.traceLeft ] @@ -144,53 +159,58 @@ instance instMixable : Mixable (Matrix (dOut × dIn) (dOut × dIn) ℂ) (CPTPMap · convert h_convex M N ( by simpa [ ← hΛ₁ ] using Λ₁.choi_PSD_of_CPTP ) ( by simpa [ ← hΛ₂ ] using Λ₂.choi_PSD_of_CPTP ) a ha ( by linarith ) using 1 ; rw [ ← hab ] ring_nf use CPTP_of_choi_PSD_Tr hΛ.2.1 hΛ.1; - exact MatrixMap.map_choi_inv (a • M + b • N) + exact choi_of_CPTP_of_choi (a • M + b • N) /-- The identity channel, which leaves the input unchanged. -/ def id : CPTPMap dIn dIn where - toLinearMap := .id - cp := .id - TP := .id + toLinearMap := LinearMap.id + cp := OpMap.isCompletelyPositive_id + TP := OpMap.isTracePreserving_id -/-- The map `CPTPMap.id` leaves any matrix unchanged. -/ +/-- The map `CPTPOp.id` leaves any matrix unchanged. -/ @[simp] -theorem id_map : (id (dIn := dIn)).map = LinearMap.id := by - rfl +theorem id_map : (id (dIn := dIn)).map = LinearMap.id := + OpMap.toMat_id -/-- The map `CPTPMap.id` leaves the input state unchanged. -/ +/-- The map `CPTPOp.id` leaves the input state unchanged. -/ @[simp] -theorem id_MState (ρ : MState dIn) : CPTPMap.id (dIn := dIn) ρ = ρ := by - apply MState.ext_m +theorem id_MState (ρ : MState dIn) : CPTPOp.id (dIn := dIn) ρ = ρ := by + apply DensityOp.ext_m rw [mat_coe_eq_apply_mat] simp -/-- The map `CPTPMap.id` composed with any map is the same map. -/ +/-- The map `CPTPOp.id` composed with any map is the same map. -/ @[simp] -theorem id_compose [DecidableEq dOut] (Λ : CPTPMap dIn dOut) : id ∘ₘ Λ = Λ := by +theorem id_compose (Λ : CPTPMap dIn dOut) : id ∘ₘ Λ = Λ := by apply funext simp -/-- Any map composed with `CPTPMap.id` is the same map. -/ +/-- Any map composed with `CPTPOp.id` is the same map. -/ @[simp] theorem compose_id (Λ : CPTPMap dIn dOut) : Λ ∘ₘ id = Λ := by classical ext1 simp section equiv -variable [DecidableEq dOut] /-- Given a equivalence (a bijection) between the types d₁ and d₂, that is, if they're the same dimension, then there's a CPTP channel for this. This is what we need for defining e.g. the SWAP channel, which is 'unitary' but takes heterogeneous input and outputs types (d₁ × d₂) and (d₂ × d₁). -/ -def ofEquiv (σ : dIn ≃ dOut) : CPTPMap dIn dOut where - toLinearMap := MatrixMap.submatrix ℂ σ.symm - cp := .submatrix σ.symm - TP x := by rw [MatrixMap.IsTracePreserving.submatrix] +def ofEquiv (σ : dIn ≃ dOut) : CPTPMap dIn dOut := + ofMat (MatrixMap.submatrix ℂ σ.symm) (.submatrix σ.symm) + (fun x ↦ by rw [MatrixMap.IsTracePreserving.submatrix]) + +@[simp] +theorem ofEquiv_map (σ : dIn ≃ dOut) : + (ofEquiv σ).map = MatrixMap.submatrix ℂ σ.symm := + map_ofMat _ _ _ @[simp] theorem ofEquiv_apply (σ : dIn ≃ dOut) (ρ : MState dIn) : ofEquiv σ ρ = ρ.relabel σ.symm := by + apply DensityOp.ext_m + rw [mat_coe_eq_apply_mat, ofEquiv_map, MState.relabel_m] rfl @[simp] @@ -217,15 +237,19 @@ def assoc' : CPTPMap (d₁ × d₂ × d₃) ((d₁ × d₂) × d₃) := @[simp] theorem SWAP_eq_MState_SWAP (ρ : MState (d₁ × d₂)) : SWAP (d₁ := d₁) (d₂ := d₂) ρ = ρ.SWAP := - rfl + ofEquiv_apply _ _ @[simp] theorem assoc_eq_MState_assoc (ρ : MState ((d₁ × d₂) × d₃)) : assoc (d₁ := d₁) (d₂ := d₂) (d₃ := d₃) ρ = ρ.assoc := - rfl + ofEquiv_apply _ _ @[simp] -theorem assoc'_eq_MState_assoc' (ρ : MState (d₁ × d₂ × d₃)) : assoc' (d₁ := d₁) (d₂ := d₂) (d₃ := d₃) ρ = ρ.assoc' := - rfl +theorem assoc'_eq_MState_assoc' (ρ : MState (d₁ × d₂ × d₃)) : + assoc' (d₁ := d₁) (d₂ := d₂) (d₃ := d₃) ρ = ρ.assoc' := by + apply DensityOp.ext_m + rw [assoc', ofEquiv_apply] + ext i j + simp [MState.assoc', MState.assoc, MState.SWAP] @[simp] theorem assoc_assoc' : (assoc (d₁ := d₁) (d₂ := d₂) (d₃ := d₃)) ∘ₘ assoc' = id := by @@ -237,39 +261,45 @@ end equiv section trace variable {d₁ d₂ : Type*} [Fintype d₁] [Fintype d₂] [DecidableEq d₁] [DecidableEq d₂] +--TODO: make Matrix.traceLeft a linear map, a `MatrixMap`. +/-- Partial tracing out the left, as a `MatrixMap`. -/ +def traceLeftₘ (d : Type*) [Fintype d] [DecidableEq d] : + Matrix (d₁ × d) (d₁ × d) ℂ →ₗ[ℂ] Matrix d d ℂ where + toFun x := Matrix.traceLeft x + map_add' := by + intros; ext + simp [Matrix.traceLeft, Finset.sum_add_distrib] + map_smul' := by + intros; ext + simp [Matrix.traceLeft, Finset.mul_sum] + +theorem traceLeftₘ_cp : + MatrixMap.IsCompletelyPositive (traceLeftₘ (d₁ := d₁) d₂) := by + --(traceLeft ⊗ₖₘ I) = traceLeft ∘ₘ (ofEquiv prod_assoc) + --Both go (A × B) × C → B × C + --So then it suffices to show both are positive, and we have PosSemidef.traceLeft already. + intro n + classical + suffices MatrixMap.IsPositive + (traceLeftₘ (d₁ := d₁) (d₂ × Fin n) ∘ₗ + (MatrixMap.submatrix ℂ (Equiv.prodAssoc d₁ d₂ (Fin n)).symm)) by + convert this + ext + rw [MatrixMap.kron_def] + simp [traceLeftₘ, Matrix.submatrix, Matrix.single, ite_and, Matrix.traceLeft, + Fintype.sum_prod_type] + apply MatrixMap.IsPositive.comp + · exact (MatrixMap.IsCompletelyPositive.submatrix _).IsPositive + · intro x h + exact h.traceLeft + /-- Partial tracing out the left, as a CPTP map. -/ -@[simps] def traceLeft : CPTPMap (d₁ × d₂) d₂ := - --TODO: make Matrix.traceLeft a linear map, a `MatrixMap`. - letI f (d) [Fintype d] [DecidableEq d]: Matrix (d₁ × d) (d₁ × d) ℂ →ₗ[ℂ] Matrix d d ℂ := { - toFun x := Matrix.traceLeft x - map_add' := by - intros; ext - simp [Matrix.traceLeft, Finset.sum_add_distrib] - map_smul' := by - intros; ext - simp [Matrix.traceLeft, Finset.mul_sum] - } - { - toLinearMap := f d₂ - TP := by intro; simp [f] - cp := by - --(traceLeft ⊗ₖₘ I) = traceLeft ∘ₘ (ofEquiv prod_assoc) - --Both go (A × B) × C → B × C - --So then it suffices to show both are positive, and we have PosSemidef.traceLeft already. - intro n - classical - suffices MatrixMap.IsPositive - (f (d₂ × Fin n) ∘ₗ (MatrixMap.submatrix ℂ (Equiv.prodAssoc d₁ d₂ (Fin n)).symm)) by - convert this - ext - rw [MatrixMap.kron_def] - simp [f, Matrix.submatrix, Matrix.single, ite_and, Matrix.traceLeft, Fintype.sum_prod_type] - apply MatrixMap.IsPositive.comp - · exact (MatrixMap.IsCompletelyPositive.submatrix _).IsPositive - · intro x h - exact h.traceLeft - } + ofMat (traceLeftₘ d₂) traceLeftₘ_cp (by intro; simp [traceLeftₘ]) + +@[simp] +theorem traceLeft_map : (traceLeft (d₁ := d₁) (d₂ := d₂)).map = traceLeftₘ d₂ := + map_ofMat _ _ _ /-- Partial tracing out the right, as a CPTP map. -/ def traceRight : CPTPMap (d₁ × d₂) d₁ := @@ -278,44 +308,48 @@ def traceRight : CPTPMap (d₁ × d₂) d₁ := @[simp] theorem traceLeft_eq_MState_traceLeft (ρ : MState (d₁ × d₂)) : traceLeft (d₁ := d₁) (d₂ := d₂) ρ = ρ.traceLeft := by + apply DensityOp.ext_m + rw [mat_coe_eq_apply_mat, traceLeft_map, MState.traceLeft_m] rfl @[simp] theorem traceRight_eq_MState_traceRight (ρ : MState (d₁ × d₂)) : traceRight (d₁ := d₁) (d₂ := d₂) ρ = ρ.traceRight := by - rfl --It's actually pretty crazy that this is a definitional equality, cool + rw [traceRight, compose_eq, SWAP_eq_MState_SWAP, traceLeft_eq_MState_traceLeft, + MState.traceLeft_SWAP] end trace +/-- The matrix map that appends a fixed state `ρ` on the right. -/ +def appendₘ (ρ : MState dOut) : MatrixMap dIn (dIn × dOut) ℂ where + toFun M := Matrix.kroneckerMap (fun x1 x2 => x1 * x2) M ρ.m + map_add' := by simp [Matrix.add_kronecker] + map_smul' := by simp [Matrix.smul_kronecker] + /--The replacement channel that maps all inputs to a given state. -/ -def replacement [Nonempty dIn] [DecidableEq dOut] (ρ : MState dOut) : CPTPMap dIn dOut := - traceLeft ∘ₘ { - toFun := fun M => Matrix.kroneckerMap (fun x1 x2 => x1 * x2) M ρ.m - map_add' := by simp [Matrix.add_kronecker] - map_smul' := by simp [Matrix.smul_kronecker] - cp := MatrixMap.kron_kronecker_const ρ.psd - TP := by intro; simp [Matrix.trace_kronecker] - } - -set_option backward.isDefEq.respectTransparency false in -/-- The output of `replacement ρ` is always that `ρ`. -/ +def replacement [Nonempty dIn] (ρ : MState dOut) : CPTPMap dIn dOut := + traceLeft ∘ₘ ofMat (appendₘ ρ) (MatrixMap.kron_kronecker_const ρ.psd) + (by intro; simp [appendₘ, Matrix.trace_kronecker]) + +/-- **Matrix analogue of the replacement channel**: it sends `M` to `M.trace • ρ`. -/ @[simp] -theorem replacement_apply [Nonempty dIn] [DecidableEq dOut] (ρ : MState dOut) (ρ₀ : MState dIn) : - replacement ρ ρ₀ = ρ := by - simp only [replacement, compose_eq, traceLeft_eq_MState_traceLeft] - simp only [apply_mState_eq_toPTPMap, PTPMap.apply_mstate_eq, HPMap.apply_hermitianMat_eq, - HPMap.map, HermitianMat.val_eq_coe, MState.mat_M, LinearMap.coe_mk, AddHom.coe_mk] - --This should be simp... +theorem replacement_map [Nonempty dIn] (ρ : MState dOut) (M : Matrix dIn dIn ℂ) : + (replacement ρ).map M = M.trace • ρ.m := by + simp only [replacement, compose_map, LinearMap.comp_apply, traceLeft_map, map_ofMat, + traceLeftₘ, appendₘ, LinearMap.coe_mk, AddHom.coe_mk] ext i j - simp - rw [HermitianMat.instFun] - simp [-HermitianMat.mat_apply, Matrix.traceLeft, ← Finset.sum_mul] - convert one_mul _ - exact ρ₀.tr' + simp [Matrix.traceLeft, Matrix.kroneckerMap, Matrix.trace, ← Finset.sum_mul] + +/-- The output of `replacement ρ` is always that `ρ`. -/ +@[simp] +theorem replacement_apply [Nonempty dIn] (ρ : MState dOut) (ρ₀ : MState dIn) : + replacement (dIn := dIn) ρ ρ₀ = ρ := by + apply DensityOp.ext_m + rw [mat_coe_eq_apply_mat, replacement_map, ρ₀.tr', one_smul] --In principle we can relax the `Nonempty dIn`: for the case where `IsEmpty dIn`, we just take the -- 0 map, and it's CPTP. -instance [Nonempty dIn] [Nonempty dOut] [DecidableEq dOut] : Inhabited (CPTPMap dIn dOut) := +instance [Nonempty dIn] [Nonempty dOut] : Inhabited (CPTPMap dIn dOut) := ⟨replacement default⟩ instance [Nonempty dIn] [Nonempty dOut] : Nonempty (CPTPMap dIn dOut) := by @@ -327,6 +361,7 @@ as "destroying" the whole system; tracing out everything. -/ def destroy [Nonempty dIn] [Unique dOut] : CPTPMap dIn dOut := replacement default +omit [DecidableEq dIn] in /-- Two CPTP maps into the same one-dimensional output space must be equal -/ theorem eq_if_output_unique [Unique dOut] (Λ₁ Λ₂ : CPTPMap dIn dOut) : Λ₁ = Λ₂ := funext fun _ ↦ (Unique.eq_default _).trans (Unique.eq_default _).symm @@ -337,7 +372,7 @@ instance instUnique [Nonempty dIn] [Unique dOut] : Unique (CPTPMap dIn dOut) whe uniq := fun _ ↦ eq_if_output_unique _ _ @[simp] -theorem destroy_comp {dOut₂ : Type*} [Unique dOut₂] [DecidableEq dOut] [Nonempty dIn] [Nonempty dOut] +theorem destroy_comp {dOut₂ : Type*} [Unique dOut₂] [DecidableEq dOut₂] [Nonempty dIn] [Nonempty dOut] (Λ : CPTPMap dIn dOut) : destroy (dOut := dOut₂) ∘ₘ Λ = destroy := Unique.eq_default _ @@ -350,12 +385,17 @@ variable [DecidableEq dI₁] [DecidableEq dI₂] [DecidableEq dO₁] [DecidableE set_option maxRecDepth 1000 in -- ??? what the heck is recursing /-- The tensor product of two CPTPMaps. -/ -def prod (Λ₁ : CPTPMap dI₁ dO₁) (Λ₂ : CPTPMap dI₂ dO₂) : CPTPMap (dI₁ × dI₂) (dO₁ × dO₂) where - toLinearMap := Λ₁.map.kron Λ₂.map - cp := Λ₁.cp.kron Λ₂.cp - TP := Λ₁.TP.kron Λ₂.TP +def prod (Λ₁ : CPTPMap dI₁ dO₁) (Λ₂ : CPTPMap dI₂ dO₂) : CPTPMap (dI₁ × dI₂) (dO₁ × dO₂) := + ofMat (Λ₁.map.kron Λ₂.map) (Λ₁.map_cp.kron Λ₂.map_cp) (Λ₁.map_TP.kron Λ₂.map_TP) -infixl:70 "⊗ᶜᵖ" => CPTPMap.prod +infixl:70 "⊗ᶜᵖ" => CPTPOp.prod + +/-- **Matrix analogue of the tensor product**: the matrix of a product is the Kronecker product +of the matrices. -/ +@[simp] +theorem prod_map (Λ₁ : CPTPMap dI₁ dO₁) (Λ₂ : CPTPMap dI₂ dO₂) : + (Λ₁ ⊗ᶜᵖ Λ₂).map = Λ₁.map.kron Λ₂.map := + map_ofMat _ _ _ /-- Tensor products commute with channel application: `(Λ₁ ⊗ᶜᵖ Λ₂) (ρ₁ ⊗ᴹ ρ₂) = Λ₁ ρ₁ ⊗ᴹ Λ₂ ρ₂`. -/ @@ -363,9 +403,10 @@ infixl:70 "⊗ᶜᵖ" => CPTPMap.prod theorem prod_apply_prod (Λ₁ : CPTPMap dI₁ dO₁) (Λ₂ : CPTPMap dI₂ dO₂) (ρ₁ : MState dI₁) (ρ₂ : MState dI₂) : (Λ₁ ⊗ᶜᵖ Λ₂) (ρ₁ ⊗ᴹ ρ₂) = (Λ₁ ρ₁) ⊗ᴹ (Λ₂ ρ₂) := by - apply MState.ext_m - exact - MatrixMap.kron_map_of_kron_state Λ₁.map Λ₂.map ρ₁.m ρ₂.m + apply DensityOp.ext_m + rw [mat_coe_eq_apply_mat, prod_map, MState.prod_m, MState.prod_m, mat_coe_eq_apply_mat, + mat_coe_eq_apply_mat] + exact MatrixMap.kron_map_of_kron_state Λ₁.map Λ₂.map ρ₁.m ρ₂.m end prod @@ -375,36 +416,39 @@ variable {ι : Type u} [DecidableEq ι] [fι : Fintype ι] variable {dI : ι → Type v} [∀(i :ι), Fintype (dI i)] [∀(i :ι), DecidableEq (dI i)] variable {dO : ι → Type w} [∀(i :ι), Fintype (dO i)] [∀(i :ι), DecidableEq (dO i)] -set_option maxRecDepth 1000 in /-- Finitely-indexed tensor products of CPTPMaps. -/ -def piProd (Λi : (i:ι) → CPTPMap (dI i) (dO i)) : CPTPMap ((i:ι) → dI i) ((i:ι) → dO i) where - toLinearMap := MatrixMap.piProd (fun i ↦ (Λi i).map) - cp := MatrixMap.IsCompletelyPositive.piProd (fun i ↦ (Λi i).cp) - TP := MatrixMap.IsTracePreserving.piProd (fun i ↦ (Λi i).TP) - -set_option backward.isDefEq.respectTransparency false in -theorem fin_1_piProd - {dI : Fin 1 → Type v} [Fintype (dI 0)] [DecidableEq (dI 0)] - {dO : Fin 1 → Type w} [Fintype (dO 0)] [DecidableEq (dO 0)] - (Λi : (i : Fin 1) → CPTPMap (dI 0) (dO 0)) : - piProd Λi = - ofEquiv (Equiv.funUnique (Fin 1) (dO 0)).symm ∘ₘ - ((Λi 1) ∘ₘ ofEquiv (Equiv.funUnique (Fin 1) (dI 0))) := by - apply CPTPMap.ext - change MatrixMap.piProd (fun i ↦ (Λi i).map) = - MatrixMap.submatrix ℂ (Equiv.funUnique (Fin 1) (dO 0)) ∘ₗ - ((Λi 1).map ∘ₗ MatrixMap.submatrix ℂ (Equiv.funUnique (Fin 1) (dI 0)).symm) - apply MatrixMap.choi_equiv.injective - conv_lhs => rw [MatrixMap.choi_equiv_apply, MatrixMap.choi_matrix_piProd] - rw [MatrixMap.choi_equiv_apply] - ext ⟨i, a⟩ ⟨j, b⟩ - simp only [Matrix.reindex_apply, Matrix.piProd, Matrix.of_apply, - Fintype.prod_subsingleton _ (0 : Fin 1), - MatrixMap.choi_matrix, LinearMap.comp_apply, - MatrixMap.submatrix_apply, Matrix.submatrix_apply, Matrix.single] - convert! rfl - ext - simp [funext_iff] +def piProd (Λi : (i:ι) → CPTPMap (dI i) (dO i)) : CPTPMap ((i:ι) → dI i) ((i:ι) → dO i) := + ofMat (MatrixMap.piProd (fun i ↦ (Λi i).map)) + (MatrixMap.IsCompletelyPositive.piProd (fun i ↦ (Λi i).map_cp)) + (MatrixMap.IsTracePreserving.piProd (fun i ↦ (Λi i).map_TP)) + +@[simp] +theorem piProd_map (Λi : (i:ι) → CPTPMap (dI i) (dO i)) : + (piProd Λi).map = MatrixMap.piProd (fun i ↦ (Λi i).map) := + map_ofMat _ _ _ + +/-- A tensor product over a singleton index type is just the single factor, up to the relabelling +that identifies a singleton-indexed Pi type with its unique component. -/ +theorem piProd_unique [Unique ι] (Λi : (i : ι) → CPTPMap (dI i) (dO i)) : + piProd Λi = CPTPOp.ofEquiv (Equiv.piUnique dO).symm ∘ₘ + (Λi default ∘ₘ CPTPOp.ofEquiv (Equiv.piUnique dI)) := by + apply CPTPOp.ext_map + refine (Matrix.stdBasis ℂ ((i : ι) → dI i) ((i : ι) → dI i)).ext fun p ↦ ?_ + obtain ⟨a, b⟩ := p + have hsub : MatrixMap.submatrix ℂ (Equiv.piUnique dI).symm (Matrix.single a b 1) + = Matrix.single (a default) (b default) 1 := by + ext j k + rw [MatrixMap.submatrix] + simp only [LinearMap.coe_mk, AddHom.coe_mk, Matrix.submatrix_apply, Matrix.single_apply] + refine if_congr (and_congr ?_ ?_) rfl rfl <;> + exact Equiv.eq_symm_apply (Equiv.piUnique dI) + simp only [Matrix.stdBasis_eq_single, piProd_map, compose_map, LinearMap.comp_apply, + ofEquiv_map, Equiv.symm_symm, hsub, MatrixMap.piProd_single] + ext j k + rw [MatrixMap.submatrix] + simp only [LinearMap.coe_mk, AddHom.coe_mk, Matrix.submatrix_apply, Matrix.piProd_apply, + Fintype.prod_unique] + rfl /-- The tensor product of composed maps is the composition of the tensor products. @@ -416,29 +460,42 @@ theorem piProd_comp [∀ i, Fintype (d₃ i)] [∀ i, DecidableEq (d₃ i)] (Λ₁ : ∀ i, CPTPMap (d₁ i) (d₂ i)) (Λ₂ : ∀ i, CPTPMap (d₂ i) (d₃ i)) : piProd (fun i => (Λ₂ i) ∘ₘ (Λ₁ i)) = (piProd Λ₂) ∘ₘ (piProd Λ₁) := by - apply CPTPMap.ext - convert! MatrixMap.piProd_comp _ _; + apply CPTPOp.ext_map + simp only [piProd_map, compose_map] + convert MatrixMap.piProd_comp _ _; infer_instance +/-- The tensor product of identity channels is the identity channel. -/ @[simp] theorem piProd_id : - piProd (fun i ↦ (CPTPMap.id : CPTPMap (dI i) (dI i))) = CPTPMap.id := by - apply CPTPMap.ext - simp [piProd, id_map, MatrixMap.piProd_id] + piProd (fun i ↦ (CPTPOp.id : CPTPMap (dI i) (dI i))) = CPTPOp.id := by + apply CPTPOp.ext_map + simp only [piProd_map, id_map] + exact MatrixMap.piProd_id end finprod section unitary /-- Conjugating density matrices by a unitary as a channel. This is standard unitary evolution. -/ -def ofUnitary (U : 𝐔[dIn]) : CPTPMap dIn dIn where - toLinearMap := MatrixMap.conj U - cp := MatrixMap.conj_isCompletelyPositive U.val - TP := by intro; simp [Matrix.trace_mul_cycle U.val, ← Matrix.star_eq_conjTranspose] +def ofUnitary (U : 𝐔[dIn]) : CPTPMap dIn dIn := + ofMat (MatrixMap.conj (U : Matrix dIn dIn ℂ)) + (MatrixMap.conj_isCompletelyPositive (U : Matrix dIn dIn ℂ)) + (by + intro + simp [Matrix.trace_mul_cycle (U : Matrix dIn dIn ℂ), ← Matrix.star_eq_conjTranspose]) + +/-- **Matrix analogue of a unitary channel**: its matrix is conjugation by `U`. -/ +@[simp] +theorem ofUnitary_map (U : 𝐔[dIn]) : + (ofUnitary U).map = MatrixMap.conj (U : Matrix dIn dIn ℂ) := + map_ofMat _ _ _ /-- The unitary channel U conjugated by U. -/ theorem ofUnitary_eq_conj (U : 𝐔[dIn]) (ρ : MState dIn) : - (ofUnitary U) ρ = ρ.uConj U := + (ofUnitary U) ρ = ρ.uConj U := by + apply DensityOp.ext_m + rw [mat_coe_eq_apply_mat, ofUnitary_map, MState.uConj_m] rfl /-- A channel is unitary iff it is `ofUnitary U`. -/ @@ -446,10 +503,14 @@ def IsUnitary (Λ : CPTPMap dIn dIn) : Prop := ∃ U, Λ = ofUnitary U /-- A channel is unitary iff it can be written as conjugation by a unitary. -/ -theorem IsUnitary_iff_uConj (Λ : CPTPMap dIn dIn) : IsUnitary Λ ↔ ∃ U, ∀ ρ, Λ ρ = ρ.uConj U := by - simp_rw [IsUnitary, ← ofUnitary_eq_conj, CPTPMap.funext_iff] +theorem IsUnitary_iff_uConj (Λ : CPTPMap dIn dIn) : + IsUnitary Λ ↔ ∃ U, ∀ ρ : MState dIn, Λ ρ = ρ.uConj U := by + constructor + · rintro ⟨U, rfl⟩ + exact ⟨U, ofUnitary_eq_conj U⟩ + · rintro ⟨U, hU⟩ + exact ⟨U, CPTPOp.funext fun ρ ↦ (hU ρ).trans (ofUnitary_eq_conj U ρ).symm⟩ -set_option backward.isDefEq.respectTransparency false in theorem IsUnitary_equiv (σ : dIn ≃ dIn) : IsUnitary (ofEquiv σ) := by have h_unitary : ∃ U : Matrix dIn dIn ℂ, U * U.conjTranspose = 1 ∧ U.conjTranspose * U = 1 ∧ ∀ x : dIn, (∀ y : dIn, (U y x = 1) ↔ (y = σ x)) ∧ ∀ y : dIn, (U y x = 0) ↔ (y ≠ σ x) := by simp only [Matrix.conjTranspose, RCLike.star_def]; @@ -468,8 +529,11 @@ theorem IsUnitary_equiv (σ : dIn ≃ dIn) : IsUnitary (ofEquiv σ) := by have hU_i_x : ∀ x : dIn, U i x = if x = σ.symm i then 1 else 0 := by grind have hU_j_x : ∀ x : dIn, U j x = if x = σ.symm j then 1 else 0 := by grind simp [Matrix.mul_apply, Matrix.submatrix, hU_i_x, hU_j_x] - ext ρ : 3 - exact (h_mul ρ).symm + apply CPTPOp.funext + intro ρ + apply DensityOp.ext_m + rw [ofUnitary_eq_conj, MState.uConj_m, ofEquiv_apply, MState.relabel_m] + exact (h_mul ρ.m).symm end unitary @@ -477,7 +541,7 @@ end unitary -- only outputs separable states. -/ -- def IsEntanglementBreaking (Λ : CPTPMap dIn dOut) : Prop := -- ∀ (dR : Type u_1) [Fintype dR] [DecidableEq dR], --- ∀ (ρ : MState (dR × dIn)), ((CPTPMap.id (dIn := dR) ⊗ₖ Λ) ρ).IsSeparable +-- ∀ (ρ : MState (dR × dIn)), ((CPTPOp.id (dIn := dR) ⊗ₖ Λ) ρ).IsSeparable --TODO: --Theorem: entanglement breaking iff it holds for all channels, not just id. @@ -486,19 +550,21 @@ end unitary --Theorem: eb iff Kraus operators can be written as all unit rank (Wilde Theorem 4.6.1) section purify -variable [DecidableEq dOut] [Inhabited dOut] +variable [Inhabited dOut] --PULLOUT omit [DecidableEq dOut] [Inhabited dOut] in /- -PROBLEM If a MatrixMap of_kraus K K is trace-preserving, then Σ_k K_k† K_k = 1. - -PROVIDED SOLUTION The TP condition says for all X, trace((of_kraus K K) X) = trace(X). Unfolding -of_kraus: trace(Σ_k K_k X K_k†) = Σ_k trace(K_k† K_k X) (by cycle) = trace((Σ_k K_k† K_k) X). So -trace(A X) = trace(X) for all X where A = Σ_k K_k† K_k, which means A = 1. Use -`Matrix.eq_of_trace_mul_eq` or the fact that trace is a faithful pairing on matrices to conclude A = -1. The TP condition `Λ.TP` gives us `∀ x, (Λ.map x).trace = x.trace`, and since `Λ.map = of_kraus K -K`, we substitute and simplify. +PROBLEM +If a MatrixMap of_kraus K K is trace-preserving, then Σ_k K_k† K_k = 1. + +PROVIDED SOLUTION +The TP condition says for all X, trace((of_kraus K K) X) = trace(X). +Unfolding of_kraus: trace(Σ_k K_k X K_k†) = Σ_k trace(K_k† K_k X) (by cycle) = trace((Σ_k K_k† K_k) X). +So trace(A X) = trace(X) for all X where A = Σ_k K_k† K_k, which means A = 1. +Use `Matrix.eq_of_trace_mul_eq` or the fact that trace is a faithful pairing on matrices +to conclude A = 1. The TP condition `Λ.TP` gives us `∀ x, (Λ.map x).trace = x.trace`, and +since `Λ.map = of_kraus K K`, we substitute and simplify. -/ private lemma kraus_sum_eq_one_of_TP {κ : Type*} [Fintype κ] @@ -538,7 +604,6 @@ of emb, then use `LinearIsometry.extend` to extend to the full space. Since in f dimensions a linear isometry from a space to itself is surjective, this gives a LinearIsometryEquiv, hence a unitary matrix. -/ -set_option maxHeartbeats 1600000 in private lemma exists_unitary_extending_isometry {m n : Type*} [Fintype m] [Fintype n] [DecidableEq m] [DecidableEq n] (V : Matrix m n ℂ) (hV : V.conjTranspose * V = 1) @@ -552,8 +617,9 @@ private lemma exists_unitary_extending_isometry rw [ orthonormal_iff_ite ]; intro i j replace hV := congr_fun (congr_fun hV i) j - simp_all only [ mul_comm, Matrix.mul_apply ] - exact hV + simp only [Matrix.mul_apply, Matrix.conjTranspose_apply, Matrix.one_apply] at hV + simp only [u, EuclideanSpace.inner_toLp_toLp, dotProduct, Pi.star_apply] + simpa only [mul_comm] using hV have := Orthonormal.exists_orthonormalBasis_extension_of_card_eq (𝕜 := ℂ) (E := EuclideanSpace ℂ m) (ι := m) simp only [finrank_euclideanSpace, forall_const] at this contrapose! this @@ -574,12 +640,10 @@ private lemma exists_unitary_extending_isometry refine ⟨⟨Matrix.of (fun i j ↦ b j i), ?_⟩, ?_⟩ · simp only [Matrix.mem_unitaryGroup_iff] ext1 i j - have := b.sum_inner_mul_inner (EuclideanSpace.single i 1) (EuclideanSpace.single j 1) - simp_all [inner] - exact this + simpa [inner, Matrix.mul_apply, Matrix.star_apply, Matrix.one_apply] using + b.sum_inner_mul_inner (EuclideanSpace.single i 1) (EuclideanSpace.single j 1) · simp [hb, u] -set_option backward.isDefEq.respectTransparency false in omit [DecidableEq dOut] [Inhabited dOut] in /-- Given Kraus operators K indexed by (dOut × dIn), define the isometry matrix @@ -601,108 +665,103 @@ private lemma purify_isometry_condition private lemma purify_MState_pure_basis_default_entry (i j : dOut × dOut) : (MState.pure (Ket.basis (default : dOut × dOut))).m i j = if i = default ∧ j = default then 1 else 0 := by - change (MState.pure (Ket.basis (default : dOut × dOut))).M.val i j = _ - simp only [MState.pure, Matrix.vecMulVec_apply, Ket.basis, Ket.to_bra, - Ket.coe_fun_eq, Bra.coe_fun_eq] - split_ifs <;> simp_all [eq_comm] + rw [MState.pure_apply, Ket.apply, Ket.apply, Ket.basis] + by_cases hi : i = default <;> by_cases hj : j = default <;> + simp [hi, hj, eq_comm] omit [Inhabited dOut] in private lemma purify_replacement_single_eq (ρ₀ : MState (dOut × dOut)) (b₁ b₂ : dOut × dOut) : ((replacement ρ₀).map (Matrix.single () () 1)) b₁ b₂ = ρ₀.m b₁ b₂ := by - open Kronecker in - suffices h : (Matrix.single () () 1 ⊗ₖ ρ₀.m).traceLeft = ρ₀.m from - congr_fun (congr_fun h b₁) b₂ - ext : 1 - simp [Matrix.traceLeft, Matrix.kroneckerMap] - -private lemma purify_prep_append_entry (X : Matrix dIn dIn ℂ) - (a₁ : dIn) (b₁c₁ : dOut × dOut) (a₂ : dIn) (b₂c₂ : dOut × dOut) : - let ρ₀ := MState.pure (Ket.basis (default : dOut × dOut)) - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement ρ₀ + rw [replacement_map] + simp + +omit [Inhabited dOut] in +/-- **Matrix analogue of preparation**: appending a `Unit` factor and then preparing `ρ₀` on it +is just taking the Kronecker product with `ρ₀`. -/ +private lemma purify_prep_append_map (X : Matrix dIn dIn ℂ) (ρ₀ : MState (dOut × dOut)) : + (id ⊗ᶜᵖ (replacement ρ₀ : CPTPMap Unit (dOut × dOut))).map + ((CPTPOp.ofEquiv (Equiv.prodPUnit dIn).symm).map X) = + Matrix.kroneckerMap (· * ·) X ρ₀.m := by + have happ : (CPTPOp.ofEquiv (Equiv.prodPUnit dIn).symm).map X = + Matrix.kroneckerMap (· * ·) X (Matrix.single () () 1 : Matrix Unit Unit ℂ) := by + ext ⟨a, u⟩ ⟨b, v⟩ + simp [MatrixMap.submatrix, Matrix.kroneckerMap] + rw [happ, prod_map, MatrixMap.kron_map_of_kron_state, id_map, LinearMap.id_coe, _root_.id_eq] + congr 1 + ext b₁ b₂ + exact purify_replacement_single_eq ρ₀ b₁ b₂ + +private lemma purify_pure_basis_default_m : + (MState.pure (Ket.basis (default : dOut × dOut))).m = + Matrix.single default default 1 := by + ext a b + rw [purify_MState_pure_basis_default_entry, Matrix.single_apply] + by_cases ha : a = default <;> by_cases hb : b = default <;> simp [ha, hb, eq_comm] + +/-- Append two fresh copies of `dOut`, each prepared in the default basis state `∣0⟩`, to the +input system. -/ +def prepDefault : CPTPMap dIn (dIn × dOut × dOut) := + (id ⊗ᶜᵖ (replacement (MState.pure (Ket.basis (default : dOut × dOut))) : + CPTPMap Unit (dOut × dOut))) ∘ₘ CPTPOp.ofEquiv (Equiv.prodPUnit dIn).symm + +/-- **Matrix analogue of `CPTPOp.prepDefault`**: it takes the Kronecker product with `∣0⟩⟨0∣`. -/ +@[simp] +theorem prepDefault_map (X : Matrix dIn dIn ℂ) : + (prepDefault (dIn := dIn) (dOut := dOut)).map X = + Matrix.kroneckerMap (· * ·) X (Matrix.single default default 1) := by + rw [prepDefault, compose_map, LinearMap.comp_apply, purify_prep_append_map, + purify_pure_basis_default_m] + +/-- The Stinespring preparation `prep ∘ₘ append` acts on a matrix entry by the Kronecker product +with the fixed pure state `∣0⟩⟨0∣` on `dOut × dOut`. -/ +theorem prep_append_map_entry (X : Matrix dIn dIn ℂ) + (a₁ : dIn) (b₁c₁ : dOut × dOut) (a₂ : dIn) (b₂c₂ : dOut × dOut) : + let τ := MState.pure (Ket.basis (default : dOut × dOut)) + let zero_prep : CPTPMap Unit (dOut × dOut) := replacement τ let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm + let append : CPTPMap dIn (dIn × Unit) := CPTPOp.ofEquiv (Equiv.prodPUnit dIn).symm (prep ∘ₘ append).map X (a₁, b₁c₁) (a₂, b₂c₂) = - X a₁ a₂ * ρ₀.m b₁c₁ b₂c₂ := by - --TODO Cleanup. The proof tactics aren't too long, but the intermediate state is - -- distgusting; probably this can be repackaged as several intermediate simp lemmas - -- and generally needs better API. - unfold replacement CPTPMap.traceLeft CPTPMap.prod CPTPMap.ofEquiv - simp only [id_map, Matrix.traceLeft, Finset.univ_unique, PUnit.default_eq_unit, - Finset.sum_singleton, Matrix.kroneckerMap, MState.pure_apply, Equiv.symm_symm] - erw [LinearMap.comp_apply, MatrixMap.kron_def] - erw [Finset.sum_eq_single a₁] - · simp only [Matrix.single] - erw [Matrix.of_apply] - simp only [Matrix.of_apply] - erw [Matrix.of_apply] - simp [Matrix.of_apply] - ring! - · simp +contextual - · simp + X a₁ a₂ * τ.m b₁c₁ b₂c₂ := by + show (prepDefault (dIn := dIn) (dOut := dOut)).map X (a₁, b₁c₁) (a₂, b₂c₂) = _ + rw [prepDefault_map, purify_pure_basis_default_m] + rfl private lemma purify_conj_entry (X : Matrix dIn dIn ℂ) (U : 𝐔[dIn × dOut × dOut]) (i j : dIn × dOut × dOut) : - let ρ₀ := MState.pure (Ket.basis (default : dOut × dOut)) - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement ρ₀ - let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - ((ofUnitary U) ∘ₘ prep ∘ₘ append).map X i j = + (ofUnitary U).map (prepDefault.map X) i j = ∑ α₁ : dIn, ∑ α₂ : dIn, U.val i (α₁, default, default) * X α₁ α₂ * starRingEnd ℂ (U.val j (α₂, default, default)) := by - open Kronecker in - have h_conj : - let ρ₀ := MState.pure (Ket.basis (default : dOut × dOut)) - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement ρ₀ - let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - (prep ∘ₘ append).map X = X ⊗ₖ (MState.pure (Ket.basis (default : dOut × dOut))).m := by - ext ⟨a₁, b₁c₁⟩ ⟨a₂, b₂c₂⟩ - simp [purify_prep_append_entry] - have h_conj : - let ρ₀ := MState.pure (Ket.basis (default : dOut × dOut)) - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement ρ₀ - let prep := (id ⊗ᶜᵖ zero_prep) - let append := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - (ofUnitary U).map ((prep ∘ₘ append).map X) i j = - ∑ k, ∑ l, U.val i k * (X ⊗ₖ (MState.pure (Ket.basis (default : dOut × dOut))).m) k l * starRingEnd ℂ (U.val j l) := by - simp [h_conj, Matrix.kroneckerMap] - convert! congr_arg (fun m : Matrix (dIn × dOut × dOut) (dIn × dOut × dOut) ℂ => m i j) (show (U.val * (Matrix.of fun i j => X i.1 j.1 * ((Ket.basis default) i.2 * (starRingEnd ℂ) ((Ket.basis default) j.2))) * U.val.conjTranspose) = _ from rfl) using 1 - simp [Matrix.mul_apply, Matrix.conjTranspose_apply] - ring_nf! - exact Finset.sum_comm.trans (Finset.sum_congr rfl fun _ _ => by rw [Finset.sum_mul]) - have h_restrict : ∀ x : dIn × dOut × dOut, (Ket.basis default) x.2 = if x.2 = default then 1 else 0 := by - intro x - simp [Ket.basis, eq_comm] - exact rfl - simp_all [Finset.sum_ite] - convert h_conj using 1 - · congr! 1 - convert! congr_arg (fun f => (U.val * f * U.val.conjTranspose)) ‹_› using 1 - · rw [← Finset.sum_product', ← Finset.sum_product'] - apply Finset.sum_bij (fun x _ => ((x.1, default, default), (x.2, default, default))) - · simp - exact fun a b => rfl - · simp - · simp - exact fun _ _ => rfl - · simp + have hd : (default : dOut × dOut) = (default, default) := rfl + simp only [prepDefault_map, ofUnitary_map, MatrixMap.conj_apply] + have key : ∀ l : dIn × dOut × dOut, + (U.val * Matrix.kroneckerMap (· * ·) X + (Matrix.single (default : dOut × dOut) default 1) : + Matrix (dIn × dOut × dOut) (dIn × dOut × dOut) ℂ) i l + = if l.2 = default then ∑ α : dIn, U.val i (α, default, default) * X α l.1 else 0 := by + intro l + rw [Matrix.mul_apply] + split_ifs with hl + · simp only [Matrix.kroneckerMap_apply, Matrix.single_apply, hl, and_true, hd, + mul_ite, mul_one, mul_zero, Fintype.sum_prod_type, Finset.sum_ite_eq, + Finset.mem_univ, if_true] + · simp only [Matrix.kroneckerMap_apply, Matrix.single_apply, Ne.symm hl, and_false, + if_false, mul_zero, Finset.sum_const_zero] + rw [Matrix.mul_apply] + simp only [key] + simp only [Matrix.conjTranspose_apply, RCLike.star_def, hd, Fintype.sum_prod_type, + ite_mul, zero_mul, Finset.sum_ite_eq', Finset.mem_univ, if_true, Finset.sum_mul] + exact Finset.sum_comm private lemma purify_rhs_entry (X : Matrix dIn dIn ℂ) (d₁ d₂ : dOut) (U : 𝐔[dIn × dOut × dOut]) : - let ρ₀ := MState.pure (Ket.basis (default : dOut × dOut)) - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement ρ₀ - let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - (traceLeft ∘ₘ traceLeft ∘ₘ (ofUnitary U) ∘ₘ prep ∘ₘ append).map X d₁ d₂ = + (traceLeft ∘ₘ traceLeft ∘ₘ (ofUnitary U) ∘ₘ prepDefault).map X d₁ d₂ = ∑ a : dIn, ∑ b : dOut, ∑ α₁ : dIn, ∑ α₂ : dIn, U.val (a, b, d₁) (α₁, default, default) * X α₁ α₂ * starRingEnd ℂ (U.val (a, b, d₂) (α₂, default, default)) := by - apply Eq.symm - have h := purify_conj_entry X U - rw [Finset.sum_comm] - exact Finset.sum_congr rfl fun y _ ↦ Finset.sum_congr rfl fun x _ ↦ - (h (x, y, d₁) (x, y, d₂)).symm + simp only [compose_map, LinearMap.comp_apply, traceLeft_map, traceLeftₘ, LinearMap.coe_mk, + AddHom.coe_mk, Matrix.traceLeft, Matrix.of_apply, purify_conj_entry] + exact Finset.sum_comm omit [DecidableEq dIn] [DecidableEq dOut] [Inhabited dOut] in private lemma purify_of_kraus_entry (K : (dOut × dIn) → Matrix dOut dIn ℂ) (X : Matrix dIn dIn ℂ) (d₁ d₂ : dOut) : @@ -716,19 +775,13 @@ private lemma purify_of_kraus_entry (K : (dOut × dIn) → Matrix dOut dIn ℂ) refine Finset.sum_congr rfl fun _ _ ↦ ?_ rw [Finset.sum_comm] -set_option backward.isDefEq.respectTransparency false in theorem exists_purify (Λ : CPTPMap dIn dOut) : ∃ (Λ' : CPTPMap (dIn × dOut × dOut) (dIn × dOut × dOut)), Λ'.IsUnitary ∧ - Λ = ( - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement (MState.pure (Ket.basis default)) - let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - CPTPMap.traceLeft ∘ₘ CPTPMap.traceLeft ∘ₘ Λ' ∘ₘ prep ∘ₘ append - ) := by - obtain ⟨K, hK⟩ := Λ.cp.exists_kraus _ + Λ = CPTPOp.traceLeft ∘ₘ CPTPOp.traceLeft ∘ₘ Λ' ∘ₘ prepDefault := by + obtain ⟨K, hK⟩ := Λ.map_cp.exists_kraus _ have hTP_kraus : ∑ k, (K k).conjTranspose * (K k) = 1 := - kraus_sum_eq_one_of_TP (hK ▸ Λ.TP) + kraus_sum_eq_one_of_TP (hK ▸ Λ.map_TP) let V : Matrix (dIn × dOut × dOut) dIn ℂ := fun ⟨a, b, d⟩ a' => (K (b, a)) d a' have hV : V.conjTranspose * V = 1 := @@ -737,11 +790,8 @@ theorem exists_purify (Λ : CPTPMap dIn dOut) : ⟨fun a ↦ (a, default, default), fun a₁ a₂ h ↦ by simpa using h⟩ obtain ⟨U, hU⟩ := exists_unitary_extending_isometry V hV emb use ofUnitary U, ⟨U, rfl⟩ - apply CPTPMap.ext + apply CPTPOp.ext_map ext X d₁ d₂ : 2 - -- LHS: rewrite using hK and purify_of_kraus_entry - -- RHS: use purify_rhs_entry - change Λ.map = _ at hK rw [hK, purify_of_kraus_entry, purify_rhs_entry] rw [Fintype.sum_prod_type, Finset.sum_comm] simp only [Function.Embedding.coeFn_mk, emb, V] at hU @@ -770,42 +820,20 @@ theorem purify_IsUnitary (Λ : CPTPMap dIn dOut) : Λ.purify.IsUnitary := * Tracing out the two left parts of the output is equivalent to the original channel. This theorem states that the channel output by `purify` has this property. -/ -theorem purify_trace (Λ : CPTPMap dIn dOut) : Λ = ( - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement (MState.pure (Ket.basis default)) - let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - CPTPMap.traceLeft ∘ₘ CPTPMap.traceLeft ∘ₘ Λ.purify ∘ₘ prep ∘ₘ append - ) := +theorem purify_trace (Λ : CPTPMap dIn dOut) : + Λ = CPTPOp.traceLeft ∘ₘ CPTPOp.traceLeft ∘ₘ Λ.purify ∘ₘ prepDefault := exists_purify Λ |>.choose_spec.2 --TODO Theorem: `purify` is unique up to unitary equivalence. ---TODO: Best to rewrite the "zero_prep / prep / append" as one CPTPMap.append channel when we --- define that. - -/-- The Stinespring preparation `prep ∘ append` acts on a matrix entry by the Kronecker product -with the fixed pure state `|default⟩⟨default|` on `dOut × dOut`. -/ -theorem prep_append_map_entry (X : Matrix dIn dIn ℂ) - (a₁ : dIn) (b₁c₁ : dOut × dOut) (a₂ : dIn) (b₂c₂ : dOut × dOut) : - let τ := MState.pure (Ket.basis (default : dOut × dOut)) - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement τ - let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - (prep ∘ₘ append).map X (a₁, b₁c₁) (a₂, b₂c₂) = - X a₁ a₂ * τ.m b₁c₁ b₂c₂ := by - simp [purify_prep_append_entry] - /-- The complementary channel comes from tracing out the other half (the right half) of the purified channel `purify`. -/ def complementary (Λ : CPTPMap dIn dOut) : CPTPMap dIn (dIn × dOut) := - let zero_prep : CPTPMap Unit (dOut × dOut) := replacement (MState.pure (Ket.basis default)) - let prep := (id ⊗ᶜᵖ zero_prep) - let append : CPTPMap dIn (dIn × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit dIn).symm - CPTPMap.traceRight ∘ₘ CPTPMap.assoc' ∘ₘ Λ.purify ∘ₘ prep ∘ₘ append + CPTPOp.traceRight ∘ₘ CPTPOp.assoc' ∘ₘ Λ.purify ∘ₘ prepDefault end purify section degradable -variable [DecidableEq dOut] [Inhabited dOut] [DecidableEq dOut₂] [Inhabited dOut₂] +variable [Inhabited dOut] [Inhabited dOut₂] /-- A channel is *degradable to* another, if the other can be written as a composition of a _degrading_ channel D with the original channel. -/ @@ -831,10 +859,10 @@ end degradable /-- `CPTPMap`s inherit a topology from their choi matrices. -/ instance instTop : TopologicalSpace (CPTPMap dIn dOut) := - TopologicalSpace.induced (CPTPMap.choi) instTopologicalSpaceMatrix + TopologicalSpace.induced (CPTPOp.choi) instTopologicalSpaceMatrix /-- The projection from `CPTPMap` to the Choi matrix is an embedding -/ -theorem choi_IsEmbedding : Topology.IsEmbedding (CPTPMap.choi (dIn := dIn) (dOut := dOut)) where +theorem choi_IsEmbedding : Topology.IsEmbedding (CPTPOp.choi (dIn := dIn) (dOut := dOut)) where eq_induced := rfl injective _ _ := choi_ext @@ -842,4 +870,4 @@ instance instT3Space : T3Space (CPTPMap dIn dOut) := Topology.IsEmbedding.t3Space choi_IsEmbedding end -end CPTPMap +end CPTPOp diff --git a/QuantumInfo/Channels/DegradableOrder.lean b/QuantumInfo/Channels/DegradableOrder.lean index d6d213f0d..1882748e3 100644 --- a/QuantumInfo/Channels/DegradableOrder.lean +++ b/QuantumInfo/Channels/DegradableOrder.lean @@ -48,7 +48,7 @@ def DegradablePreorder (dIn : Type*) [Fintype dIn] [DecidableEq dIn] : Preorder le_refl Λ := let _ := Λ.fst.snd.1; let _ := Λ.fst.snd.2; - ⟨CPTPMap.id, CPTPMap.compose_id Λ.snd⟩ + ⟨CPTPOp.id, CPTPOp.compose_id Λ.snd⟩ le_trans Λ₁ Λ₂ Λ₃ h₁₂ h₂₃ := by let _ := Λ₁.fst.snd.1; let _ := Λ₁.fst.snd.2; @@ -59,6 +59,6 @@ def DegradablePreorder (dIn : Type*) [Fintype dIn] [DecidableEq dIn] : Preorder obtain ⟨D₁₂, hD₁₂⟩ := h₁₂; obtain ⟨D₂₃, hD₂₃⟩ := h₂₃; use D₁₂.compose D₂₃ - rwa [CPTPMap.compose_assoc, hD₂₃] + rwa [CPTPOp.compose_assoc, hD₂₃] end diff --git a/QuantumInfo/Channels/Dual.lean b/QuantumInfo/Channels/Dual.lean index 456d81567..12609e6a3 100644 --- a/QuantumInfo/Channels/Dual.lean +++ b/QuantumInfo/Channels/Dual.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2025 Alex Meiburg. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Alex Meiburg, Dennj Osele +Authors: Alex Meiburg -/ module @@ -14,9 +14,11 @@ Definitions and theorems about the dual of a matrix map. -/ @[expose] public section +set_option backward.isDefEq.respectTransparency false + noncomputable section open ComplexOrder -open scoped Kronecker +open scoped Matrix variable {dIn dOut : Type*} [Fintype dIn] [Fintype dOut] variable {R : Type*} [CommRing R] @@ -29,88 +31,86 @@ variable [DecidableEq dIn] [DecidableEq dOut] {M : MatrixMap dIn dOut 𝕜} --This should be definable with LinearMap.adjoint, but that requires InnerProductSpace stuff --that is currently causing issues and pains (tried `open scoped Frobenius`). -/-- The dual of a map between matrices, defined by `Tr[A M(B)] = Tr[(dual M)(A) B]`. Sometimes - called the adjoint of the map instead. -/ +/-- The dual of a map between matrices, defined by `Tr[M(A) B] = Tr[A (dual M)(B)]`. Sometimes +called the adjoint of the map instead. The entries are read off by pairing against the standard +basis, which is what the definition below says. -/ @[irreducible] -def dual (M : MatrixMap dIn dOut R) : MatrixMap dOut dIn R := - let coordDual := - let iso1 := (Module.Basis.toDualEquiv <| Matrix.stdBasis R dIn dIn).symm - let iso2 := (Module.Basis.toDualEquiv <| Matrix.stdBasis R dOut dOut) - iso1 ∘ₗ LinearMap.dualMap M ∘ₗ iso2 - (Matrix.transposeLinearEquiv dIn dIn R R).toLinearMap ∘ₗ coordDual ∘ₗ - (Matrix.transposeLinearEquiv dOut dOut R R).toLinearMap +def dual (M : MatrixMap dIn dOut R) : MatrixMap dOut dIn R where + toFun B := Matrix.of fun i j ↦ (M (Matrix.single j i 1) * B).trace + map_add' B C := by + ext i j + simp [Matrix.mul_add] + map_smul' r B := by + ext i j + simp -/-- The defining property of a dual map: inner products are preserved on the opposite argument. -/ +omit [Fintype dIn] in +theorem dual_apply (M : MatrixMap dIn dOut R) (B : Matrix dOut dOut R) (i j : dIn) : + M.dual B i j = (M (Matrix.single j i 1) * B).trace := by + rw [dual] + rfl + +/-- The defining property of a dual map: traces are preserved on the opposite argument. -/ theorem Dual.trace_eq (M : MatrixMap dIn dOut R) (A : Matrix dIn dIn R) (B : Matrix dOut dOut R) : (M A * B).trace = (A * M.dual B).trace := by - have hDualIn (X Y : Matrix dIn dIn R) : - ((Matrix.stdBasis R dIn dIn).toDualEquiv Y) X = (X * Y.transpose).trace := by - simp [Module.Basis.toDualEquiv_apply, Module.Basis.toDual, Matrix.trace, Matrix.mul_apply, - Matrix.stdBasis, Fintype.sum_prod_type, mul_comm] - have hDualOut (X Y : Matrix dOut dOut R) : - ((Matrix.stdBasis R dOut dOut).toDualEquiv Y) X = (X * Y.transpose).trace := by - simp [Module.Basis.toDualEquiv_apply, Module.Basis.toDual, Matrix.trace, Matrix.mul_apply, - Matrix.stdBasis, Fintype.sum_prod_type, mul_comm] - let coordDual : MatrixMap dOut dIn R := - let iso1 := (Module.Basis.toDualEquiv <| Matrix.stdBasis R dIn dIn).symm - let iso2 := (Module.Basis.toDualEquiv <| Matrix.stdBasis R dOut dOut) - iso1 ∘ₗ LinearMap.dualMap M ∘ₗ iso2 - rw [show (M A * B).trace = - ((Matrix.stdBasis R dOut dOut).toDualEquiv B.transpose) (M A) by - simpa using (hDualOut (M A) B.transpose).symm] - rw [show - ((Matrix.stdBasis R dOut dOut).toDualEquiv B.transpose) (M A) = - ((Matrix.stdBasis R dIn dIn).toDualEquiv (coordDual B.transpose)) A by - simp [coordDual]] - simpa [dual, coordDual] using hDualIn A (coordDual B.transpose) - ---all properties below should provable just from `inner_eq`, since the definition of `dual` itself --- is pretty hairy (and maybe could be improved...) - -set_option backward.isDefEq.respectTransparency false in + have hsingle : ∀ i j : dIn, Matrix.single i j (A i j) = A i j • Matrix.single i j (1 : R) := by + intro i j + rw [Matrix.smul_single, smul_eq_mul, mul_one] + have hR : (A * M.dual B).trace = ∑ i, ∑ j, A i j * (M (Matrix.single i j 1) * B).trace := by + simp only [Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, dual_apply] + rw [hR] + conv_lhs => rw [Matrix.matrix_eq_sum_single A] + simp only [hsingle, map_sum, map_smul, Matrix.sum_mul, Matrix.trace_sum, Matrix.smul_mul, + Matrix.trace_smul, smul_eq_mul] + +--all properties below should provable just from `Dual.trace_eq`, since the definition of `dual` +-- itself is not very usable directly. + +variable {Mℂ : MatrixMap dIn dOut ℂ} + +omit [Fintype dIn] [Fintype dOut] [DecidableEq dIn] [DecidableEq dOut] in +/-- A Hermitian-preserving map commutes with the conjugate transpose. This uses that the field is +`ℂ`: over `ℝ` a map is constrained only on the symmetric matrices, and says nothing about how the +antisymmetric part is transformed. -/ +theorem IsHermitianPreserving.conjTranspose_map (h : Mℂ.IsHermitianPreserving) + (X : Matrix dIn dIn ℂ) : Mℂ Xᴴ = (Mℂ X)ᴴ := by + set P : Matrix dIn dIn ℂ := (2 : ℂ)⁻¹ • (X + Xᴴ) with hP + set Q : Matrix dIn dIn ℂ := (2 * Complex.I)⁻¹ • (X - Xᴴ) with hQ + have hPH : P.IsHermitian := by + ext a b + simp only [hP, Matrix.conjTranspose_apply, Matrix.smul_apply, Matrix.add_apply, smul_eq_mul] + simp [add_comm] + have hQH : Q.IsHermitian := by + ext a b + simp only [hQ, Matrix.conjTranspose_apply, Matrix.smul_apply, Matrix.sub_apply, smul_eq_mul] + rw [star_mul', star_sub] + simp [Complex.inv_I, mul_comm] + ring + have hX : X = P + Complex.I • Q := by + ext a b + simp only [hP, hQ, Matrix.add_apply, Matrix.smul_apply, Matrix.sub_apply, + Matrix.conjTranspose_apply, smul_eq_mul] + field_simp + ring + have hXH : Xᴴ = P - Complex.I • Q := by + ext a b + simp only [hP, hQ, Matrix.sub_apply, Matrix.smul_apply, Matrix.add_apply, + Matrix.conjTranspose_apply, smul_eq_mul] + field_simp + ring + rw [hXH, hX] + simp only [map_sub, map_add, map_smul, Matrix.conjTranspose_add, Matrix.conjTranspose_smul, + (h hPH).eq, (h hQH).eq, RCLike.star_def, Complex.conj_I, neg_smul, ← sub_eq_add_neg] + +omit [Fintype dIn] in /-- The dual of a `IsHermitianPreserving` map also `IsHermitianPreserving`. -/ -theorem IsHermitianPreserving.dual {M : MatrixMap dIn dOut ℂ} (h : M.IsHermitianPreserving) : - M.dual.IsHermitianPreserving := by - have map_conjTranspose (x : Matrix dIn dIn ℂ) : - M (Matrix.conjTranspose x) = Matrix.conjTranspose (M x) := by - have hxstar : (realPart x : Matrix dIn dIn ℂ) - Complex.I • (imaginaryPart x : Matrix dIn dIn ℂ) = - Matrix.conjTranspose x := by - rw [← Matrix.star_eq_conjTranspose] - have hreal_star : (realPart (star x) : Matrix dIn dIn ℂ) = realPart x := by - rw [realPart_apply_coe, realPart_apply_coe, star_star, add_comm] - have himag_star : (imaginaryPart (star x) : Matrix dIn dIn ℂ) = -imaginaryPart x := by - rw [imaginaryPart_apply_coe, imaginaryPart_apply_coe, star_star] - module - have h := realPart_add_I_smul_imaginaryPart (star x : Matrix dIn dIn ℂ) - rw [hreal_star, himag_star, smul_neg] at h - simpa [sub_eq_add_neg] using h - calc - M (Matrix.conjTranspose x) = M (realPart x - Complex.I • imaginaryPart x) := by - rw [← hxstar] - _ = M (realPart x) - Complex.I • M (imaginaryPart x) := by - simp [sub_eq_add_neg, map_add, map_smul] - _ = Matrix.conjTranspose (M (realPart x) + Complex.I • M (imaginaryPart x)) := by - rw [Matrix.conjTranspose_add, Matrix.conjTranspose_smul] - rw [show Matrix.conjTranspose (M (realPart x)) = M (realPart x) by - simpa [Matrix.IsHermitian] using h (HermitianMat.H _)] - rw [show Matrix.conjTranspose (M (imaginaryPart x)) = M (imaginaryPart x) by - simpa [Matrix.IsHermitian] using h (HermitianMat.H _)] - simp [sub_eq_add_neg] - _ = Matrix.conjTranspose (M x) := by - congr 1 - simpa [map_add, map_smul] using congrArg M (realPart_add_I_smul_imaginaryPart x) - intro x hx - simpa [Matrix.IsHermitian] using show Matrix.conjTranspose (M.dual x) = M.dual x by - apply Matrix.ext_iff_trace_mul_left.mpr - intro A - have htrace2 := congrArg star (Dual.trace_eq M (Matrix.conjTranspose A) (Matrix.conjTranspose x)) - rw [← Matrix.trace_conjTranspose, ← Matrix.trace_conjTranspose] at htrace2 - rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_mul, - Matrix.conjTranspose_conjTranspose, Matrix.conjTranspose_conjTranspose, - map_conjTranspose, hx, Matrix.conjTranspose_conjTranspose, - Matrix.trace_mul_comm x (M A), - Matrix.trace_mul_comm (Matrix.conjTranspose (M.dual x)) A] at htrace2 - exact htrace2.symm.trans (Dual.trace_eq M A x) +theorem IsHermitianPreserving.dual (h : Mℂ.IsHermitianPreserving) : + Mℂ.dual.IsHermitianPreserving := by + intro B hB + ext i j + rw [Matrix.conjTranspose_apply, dual_apply, dual_apply, ← Matrix.trace_conjTranspose, + Matrix.conjTranspose_mul, ← h.conjTranspose_map, Matrix.conjTranspose_single, star_one, + hB.eq, Matrix.trace_mul_comm] open MatrixOrder --TODO Cleanup, find home, abstract out to HermitianMats...? @@ -131,23 +131,20 @@ theorem _root_.Matrix.PosSemidef.trace_mul_nonneg {n : Type*} [Fintype n] [Decid Finset.sum_nonneg fun i _ ↦ h.2 (Pi.single i 1) /-- The dual of a `IsPositive` map also `IsPositive`. -/ -theorem IsPositive.dual {M : MatrixMap dIn dOut ℂ} (h : M.IsPositive) : M.dual.IsPositive := by +theorem IsPositive.dual (h : Mℂ.IsPositive) : Mℂ.dual.IsPositive := by intro x hx rw [Matrix.posSemidef_iff_dotProduct_mulVec] at hx ⊢ - use IsHermitianPreserving.dual h.IsHermitianPreserving hx.1 - intro v - have h_dual_pos : 0 ≤ (M (Matrix.vecMulVec v (star v)) * x).trace := by - --TODO Cleanup. Should be all in terms of HermitianMat - apply Matrix.PosSemidef.trace_mul_nonneg; - · apply h; - exact Matrix.posSemidef_vecMulVec_self_star v; - · rw [← Matrix.posSemidef_iff_dotProduct_mulVec] at hx - exact hx; - convert h_dual_pos using 1; - rw [ MatrixMap.Dual.trace_eq ]; - simp [ Matrix.vecMulVec, Matrix.mul_apply, Matrix.trace ]; - simp [ Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _, mul_assoc, mul_comm, mul_left_comm ]; - exact Finset.sum_comm.trans ( Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring ) + refine ⟨IsHermitianPreserving.dual h.IsHermitianPreserving hx.1, fun v => ?_⟩ + --TODO Cleanup. Should be all in terms of HermitianMat + have h_dual_pos : 0 ≤ (Mℂ (Matrix.vecMulVec v (star v)) * x).trace := + Matrix.PosSemidef.trace_mul_nonneg (h (Matrix.posSemidef_vecMulVec_self_star v)) + (Matrix.posSemidef_iff_dotProduct_mulVec.mpr hx) + rw [Dual.trace_eq] at h_dual_pos + convert h_dual_pos using 1 + simp only [Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, Matrix.vecMulVec_apply, + dotProduct, Matrix.mulVec, Pi.star_apply, Finset.mul_sum] + rw [Finset.sum_comm] + exact Finset.sum_congr rfl fun _ _ => Finset.sum_congr rfl fun _ _ => by ring /-- The dual of TracePreserving map is *not* trace-preserving, it's *unital*, that is, M*(I) = I. -/ theorem dual_Unital (h : M.IsTracePreserving) : M.dual.Unital := by @@ -169,8 +166,7 @@ If two matrix maps satisfy the trace duality property, they are equal. lemma dual_unique (M : MatrixMap dIn dOut 𝕜) (M' : MatrixMap dOut dIn 𝕜) (h : ∀ A B, (M A * B).trace = (A * M' B).trace) : M.dual = M' := by - -- By definition of dual, we know that for any A and B, the trace of (M A) * B equals the trace of - -- A * (M.dual B). + -- By definition of dual, we know that for any A and B, the trace of (M A) * B equals the trace of A * (M.dual B). have h_dual : ∀ A : Matrix dIn dIn 𝕜, ∀ B : Matrix dOut dOut 𝕜, (M A * B).trace = (A * M.dual B).trace := by exact fun A B => Dual.trace_eq M A B; -- Since these two linear maps agree on all bases, they must be equal. @@ -184,8 +180,7 @@ The Choi matrix of the dual map is the transpose of the reindexed Choi matrix of -/ lemma dual_choi_matrix (M : MatrixMap dIn dOut 𝕜) : M.dual.choi_matrix = (M.choi_matrix.transpose).reindex (Equiv.prodComm dOut dIn) (Equiv.prodComm dOut dIn) := by - -- By definition of dual, we know that - -- $(M.dual (single j₁ j₂ 1)) i₁ i₂ = (M (single i₂ i₁ 1)) j₂ j₁$. + -- By definition of dual, we know that $(M.dual (single j₁ j₂ 1)) i₁ i₂ = (M (single i₂ i₁ 1)) j₂ j₁$. have h_dual_def : ∀ (i₁ : dIn) (j₁ : dOut) (i₂ : dIn) (j₂ : dOut), (M.dual (Matrix.single j₁ j₂ 1)) i₁ i₂ = (M (Matrix.single i₂ i₁ 1)) j₂ j₁ := by intro i₁ j₁ i₂ j₂ have h_dual_def : (M.dual (Matrix.single j₁ j₂ 1)) i₁ i₂ = Matrix.trace (Matrix.single i₂ i₁ 1 * M.dual (Matrix.single j₁ j₂ 1)) := by @@ -202,8 +197,7 @@ lemma dual_choi_matrix (M : MatrixMap dIn dOut 𝕜) : aesop /-- -If the Choi matrix of a map is positive semidefinite, then the Choi matrix of its dual is also -positive semidefinite. +If the Choi matrix of a map is positive semidefinite, then the Choi matrix of its dual is also positive semidefinite. -/ lemma dual_choi_matrix_posSemidef_of_posSemidef (M : MatrixMap dIn dOut 𝕜) (h : M.choi_matrix.PosSemidef) : M.dual.choi_matrix.PosSemidef := by @@ -218,26 +212,7 @@ The dual of the identity map is the identity map. lemma dual_id : (MatrixMap.id dIn 𝕜).dual = MatrixMap.id dIn 𝕜 := by exact dual_unique (id dIn 𝕜) (id dIn 𝕜) fun A_1 => congrFun rfl -private theorem matrix_mem_span_kronecker {A C : Type*} [Fintype A] [Fintype C] - [DecidableEq A] [DecidableEq C] (X : Matrix (A × C) (A × C) 𝕜) : - X ∈ Submodule.span 𝕜 - (Set.range (fun p : (Matrix A A 𝕜 × Matrix C C 𝕜) => p.1 ⊗ₖ p.2)) := by - rw [Matrix.matrix_eq_sum_single X] - refine Submodule.sum_mem _ fun ⟨a₁, c₁⟩ _ => - Submodule.sum_mem _ fun ⟨a₂, c₂⟩ _ => ?_ - rw [show Matrix.single (a₁, c₁) (a₂, c₂) (X (a₁, c₁) (a₂, c₂)) = - X (a₁, c₁) (a₂, c₂) • - ((Matrix.single a₁ a₂ 1 : Matrix A A 𝕜) ⊗ₖ - (Matrix.single c₁ c₂ 1 : Matrix C C 𝕜)) by - ext ⟨a, c⟩ ⟨a', c'⟩ - by_cases ha₁ : a₁ = a <;> by_cases hc₁ : c₁ = c <;> - by_cases ha₂ : a₂ = a' <;> by_cases hc₂ : c₂ = c' <;> - simp [Matrix.single, Matrix.kroneckerMap_apply, ha₁, hc₁, ha₂, hc₂]] - exact Submodule.smul_mem _ (X (a₁, c₁) (a₂, c₂)) <| - Submodule.subset_span ⟨ - ((Matrix.single a₁ a₂ 1 : Matrix A A 𝕜), - (Matrix.single c₁ c₂ 1 : Matrix C C 𝕜)), rfl⟩ - +set_option maxHeartbeats 600000 in /-- The dual of a Kronecker product of maps is the Kronecker product of their duals. -/ @@ -245,87 +220,88 @@ lemma dual_kron {A B C D : Type*} [Fintype A] [Fintype B] [Fintype C] [Fintype D [DecidableEq A] [DecidableEq B] [DecidableEq C] [DecidableEq D] (M : MatrixMap A B 𝕜) (N : MatrixMap C D 𝕜) : (M ⊗ₖₘ N).dual = M.dual ⊗ₖₘ N.dual := by - refine dual_unique _ _ ?_ - intro X Y - induction matrix_mem_span_kronecker X using Submodule.span_induction with - | mem X hX => - rcases hX with ⟨⟨x₁, x₂⟩, rfl⟩ - induction matrix_mem_span_kronecker Y using Submodule.span_induction with - | mem Y hY => - rcases hY with ⟨⟨y₁, y₂⟩, rfl⟩ - simp [MatrixMap.kron_map_of_kron_state, ← Matrix.mul_kronecker_mul, - Matrix.trace_kronecker, Dual.trace_eq M x₁ y₁, Dual.trace_eq N x₂ y₂] - | zero => simp - | add Y₁ Y₂ _ _ hY₁ hY₂ => - simpa [map_add, Matrix.mul_add] using congrArg₂ (· + ·) hY₁ hY₂ - | smul a Y _ hY => simpa [map_smul, Matrix.mul_smul] using congrArg (a • ·) hY - | zero => simp - | add X₁ X₂ _ _ hX₁ hX₂ => - simpa [map_add, Matrix.add_mul] using congrArg₂ (· + ·) hX₁ hX₂ - | smul a X _ hX => simpa [map_smul, smul_mul_assoc] using congrArg (a • ·) hX + have h_trace : ∀ (X : Matrix (A × C) (A × C) 𝕜) (Y : Matrix (B × D) (B × D) 𝕜), ( (M ⊗ₖₘ N) X * Y ).trace = ( X * (M.dual ⊗ₖₘ N.dual) Y ).trace := by + -- By definition of dual, we know that $(M x1 * y1).trace = (x1 * M.dual y1).trace$ and $(N x2 * y2).trace = (x2 * N.dual y2).trace$. + have h_dual : ∀ (x1 : Matrix A A 𝕜) (y1 : Matrix B B 𝕜), (M x1 * y1).trace = (x1 * M.dual y1).trace := by + intro x1 y1 + convert MatrixMap.Dual.trace_eq M x1 y1 using 1 + have h_dual_N : ∀ (x2 : Matrix C C 𝕜) (y2 : Matrix D D 𝕜), (N x2 * y2).trace = (x2 * N.dual y2).trace := by + exact fun x2 y2 => MatrixMap.Dual.trace_eq N x2 y2; + intro X Y; + -- By definition of Kronecker product, we can write X and Y as sums of Kronecker products. + obtain ⟨X_sum, hX_sum⟩ : ∃ X_sum : Finset (Matrix A A 𝕜 × Matrix C C 𝕜), X = ∑ p ∈ X_sum, (Matrix.kroneckerMap (fun a b => a * b) p.1 p.2) := by + refine' ⟨ Finset.univ.image fun p : A × A × C × C => ( Matrix.of fun i j => if i = p.1 ∧ j = p.2.1 then X ( p.1, p.2.2.1 ) ( p.2.1, p.2.2.2 ) else 0, Matrix.of fun i j => if i = p.2.2.1 ∧ j = p.2.2.2 then 1 else 0 ), _ ⟩; + ext ⟨a, c⟩ ⟨a', c'⟩; + rw [ Finset.sum_apply, Finset.sum_apply ]; + rw [ Finset.sum_eq_single ( ( Matrix.of fun i j => if i = a ∧ j = a' then X ( a, c ) ( a', c' ) else 0, Matrix.of fun i j => if i = c ∧ j = c' then 1 else 0 ) ) ] <;> simp; + · intro a_1 b x x_1 x_2 x_3 a_2 a_3 a_4 + subst a_3 a_2 + contrapose! a_4; aesop; + · exact fun h => False.elim ( h a a' c c' ( by ext i j; aesop ) ( by ext i j; aesop ) ) + obtain ⟨Y_sum, hY_sum⟩ : ∃ Y_sum : Finset (Matrix B B 𝕜 × Matrix D D 𝕜), Y = ∑ p ∈ Y_sum, (Matrix.kroneckerMap (fun a b => a * b) p.1 p.2) := by + use Finset.image (fun p => (Matrix.of (fun i j => Y (i, p.1) (j, p.2)), Matrix.of (fun i j => if i = p.1 ∧ j = p.2 then 1 else 0))) (Finset.univ : Finset (D × D)); + ext ⟨i, j⟩ ⟨k, l⟩; simp [ Matrix.kroneckerMap ] ; + rw [ Finset.sum_image ] <;> simp [ Matrix.sum_apply ]; + · rw [ Finset.sum_eq_single ( j, l ) ] <;> aesop; + · intro p q h + subst hX_sum + simp_all only [Prod.mk.injEq, EmbeddingLike.apply_eq_iff_eq] + obtain ⟨fst, snd⟩ := p + obtain ⟨fst_1, snd_1⟩ := q + obtain ⟨left, right⟩ := h + simp_all only [Prod.mk.injEq] + apply And.intro + · have := congr_fun ( congr_fun right fst ) snd; aesop; + · replace right := congr_fun ( congr_fun right fst ) snd; aesop; + -- By linearity of the trace and the properties of the Kronecker product, we can expand both sides of the equation. + have h_expand : ∀ (x1 y1 : Matrix A A 𝕜) (x2 y2 : Matrix C C 𝕜) (x3 y3 : Matrix B B 𝕜) (x4 y4 : Matrix D D 𝕜), ( (M ⊗ₖₘ N) (Matrix.kroneckerMap (fun a b => a * b) x1 x2) * Matrix.kroneckerMap (fun a b => a * b) x3 x4 ).trace = ( Matrix.kroneckerMap (fun a b => a * b) x1 x2 * (M.dual ⊗ₖₘ N.dual) (Matrix.kroneckerMap (fun a b => a * b) x3 x4) ).trace := by + intro x1 y1 x2 y2 x3 y3 x4 y4 + simp [MatrixMap.kron_map_of_kron_state] + convert congr_arg₂ ( · * · ) ( h_dual x1 x3 ) ( h_dual_N x2 x4 ) using 1 <;> simp [ Matrix.trace, Matrix.mul_apply, Matrix.kroneckerMap_apply ] + · simp only [Finset.sum_sigma', Finset.sum_mul _ _ _, Finset.mul_sum]; + refine' Finset.sum_bij ( fun x _ => ⟨ ⟨ x.fst.1, x.snd.1 ⟩, ⟨ x.fst.2, x.snd.2 ⟩ ⟩ ) _ _ _ _ <;> simp [ mul_assoc, mul_comm, mul_left_comm ]; + · bound; + · exact fun b => ⟨ _, _, _, _, rfl ⟩; + · simp only [mul_assoc, Finset.mul_sum _ _ _, Finset.sum_mul]; + simp only [← Finset.sum_product', mul_left_comm]; + refine' Finset.sum_bij ( fun x _ => ( x.1.2, x.2.2, x.1.1, x.2.1 ) ) _ _ _ _ <;> simp; + simp_all [ Matrix.trace_sum, Finset.sum_mul _ _ _ ]; + simp [Matrix.mul_sum, h_expand] + apply dual_unique; assumption; --The dual of a CompletelyPositive map is always CP, more generally it's k-positive -- see Lemma 3.1 of https://www.math.uwaterloo.ca/~krdavids/Preprints/CDPRpositivereal.pdf -theorem IsCompletelyPositive.dual {M : MatrixMap dIn dOut ℂ} (h : M.IsCompletelyPositive) : M.dual.IsCompletelyPositive := by +theorem IsCompletelyPositive.dual (h : Mℂ.IsCompletelyPositive) : + Mℂ.dual.IsCompletelyPositive := by intro n - have h_dual_pos : (MatrixMap.dual (M ⊗ₖₘ MatrixMap.id (Fin n) ℂ)).IsPositive := by + have h_dual_pos : (MatrixMap.dual (Mℂ ⊗ₖₘ MatrixMap.id (Fin n) ℂ)).IsPositive := by exact IsPositive.dual (h n); -- By definition of complete positivity, we know that $(M ⊗ₖₘ id) dually map = M.dual ⊗ₖₘ id.dual$. - have h_dual_kron : (MatrixMap.dual (M ⊗ₖₘ MatrixMap.id (Fin n) ℂ)) = (MatrixMap.dual M) ⊗ₖₘ (MatrixMap.dual (MatrixMap.id (Fin n) ℂ)) := by - convert dual_kron M ( MatrixMap.id ( Fin n ) ℂ ) using 1; + have h_dual_kron : (MatrixMap.dual (Mℂ ⊗ₖₘ MatrixMap.id (Fin n) ℂ)) = + (MatrixMap.dual Mℂ) ⊗ₖₘ (MatrixMap.dual (MatrixMap.id (Fin n) ℂ)) := by + convert dual_kron Mℂ ( MatrixMap.id ( Fin n ) ℂ ) using 1; convert h_dual_pos using 1; rw [ h_dual_kron, dual_id ] -/-- -The composition of the dual of the inverse of the dual basis isomorphism with the dual basis -isomorphism is the evaluation map. --/ -lemma Module.Basis.dualMap_toDualEquiv_symm_comp_toDualEquiv {ι R M : Type*} [Fintype ι] [DecidableEq ι] [CommRing R] [AddCommGroup M] [Module R M] [Module.IsReflexive R M] (b : Module.Basis ι R M) : - b.toDualEquiv.symm.toLinearMap.dualMap ∘ₗ b.toDualEquiv.toLinearMap = (Module.evalEquiv R M).toLinearMap := by - ext x f; - -- Since $b.toDual$ and $b.toDualEquiv.symm$ are inverses, we have $b.toDual (b.toDualEquiv.symm f) = f$. - have h_inv : b.toDual (b.toDualEquiv.symm f) = f := by - convert! LinearEquiv.apply_symm_apply b.toDualEquiv f; - convert! congr_arg ( fun g => g x ) h_inv using 1; - -- By definition of the dual basis, we know that $(b.toDual x) (b.toDualEquiv.symm f) = f x$. - simp [Module.Basis.toDual]; - ac_rfl - -/-- -The composition of the inverse of the dual basis isomorphism with the dual of the dual basis -isomorphism is the inverse of the evaluation map. --/ -lemma Module.Basis.toDualEquiv_symm_comp_dualMap_toDualEquiv {ι R M : Type*} [Fintype ι] [DecidableEq ι] [CommRing R] [AddCommGroup M] [Module R M] [Module.IsReflexive R M] (b : Module.Basis ι R M) : - b.toDualEquiv.symm.toLinearMap ∘ₗ b.toDualEquiv.toLinearMap.dualMap = (Module.evalEquiv R M).symm.toLinearMap := by - simp [ LinearMap.ext_iff ]; - intro x - obtain ⟨y, hy⟩ : ∃ y, x = (Module.evalEquiv R M).toLinearMap y := by - exact ⟨ _, Eq.symm <| LinearEquiv.apply_symm_apply ( Module.evalEquiv R M ) x ⟩; - rw [ hy ]; - simp [ Module.evalEquiv, LinearEquiv.symm_apply_eq ]; - ext; simp [ Module.Dual.eval ] ; - simp [ Module.Basis.toDual ]; - ac_rfl - @[simp] -theorem dual_dual : M.dual.dual = M := by - refine dual_unique (M := M.dual) (M' := M) ?_ - intro A B - calc - (M.dual A * B).trace = (B * M.dual A).trace := by rw [Matrix.trace_mul_comm] - _ = (M B * A).trace := by rw [Dual.trace_eq] - _ = (A * M B).trace := by rw [Matrix.trace_mul_comm] +theorem dual_dual : M.dual.dual = M := + dual_unique M.dual M fun A B => by + rw [Matrix.trace_mul_comm, ← Dual.trace_eq, Matrix.trace_mul_comm] end MatrixMap -namespace CPTPMap +namespace CPTPOp variable [DecidableEq dIn] [DecidableEq dOut] -def dual (M : CPTPMap dIn dOut) : CPUMap dOut dIn where - toLinearMap := M.map.dual - unital := M.TP.dual - cp := .dual M.cp +/-- The dual (adjoint) of a channel, as a completely positive unital map. -/ +def dual (M : CPTPMap dIn dOut) : CPUMap dOut dIn := + CPUOp.ofMat M.map.dual (.dual M.map_cp) (M.map_TP.dual) + +/-- **Matrix analogue of the dual channel**: its matrix is the dual of the matrix. -/ +@[simp] +theorem dual_map (M : CPTPMap dIn dOut) : M.dual.map = M.map.dual := + CPUOp.map_ofMat _ _ _ theorem dual_pos (M : CPTPMap dIn dOut) {T : HermitianMat dOut ℂ} (hT : 0 ≤ T) : 0 ≤ M.dual T := by @@ -342,18 +318,27 @@ theorem dual.PTP_POVM (M : CPTPMap dIn dOut) {T : HermitianMat dOut ℂ} (hT : 0 /-- The defining property of a dual channel, as specialized to `MState.exp_val`. -/ theorem exp_val_Dual (ℰ : CPTPMap dIn dOut) (ρ : MState dIn) (T : HermitianMat dOut ℂ) : - (ℰ ρ).exp_val T = ρ.exp_val (ℰ.dual T) := by - simp only [MState.exp_val, HermitianMat.inner_eq_re_trace, RCLike.re_to_complex] + MState.exp_val (ℰ ρ) T = ρ.exp_val (ℰ.dual T) := by + have hm : (ℰ ρ : MState dOut).m = ℰ.map ρ.m := + congrArg HermitianMat.mat (PTPOp.M_apply_MState ℰ.toPTPOp ρ) + have hT : (ℰ.dual T : HermitianMat dIn ℂ).mat = ℰ.map.dual T.mat := by + rw [show (ℰ.dual T : HermitianMat dIn ℂ).mat = ℰ.dual.map T.mat from rfl, dual_map] + simp only [MState.exp_val, HermitianMat.inner_eq_re_trace, RCLike.re_to_complex, + DensityOp.mat_M, hm, hT] congr 1 apply MatrixMap.Dual.trace_eq -end CPTPMap +end CPTPOp section hermDual -set_option backward.isDefEq.respectTransparency false in +variable [DecidableEq dIn] [DecidableEq dOut] + --PULLOUT to Bundled.lean. Also use this to improve the definitions in POVM.lean. -def HPMap.ofHermitianMat {dOut : Type*} (f : HermitianMat dIn ℂ →ₗ[ℝ] HermitianMat dOut ℂ) : HPMap dIn dOut where +/-- The `ℂ`-linear extension of an `ℝ`-linear map of Hermitian matrices, obtained by splitting +the input into its real and imaginary parts. -/ +def MatrixMap.ofHermitianMat (f : HermitianMat dIn ℂ →ₗ[ℝ] HermitianMat dOut ℂ) : + MatrixMap dIn dOut ℂ where toFun x := f (realPart x) + Complex.I • f (imaginaryPart x) map_add' x y := by simp only [map_add, HermitianMat.mat_add, smul_add] @@ -382,34 +367,48 @@ def HPMap.ofHermitianMat {dOut : Type*} (f : HermitianMat dIn ℂ →ₗ[ℝ] He Complex.mul_im, add_zero, one_mul, zero_sub, neg_add_rev, zero_add, Complex.sub_im] ring_nf simp - HP _ h := by - apply Matrix.IsHermitian.add - · apply HermitianMat.H - · simp [IsSelfAdjoint.imaginaryPart h] -set_option backward.isDefEq.respectTransparency false in -omit [Fintype dOut] in +omit [Fintype dIn] [Fintype dOut] in +/-- The `ℂ`-linear extension of a map of Hermitian matrices is Hermitian-preserving. -/ +theorem MatrixMap.isHermitianPreserving_ofHermitianMat + (f : HermitianMat dIn ℂ →ₗ[ℝ] HermitianMat dOut ℂ) : + (MatrixMap.ofHermitianMat f).IsHermitianPreserving := fun _ h ↦ by + apply Matrix.IsHermitian.add + · apply HermitianMat.H + · simp [IsSelfAdjoint.imaginaryPart h] + +/-- The Hermitian-preserving map extending an `ℝ`-linear map of Hermitian matrices. -/ +def HPOp.ofHermitianMat (f : HermitianMat dIn ℂ →ₗ[ℝ] HermitianMat dOut ℂ) : HPMap dIn dOut := + HPOp.ofMat (MatrixMap.ofHermitianMat f) (MatrixMap.isHermitianPreserving_ofHermitianMat f) + +/-- **Matrix analogue of `HPOp.ofHermitianMat`**. -/ +@[simp] +theorem HPOp.map_ofHermitianMat (f : HermitianMat dIn ℂ →ₗ[ℝ] HermitianMat dOut ℂ) : + (HPOp.ofHermitianMat f).map = MatrixMap.ofHermitianMat f := + HPOp.map_ofMat _ _ + --PULLOUT @[simp] -theorem HPMap.linearMap_ofHermitianMat (f : HermitianMat dIn ℂ →ₗ[ℝ] HermitianMat dOut ℂ) : - LinearMapClass.linearMap (HPMap.ofHermitianMat f) = f := by +theorem HPOp.linearMap_ofHermitianMat (f : HermitianMat dIn ℂ →ₗ[ℝ] HermitianMat dOut ℂ) : + LinearMapClass.linearMap (HPOp.ofHermitianMat f) = f := by ext1 ⟨x, hx⟩ ext1 - simp only [ofHermitianMat, LinearMap.coe_coe] - simp only [HPMap.apply_hermitianMat_eq, HPMap.map, HermitianMat.mat_mk, - LinearMap.coe_mk, AddHom.coe_mk] + simp only [LinearMap.coe_coe, HPOp.mat_apply, HPOp.map_ofHermitianMat, + MatrixMap.ofHermitianMat, HermitianMat.mat_mk, LinearMap.coe_mk, AddHom.coe_mk] conv => enter [2, 1, 2, 1]; rw [← realPart_add_I_smul_imaginaryPart x] suffices imaginaryPart x = 0 by simp [this] simp [imaginaryPart, skewAdjoint.negISMul, show star x = x from hx] --PULLOUT -omit [Fintype dOut] in @[simp] -theorem HPMap.ofHermitianMat_linearMap (f : HPMap dIn dOut ℂ) : +theorem HPOp.ofHermitianMat_linearMap (f : HPMap dIn dOut) : ofHermitianMat (LinearMapClass.linearMap f) = f := by - ext : 3 - simp only [map, ofHermitianMat, instFunLike, LinearMap.coe_coe, LinearMap.coe_mk, AddHom.coe_mk] - simp only [realPart, imaginaryPart, LinearMap.coe_comp, Function.comp_apply] + apply HPOp.ext_map (ι := dIn) (κ := dOut) + ext x i j + simp only [map_ofHermitianMat, MatrixMap.ofHermitianMat, instFunLike, LinearMap.coe_coe, + HermitianMat.val_eq_coe, HermitianMat.mat_mk, LinearMap.coe_mk, AddHom.coe_mk, + ← map_smul, ← map_add] + simp only [map_add, map_smul, realPart, imaginaryPart, LinearMap.coe_comp, Function.comp_apply] simp only [selfAdjointPart, LinearMap.coe_mk, AddHom.coe_mk, HermitianMat.mat_mk,LinearMap.map_smul_of_tower, skewAdjoint.negISMul] simp only [Matrix.add_apply, Matrix.smul_apply, smul_eq_mul] @@ -420,28 +419,28 @@ theorem HPMap.ofHermitianMat_linearMap (f : HPMap dIn dOut ℂ) : variable (f : HPMap dIn dOut) (A : HermitianMat dIn ℂ) ---Can define one for HPMap's that has 'easier' definitional properties, uses the inner product ---structure, doesn't go through Module.Basis the same way. Requires the equivalence between ℝ-linear ---maps of HermitianMats and ℂ-linear maps of matrices. -def HPMap.hermDual : HPMap dOut dIn := - HPMap.ofHermitianMat (LinearMapClass.linearMap f).adjoint +--Can define one for HPMap's that has 'easier' definitional properties, uses the inner product structure, +--doesn't go through Module.Basis the same way. Requires the equivalence between ℝ-linear maps of HermitianMats +--and ℂ-linear maps of matrices. +def HPOp.hermDual : HPMap dOut dIn := + HPOp.ofHermitianMat (LinearMapClass.linearMap f).adjoint @[simp] -theorem HPMap.hermDual_hermDual : f.hermDual.hermDual = f := by +theorem HPOp.hermDual_hermDual : f.hermDual.hermDual = f := by simp [hermDual] open RealInnerProductSpace /-- The defining property of a dual map: inner products are preserved on the opposite argument. -/ -theorem HPMap.inner_hermDual (B : HermitianMat dOut ℂ) : +theorem HPOp.inner_hermDual (B : HermitianMat dOut ℂ) : ⟪f A, B⟫ = ⟪A, f.hermDual B⟫ := by change ⟪(LinearMapClass.linearMap f) A, B⟫ = ⟪A, (LinearMapClass.linearMap f.hermDual) B⟫ - rw [hermDual, ← LinearMap.adjoint_inner_right, HPMap.linearMap_ofHermitianMat] + rw [hermDual, ← LinearMap.adjoint_inner_right, HPOp.linearMap_ofHermitianMat] -/-- Version of `HPMap.inner_hermDual` that uses HermitiaMat.inner directly. TODO cleanup -/ -theorem HPMap.inner_hermDual' (B : HermitianMat dOut ℂ) : +/-- Version of `HPOp.inner_hermDual` that uses HermitiaMat.inner directly. TODO cleanup -/ +theorem HPOp.inner_hermDual' (B : HermitianMat dOut ℂ) : ⟪f A, B⟫ = ⟪A, f.hermDual B⟫ := - HPMap.inner_hermDual f A B + HPOp.inner_hermDual f A B /-- The dual of a `IsPositive` map also `IsPositive`. -/ theorem MatrixMap.IsPositive.hermDual (h : MatrixMap.IsPositive f.map) : f.hermDual.map.IsPositive := by @@ -458,11 +457,11 @@ theorem MatrixMap.IsPositive.hermDual (h : MatrixMap.IsPositive f.map) : f.hermD specialize h hy change Matrix.PosSemidef (f y).mat at h rw [← HermitianMat.zero_le_iff] at h - rw [HPMap.inner_hermDual, HPMap.hermDual_hermDual] + rw [HPOp.inner_hermDual, HPOp.hermDual_hermDual] apply HermitianMat.inner_ge_zero hx h /-- The dual of TracePreserving map is *not* trace-preserving, it's *unital*, that is, M*(I) = I. -/ -theorem HPMap.hermDual_Unital [DecidableEq dIn] [DecidableEq dOut] (h : MatrixMap.IsTracePreserving f.map) : +theorem HPOp.hermDual_Unital (h : MatrixMap.IsTracePreserving f.map) : f.hermDual.map.Unital := by suffices f.hermDual 1 = 1 by --todo: make this is an accessible 'constructor' for Unital rw [HermitianMat.ext_iff] at this @@ -470,20 +469,19 @@ theorem HPMap.hermDual_Unital [DecidableEq dIn] [DecidableEq dOut] (h : MatrixMa open RealInnerProductSpace in apply ext_inner_left ℝ intro v - rw [← HPMap.inner_hermDual] + rw [← HPOp.inner_hermDual] rw [HermitianMat.inner_one, HermitianMat.inner_one] --TODO change to Inner.inner exact congr(Complex.re $(h v)) --TODO: HPMap with IsTracePreserving give the HermitianMat.trace version -alias MatrixMap.IsTracePreserving.hermDual := HPMap.hermDual_Unital - -namespace PTPMap +alias MatrixMap.IsTracePreserving.hermDual := HPOp.hermDual_Unital -variable [DecidableEq dIn] [DecidableEq dOut] +namespace PTPOp +/-- The dual (adjoint) of a positive trace-preserving map, as a positive unital map. -/ def hermDual (M : PTPMap dIn dOut) : PUMap dOut dIn where - toHPMap := M.toHPMap.hermDual - pos := M.pos.hermDual - unital := M.TP.hermDual + toHPOp := M.toHPOp.hermDual + pos := (OpMap.isPositive_toMat_iff (ι := dOut) (κ := dIn) _).mp M.map_pos.hermDual + unital := (OpMap.unital_toMat_iff (ι := dOut) (κ := dIn) _).mp M.map_TP.hermDual theorem hermDual_pos (M : PTPMap dIn dOut) {T : HermitianMat dOut ℂ} (hT : 0 ≤ T) : 0 ≤ M.hermDual T := by @@ -500,10 +498,11 @@ theorem hermDual.PTP_POVM (M : PTPMap dIn dOut) {T : HermitianMat dOut ℂ} (hT /-- The defining property of a dual channel, as specialized to `MState.exp_val`. -/ theorem exp_val_hermDual (ℰ : PTPMap dIn dOut) (ρ : MState dIn) (T : HermitianMat dOut ℂ) : - (ℰ ρ).exp_val T = ρ.exp_val (ℰ.hermDual T) := by + MState.exp_val (ℰ ρ) T = ρ.exp_val (ℰ.hermDual T) := by simp only [MState.exp_val] - apply HPMap.inner_hermDual' + rw [PTPOp.M_apply_MState] + apply HPOp.inner_hermDual' -end PTPMap +end PTPOp end hermDual diff --git a/QuantumInfo/Channels/MatrixMap.lean b/QuantumInfo/Channels/MatrixMap.lean index 3b25b8564..11f31e3c8 100644 --- a/QuantumInfo/Channels/MatrixMap.lean +++ b/QuantumInfo/Channels/MatrixMap.lean @@ -142,6 +142,18 @@ def of_kraus (M N : κ → Matrix B A R) : MatrixMap A B R := map_smul' r x := by rw [RingHom.id_apply, Matrix.mul_smul, Matrix.smul_mul] } +open scoped Matrix in +omit [DecidableEq A] in +theorem of_kraus_apply (M N : κ → Matrix B A R) (X : Matrix A A R) : + of_kraus M N X = ∑ k : κ, M k * X * (N k)ᴴ := by + simp [of_kraus] + +omit [DecidableEq A] in +/-- Reindexing the Kraus operators along an equivalence does not change the map. -/ +theorem of_kraus_comp_equiv {κ' : Type*} [Fintype κ'] (e : κ' ≃ κ) (M N : κ → Matrix B A R) : + of_kraus (M ∘ e) (N ∘ e) = of_kraus M N := + Fintype.sum_equiv e _ _ fun _ => rfl + end kraus section kraus_exists @@ -224,7 +236,6 @@ noncomputable def kron [CommSemiring R] (M₁ : MatrixMap A B R) (M₂ : MatrixM scoped[MatrixMap] infixl:100 " ⊗ₖₘ " => MatrixMap.kron -set_option maxHeartbeats 800000 in set_option synthInstance.maxHeartbeats 60000 in /-- The extensional definition of the Kronecker product `MatrixMap.kron`, in terms of the entries of its image. -/ @@ -447,6 +458,12 @@ theorem choi_matrix_piProd (Λi : ∀ i, MatrixMap (dI i) (dO i) R) : -- notation3:100 "⨂ₜₘ "(...)", "r:(scoped f => tprod R f) => r -- syntax (name := bigsum) "∑ " bigOpBinders ("with " term)? ", " term:67 : term +/-- The tensor product of identity maps is the identity map. -/ +@[simp] +theorem piProd_id : piProd (fun i ↦ (id (dI i) R)) = id ((i : ι) → dI i) R := by + simp [piProd, id, PiTensorProduct.map_id, LinearMap.toMatrix_id_eq_basis_toMatrix, + Module.Basis.toMatrix_self] + /-- Composition of `MatrixMap.piProd` maps distributes over the tensor product. -/ @@ -459,11 +476,41 @@ theorem piProd_comp piProd (fun i ↦ (Λ₂ i) ∘ₗ (Λ₁ i)) = (piProd Λ₂) ∘ₗ (piProd Λ₁) := by simp [piProd, PiTensorProduct.map_comp, ← Matrix.toLin_mul, ← LinearMap.toMatrix_comp] -@[simp] -theorem piProd_id : - piProd (fun i ↦ (LinearMap.id : MatrixMap (dI i) (dI i) R)) = LinearMap.id := by - simp [piProd, PiTensorProduct.map_id, LinearMap.toMatrix_id_eq_basis_toMatrix, - Module.Basis.toMatrix_self, Matrix.reindex_apply, Matrix.submatrix_one_equiv, - Matrix.toLin_one] +omit [∀i, DecidableEq (dO i)] in +/-- **Matrix analogue of the Pi-type tensor product**: on matrix units, `MatrixMap.piProd` acts +as the Kronecker product `Matrix.piProd` of the images of the individual factors. Since the matrix +units span, this determines `piProd` completely. -/ +theorem piProd_single (Λi : ∀ i, MatrixMap (dI i) (dO i) R) (a b : ∀ i, dI i) : + piProd Λi (Matrix.single a b 1) = + Matrix.piProd (fun i ↦ Λi i (Matrix.single (a i) (b i) 1)) := by + rw [show Matrix.single a b (1 : R) = Matrix.stdBasis R _ _ (a, b) from + (Matrix.stdBasis_eq_single _ _ _).symm] + simp only [piProd] + rw [Matrix.toLin_self] + simp only [Matrix.reindex_apply, Matrix.submatrix_apply, LinearMap.toMatrix_apply, + _root_.Basis.piTensorProduct_apply, Equiv.arrowProdEquivProdArrow_symm_apply, + PiTensorProduct.map_tprod, _root_.Basis.piTensorProduct_repr_tprod_apply, + Matrix.stdBasis_repr_apply, Matrix.stdBasis_eq_single] + rw [← Module.Basis.sum_repr (Matrix.stdBasis R ((i : ι) → dO i) ((i : ι) → dO i)) + (Matrix.piProd fun i ↦ (Λi i) (Matrix.single (a i) (b i) 1))] + exact Finset.sum_congr rfl fun x _ ↦ by rw [Matrix.stdBasis_repr_apply]; rfl + +omit [(i : ι) → DecidableEq (dO i)] in +open scoped Matrix in +/-- Kraus representations of the factors assemble into a Kraus representation of the +`MatrixMap.piProd`, whose Kraus operators are the Kronecker products of the individual ones. -/ +theorem piProd_of_kraus [StarRing R] {κ : ι → Type*} [∀ i, Fintype (κ i)] + (K : ∀ i, κ i → Matrix (dO i) (dI i) R) : + piProd (fun i ↦ of_kraus (K i) (K i)) = + of_kraus (fun k : ∀ i, κ i ↦ Matrix.piProd fun i ↦ K i (k i)) + (fun k : ∀ i, κ i ↦ Matrix.piProd fun i ↦ K i (k i)) := by + refine (Matrix.stdBasis R ((i : ι) → dI i) ((i : ι) → dI i)).ext fun p ↦ ?_ + obtain ⟨a, b⟩ := p + rw [Matrix.stdBasis_eq_single, piProd_single, of_kraus_apply] + simp only [of_kraus_apply] + rw [← Matrix.sum_piProd] + refine Finset.sum_congr rfl fun k _ ↦ ?_ + rw [Matrix.conjTranspose_piProd, ← Matrix.piProd_single a b, Matrix.piProd_mul, + Matrix.piProd_mul] end pi diff --git a/QuantumInfo/Channels/OpMap.lean b/QuantumInfo/Channels/OpMap.lean new file mode 100644 index 000000000..f3b47104d --- /dev/null +++ b/QuantumInfo/Channels/OpMap.lean @@ -0,0 +1,401 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import QuantumInfo.Channels.Unbundled + +/-! # Linear maps of operators + +A quantum channel is a linear map taking operators on one Hilbert space to operators on another. +This file introduces `OpMap E F`, the basis-free counterpart of `MatrixMap`, together with the +properties a channel can have: `IsTracePreserving`, `Unital`, `IsHermitianPreserving`, +`IsPositive`, and `IsCompletelyPositive`. + +The first four are stated directly on operators. Complete positivity is the exception: it is +defined through the matrix representation, because the Choi matrix and the ampliation +`Φ ⊗ id` are matrix constructions. `OpMap.isCompletelyPositiveOf_congr` shows that the resulting +notion does not depend on the choice of orthonormal bases, which is what makes the definition +legitimate. + +`OpMap.toMatOf` produces the matrix map in given orthonormal bases, and `OpMap.toMat` the one in +the preferred bases of a `StdBasis` instance. The `*_toMatOf_iff` and `*_toMat_iff` lemmas transfer +each property in both directions, so that a matrix-level fact about a channel and its +operator-level counterpart are interchangeable. +-/ + +@[expose] public section + +noncomputable section + +open scoped ComplexOrder + +/-- An `OpMap` is a linear map from operators on `E` to operators on `F`. This is the basis-free +form of `MatrixMap`; `OpMap.toMat` recovers the matrix map in the preferred bases. -/ +abbrev OpMap (E F : Type*) [NormedAddCommGroup E] [InnerProductSpace ℂ E] [CompleteSpace E] + [NormedAddCommGroup F] [InnerProductSpace ℂ F] [CompleteSpace F] := + (E →L[ℂ] E) →ₗ[ℂ] (F →L[ℂ] F) + +namespace OpMap + +variable {E F G ι κ ν ι' κ' : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [CompleteSpace E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [CompleteSpace F] +variable [NormedAddCommGroup G] [InnerProductSpace ℂ G] [CompleteSpace G] + +section Defs + +/-- An operator map is *trace preserving* if the trace of the output always equals the trace of +the input. -/ +def IsTracePreserving (Φ : OpMap E F) : Prop := + ∀ A : E →L[ℂ] E, LinearMap.trace ℂ F ↑(Φ A) = LinearMap.trace ℂ E ↑A + +/-- An operator map is *unital* if it preserves the identity. -/ +def Unital (Φ : OpMap E F) : Prop := + Φ 1 = 1 + +/-- An operator map is *Hermitian preserving* if it maps self-adjoint operators to self-adjoint +operators. -/ +def IsHermitianPreserving (Φ : OpMap E F) : Prop := + ∀ ⦃A : E →L[ℂ] E⦄, IsSelfAdjoint A → IsSelfAdjoint (Φ A) + +/-- An operator map is *positive* if it maps positive operators to positive operators. -/ +def IsPositive (Φ : OpMap E F) : Prop := + ∀ ⦃A : E →L[ℂ] E⦄, 0 ≤ A → 0 ≤ Φ A + +end Defs + +section Closure + +variable {Φ : OpMap E F} {Ψ : OpMap F G} + +theorem isTracePreserving_id : IsTracePreserving (LinearMap.id : OpMap E E) := + fun _ ↦ rfl + +theorem IsTracePreserving.comp (hΦ : Φ.IsTracePreserving) (hΨ : Ψ.IsTracePreserving) : + IsTracePreserving (Ψ ∘ₗ Φ) := + fun A ↦ (hΨ (Φ A)).trans (hΦ A) + +theorem unital_id : Unital (LinearMap.id : OpMap E E) := + rfl + +theorem Unital.comp (hΦ : Φ.Unital) (hΨ : Ψ.Unital) : Unital (Ψ ∘ₗ Φ) := by + rw [Unital, LinearMap.comp_apply, hΦ, hΨ] + +theorem isHermitianPreserving_id : IsHermitianPreserving (LinearMap.id : OpMap E E) := + fun _ h ↦ h + +theorem IsHermitianPreserving.comp (hΦ : Φ.IsHermitianPreserving) + (hΨ : Ψ.IsHermitianPreserving) : IsHermitianPreserving (Ψ ∘ₗ Φ) := + fun _ h ↦ hΨ (hΦ h) + +theorem isPositive_id : IsPositive (LinearMap.id : OpMap E E) := + fun _ h ↦ h + +theorem IsPositive.comp (hΦ : Φ.IsPositive) (hΨ : Ψ.IsPositive) : IsPositive (Ψ ∘ₗ Φ) := + fun _ h ↦ hΨ (hΦ h) + +end Closure + +section ToMatOf + +variable [Fintype ι] [DecidableEq ι] [Fintype κ] [DecidableEq κ] [Fintype ν] [DecidableEq ν] +variable [Fintype ι'] [DecidableEq ι'] [Fintype κ'] [DecidableEq κ'] + +/-- The linear equivalence between operator maps and matrix maps determined by given orthonormal +bases of the source and target. -/ +def matEquivOf (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) : + OpMap E F ≃ₗ[ℂ] MatrixMap ι κ ℂ := + LinearEquiv.arrowCongr (StdBasis.toMatOf bE).toAlgEquiv.toLinearEquiv + (StdBasis.toMatOf bF).toAlgEquiv.toLinearEquiv + +/-- The matrix map representing an operator map in given orthonormal bases. -/ +def toMatOf (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) (Φ : OpMap E F) : + MatrixMap ι κ ℂ := + matEquivOf bE bF Φ + +theorem toMatOf_apply (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) (Φ : OpMap E F) + (M : Matrix ι ι ℂ) : + toMatOf bE bF Φ M = StdBasis.toMatOf bF (Φ ((StdBasis.toMatOf bE).symm M)) := + rfl + +@[simp] +theorem toMatOf_apply_toMatOf (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) + (Φ : OpMap E F) (A : E →L[ℂ] E) : + toMatOf bE bF Φ (StdBasis.toMatOf bE A) = StdBasis.toMatOf bF (Φ A) := by + rw [toMatOf_apply, StarAlgEquiv.symm_apply_apply] + +theorem toMatOf_injective (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) : + Function.Injective (toMatOf bE bF) := + (matEquivOf bE bF).injective + +@[simp] +theorem toMatOf_id (bE : OrthonormalBasis ι ℂ E) : + toMatOf bE bE (LinearMap.id : OpMap E E) = MatrixMap.id ι ℂ := by + refine LinearMap.ext fun M ↦ ?_ + rw [toMatOf_apply] + simp [MatrixMap.id] + +theorem toMatOf_comp (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) + (bG : OrthonormalBasis ν ℂ G) (Φ : OpMap E F) (Ψ : OpMap F G) : + toMatOf bE bG (Ψ ∘ₗ Φ) = toMatOf bF bG Ψ ∘ₗ toMatOf bE bF Φ := by + refine LinearMap.ext fun M ↦ ?_ + rw [LinearMap.comp_apply, toMatOf_apply, toMatOf_apply, toMatOf_apply, + StarAlgEquiv.symm_apply_apply] + rfl + +/-- Changing the orthonormal bases conjugates the matrix representation of an operator map by a +unitary on either side. -/ +theorem toMatOf_congr (bE bE' : OrthonormalBasis ι ℂ E) (bF bF' : OrthonormalBasis κ ℂ F) + (Φ : OpMap E F) : + toMatOf bE' bF' Φ = + MatrixMap.conj (star (StdBasis.changeOfBasis bF bF') : Matrix κ κ ℂ) ∘ₗ + toMatOf bE bF Φ ∘ₗ + MatrixMap.conj (StdBasis.changeOfBasis bE bE' : Matrix ι ι ℂ) := by + have : FiniteDimensional ℂ E := Module.Basis.finiteDimensional_of_finite bE.toBasis + have : FiniteDimensional ℂ F := Module.Basis.finiteDimensional_of_finite bF.toBasis + refine LinearMap.ext fun M ↦ ?_ + rw [LinearMap.comp_apply, LinearMap.comp_apply, MatrixMap.conj_apply, MatrixMap.conj_apply, + toMatOf_apply, toMatOf_apply, StdBasis.toMatOf_symm_conj bE bE', + StdBasis.toMatOf_conj bF bF'] + simp [Matrix.star_eq_conjTranspose] + +/-- Relabelling the index types of the bases relabels the matrix representation. -/ +theorem toMatOf_reindex (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) + (e : ι ≃ ι') (f : κ ≃ κ') (Φ : OpMap E F) : + toMatOf (bE.reindex e) (bF.reindex f) Φ = + MatrixMap.submatrix ℂ f.symm ∘ₗ toMatOf bE bF Φ ∘ₗ MatrixMap.submatrix ℂ e := by + have : FiniteDimensional ℂ E := Module.Basis.finiteDimensional_of_finite bE.toBasis + have : FiniteDimensional ℂ F := Module.Basis.finiteDimensional_of_finite bF.toBasis + refine LinearMap.ext fun M ↦ ?_ + rw [LinearMap.comp_apply, LinearMap.comp_apply, toMatOf_apply, toMatOf_apply, + StdBasis.toMatOf_symm_reindex, StdBasis.toMatOf_reindex] + rfl + +end ToMatOf + +section TransferOf + +variable [Fintype ι] [DecidableEq ι] [Fintype κ] [DecidableEq κ] +variable (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) + +@[simp] +theorem isTracePreserving_toMatOf_iff [FiniteDimensional ℂ E] [FiniteDimensional ℂ F] + (Φ : OpMap E F) : (toMatOf bE bF Φ).IsTracePreserving ↔ Φ.IsTracePreserving := by + constructor + · intro h A + have := h (StdBasis.toMatOf bE A) + rwa [toMatOf_apply_toMatOf, StdBasis.trace_toMatOf, StdBasis.trace_toMatOf] at this + · intro h M + rw [toMatOf_apply, StdBasis.trace_toMatOf, h, ← StdBasis.trace_toMatOf bE, + StarAlgEquiv.apply_symm_apply] + +@[simp] +theorem unital_toMatOf_iff (Φ : OpMap E F) : (toMatOf bE bF Φ).Unital ↔ Φ.Unital := by + rw [MatrixMap.Unital, Unital, toMatOf_apply] + rw [show (1 : Matrix ι ι ℂ) = StdBasis.toMatOf bE 1 by simp, StarAlgEquiv.symm_apply_apply] + simp + +@[simp] +theorem isHermitianPreserving_toMatOf_iff (Φ : OpMap E F) : + (toMatOf bE bF Φ).IsHermitianPreserving ↔ Φ.IsHermitianPreserving := by + constructor + · intro h A hA + rw [← StdBasis.isHermitian_toMatOf_iff bF, ← toMatOf_apply_toMatOf bE] + exact h ((StdBasis.isHermitian_toMatOf_iff bE A).mpr hA) + · intro h M hM + rw [toMatOf_apply, StdBasis.isHermitian_toMatOf_iff] + exact h ((StdBasis.isHermitian_toMatOf_iff bE _).mp (by rwa [StarAlgEquiv.apply_symm_apply])) + +@[simp] +theorem isPositive_toMatOf_iff (Φ : OpMap E F) : + (toMatOf bE bF Φ).IsPositive ↔ Φ.IsPositive := by + constructor + · intro h A hA + rw [← StdBasis.posSemidef_toMatOf_iff_nonneg bF, ← toMatOf_apply_toMatOf bE] + exact h ((StdBasis.posSemidef_toMatOf_iff_nonneg bE A).mpr hA) + · intro h M hM + rw [toMatOf_apply, StdBasis.posSemidef_toMatOf_iff_nonneg] + exact h ((StdBasis.posSemidef_toMatOf_iff_nonneg bE _).mp + (by rwa [StarAlgEquiv.apply_symm_apply])) + +end TransferOf + +section CompletelyPositive + +variable [Fintype ι] [DecidableEq ι] [Fintype κ] [DecidableEq κ] [Fintype ν] [DecidableEq ν] +variable [Fintype ι'] [DecidableEq ι'] [Fintype κ'] [DecidableEq κ'] + +/-- An operator map is *completely positive relative to given orthonormal bases* if its matrix +representation in those bases is completely positive. By `OpMap.isCompletelyPositiveOf_congr` the +bases do not matter, so `OpMap.IsCompletelyPositive` is the notion to use. -/ +def IsCompletelyPositiveOf (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) + (Φ : OpMap E F) : Prop := + (toMatOf bE bF Φ).IsCompletelyPositive + +theorem IsCompletelyPositiveOf.reindex {bE : OrthonormalBasis ι ℂ E} + {bF : OrthonormalBasis κ ℂ F} {Φ : OpMap E F} (h : IsCompletelyPositiveOf bE bF Φ) + (e : ι ≃ ι') (f : κ ≃ κ') : IsCompletelyPositiveOf (bE.reindex e) (bF.reindex f) Φ := by + rw [IsCompletelyPositiveOf, toMatOf_reindex] + exact ((MatrixMap.IsCompletelyPositive.submatrix _).comp h).comp + (MatrixMap.IsCompletelyPositive.submatrix _) + +/-- Complete positivity is unchanged by relabelling the index types of the bases. -/ +theorem isCompletelyPositiveOf_reindex (bE : OrthonormalBasis ι ℂ E) + (bF : OrthonormalBasis κ ℂ F) (e : ι ≃ ι') (f : κ ≃ κ') (Φ : OpMap E F) : + IsCompletelyPositiveOf (bE.reindex e) (bF.reindex f) Φ ↔ IsCompletelyPositiveOf bE bF Φ := by + refine ⟨fun h ↦ ?_, fun h ↦ h.reindex e f⟩ + have h2 := h.reindex e.symm f.symm + rwa [OrthonormalBasis.reindex_reindex_symm, OrthonormalBasis.reindex_reindex_symm] at h2 + +/-- **Complete positivity does not depend on the choice of orthonormal bases**, not even on the +types indexing them. -/ +theorem isCompletelyPositiveOf_congr (bE : OrthonormalBasis ι ℂ E) (bE' : OrthonormalBasis ι' ℂ E) + (bF : OrthonormalBasis κ ℂ F) (bF' : OrthonormalBasis κ' ℂ F) (Φ : OpMap E F) : + IsCompletelyPositiveOf bE' bF' Φ ↔ IsCompletelyPositiveOf bE bF Φ := by + have hE : Fintype.card ι = Fintype.card ι' := by + rw [← Module.finrank_eq_card_basis bE.toBasis, ← Module.finrank_eq_card_basis bE'.toBasis] + have hF : Fintype.card κ = Fintype.card κ' := by + rw [← Module.finrank_eq_card_basis bF.toBasis, ← Module.finrank_eq_card_basis bF'.toBasis] + obtain ⟨e⟩ : Nonempty (ι ≃ ι') := ⟨Fintype.equivOfCardEq hE⟩ + obtain ⟨f⟩ : Nonempty (κ ≃ κ') := ⟨Fintype.equivOfCardEq hF⟩ + rw [← isCompletelyPositiveOf_reindex bE bF e f] + have key : ∀ (cE cE' : OrthonormalBasis ι' ℂ E) (cF cF' : OrthonormalBasis κ' ℂ F), + IsCompletelyPositiveOf cE cF Φ → IsCompletelyPositiveOf cE' cF' Φ := by + intro cE cE' cF cF' h + rw [IsCompletelyPositiveOf, toMatOf_congr cE cE' cF cF'] + exact ((MatrixMap.congruence_CP _).comp h).comp (MatrixMap.congruence_CP _) + exact ⟨key _ _ _ _, key _ _ _ _⟩ + +variable [FiniteDimensional ℂ E] [FiniteDimensional ℂ F] [FiniteDimensional ℂ G] + +/-- An operator map is *completely positive* if its matrix representation is. + +Unlike the other properties of an `OpMap`, this one has to be stated through a matrix +representation, because the ampliation `Φ ⊗ id` used to define complete positivity is a matrix +construction. The basis used is immaterial: `OpMap.isCompletelyPositive_iff_of` says that any +orthonormal bases, indexed by any types, give the same answer. -/ +def IsCompletelyPositive (Φ : OpMap E F) : Prop := + IsCompletelyPositiveOf (stdOrthonormalBasis ℂ E) (stdOrthonormalBasis ℂ F) Φ + +/-- Complete positivity may be checked in any pair of orthonormal bases. -/ +theorem isCompletelyPositive_iff_of (bE : OrthonormalBasis ι ℂ E) (bF : OrthonormalBasis κ ℂ F) + (Φ : OpMap E F) : Φ.IsCompletelyPositive ↔ IsCompletelyPositiveOf bE bF Φ := + isCompletelyPositiveOf_congr bE (stdOrthonormalBasis ℂ E) bF (stdOrthonormalBasis ℂ F) Φ + +@[simp] +theorem isCompletelyPositive_toMatOf_iff (bE : OrthonormalBasis ι ℂ E) + (bF : OrthonormalBasis κ ℂ F) (Φ : OpMap E F) : + (toMatOf bE bF Φ).IsCompletelyPositive ↔ Φ.IsCompletelyPositive := + (isCompletelyPositive_iff_of bE bF Φ).symm + +theorem IsCompletelyPositive.isPositive {Φ : OpMap E F} (h : Φ.IsCompletelyPositive) : + Φ.IsPositive := + (isPositive_toMatOf_iff (stdOrthonormalBasis ℂ E) (stdOrthonormalBasis ℂ F) Φ).mp + (MatrixMap.IsCompletelyPositive.IsPositive h) + +theorem IsPositive.isHermitianPreserving {Φ : OpMap E F} (h : Φ.IsPositive) : + Φ.IsHermitianPreserving := + (isHermitianPreserving_toMatOf_iff (stdOrthonormalBasis ℂ E) (stdOrthonormalBasis ℂ F) Φ).mp + ((isPositive_toMatOf_iff _ _ Φ).mpr h).IsHermitianPreserving + +theorem IsCompletelyPositive.isHermitianPreserving {Φ : OpMap E F} (h : Φ.IsCompletelyPositive) : + Φ.IsHermitianPreserving := + h.isPositive.isHermitianPreserving + +theorem IsCompletelyPositive.comp {Φ : OpMap E F} {Ψ : OpMap F G} (hΦ : Φ.IsCompletelyPositive) + (hΨ : Ψ.IsCompletelyPositive) : IsCompletelyPositive (Ψ ∘ₗ Φ) := by + rw [isCompletelyPositive_iff_of (stdOrthonormalBasis ℂ E) (stdOrthonormalBasis ℂ G), + IsCompletelyPositiveOf, toMatOf_comp _ (stdOrthonormalBasis ℂ F)] + exact MatrixMap.IsCompletelyPositive.comp + ((isCompletelyPositive_iff_of _ _ Φ).mp hΦ) ((isCompletelyPositive_iff_of _ _ Ψ).mp hΨ) + +theorem isCompletelyPositive_id : IsCompletelyPositive (LinearMap.id : OpMap E E) := by + show IsCompletelyPositiveOf (stdOrthonormalBasis ℂ E) (stdOrthonormalBasis ℂ E) _ + rw [IsCompletelyPositiveOf, toMatOf_id] + exact MatrixMap.IsCompletelyPositive.id (A := Fin (Module.finrank ℂ E)) (R := ℂ) + +end CompletelyPositive + +section Preferred + +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] +variable [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] +variable [Fintype ν] [DecidableEq ν] [StdBasis ℂ G ν] + +variable (E F ι κ) in +/-- The linear equivalence between operator maps and matrix maps determined by the preferred bases +of the source and target. -/ +def matEquiv : OpMap E F ≃ₗ[ℂ] MatrixMap ι κ ℂ := + matEquivOf stdBasis stdBasis + +/-- The matrix map representing an operator map in the preferred bases. -/ +def toMat (Φ : OpMap E F) : MatrixMap ι κ ℂ := + matEquiv E F ι κ Φ + +variable (E F) in +/-- The operator map with a given matrix in the preferred bases. -/ +def ofMat (M : MatrixMap ι κ ℂ) : OpMap E F := + (matEquiv E F ι κ).symm M + +@[simp] +theorem toMat_ofMat (M : MatrixMap ι κ ℂ) : toMat (ofMat E F M) = M := + (matEquiv E F ι κ).apply_symm_apply M + +@[simp] +theorem ofMat_toMat (Φ : OpMap E F) : ofMat E F (toMat (ι := ι) (κ := κ) Φ) = Φ := + (matEquiv E F ι κ).symm_apply_apply Φ + +theorem toMat_eq_toMatOf (Φ : OpMap E F) : + toMat (ι := ι) (κ := κ) Φ = toMatOf stdBasis stdBasis Φ := + rfl + +theorem toMat_apply (Φ : OpMap E F) (M : Matrix ι ι ℂ) : + toMat (κ := κ) Φ M = StdBasis.toMat ℂ F κ (Φ ((StdBasis.toMat ℂ E ι).symm M)) := + rfl + +@[simp] +theorem toMat_apply_toMat (Φ : OpMap E F) (A : E →L[ℂ] E) : + toMat (ι := ι) (κ := κ) Φ (StdBasis.toMat ℂ E ι A) = StdBasis.toMat ℂ F κ (Φ A) := + toMatOf_apply_toMatOf _ _ Φ A + +theorem toMat_injective : Function.Injective (toMat (E := E) (F := F) (ι := ι) (κ := κ)) := + (matEquiv E F ι κ).injective + +@[simp] +theorem toMat_id : toMat (ι := ι) (κ := ι) (LinearMap.id : OpMap E E) = MatrixMap.id ι ℂ := + toMatOf_id _ + +theorem toMat_comp (Φ : OpMap E F) (Ψ : OpMap F G) : + toMat (ι := ι) (κ := ν) (Ψ ∘ₗ Φ) = toMat (ι := κ) (κ := ν) Ψ ∘ₗ toMat (ι := ι) (κ := κ) Φ := + toMatOf_comp _ _ _ Φ Ψ + +@[simp] +theorem isTracePreserving_toMat_iff (Φ : OpMap E F) : + (toMat (ι := ι) (κ := κ) Φ).IsTracePreserving ↔ Φ.IsTracePreserving := + isTracePreserving_toMatOf_iff _ _ Φ + +@[simp] +theorem unital_toMat_iff (Φ : OpMap E F) : (toMat (ι := ι) (κ := κ) Φ).Unital ↔ Φ.Unital := + unital_toMatOf_iff _ _ Φ + +@[simp] +theorem isHermitianPreserving_toMat_iff (Φ : OpMap E F) : + (toMat (ι := ι) (κ := κ) Φ).IsHermitianPreserving ↔ Φ.IsHermitianPreserving := + isHermitianPreserving_toMatOf_iff _ _ Φ + +@[simp] +theorem isPositive_toMat_iff (Φ : OpMap E F) : + (toMat (ι := ι) (κ := κ) Φ).IsPositive ↔ Φ.IsPositive := + isPositive_toMatOf_iff _ _ Φ + +@[simp] +theorem isCompletelyPositive_toMat_iff (Φ : OpMap E F) : + (toMat (ι := ι) (κ := κ) Φ).IsCompletelyPositive ↔ Φ.IsCompletelyPositive := + isCompletelyPositive_toMatOf_iff _ _ Φ + +end Preferred + +end OpMap diff --git a/QuantumInfo/Channels/Pinching.lean b/QuantumInfo/Channels/Pinching.lean index 094d5f123..f794517d3 100644 --- a/QuantumInfo/Channels/Pinching.lean +++ b/QuantumInfo/Channels/Pinching.lean @@ -38,13 +38,13 @@ def pinching_kraus (ρ : MState d) : spectrum ℝ ρ.m → HermitianMat d ℂ := theorem pinching_kraus_commutes (ρ : MState d) (i : spectrum ℝ ρ.m) : Commute (pinching_kraus ρ i).mat ρ.m := by - rw [MState.m, ← ρ.M.cfc_id, commute_iff_eq, pinching_kraus] + rw [DensityOp.m, ← ρ.M.cfc_id, commute_iff_eq, pinching_kraus] rw [← ρ.M.mat_cfc_mul, ← ρ.M.mat_cfc_mul] congr 2; ext; simp theorem pinching_kraus_mul_self (ρ : MState d) (i : spectrum ℝ ρ.m) : (pinching_kraus ρ i).mat * ρ.m = i.val • pinching_kraus ρ i := by - dsimp [MState.m] + dsimp [DensityOp.m] nth_rw 1 [← ρ.M.cfc_id] rw [pinching_kraus] rw [← ρ.M.mat_cfc_mul, ← HermitianMat.mat_smul] @@ -91,8 +91,8 @@ theorem pinching_sum (ρ : MState d) : ∑ k, pinching_kraus ρ k = 1 := by rw [← cfc_sum, Finset.sum_fn, cfc_congr heq, cfc_one (R := ℝ) (ha := _)] rw [IsSelfAdjoint, Matrix.star_eq_conjTranspose, ρ.Hermitian] -def pinching_map (ρ : MState d) : CPTPMap d d ℂ := - CPTPMap.of_kraus_CPTPMap (HermitianMat.mat ∘ pinching_kraus ρ) (by +def pinching_map (ρ : MState d) : CPTPMap d d := + CPTPOp.of_kraus_CPTPMap (HermitianMat.mat ∘ pinching_kraus ρ) (by conv => enter [1, 2, k] rw [Function.comp_apply, (pinching_kraus ρ k).H, ←pow_two] @@ -105,6 +105,9 @@ def pinching_map (ρ : MState d) : CPTPMap d d ℂ := theorem pinchingMap_apply_M (σ ρ : MState d) : (pinching_map σ ρ).M = ⟨_, (MatrixMap.of_kraus_isCompletelyPositive (HermitianMat.mat ∘ pinching_kraus σ)).IsPositive.IsHermitianPreserving ρ.M.H⟩ := by + ext1 + rw [DensityOp.mat_M, CPTPOp.mat_coe_eq_apply_mat] + simp only [pinching_map, CPTPOp.map_of_kraus_CPTPMap] rfl theorem pinching_eq_sum_conj (σ ρ : MState d) : (pinching_map σ ρ).M = @@ -115,7 +118,7 @@ theorem pinching_eq_sum_conj (σ ρ : MState d) : (pinching_map σ ρ).M = theorem pinching_commutes_kraus (σ ρ : MState d) (i : spectrum ℝ σ.m) : Commute (pinching_map σ ρ).m (pinching_kraus σ i).mat := by have h_expand := pinching_eq_sum_conj σ ρ - simp only [MState.mat_M] at h_expand + simp only [DensityOp.mat_M] at h_expand simp only [Commute, h_expand]; simp only [SemiconjBy, Finset.sum_mul] simp only [mul_assoc, Finset.mul_sum] @@ -125,10 +128,10 @@ theorem pinching_commutes_kraus (σ ρ : MState d) (i : spectrum ℝ σ.m) : theorem pinching_commutes (ρ σ : MState d) : Commute (pinching_map σ ρ).m σ.m := by - dsimp [MState.m, Commute, SemiconjBy] + dsimp [DensityOp.m, Commute, SemiconjBy] rw [pinchingMap_apply_M] simp only [MatrixMap.of_kraus, Function.comp_apply] - simp only [HermitianMat.conjTranspose_mat, MState.mat_M, LinearMap.coe_sum, + simp only [HermitianMat.conjTranspose_mat, DensityOp.mat_M, LinearMap.coe_sum, LinearMap.coe_mk, AddHom.coe_mk, Finset.sum_apply] simp only [HermitianMat.mat_mk, Finset.sum_mul, Finset.mul_sum] congr! 1 with i @@ -145,7 +148,7 @@ theorem pinching_self (ρ : MState d) : pinching_map ρ ρ = ρ := by ext1 rw [pinchingMap_apply_M] simp only [MatrixMap.of_kraus, Function.comp_apply] - simp only [HermitianMat.conjTranspose_mat, MState.mat_M, LinearMap.coe_sum, + simp only [HermitianMat.conjTranspose_mat, DensityOp.mat_M, LinearMap.coe_sum, LinearMap.coe_mk, AddHom.coe_mk, Finset.sum_apply] simp_rw [(pinching_kraus_commutes ρ _).eq, mul_assoc, ← sq] conv_lhs => @@ -183,16 +186,16 @@ theorem pinching_bound (ρ σ : MState d) : ρ.M ≤ (↑(Fintype.card (spectrum dsimp [MState.spectrum, ProbDistribution.mk'] rw [Complex.zero_le_real] exact (HermitianMat.zero_le_iff.mp ρ.nonneg).eigenvalues_nonneg _ - have h1 : (1 : Matrix d d ℂ) = (1 : HermitianMat d ℂ) := by exact selfAdjoint.val_one + have hpure : (Matrix.vecMulVec (ψs i) ((ψs i).to_bra) : Matrix d d ℂ) + = (∑ k, pinching_kraus σ k).mat * (Matrix.vecMulVec (ψs i) ((ψs i).to_bra) : Matrix d d ℂ) + * (∑ k, pinching_kraus σ k).mat := by + rw [pinching_sum] + simp conv_lhs => enter [2, 1] - rw [← one_mul (MState.m _), h1, ← congr(HermitianMat.mat $(pinching_sum σ))] - enter [2] - rw [← mul_one (MState.m _), h1, ← congr(HermitianMat.mat $(pinching_sum σ))] + rw [hpure] simp only [HermitianMat.mat_finset_sum] simp only [Matrix.mul_sum, Matrix.sum_mul, Matrix.sum_mulVec, dotProduct_sum] - simp only [MState.pure] - dsimp [MState.m] --This out to be Cauchy-Schwarz. have hschwarz := inner_mul_inner_self_le (𝕜 := ℂ) (E := EuclideanSpace ℂ (↑(spectrum ℝ σ.m))) (x := .toLp 2 fun i ↦ 1) (y := .toLp 2 fun k ↦ ( @@ -203,7 +206,6 @@ theorem pinching_bound (ρ σ : MState d) : ρ.M ≤ (↑(Fintype.card (spectrum simp only [RCLike.inner_apply, map_one, mul_one, one_mul, Complex.ofReal_mul, Finset.sum_const, Finset.card_univ, nsmul_eq_mul, RCLike.natCast_re, map_sum, RCLike.re_to_complex, Complex.ofReal_natCast, Complex.ofReal_sum] at hschwarz - simp only [HermitianMat.mat_mk] at ⊢ have h_mul (x y : spectrum ℝ σ.m) : star v ⬝ᵥ ((pinching_kraus σ y).mat * (Matrix.vecMulVec ⇑(ψs i) ((ψs i).to_bra) : Matrix d d ℂ) @@ -225,7 +227,6 @@ theorem pinching_bound (ρ σ : MState d) : ρ.M ≤ (↑(Fintype.card (spectrum rw [Complex.mul_conj, Complex.norm_real, Real.norm_of_nonneg (Complex.normSq_nonneg _)] simp_rw [← Complex.mul_conj, map_sum, Finset.mul_sum, Finset.sum_mul] congr! with x _ y _ - rw [← Matrix.mul_assoc] exact h_mul x y · simp · have hc (c d : ℂ) : d = starRingEnd ℂ d → c = d → c = d.re := by @@ -246,12 +247,13 @@ theorem ker_le_ker_pinching_of_PosDef (ρ σ : MState d) (hpos : σ.m.PosDef) : theorem pinching_idempotent (ρ σ : MState d) : (pinching_map σ) (pinching_map σ ρ) = (pinching_map σ ρ) := by - rw [MState.ext_iff] have h_idempotent : (∑ k, (pinching_kraus σ k).mat * (∑ l, (pinching_kraus σ l).mat * ρ.M * (pinching_kraus σ l).mat) * (pinching_kraus σ k).mat) = (∑ k, (pinching_kraus σ k).mat * ρ.M * (pinching_kraus σ k).mat) := by simp only [Matrix.mul_sum, Matrix.sum_mul, ← mul_assoc, pinching_kraus_ortho] simp [mul_assoc, pinching_kraus_ortho] + apply DensityOp.ext ext1 - grind [pinching_eq_sum_conj] + rw [pinching_eq_sum_conj σ (pinching_map σ ρ), pinching_eq_sum_conj σ ρ] + exact h_idempotent set_option backward.isDefEq.respectTransparency false in theorem inner_cfc_pinching (ρ σ : MState d) (f : ℝ → ℝ) : @@ -259,7 +261,7 @@ theorem inner_cfc_pinching (ρ σ : MState d) (f : ℝ → ℝ) : nth_rw 2 [pinchingMap_apply_M] rw [HermitianMat.inner_eq_re_trace, HermitianMat.inner_eq_re_trace] congr 1 - simp only [MState.mat_M, HermitianMat.mat_mk] + simp only [DensityOp.mat_M, HermitianMat.mat_mk] conv_rhs => rw [MatrixMap.of_kraus, LinearMap.sum_apply, Finset.sum_mul] rw [Matrix.trace_sum] @@ -386,7 +388,7 @@ theorem ker_le_ker_pinching_map_ker (ρ σ : MState d) (h : σ.M.ker ≤ ρ.M.ke convert congr_arg ( fun x : Matrix d d ℂ => x.mulVec v ) ( pinching_eq_sum_conj σ ρ ) using 1; simp [ Matrix.mul_assoc, Matrix.sum_mulVec ]; refine' Eq.trans congr(WithLp.toLp 2 $(h_sum)) _; - simp only [MState.mat_M, Matrix.mulVec_mulVec, WithLp.toLp_sum] + simp only [DensityOp.mat_M, Matrix.mulVec_mulVec, WithLp.toLp_sum] refine' Finset.sum_eq_zero fun k hk => _; by_cases hk_zero : k.val = 0 · simp_all only [ne_eq, Subtype.forall, Matrix.mulVec_mulVec, Finset.mem_univ] @@ -416,10 +418,5 @@ theorem pinching_pythagoras (ρ σ : MState d) : rw [h_eq₂, h_eq₁] simp only [EReal.coe_sub] rw [← add_sub_assoc, EReal.sub_add_cancel] - · simp only [qRelativeEnt, SandwichedRelRentropy, dif_pos zero_lt_one] - trans ⊤ - · exact dif_neg h_ker - · convert (add_top _).symm - apply dif_neg ?_ - contrapose! h_ker - exact h_ker.trans (pinching_map_ker_le ρ σ) + · rw [qRelativeEnt_eq_top h_ker, + qRelativeEnt_eq_top (fun hc ↦ h_ker (hc.trans (pinching_map_ker_le ρ σ))), add_top] diff --git a/QuantumInfo/Channels/Unbundled.lean b/QuantumInfo/Channels/Unbundled.lean index b12dce9eb..6a96b6a61 100644 --- a/QuantumInfo/Channels/Unbundled.lean +++ b/QuantumInfo/Channels/Unbundled.lean @@ -125,41 +125,6 @@ theorem kron {M₁ : MatrixMap A B R} {M₂ : MatrixMap C D R} (h₁ : M₁.IsTr · exact Matrix.trace_single_eq_of_ne _ _ _ h simp [h_simp] -section piProd - -variable {ι : Type u} [DecidableEq ι] [Fintype ι] -variable {dI : ι → Type v} [∀ i, Fintype (dI i)] [∀ i, DecidableEq (dI i)] -variable {dO : ι → Type w} [∀ i, Fintype (dO i)] [∀ i, DecidableEq (dO i)] -variable {R : Type*} [CommSemiring R] - -/-- The `MatrixMap.piProd` product of IsTracePreserving maps is also trace preserving. -/ -theorem piProd {Λi : ∀ i, MatrixMap (dI i) (dO i) R} (h₁ : ∀ i, (Λi i).IsTracePreserving) : - (MatrixMap.piProd Λi).IsTracePreserving := by - rw [IsTracePreserving_iff_trace_choi, MatrixMap.choi_matrix_piProd] - ext f g - simp [Matrix.traceLeft, Matrix.piProd, Matrix.reindex_apply] - have htrace : ∀ i, (Λi i).choi_matrix.traceLeft = 1 := fun i => - (IsTracePreserving_iff_trace_choi (Λi i)).1 (h₁ i) - have hprod : ∀ a b : ∀ i, dI i, - (∑ x : ∀ i, dO i, ∏ i, (Λi i).choi_matrix (x i, a i) (x i, b i)) = - ∏ i, ∑ x, (Λi i).choi_matrix (x, a i) (x, b i) := fun a b => by - simpa using - (Fintype.prod_sum (f := fun i x => (Λi i).choi_matrix (x, a i) (x, b i))).symm - by_cases hfg : f = g - · subst hfg - rw [hprod] - have hdiag : ∀ i, ∑ x, (Λi i).choi_matrix (x, f i) (x, f i) = 1 := fun i => by - simpa [Matrix.traceLeft] using congrFun₂ (htrace i) (f i) (f i) - simp [hdiag] - · obtain ⟨i, hi⟩ := Function.ne_iff.mp hfg - have hfactor : ∑ x, (Λi i).choi_matrix (x, f i) (x, g i) = 0 := by - simpa [Matrix.traceLeft, Matrix.one_apply, hi] - using congrFun₂ (htrace i) (f i) (g i) - rw [hprod, Finset.prod_eq_zero (Finset.mem_univ i) hfactor] - simp [hfg] - -end piProd - variable {S : Type*} [CommSemiring S] [Star S] [DecidableEq A] in /-- The channel X ↦ ∑ k : κ, (M k) * X * (N k)ᴴ formed by Kraus operators M, N : κ → Matrix B A R is trace-preserving if ∑ k : κ, (N k)ᴴ * (M k) = 1 -/ @@ -179,6 +144,32 @@ theorem of_kraus_isTracePreserving theorem submatrix (e : A ≃ B) : (MatrixMap.submatrix R e).IsTracePreserving := by intro; simp +section piProd + +variable {R : Type*} [CommSemiring R] +variable {ι : Type u} [DecidableEq ι] [Fintype ι] +variable {dI : ι → Type v} [∀i, Fintype (dI i)] [∀i, DecidableEq (dI i)] +variable {dO : ι → Type w} [∀i, Fintype (dO i)] [∀i, DecidableEq (dO i)] + +omit [(i : ι) → DecidableEq (dO i)] in +/-- The `MatrixMap.piProd` product of trace-preserving maps is trace preserving. -/ +theorem piProd {Λi : ∀ i, MatrixMap (dI i) (dO i) R} (h : ∀ i, (Λi i).IsTracePreserving) : + (MatrixMap.piProd Λi).IsTracePreserving := by + intro x + conv_lhs => rw [Matrix.matrix_eq_sum_single x] + conv_rhs => rw [Matrix.matrix_eq_sum_single x] + simp only [map_sum, Matrix.trace_sum] + refine Finset.sum_congr rfl fun a _ ↦ Finset.sum_congr rfl fun b _ ↦ ?_ + rw [show Matrix.single a b (x a b) = x a b • Matrix.single a b (1 : R) by + rw [Matrix.smul_single, smul_eq_mul, mul_one]] + rw [map_smul, Matrix.trace_smul, Matrix.trace_smul] + congr 1 + rw [MatrixMap.piProd_single, Matrix.trace_piProd, ← Matrix.piProd_single a b, + Matrix.trace_piProd] + exact Finset.prod_congr rfl fun i _ ↦ h i _ + +end piProd + end IsTracePreserving end tp @@ -283,6 +274,73 @@ theorem smul {M : MatrixMap A B R} (hM : M.IsPositive) {x : R} (hx : 0 ≤ x) : (x • M).IsPositive := fun _ h ↦ (hM h).smul hx +section Contraction + +variable {M : MatrixMap A B ℂ} + +/-- A positive map, viewed as a map on Hermitian matrices. -/ +noncomputable def herm (hM : M.IsPositive) (X : HermitianMat A ℂ) : HermitianMat B ℂ := + ⟨M X.mat, hM.IsHermitianPreserving X.H⟩ + +@[simp] +theorem mat_herm (hM : M.IsPositive) (X : HermitianMat A ℂ) : (hM.herm X).mat = M X.mat := + rfl + +/-- A positive map is monotone for the Loewner order. -/ +theorem herm_mono (hM : M.IsPositive) {X Y : HermitianMat A ℂ} (h : X ≤ Y) : + hM.herm X ≤ hM.herm Y := by + rw [HermitianMat.le_iff] at h ⊢ + have hsub : (hM.herm Y - hM.herm X).mat = M ((Y - X).mat) := by + rw [HermitianMat.mat_sub, HermitianMat.mat_sub, map_sub, mat_herm, mat_herm] + rw [hsub] + exact hM h + +/-- On positive semidefinite inputs, a trace-preserving positive map preserves the trace norm, +because the trace norm of a positive semidefinite matrix is its trace. -/ +theorem traceNorm_map_eq_trace (hM : M.IsPositive) (hTP : M.IsTracePreserving) + {X : HermitianMat A ℂ} (hX : 0 ≤ X) : (M X.mat).traceNorm = X.trace := by + have hpsd : (M X.mat).PosSemidef := hM (HermitianMat.zero_le_iff.mp hX) + have h := hpsd.traceNorm_eq_trace + rw [hTP X.mat, ← HermitianMat.trace_eq_trace_rc] at h + exact_mod_cast h + +variable [DecidableEq A] + +/-- A positive trace-preserving map is a contraction in the trace norm, on Hermitian inputs. + +The Jordan decomposition `X = X⁺ - X⁻` reduces this to the positive semidefinite case, where the +trace norm is the trace and so is exactly preserved. -/ +theorem traceNorm_le (hM : M.IsPositive) (hTP : M.IsTracePreserving) (X : HermitianMat A ℂ) : + (M X.mat).traceNorm ≤ X.mat.traceNorm := + calc (M X.mat).traceNorm = (M (X⁺).mat - M (X⁻).mat).traceNorm := by + rw [← map_sub, ← HermitianMat.mat_sub, HermitianMat.posPart_add_negPart] + _ ≤ (M (X⁺).mat).traceNorm + (M (X⁻).mat).traceNorm := Matrix.traceNorm_sub_le _ _ + _ = (X⁺).trace + (X⁻).trace := by + rw [hM.traceNorm_map_eq_trace hTP X.posPart_nonneg, + hM.traceNorm_map_eq_trace hTP X.negPart_nonneg] + _ = X.mat.traceNorm := (HermitianMat.traceNorm_eq_trace_posPart_add_negPart X).symm + +variable [DecidableEq B] + +theorem herm_smul_one (hM : M.IsPositive) (hu : M.Unital) (c : ℝ) : + hM.herm (c • 1) = c • 1 := by + apply HermitianMat.ext + rw [mat_herm, HermitianMat.mat_smul, HermitianMat.mat_one, HermitianMat.mat_smul, + HermitianMat.mat_one, LinearMap.map_smul_of_tower, hu.map_1] + +/-- A positive unital map is a contraction in the operator norm, on Hermitian inputs: it maps the +Loewner order interval `[-c, c]` into itself. -/ +theorem mem_Icc_smul_one_of_unital (hM : M.IsPositive) (hu : M.Unital) {X : HermitianMat A ℂ} + {c : ℝ} (h₁ : -(c • 1) ≤ X) (h₂ : X ≤ c • 1) : + -(c • 1) ≤ hM.herm X ∧ hM.herm X ≤ c • 1 := by + have hneg : hM.herm ((-c) • 1) = -(c • 1) := by + rw [hM.herm_smul_one hu, neg_smul] + refine ⟨hneg ▸ hM.herm_mono ?_, hM.herm_smul_one hu c ▸ hM.herm_mono h₂⟩ + rw [neg_smul] + exact h₁ + +end Contraction + end IsPositive namespace IsCompletelyPositive diff --git a/QuantumInfo/ClassicalInfo/Distribution.lean b/QuantumInfo/ClassicalInfo/Distribution.lean index 8d395c1ed..c6edbbcb8 100644 --- a/QuantumInfo/ClassicalInfo/Distribution.lean +++ b/QuantumInfo/ClassicalInfo/Distribution.lean @@ -254,6 +254,12 @@ def expect_val (X : RandVar α T) : T := by exact Set.mem_range.mp (inst.convex.sum_mem h₀ h₁ hz) exact (inst.mkT ht).1 +/-- The defining property of `expect_val`: in the ambient module `U`, it is the weighted sum. -/ +@[simp] +theorem to_U_expect_val (X : RandVar α T) : + inst.to_U (expect_val X) = ∑ i, (X.distr i : ℝ) • inst.to_U (X.var i) := + (inst.mkT _).2 + set_option backward.isDefEq.respectTransparency false in /-- The expectation value of a random variable over `α = Fin 2` is the same as `Mixable.mix` with probabiliy weight `X.distr 0` -/ diff --git a/QuantumInfo/Entropy/Axiomatized/Defs.lean b/QuantumInfo/Entropy/Axiomatized/Defs.lean index 6677c9ef5..fbf5e4dfe 100644 --- a/QuantumInfo/Entropy/Axiomatized/Defs.lean +++ b/QuantumInfo/Entropy/Axiomatized/Defs.lean @@ -52,6 +52,10 @@ universe u open scoped NNReal open scoped ENNReal +--The entropy function is a variable throughout this file, so every simp lemma about it +--necessarily has a variable as its head symbol. +set_option warning.simp.varHead false + variable (f : ∀ {d : Type u} [Fintype d] [DecidableEq d], MState d → HermitianMat d ℂ → ℝ≥0∞) /-- The axioms to be a well-behaved quantum relative entropy, as given by @@ -65,7 +69,7 @@ class RelEntropy : Prop where (ρ σ : MState d₁) (Λ : CPTPMap d₁ d₂) : f (Λ ρ) (Λ σ) ≤ f ρ σ /-- Entropy is additive under tensor products -/ of_kron {d₁ d₂ : Type u} [Fintype d₁] [Fintype d₂] [DecidableEq d₁] [DecidableEq d₂] : - ∀ (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂), f (ρ₁ ⊗ ρ₂) (σ₁ ⊗ σ₂) = f ρ₁ σ₁ + f ρ₂ σ₂ + ∀ (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂), f (ρ₁ ⊗ᴹ ρ₂) (σ₁ ⊗ᴹ σ₂) = f ρ₁ σ₁ + f ρ₂ σ₂ /-- Normalization of entropy to be `ln N` for a pure state vs. uniform on `N` many states. -/ normalized {d : Type u} [fin : Fintype d] [DecidableEq d] [Nonempty d] (i : d) : f (.ofClassical (.constant i)) MState.uniform.M = @@ -104,41 +108,47 @@ At that point we need the fact that it's not `⊤`, and then it must be zero. -/ -/-- Relabelling a state with `CPTPMap.of_equiv` leaves relative entropies unchanged. -/ +/-- Relabelling a state with `CPTPOp.ofEquiv` leaves relative entropies unchanged. -/ @[simp] theorem of_equiv_eq (e : d ≃ d₂) (ρ σ : MState d) : - f (CPTPMap.of_equiv e ρ) (CPTPMap.of_equiv e σ) = f ρ σ := by - apply le_antisymm - · apply DPI - · convert DPI (f := f) ((CPTPMap.of_equiv e) ρ) ((CPTPMap.of_equiv e) σ) (CPTPMap.of_equiv e.symm) - · symm - exact congrFun (CPTPMap.equiv_inverse e.symm) ρ - · symm - exact congrFun (CPTPMap.equiv_inverse e.symm) σ + f (CPTPOp.ofEquiv e ρ) (CPTPOp.ofEquiv e σ) = f ρ σ := by + have hinv : ∀ τ : MState d, (CPTPOp.ofEquiv e.symm) ((CPTPOp.ofEquiv e) τ) = τ := by + intro τ + simp + refine le_antisymm (DPI ρ σ _) ?_ + calc f ρ σ + = f ((CPTPOp.ofEquiv e.symm) ((CPTPOp.ofEquiv e) ρ)) + ((CPTPOp.ofEquiv e.symm) ((CPTPOp.ofEquiv e) σ)) := by rw [hinv, hinv] + _ ≤ f ((CPTPOp.ofEquiv e) ρ) ((CPTPOp.ofEquiv e) σ) := DPI _ _ _ /-- Relabelling a state with `MState.relabel` leaves relative entropies unchanged. -/ @[simp] theorem relabel_eq (e : d₂ ≃ d) (ρ σ : MState d) : f (ρ.relabel e) (σ.relabel e) = f ρ σ := by - apply of_equiv_eq + have h : ∀ τ : MState d, τ.relabel e = (CPTPOp.ofEquiv e.symm) τ := by + intro τ + rw [CPTPOp.ofEquiv_apply, Equiv.symm_symm] + rw [h, h] + exact of_equiv_eq f e.symm ρ σ --Tomamichel's "4. Positivity" theorem is implicit true in our description because we --only allow ENNReals. The only part to prove is that "D(ρ‖σ) = 0 if ρ = σ". /-- The relative entropy is zero between any two states on a 1-D Hilbert space. -/ private lemma wrt_self_eq_zero' [Unique d] (ρ σ : MState d) : f ρ σ = 0 := by - convert normalized (f := f) (d := d) default - · apply Subsingleton.allEq - · apply Subsingleton.allEq - · simp + have h := normalized (f := f) (d := d) default + rw [Subsingleton.allEq (MState.ofClassical (ProbDistribution.constant default)) ρ, + Subsingleton.allEq (MState.uniform (d := d)) σ] at h + rw [h] + simp + rfl /-- The relative entropy `D(ρ‖ρ) = 0`. -/ @[simp] theorem wrt_self_eq_zero (ρ : MState d) : f ρ ρ.M = 0 := by rw [← nonpos_iff_eq_zero, ← wrt_self_eq_zero' f (d := PUnit) default default] - convert DPI (f := f) _ _ (CPTPMap.const_state ρ) - · rw [CPTPMap.const_state_apply] - · rw [CPTPMap.const_state_apply] + have h := DPI (f := f) (default : MState PUnit) default (CPTPOp.replacement ρ) + rwa [CPTPOp.replacement_apply] at h end possibly_trivial @@ -156,7 +166,7 @@ section bounds open Prob in /-- Quantum relative min-entropy. -/ def min (ρ : MState d) (σ : HermitianMat d ℂ) : ENNReal := - —log ⟨_, ρ.exp_val_prob ⟨proj_le_nonneg 0 σ, proj_le_le_one _ _⟩⟩ + —log ⟨_, ρ.exp_val_prob ⟨HermitianMat.projLE_nonneg 0 σ, HermitianMat.projLE_le_one _ _⟩⟩ @[aesop (rule_sets := [finiteness]) simp] theorem min_eq_top_iff (ρ : MState d) (σ : HermitianMat d ℂ) : @@ -164,7 +174,7 @@ theorem min_eq_top_iff (ρ : MState d) (σ : HermitianMat d ℂ) : open scoped HermitianMat in have h₂ : {0 ≤ₚ σ}.ker = σ.ker := by sorry --missing simp lemma - simp [min, Prob.negLog_eq_top_iff, MState.exp_val_eq_zero_iff, Subtype.ext_iff, proj_le_nonneg, h₂] + simp [min, Prob.negLog_eq_top_iff, MState.exp_val_eq_zero_iff, Subtype.ext_iff, HermitianMat.projLE_nonneg, h₂] open scoped HermitianMat in protected theorem toReal_min (ρ : MState d) (σ : HermitianMat d ℂ) : @@ -180,9 +190,9 @@ instance : RelEntropy min where theorem not_Nontrivial_min : ¬Nontrivial min := by rintro ⟨h⟩ obtain ⟨ρ, σ, h₁, h₂, h₃⟩ := h (ULift (Fin 2)) - replace h₂ : proj_le 0 σ = (1 : HermitianMat (ULift (Fin 2)) ℂ) := by + replace h₂ : HermitianMat.projLE 0 σ = (1 : HermitianMat (ULift (Fin 2)) ℂ) := by sorry--TODO - simp [min, Subtype.ext_iff, MState.exp_val_eq_one_iff, proj_le_le_one, h₁, h₂] at h₃ + simp [min, Subtype.ext_iff, h₂] at h₃ /-- The relative min-entropy is a lower bound on all relative entropies. -/ theorem min_le (ρ σ : MState d) : min ρ σ ≤ f ρ σ := by @@ -192,44 +202,29 @@ open Classical in /-- Quantum relative max-entropy. -/ def max (ρ : MState d) (σ : HermitianMat d ℂ) : ENNReal := if ∃ (x : ℝ), ρ.M ≤ Real.exp x • σ then - some (sInf { x : NNReal | ρ.M ≤ Real.exp x • σ }) + ((sInf { x : NNReal | ρ.M ≤ Real.exp x • σ } : ℝ≥0) : ℝ≥0∞) else ⊤ @[aesop (rule_sets := [finiteness]) simp] protected theorem max_not_top (ρ : MState d) (σ : HermitianMat d ℂ) : (max ρ σ) ≠ ⊤ ↔ σ.ker ≤ ρ.M.ker := by - open ComplexOrder in constructor - · intro h - contrapose! h - simp only [max, ENNReal.some_eq_coe, ite_eq_right_iff, ENNReal.coe_ne_top, imp_false, - not_exists] - intro x - contrapose! h - intro v hv - rw [HermitianMat.ker, LinearMap.mem_ker] at hv ⊢ - replace hv : σ.toMat.mulVec v = 0 := sorry --why is this not defeq?? - replace h := h.right v - rw [Matrix.sub_mulVec] at h - simp [hv, Matrix.smul_mulVec_assoc] at h - have := ρ.pos.right v - -- have := le_antisymm (ρ.pos.right v) (by ) - sorry + · sorry · intro - rw [max, if_pos] - · nofun - sorry --log ("min nonzero eigenvalue of σ" / "max eigenvalue of ρ") should work + have hex : ∃ (x : ℝ), ρ.M ≤ Real.exp x • σ := by + sorry --log ("min nonzero eigenvalue of σ" / "max eigenvalue of ρ") should work + rw [max, if_pos hex] + exact ENNReal.coe_ne_top protected theorem toReal_max (ρ : MState d) (σ : HermitianMat d ℂ) : (max ρ σ).toReal = sInf { x : ℝ | ρ.M ≤ Real.exp x • σ } := by rw [max] split_ifs with h - · have : { x : ℝ | ρ.M ≤ Real.exp x • σ }.Nonempty := by - convert h + · have : { x : ℝ | ρ.M ≤ Real.exp x • σ }.Nonempty := h simp sorry - · push_neg at h + · push Not at h simp [h] /-- The relative max-entropy is a lower bound on all relative entropies. -/ @@ -245,6 +240,6 @@ class Entropy (f : ∀ {d : Type u} [Fintype d] [DecidableEq d], MState d → of_const {d : Type u} [Fintype d] [DecidableEq d] (ψ : Ket d) : f (.pure ψ) = 0 /-- Entropy is additive under tensor products -/ of_kron {d₁ d₂ : Type u} [Fintype d₁] [Fintype d₂] [DecidableEq d₁] [DecidableEq d₂] : - ∀ (ρ : MState d₁) (σ : MState d₂), f (ρ ⊗ σ) = f ρ + f σ + ∀ (ρ : MState d₁) (σ : MState d₂), f (ρ ⊗ᴹ σ) = f ρ + f σ -- /-- Entropy is convex. TODO def? Or do we even need this? -/ -- convex : True := by trivial diff --git a/QuantumInfo/Entropy/Axiomatized/Renyi.lean b/QuantumInfo/Entropy/Axiomatized/Renyi.lean index 7abb01d15..493fd0ca4 100644 --- a/QuantumInfo/Entropy/Axiomatized/Renyi.lean +++ b/QuantumInfo/Entropy/Axiomatized/Renyi.lean @@ -7,24 +7,34 @@ module public import QuantumInfo.Entropy.Axiomatized.Defs -/-! # Quantum Relative Entropy and α-Renyi Entropy -/ +/-! # Quantum Relative Entropy and α-Renyi Entropy + +The concrete relative entropy built here is an instance of the axiomatic `RelEntropy` class of +`QuantumInfo.Entropy.Axiomatized.Defs`. It lives in the `Axiomatized` namespace so that it does +not clash with the development in `QuantumInfo.Entropy.Relative`, which defines the same quantity +directly. +-/ @[expose] public section +open scoped RealInnerProductSpace + +namespace Axiomatized + variable {d : Type*} [Fintype d] [DecidableEq d] /-- The quantum relative entropy S(ρ‖σ) = Tr[ρ (log ρ - log σ)]. -/ @[irreducible] noncomputable def qRelativeEnt (ρ : MState d) (σ : HermitianMat d ℂ) : ENNReal := open Classical in (if σ.ker ≤ ρ.M.ker then - some ⟨ρ.exp_val (HermitianMat.log ρ - HermitianMat.log σ), - /- Quantum relative entropy is nonnegative. This can be proved by an application of - Klein's inequality. -/ - sorry⟩ + ENNReal.ofNNReal ⟨ρ.exp_val (ρ.M.log - σ.log), + /- Quantum relative entropy is nonnegative. This can be proved by an application of + Klein's inequality. -/ + sorry⟩ else ⊤) -notation "𝐃(" ρ "‖" σ ")" => qRelativeEnt ρ σ +@[inherit_doc] scoped notation "𝐃(" ρ "‖" σ ")" => Axiomatized.qRelativeEnt ρ σ instance : RelEntropy qRelativeEnt where DPI := sorry @@ -36,6 +46,8 @@ instance : RelEntropy.Nontrivial qRelativeEnt where /-- Quantum relative entropy as `Tr[ρ (log ρ - log σ)]` when supports are correct. -/ theorem qRelativeEnt_ker {ρ σ : MState d} (h : σ.M.ker ≤ ρ.M.ker) : - (𝐃(ρ‖σ) : EReal) = ρ.M.inner (HermitianMat.log ρ - HermitianMat.log σ) := by - simp only [qRelativeEnt, h] - congr + 𝐃(ρ‖σ).toEReal = ⟪ρ.M, ρ.M.log - σ.M.log⟫ := by + rw [qRelativeEnt, if_pos h] + rfl + +end Axiomatized diff --git a/QuantumInfo/Entropy/DPI.lean b/QuantumInfo/Entropy/DPI.lean index 252e9eede..fb37769ff 100644 --- a/QuantumInfo/Entropy/DPI.lean +++ b/QuantumInfo/Entropy/DPI.lean @@ -11,6 +11,10 @@ public import QuantumInfo.ForMathlib.HermitianMat.LiebConcavity @[expose] public section +set_option backward.isDefEq.respectTransparency false +set_option maxHeartbeats 1000000 +set_option maxRecDepth 100000 + noncomputable section variable {d d₁ d₂ d₃ : Type*} @@ -78,11 +82,18 @@ when `σ.M.ker ≤ ρ.M.ker`. theorem sandwichedRelRentropy_eq_log_traceFunctional (hα₀ : 0 < α) (hα₁ : α ≠ 1) (hker : σ.M.ker ≤ ρ.M.ker) : D̃_ α(ρ‖σ) = ENNReal.ofReal (Real.log (Q̃_ α(ρ‖σ)) / (α - 1)) := by - rw [ENNReal.ofReal_eq_coe_nnreal] - unfold SandwichedRelRentropy sandwichedTraceFunctional - split - next h => simp_all only; norm_cast - next h => rfl + have hQ : Q̃_ α(ρ‖σ) = ((ρ.M.conj (σ.M ^ ((1 - α) / (2 * α))).mat) ^ α).trace := rfl + have hnn : 0 ≤ Real.log (Q̃_ α(ρ‖σ)) / (α - 1) := by + rw [hQ] + simpa only [if_neg hα₁] using sandwichedRelRentropy_nonneg hα₀ hker + rw [MState.sandwichedRelRentropy_eq_matrix, dif_pos hα₀, dif_pos hker, + ENNReal.ofReal_eq_coe_nnreal hnn] + congr! 1 + apply NNReal.coe_injective + show (if α = 1 then ⟪ρ.M, ρ.M.log - σ.M.log⟫ + else ((ρ.M.conj (σ.M ^ ((1 - α) / (2 * α))).mat) ^ α).trace.log / (α - 1)) + = Real.log (Q̃_ α(ρ‖σ)) / (α - 1) + rw [if_neg hα₁, hQ] /- `Q̃_α(ρ‖σ)` is nonneg when `α > 0`. @@ -120,7 +131,6 @@ The trace functional is invariant under joint unitary conjugation: This corresponds to equation (2.3) in the paper. Proved using `rpow_conj_unitary` (f(UXU†) = U f(X) U†) and `conj_conj`. -/ -set_option backward.isDefEq.respectTransparency false in theorem sandwichedTraceFunctional_conj_unitary_hermitian (U : Matrix.unitaryGroup d ℂ) (A B : HermitianMat d ℂ) : let γ := (1 - α) / (2 * α) @@ -140,7 +150,7 @@ theorem sandwichedTraceFunctional_conj_unitary_hermitian theorem sandwichedTraceFunctional_conj_unitary_MState (U : Matrix.unitaryGroup d ℂ) (ρ σ : MState d) : Q̃_ α(ρ.uConj U‖σ.uConj U) = Q̃_ α(ρ‖σ) := by - unfold sandwichedTraceFunctional MState.uConj + rw [sandwichedTraceFunctional, sandwichedTraceFunctional, MState.uConj_M, MState.uConj_M] exact sandwichedTraceFunctional_conj_unitary_hermitian U ρ.M σ.M /-! ## Joint Convexity for α > 1 @@ -854,7 +864,7 @@ theorem sandwichedTraceFunctional_self (hα : 0 < α) (ρ : MState d) : rw [← rpow_mul] exact le_of_lt this field_simp at * - simp_all only [add_sub_cancel, one_div, rpow_one, MState.tr] + simp_all only [add_sub_cancel, one_div, rpow_one, DensityOp.tr] /-- The trace functional is invariant under tensoring with a fixed state. This follows from multiplicativity (`sandwichedTraceFunctional_mul`) and @@ -878,8 +888,8 @@ def MState.conjTensorUnitary (ρ : MState (dA × dB)) (V : Matrix.unitaryGroup d the twirling set gives `ρ_A ⊗ uniform_B`. We state the HermitianMat-level equality needed for the joint convexity argument. -/ theorem MState.conjTensorUnitary_M (ρ : MState (dA × dB)) (V : Matrix.unitaryGroup dB ℂ) : - (ρ.conjTensorUnitary V).M = ρ.M.conj ((1 : Matrix.unitaryGroup dA ℂ) ⊗ᵤ V).val := by - rfl + (ρ.conjTensorUnitary V).M = ρ.M.conj ((1 : Matrix.unitaryGroup dA ℂ) ⊗ᵤ V).val := + MState.uConj_M ρ _ /-- The trace functional is invariant under `1_A ⊗ V` conjugation. -/ theorem sandwichedTraceFunctional_conj_tensorUnitary @@ -988,6 +998,8 @@ lemma conjTensorUnitary'_entry (ρ : MState (dA × dB)) (V : Matrix.unitaryGroup (ρ.conjTensorUnitary' V).M.val (a₁, b₁) (a₂, b₂) = ((V : Matrix dB dB ℂ) * (Matrix.of fun b₁' b₂' => ρ.M.val (a₁, b₁') (a₂, b₂')) * (V : Matrix dB dB ℂ).conjTranspose) b₁ b₂ := by + simp only [MState.conjTensorUnitary', MState.uConj_M, HermitianMat.val_eq_coe, + HermitianMat.conj_apply_mat] apply conj_kron_one_entry -- The RHS entry: (ρ.traceRight ⊗ᴹ uniform).M at ((a₁,b₁),(a₂,b₂)). @@ -1227,8 +1239,6 @@ theorem sandwichedRenyiEntropy_mono_traceRight [Nonempty dB] /- The sandwiched Rényi divergence is invariant under unitary conjugation. -/ -set_option backward.isDefEq.respectTransparency false in -set_option maxHeartbeats 400000 in theorem sandwichedRenyiEntropy_conj_unitary (hα : 0 < α) (ρ σ : MState d) (U : Matrix.unitaryGroup d ℂ) : D̃_ α(ρ.uConj U‖σ.uConj U) = D̃_ α(ρ‖σ) := by @@ -1272,14 +1282,23 @@ theorem sandwichedRenyiEntropy_conj_unitary (hα : 0 < α) (ρ σ : MState d) simp_all [Matrix.mulVec_mulVec] exact PiLp.ext (congrFun hy') exact hy - by_cases h : σ.M.ker ≤ ρ.M.ker <;> simp_all [SandwichedRelRentropy] - split_ifs <;> simp_all [MState.uConj] - · congr 1 - rw [inner_sub_right, inner_sub_right] - grind only [log_conj_unitary, inner_conj_unitary] - · ext1 - congr 3 - convert! congr_arg Real.log (sandwichedTraceFunctional_conj_unitary_MState U ρ σ) using 1 + rw [MState.sandwichedRelRentropy_eq_matrix, MState.sandwichedRelRentropy_eq_matrix, + dif_pos hα, dif_pos hα] + by_cases h : σ.M.ker ≤ ρ.M.ker + swap + · rw [dif_neg h, dif_neg fun hc ↦ h (h_kernel.mpr hc)] + rw [dif_pos (h_kernel.mp h), dif_pos h, ENNReal.coe_inj] + apply NNReal.coe_injective + show (if α = 1 then ⟪(ρ.uConj U).M, (ρ.uConj U).M.log - (σ.uConj U).M.log⟫ + else (((ρ.uConj U).M.conj + ((σ.uConj U).M ^ ((1 - α) / (2 * α))).mat) ^ α).trace.log / (α - 1)) + = (if α = 1 then ⟪ρ.M, ρ.M.log - σ.M.log⟫ + else ((ρ.M.conj (σ.M ^ ((1 - α) / (2 * α))).mat) ^ α).trace.log / (α - 1)) + split_ifs with hα₁ + · rw [MState.uConj_M, MState.uConj_M, log_conj_unitary, log_conj_unitary, ← map_sub, + inner_conj_unitary] + · exact congrArg (· / (α - 1)) + (congrArg Real.log (sandwichedTraceFunctional_conj_unitary_MState U ρ σ)) /- The sandwiched Rényi divergence is invariant under tensoring with a fixed pure state: @@ -1305,10 +1324,9 @@ theorem sandwichedRenyiEntropy_mono_traceRight' [Nonempty dB] D̃_ α(ρ.traceRight‖σ.traceRight) ≤ D̃_ α(ρ‖σ) := by by_cases hker : σ.M.ker ≤ ρ.M.ker · exact sandwichedRenyiEntropy_mono_traceRight hα ρ σ hker - · simp only [SandwichedRelRentropy, MState.traceRight_M] - split - next h => simp_all only [le_top] - next h => simp_all only [not_lt, le_refl] + · rw [MState.sandwichedRelRentropy_eq_matrix α ρ σ, dif_pos (by linarith : (0 : ℝ) < α), + dif_neg hker] + exact le_top /-- Monotonicity of the sandwiched Rényi divergence under `traceLeft` for `α > 1`. Follows from `sandwichedRenyiEntropy_mono_traceRight'` + SWAP invariance. -/ @@ -1322,56 +1340,57 @@ theorem sandwichedRenyiEntropy_mono_traceLeft [Nonempty dA] sandwichedRenyiEntropy_mono_traceRight' hα ρ.SWAP σ.SWAP _ = D̃_ α(ρ‖σ) := sandwichedRenyiEntropy_SWAP ρ σ -/-- Helper: The Stinespring preparation `prep ∘ append` equals tensoring with a fixed pure state. -`append = ofEquiv (Equiv.prodPUnit d₁).symm`. +/-- Helper: The Stinespring preparation `CPTPOp.prepDefault` equals tensoring with a fixed pure +state. TODO: PULLOUT to a more reasonable place. -/ -theorem prep_append_eq_tensor_pure [Inhabited d₂] (ρ : MState d₁) : - let ψ₀ : Ket (d₂ × d₂) := Ket.basis default - let τ := MState.pure ψ₀ - let zero_prep : CPTPMap Unit (d₂ × d₂) := CPTPMap.replacement τ - let prep := (CPTPMap.id ⊗ᶜᵖ zero_prep) - let append : CPTPMap d₁ (d₁ × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit d₁).symm - (prep ∘ₘ append) ρ = ρ ⊗ᴹ τ := by - apply MState.ext - ext1 - funext ⟨a₁, b₁⟩ ⟨a₂, b₂⟩ - have h := CPTPMap.prep_append_map_entry ρ.m a₁ b₁ a₂ b₂ - simp only [MState.prod, kronecker] - exact h +theorem prepDefault_eq_tensor_pure [Inhabited d₂] (ρ : MState d₁) : + (CPTPOp.prepDefault : CPTPMap d₁ (d₁ × d₂ × d₂)) ρ = + ρ ⊗ᴹ MState.pure (Ket.basis (default : d₂ × d₂)) := by + apply DensityOp.ext_m + rw [CPTPOp.mat_coe_eq_apply_mat, MState.prod_m] + ext ⟨a₁, b₁⟩ ⟨a₂, b₂⟩ + exact CPTPOp.prep_append_map_entry ρ.m a₁ b₁ a₂ b₂ /-- The Data Processing Inequality for the Sandwiched Rényi relative entropy (α > 1). Every CPTP map `Φ` satisfies `D̃_α(Φρ‖Φσ) ≤ D̃_α(ρ‖σ)`. -The proof uses the Stinespring representation (see `CPTPMap.exists_purify`): +The proof uses the Stinespring representation (see `CPTPOp.exists_purify`): every CPTP map can be written as ancilla preparation + unitary conjugation + partial trace. Since the sandwiched Rényi divergence is invariant under the first two operations (by additivity and relabel invariance) and monotone under partial trace (by `sandwichedRenyiEntropy_mono_traceRight`), the DPI follows. -/ theorem sandwichedRenyiEntropy_DPI_gt_one (hα : 1 < α) (ρ σ : MState d₁) (Φ : CPTPMap d₁ d₂) : D̃_ α(Φ ρ‖Φ σ) ≤ D̃_ α(ρ‖σ) := by - have _ : Nonempty d₁ := ρ.nonempty - have _ : Nonempty d₂ := (Φ ρ).nonempty + have _ : Nonempty d₁ := MState.nonempty ρ + have _ : Nonempty d₂ := MState.nonempty (Φ ρ) have : Inhabited d₂ := Classical.inhabited_of_nonempty ‹_› let ψ₀ : Ket (d₂ × d₂) := Ket.basis default let τ := MState.pure ψ₀ obtain ⟨U, hU⟩ := Φ.purify_IsUnitary - -- USe the `zero_prep` / `prep` / `append` from `CPTPMap.purify_trace` - let zero_prep : CPTPMap Unit (d₂ × d₂) := CPTPMap.replacement τ - let prep := ((CPTPMap.id : CPTPMap d₁ d₁) ⊗ᶜᵖ zero_prep) - let append : CPTPMap d₁ (d₁ × Unit) := CPTPMap.ofEquiv (Equiv.prodPUnit d₁).symm + let prep : CPTPMap d₁ (d₁ × d₂ × d₂) := CPTPOp.prepDefault calc D̃_ α(Φ ρ‖Φ σ) - _ = D̃_ α((Φ.purify ((prep ∘ₘ append) ρ)).traceLeft.traceLeft‖ - (Φ.purify ((prep ∘ₘ append) σ)).traceLeft.traceLeft) := by - have h_trace (ξ) : Φ ξ = (Φ.purify ((prep ∘ₘ append) ξ)).traceLeft.traceLeft := by - exact congr($Φ.purify_trace ξ) + _ = D̃_ α(MState.traceLeft (MState.traceLeft (Φ.purify (prep ρ)))‖ + MState.traceLeft (MState.traceLeft (Φ.purify (prep σ)))) := by + have h_trace (ξ) : Φ ξ = + MState.traceLeft (MState.traceLeft (Φ.purify (prep ξ))) := by + show Φ ξ = MState.traceLeft (MState.traceLeft + (Φ.purify (CPTPOp.prepDefault (dIn := d₁) (dOut := d₂) ξ))) + conv_lhs => rw [Φ.purify_trace] + simp only [CPTPOp.compose_eq, CPTPOp.traceLeft_eq_MState_traceLeft] rw [h_trace ρ, h_trace σ] - _ = D̃_ α(((ρ ⊗ᴹ τ).uConj U).traceLeft.traceLeft‖ - ((σ ⊗ᴹ τ).uConj U).traceLeft.traceLeft) := by - have h_app (ξ) : Φ.purify ξ = ξ.uConj U := congr($hU ξ) - rw [prep_append_eq_tensor_pure ρ, prep_append_eq_tensor_pure σ, h_app, h_app] - _ ≤ D̃_ α(((ρ ⊗ᴹ τ).uConj U).traceLeft‖((σ ⊗ᴹ τ).uConj U).traceLeft) := + _ = D̃_ α(MState.traceLeft (MState.traceLeft (MState.uConj (ρ ⊗ᴹ τ) U))‖ + MState.traceLeft (MState.traceLeft (MState.uConj (σ ⊗ᴹ τ) U))) := by + have h_app (ξ) : Φ.purify ξ = MState.uConj ξ U := + (congr($hU ξ)).trans (CPTPOp.ofUnitary_eq_conj U ξ) + show D̃_ α(MState.traceLeft (MState.traceLeft + (Φ.purify (CPTPOp.prepDefault (dIn := d₁) (dOut := d₂) ρ)))‖ + MState.traceLeft (MState.traceLeft + (Φ.purify (CPTPOp.prepDefault (dIn := d₁) (dOut := d₂) σ)))) = _ + rw [prepDefault_eq_tensor_pure ρ, prepDefault_eq_tensor_pure σ, h_app, h_app] + _ ≤ D̃_ α(MState.traceLeft (MState.uConj (ρ ⊗ᴹ τ) U)‖ + MState.traceLeft (MState.uConj (σ ⊗ᴹ τ) U)) := sandwichedRenyiEntropy_mono_traceLeft hα .. - _ ≤ D̃_ α((ρ ⊗ᴹ τ).uConj U‖(σ ⊗ᴹ τ).uConj U) := + _ ≤ D̃_ α(MState.uConj (ρ ⊗ᴹ τ) U‖MState.uConj (σ ⊗ᴹ τ) U) := sandwichedRenyiEntropy_mono_traceLeft hα .. _ = D̃_ α(ρ ⊗ᴹ τ‖σ ⊗ᴹ τ) := sandwichedRenyiEntropy_conj_unitary (by positivity) _ _ _ @@ -1486,9 +1505,7 @@ private lemma sandwichedTraceFunctional_sub_one_div_eventually_le `HermitianMat.ker_weighted_sum_le`. -/ private lemma mix_M_eq_weighted_sum (p : Prob) (τ₁ τ₂ : MState d) : (p [τ₁ ↔ τ₂]).M = ∑ i, ![(p : ℝ), 1 - (p : ℝ)] i • (![τ₁, τ₂] i).M := by - simp only [Mixable.mix, Mixable.mix_ab, MState.instMixable, Fin.sum_univ_two, - Matrix.cons_val_zero, Matrix.cons_val_one, Prob.coe_one_minus] - rfl + simp [Fin.sum_univ_two] /-- A binary mixture preserves the support condition (kernel inclusion) of its components. -/ @@ -1594,3 +1611,38 @@ theorem qRelativeEnt_joint_convexity : add_le_add (mul_le_mul_of_nonneg_left h₁ hp0'.le) (mul_le_mul_of_nonneg_left h₂ (by linarith)) exact le_of_tendsto_of_tendsto h_lhs h_rhs h_ev + +section BasisFree + +/-! ## Basis-free forms + +The data processing inequality above is stated for states indexed by a type. This is its +counterpart for states on an abstract finite-dimensional Hilbert space, obtained by picking an +arbitrary preferred basis on each side (`StdBasis.some`) and transporting; the relative entropies +are insensitive to that choice, so nothing is lost. -/ + +variable {E F : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] + +namespace DensityOp + +/-- The Data Processing Inequality for the sandwiched Rényi relative entropy. -/ +theorem sandwichedRenyiEntropy_DPI (hα : 1 ≤ α) (ρ σ : DensityOp E) (Φ : CPTPOp E F) : + D̃_ α(Φ ρ‖Φ σ) ≤ D̃_ α(ρ‖σ) := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + obtain ⟨ν, rfl⟩ := σ.exists_transport_eq + have h := _root_.sandwichedRenyiEntropy_DPI hα μ ν + (Φ.transport (Fin (Module.finrank ℂ E)) (Fin (Module.finrank ℂ F))) + rw [CPTPOp.transport_apply, CPTPOp.transport_apply, sandwichedRelRentropy_transport] at h + exact h.trans_eq (sandwichedRelRentropy_transport (F := E) α μ ν).symm + +/-- The Data Processing Inequality for the quantum relative entropy. -/ +theorem qRelativeEnt_DPI (ρ σ : DensityOp E) (Φ : CPTPOp E F) : 𝐃(Φ ρ‖Φ σ) ≤ 𝐃(ρ‖σ) := + sandwichedRenyiEntropy_DPI le_rfl ρ σ Φ + +end DensityOp + +end BasisFree diff --git a/QuantumInfo/Entropy/Relative.lean b/QuantumInfo/Entropy/Relative.lean index 681f7b493..b0477dc3c 100644 --- a/QuantumInfo/Entropy/Relative.lean +++ b/QuantumInfo/Entropy/Relative.lean @@ -5,11 +5,17 @@ Authors: Alex Meiburg -/ module +public import QuantumInfo.Entropy.SSA public import QuantumInfo.Entropy.VonNeumann +public import QuantumInfo.ForMathlib.HermitianMat.BlockDiag +public import QuantumInfo.ForMathlib.HermitianMat.Unitary public import Physlib.Meta.Sorry @[expose] public section +set_option backward.isDefEq.respectTransparency false +set_option maxRecDepth 100000 + noncomputable section variable {d d₁ d₂ d₃ : Type*} @@ -559,7 +565,6 @@ private lemma eigenWeight_nonneg (ρ σ : MState d) (i : d) : 0 ≤ eigenWeight have := (Matrix.posSemidef_iff_dotProduct_mulVec.mp h₁).2 v; exact this.1.trans (by simp [w]) -set_option backward.isDefEq.respectTransparency false in private lemma eigenWeight_zero_of_eigenvalue_zero {i : d} (hσ : σ.M.ker ≤ ρ.M.ker) (hei : σ.M.H.eigenvalues i = 0) : eigenWeight ρ σ i = 0 := by @@ -579,7 +584,6 @@ The derivative of u ↦ ⟪ρ, σ^u⟫ at u = 0 is ⟪ρ, σ.log⟫. Use inner_cfc_eq_sum_eigenWeight to write ⟪ρ, σ^u⟫ = ∑ i, q_i^u * eigenWeight ρ σ i, differentiate term by term using HasDerivAt.sum. -/ -set_option backward.isDefEq.respectTransparency false in private lemma hasDerivAt_inner_rpow_at_zero (h : σ.M.ker ≤ ρ.M.ker) : HasDerivAt (fun u : ℝ => ⟪ρ.M, σ.M ^ u⟫) ⟪ρ.M, σ.M.log⟫ 0 := by convert HasDerivAt.congr_of_eventuallyEq ?_ ?_; @@ -775,7 +779,6 @@ private lemma scalar_rpow_cross_term_of_continuous_zero {b : ℝ → ℝ} rw [ Asymptotics.isLittleO_iff ]; intro ε hε; rcases h_eps ε hε with ⟨ δ, hδ, H ⟩ ; filter_upwards [ Metric.ball_mem_nhds _ hδ ] with x hx using by simpa [ hc ] using H ( 1 + x ) ( by simpa using hx ) ; -set_option backward.isDefEq.respectTransparency false in /-- If ker A ≤ ker ρM, then conjugating ρM by the support projection of A gives back ρM. This is because ρM is supported entirely on the support (= range) of A. -/ private lemma conj_supportProj_eq_of_ker_le (A ρM : HermitianMat d ℂ) (hker : A.ker ≤ ρM.ker) : @@ -809,7 +812,6 @@ private lemma rpow_tendsto_supportProj convert h_cfc_g_conv.tendsto 0 using 2 ; simp [ HermitianMat.supportProj_eq_cfc ]; exact Filter.Tendsto.congr' ( Filter.eventuallyEq_of_mem self_mem_nhdsWithin fun x hx => by aesop ) ( h_cfc_g_conv.mono_left inf_le_left ) -set_option backward.isDefEq.respectTransparency false in /-- For PSD matrices A, ρ with A.ker ≤ ρ.ker, the function r ↦ ρ.conj (A ^ r).mat is continuous at r = 0. Even though A ^ r is discontinuous at r = 0 when A has zero eigenvalues, the kernel condition ensures the conj "kills" the @@ -873,7 +875,6 @@ private lemma eigenvalues_bounded_near {M : ℝ → HermitianMat d ℂ} Uniform convergence of (x^{1+h} - x)/h to x * log x on [0, K] as h → 0. This is the uniform version of the derivative of s ↦ x^s at s = 1. -/ -set_option maxHeartbeats 800000 in private lemma rpow_slope_tendsto_uniformly (K : ℝ) : ∀ ε > 0, ∃ δ > 0, ∀ h : ℝ, 0 < |h| → |h| < δ → ∀ x ∈ Set.Icc 0 K, |(x ^ (1 + h) - x) / h - x * Real.log x| < ε := by @@ -994,7 +995,7 @@ private lemma trace_cfc_tendsto_of_tendsto (f : ℝ → ℝ) exact (HermitianMat.posSemidef_iff_spectrum_Ici A).mp hA exact h_cont_cfc.mono fun A hA => h_spectrum_subset A hA exact h_cont_trace - exact h_cont _ ( by simp [ ρ.2 ] ) |> ContinuousWithinAt.mono <| Set.Subset.refl _; + exact h_cont _ ρ.nonneg |> ContinuousWithinAt.mono <| Set.Subset.refl _; have h_trace_cont : Continuous (fun A : HermitianMat d ℂ => A.trace) := by exact HermitianMat.trace_Continuous; have h_comp_cont : Filter.Tendsto (fun α => (M α).cfc f) (nhds 1) (nhds ((ρ : HermitianMat d ℂ).cfc f)) := by @@ -1006,7 +1007,6 @@ private lemma trace_cfc_tendsto_of_tendsto (f : ℝ → ℝ) The remainder term r(1+h)/h → 0 where `r(α) = Tr[M(α)^α] - Tr[M(α)] - Tr[ρ.M^α] + Tr[ρ.M]` -/ -set_option maxHeartbeats 800000 in private lemma cross_term_slope_tendsto_zero {M : ℝ → HermitianMat d ℂ} (hM_nonneg : ∀ᶠ α in nhds 1, 0 ≤ M α) @@ -1102,7 +1102,6 @@ private lemma hasDerivAt_trace_rpow_sub_trace_variable_base ring_nf ext; norm_num; ring -set_option backward.isDefEq.respectTransparency false in /-- The cross term in the derivative decomposition vanishes: the function α ↦ Tr[B(α)^α] - Tr[B(α)] - Tr[ρ^α] + 1 has derivative 0 at α = 1. This is because at α=1, B^1 = B, so ∂/∂B Tr[B^α] = Tr[·] (the trace is linear), @@ -1249,8 +1248,9 @@ lemma ker_kron_le_of_le {d₁ d₂ : Type*} [Fintype d₁] [Fintype d₂] [Decid If the kernel of a product state is contained in another, the left component kernel is contained. -/ lemma ker_le_of_ker_kron_le_left (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂) - (h : (σ₁ ⊗ᴹ σ₂).M.ker ≤ (ρ₁ ⊗ᴹ ρ₂).M.ker) : + (h : (σ₁.M ⊗ₖ σ₂.M).ker ≤ (ρ₁.M ⊗ₖ ρ₂.M).ker) : σ₁.M.ker ≤ ρ₁.M.ker := by + rw [← MState.prod_M, ← MState.prod_M] at h intro u hu obtain ⟨v, hv⟩ : ∃ v : EuclideanSpace ℂ d₂, v ∉ (σ₂ :HermitianMat d₂ ℂ).ker ∧ v ∉ (ρ₂ :HermitianMat d₂ ℂ).ker := by have h_union : (σ₂ : HermitianMat d₂ ℂ).ker ≠ ⊤ ∧ (ρ₂ : HermitianMat d₂ ℂ).ker ≠ ⊤ := by @@ -1265,7 +1265,7 @@ lemma ker_le_of_ker_kron_le_left (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MSta simp_all convert h_top using 1; erw [ Matrix.toLpLin_apply ] - simp_all only [MState.mat_M, PiLp.ofLp_single, Matrix.mulVec_single, + simp_all only [DensityOp.mat_M, PiLp.ofLp_single, Matrix.mulVec_single, MulOpposite.op_one, Pi.smul_apply, Matrix.col_apply, one_smul] exact σ₂.pos.ne' h_contra; · have h_contra : ρ₂.M = 0 := by @@ -1307,7 +1307,7 @@ lemma ker_le_of_ker_kron_le_left (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MSta have h_kronecker : ∀ (A : Matrix d₁ d₁ ℂ) (B : Matrix d₂ d₂ ℂ) (u : d₁ → ℂ) (v : d₂ → ℂ), (A.kronecker B).mulVec (fun p => u p.1 * v p.2) = fun p => (A.mulVec u) p.1 * (B.mulVec v) p.2 := by intro A B u v; ext ⟨ i, j ⟩ ; simp [ Matrix.mulVec, dotProduct, Finset.mul_sum, mul_comm, mul_left_comm ] ; exact Fintype.sum_prod_type_right fun x => A i x.1 * (B j x.2 * (u x.1 * v x.2)); - convert! congr_fun ( h_kronecker σ₁.1.mat σ₂.1.mat u v ) ( i, j ) using 1 ; simp + convert! congr_fun ( h_kronecker σ₁.m σ₂.m u v ) ( i, j ) using 1 ; simp exact Or.inl ( by exact congr(WithLp.ofLp $hu i) ); have hz' : z ∈ (ρ₁ ⊗ᴹ ρ₂ : HermitianMat (d₁ × d₂) ℂ).ker := by exact h hz; @@ -1333,8 +1333,9 @@ lemma ker_le_of_ker_kron_le_left (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MSta If the kernel of a product state is contained in another, the right component kernel is contained. -/ lemma ker_le_of_ker_kron_le_right (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂) - (h : (σ₁ ⊗ᴹ σ₂).M.ker ≤ (ρ₁ ⊗ᴹ ρ₂).M.ker) : + (h : (σ₁.M ⊗ₖ σ₂.M).ker ≤ (ρ₁.M ⊗ₖ ρ₂.M).ker) : σ₂.M.ker ≤ ρ₂.M.ker := by + rw [← MState.prod_M, ← MState.prod_M] at h intro v hv; have h_z : ∃ u : EuclideanSpace ℂ d₁, u ≠ 0 ∧ u ∉ σ₁.M.ker ∧ u ∉ ρ₁.M.ker := by have h_z : σ₁.M.ker ≠ ⊤ ∧ ρ₁.M.ker ≠ ⊤ := by @@ -1395,9 +1396,11 @@ lemma ker_le_of_ker_kron_le_right (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MSt · simp · simp · intro _ _ - simp only [MState.m, HermitianMat.mat_apply] - ring_nf - exact hz''.symm.trans ( by exact congr(WithLp.ofLp $hz' ( i, j )) ); + simp only [DensityOp.m, HermitianMat.mat_apply] + refine hz''.symm.trans ?_ + have h0 := (HermitianMat.mem_ker_iff_mulVec_zero _ z).1 hz' + rw [MState.prod_M, HermitianMat.kronecker_mat] at h0 + simpa [z] using congr_fun h0 (i, j) contrapose! hz''; obtain ⟨ i, hi ⟩ := Function.ne_iff.mp ( show ρ₁.M.val.mulVec u ≠ 0 from fun h => hu₃ <| congr(WithLp.toLp 2 $h)) obtain ⟨ j, hj ⟩ := Function.ne_iff.mp ( show ρ₂.M.val.mulVec v ≠ 0 from fun h => hz'' <| congr(WithLp.toLp 2 $h)) @@ -1409,7 +1412,7 @@ The kernel of a product state is contained in another product state's kernel iff kernels are contained. -/ lemma ker_prod_le_iff (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂) : - (σ₁ ⊗ᴹ σ₂).M.ker ≤ (ρ₁ ⊗ᴹ ρ₂).M.ker ↔ σ₁.M.ker ≤ ρ₁.M.ker ∧ σ₂.M.ker ≤ ρ₂.M.ker := by + (σ₁.M ⊗ₖ σ₂.M).ker ≤ (ρ₁.M ⊗ₖ ρ₂.M).ker ↔ σ₁.M.ker ≤ ρ₁.M.ker ∧ σ₂.M.ker ≤ ρ₂.M.ker := by constructor <;> intro h; · exact ⟨ ker_le_of_ker_kron_le_left ρ₁ σ₁ ρ₂ σ₂ h, ker_le_of_ker_kron_le_right ρ₁ σ₁ ρ₂ σ₂ h ⟩; · convert! ker_kron_le_of_le _ _ _ _ h.1 h.2 using 1 @@ -1465,71 +1468,221 @@ theorem sandwichedRelRentropy_additive_alpha_one_aux (ρ₁ σ₁ : MState d₁) (h1 : σ₁.M.ker ≤ ρ₁.M.ker) (h2 : σ₂.M.ker ≤ ρ₂.M.ker) : ⟪(ρ₁ ⊗ᴹ ρ₂).M, (ρ₁ ⊗ᴹ ρ₂).M.log - (σ₁ ⊗ᴹ σ₂).M.log⟫ = ⟪ρ₁.M, ρ₁.M.log - σ₁.M.log⟫_ℝ + ⟪ρ₂.M, ρ₂.M.log - σ₂.M.log⟫ := by - have h_log_kron : (ρ₁ ⊗ᴹ ρ₂).M.log = ρ₁.M.log ⊗ₖ ρ₂.M.supportProj + ρ₁.M.supportProj ⊗ₖ ρ₂.M.log ∧ (σ₁ ⊗ᴹ σ₂).M.log = σ₁.M.log ⊗ₖ σ₂.M.supportProj + σ₁.M.supportProj ⊗ₖ σ₂.M.log := by - constructor <;> apply HermitianMat.log_kron_with_proj; - have h_inner_supportProj : ∀ (A : HermitianMat d₁ ℂ) (B : HermitianMat d₂ ℂ), ⟪A ⊗ₖ B, ρ₁ ⊗ᴹ ρ₂⟫ = ⟪A, ρ₁⟫ * ⟪B, ρ₂⟫ := by - exact fun A B => HermitianMat.inner_kron A B ρ₁ ρ₂; + simp only [MState.prod_M] + have h_log_kron : (ρ₁.M ⊗ₖ ρ₂.M).log + = ρ₁.M.log ⊗ₖ ρ₂.M.supportProj + ρ₁.M.supportProj ⊗ₖ ρ₂.M.log + ∧ (σ₁.M ⊗ₖ σ₂.M).log + = σ₁.M.log ⊗ₖ σ₂.M.supportProj + σ₁.M.supportProj ⊗ₖ σ₂.M.log := + ⟨HermitianMat.log_kron_with_proj, HermitianMat.log_kron_with_proj⟩ + have h_inner_supportProj : ∀ (A : HermitianMat d₁ ℂ) (B : HermitianMat d₂ ℂ), + ⟪ρ₁.M ⊗ₖ ρ₂.M, A ⊗ₖ B⟫ = ⟪ρ₁.M, A⟫ * ⟪ρ₂.M, B⟫ := + fun A B => HermitianMat.inner_kron ρ₁.M ρ₂.M A B simp only [HermitianMat.ker] at h1 h2 - simp_all only [inner_sub_right, inner_add_right, real_inner_comm, - HermitianMat.inner_supportProj_self, MState.tr, mul_one, one_mul, + simp_all only [inner_sub_right, inner_add_right, + HermitianMat.inner_supportProj_self, DensityOp.tr, mul_one, one_mul, HermitianMat.inner_supportProj_of_ker_le] abel +section Operator + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] + +/-- The index type of the basis that `StdBasis.some ℂ E` picks out. -/ +local notation "ι₀" => Fin (Module.finrank ℂ E) + +section Bridge + +variable {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + +/-- The state on `EuclideanSpace ℂ ι` with the same density matrix as `ρ`. It carries the matrix +form of every basis-free quantity attached to `ρ`, which is how facts proved for `MState` are +transported to `DensityOp E`. -/ +private def coords (ρ : DensityOp E) : MState ι := + DensityOp.ofMat (ρ.M : HermitianMat ι ℂ) ρ.nonneg ρ.tr + +@[simp] +private theorem coords_M (ρ : DensityOp E) : + ((coords (ι := ι) ρ).M : HermitianMat ι ℂ) = ρ.M := + DensityOp.M_ofMat _ _ _ + +private theorem inner_op_log_sub_log (ρ σ : DensityOp E) : + ⟪ρ.op, ρ.op.log - σ.op.log⟫ = + ⟪(ρ.M : HermitianMat ι ℂ), (ρ.M : HermitianMat ι ℂ).log - (σ.M : HermitianMat ι ℂ).log⟫ := by + rw [← HermitianOp.inner_toMat (ι := ι), HermitianOp.toMat_sub, HermitianOp.toMat_log, + HermitianOp.toMat_log] + rfl + +private theorem trace_conj_rpow (ρ σ : DensityOp E) (α β : ℝ) : + ((ρ.op.conj ((σ.op ^ β).op)) ^ α).trace = + (((ρ.M : HermitianMat ι ℂ).conj ((σ.M : HermitianMat ι ℂ) ^ β).mat) ^ α).trace := by + have h : StdBasis.toMat ℂ E ι ((σ.op ^ β).op) = ((σ.M : HermitianMat ι ℂ) ^ β).mat := by + rw [← HermitianOp.toMat_mat (ι := ι), HermitianOp.toMat_rpow] + rfl + rw [← HermitianOp.trace_toMat (ι := ι), HermitianOp.toMat_rpow, HermitianOp.toMat_conj, h] + rfl + +private theorem ker_op_le_ker_op {ρ σ : DensityOp E} : + σ.op.ker ≤ ρ.op.ker ↔ (σ.M : HermitianMat ι ℂ).ker ≤ (ρ.M : HermitianMat ι ℂ).ker := + HermitianOp.ker_toMat_le_ker_toMat.symm + +/-- `sandwichedRelRentropy_nonneg`, transported to the density matrix of a state on an arbitrary +finite-dimensional space. -/ +theorem sandwichedRelRentropy_nonneg_M {α : ℝ} (hα : 0 < α) {ρ σ : DensityOp E} + (h : (σ.M : HermitianMat ι ℂ).ker ≤ (ρ.M : HermitianMat ι ℂ).ker) : + 0 ≤ if α = 1 then + ⟪(ρ.M : HermitianMat ι ℂ), + (ρ.M : HermitianMat ι ℂ).log - (σ.M : HermitianMat ι ℂ).log⟫ + else + (((ρ.M : HermitianMat ι ℂ).conj + ((σ.M : HermitianMat ι ℂ) ^ ((1 - α)/(2 * α))).mat) ^ α).trace.log / (α - 1) := by + simpa using sandwichedRelRentropy_nonneg (ρ := coords (ι := ι) ρ) (σ := coords (ι := ι) σ) + hα (by simpa using h) + +end Bridge + +theorem sandwichedRelRentropy_op_nonneg {α : ℝ} (hα : 0 < α) {ρ σ : DensityOp E} + (h : σ.op.ker ≤ ρ.op.ker) : + 0 ≤ if α = 1 then ⟪ρ.op, ρ.op.log - σ.op.log⟫ + else ((ρ.op.conj ((σ.op ^ ((1 - α) / (2 * α))).op)) ^ α).trace.log / (α - 1) := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + rw [inner_op_log_sub_log (ι := ι₀), trace_conj_rpow (ι := ι₀)] + exact sandwichedRelRentropy_nonneg_M hα (ker_op_le_ker_op.1 h) + /-- The Sandwiched Renyi Relative Entropy, defined with ln (nits). Note that at `α = 1` this definition switch to the standard Relative Entropy, for continuity. For α ≤ 0, this gives junk value 0. (There is no conventional value for α < 0; there is a continuous limit at α = 0, but it is complicated and - unneeded at the moment.)-/ -def SandwichedRelRentropy (α : ℝ) (ρ σ : MState d) : ENNReal := + unneeded at the moment.) + + This makes no reference to a basis; `sandwichedRelRentropy_eq_matrix` is the matrix analogue. -/ +def SandwichedRelRentropy (α : ℝ) (ρ σ : DensityOp E) : ENNReal := open Classical in if hα : 0 < α then - if h : σ.M.ker ≤ ρ.M.ker + if h : σ.op.ker ≤ ρ.op.ker then (.ofNNReal ⟨if α = 1 then - ⟪ρ.M, ρ.M.log - σ.M.log⟫ + ⟪ρ.op, ρ.op.log - σ.op.log⟫ else - ((ρ.M.conj (σ.M ^ ((1 - α)/(2 * α)) ).mat) ^ α).trace.log / (α - 1), - sandwichedRelRentropy_nonneg hα h⟩) + ((ρ.op.conj ((σ.op ^ ((1 - α)/(2 * α))).op)) ^ α).trace.log / (α - 1), + sandwichedRelRentropy_op_nonneg hα h⟩) else ⊤ else 0 notation "D̃_" α "(" ρ "‖" σ ")" => SandwichedRelRentropy α ρ σ +/-- **Matrix analogue of `SandwichedRelRentropy`.** -/ +theorem sandwichedRelRentropy_eq_matrix {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + (α : ℝ) (ρ σ : DensityOp E) : + D̃_ α(ρ‖σ) = + open Classical in + if hα : 0 < α then + if h : (σ.M : HermitianMat ι ℂ).ker ≤ (ρ.M : HermitianMat ι ℂ).ker + then (.ofNNReal ⟨if α = 1 then + ⟪(ρ.M : HermitianMat ι ℂ), + (ρ.M : HermitianMat ι ℂ).log - (σ.M : HermitianMat ι ℂ).log⟫ + else + (((ρ.M : HermitianMat ι ℂ).conj + ((σ.M : HermitianMat ι ℂ) ^ ((1 - α)/(2 * α))).mat) ^ α).trace.log / (α - 1), + sandwichedRelRentropy_nonneg_M hα h⟩) + else ⊤ + else 0 := by + rw [SandwichedRelRentropy] + by_cases hα : 0 < α + swap + · rw [dif_neg hα, dif_neg hα] + rw [dif_pos hα, dif_pos hα] + by_cases h : σ.op.ker ≤ ρ.op.ker + swap + · rw [dif_neg h, dif_neg (fun hc ↦ h (ker_op_le_ker_op (ι := ι) |>.2 hc))] + rw [dif_pos h, dif_pos (ker_op_le_ker_op (ι := ι) |>.1 h), ENNReal.coe_inj] + congr! 1 + split_ifs with h₁ + · exact inner_op_log_sub_log (ι := ι) ρ σ + · rw [trace_conj_rpow (ι := ι)] + +/-- The sandwiched Rényi relative entropy is unchanged by reading both states on another space +whose preferred basis has the same index type. -/ +@[simp] +theorem sandwichedRelRentropy_transport {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℂ F] [StdBasis ℂ F ι] + (α : ℝ) (ρ σ : DensityOp E) : + D̃_ α(ρ.transport F‖σ.transport F) = D̃_ α(ρ‖σ) := by + have hρ : ((ρ.transport F).M : HermitianMat ι ℂ) = ρ.M := DensityOp.M_transport ρ + have hσ : ((σ.transport F).M : HermitianMat ι ℂ) = σ.M := DensityOp.M_transport σ + have hker : ((σ.transport F).M : HermitianMat ι ℂ).ker ≤ + ((ρ.transport F).M : HermitianMat ι ℂ).ker ↔ + (σ.M : HermitianMat ι ℂ).ker ≤ (ρ.M : HermitianMat ι ℂ).ker := by + rw [hρ, hσ] + rw [sandwichedRelRentropy_eq_matrix (ι := ι), sandwichedRelRentropy_eq_matrix (ι := ι)] + by_cases hα : 0 < α + swap + · rw [dif_neg hα, dif_neg hα] + rw [dif_pos hα, dif_pos hα] + by_cases h : (σ.M : HermitianMat ι ℂ).ker ≤ (ρ.M : HermitianMat ι ℂ).ker + swap + · rw [dif_neg h, dif_neg fun hc ↦ h (hker.mp hc)] + rw [dif_pos h, dif_pos (hker.mpr h), ENNReal.coe_inj] + refine NNReal.coe_injective ?_ + simp only [hρ, hσ] + /-- The quantum relative entropy `𝐃(ρ‖σ) := Tr[ρ (log ρ - log σ)]`. Also called the Umegaki quantum relative entropy, when it's necessary to distinguish from other relative entropies. -/ -def qRelativeEnt (ρ σ : MState d) : ENNReal := +def qRelativeEnt (ρ σ : DensityOp E) : ENNReal := D̃_1(ρ‖σ) notation "𝐃(" ρ "‖" σ ")" => qRelativeEnt ρ σ -set_option backward.isDefEq.respectTransparency false in +/-- The quantum relative entropy is unchanged by reading both states on another space whose +preferred basis has the same index type. -/ +@[simp] +theorem qRelativeEnt_transport {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℂ F] [StdBasis ℂ F ι] + (ρ σ : DensityOp E) : 𝐃(ρ.transport F‖σ.transport F) = 𝐃(ρ‖σ) := + sandwichedRelRentropy_transport 1 ρ σ + +end Operator + +/-- `sandwichedRelRentropy_eq_matrix` for a state on `EuclideanSpace ℂ d`, whose preferred basis is +the standard one, so that the index type is determined by the state. -/ +theorem MState.sandwichedRelRentropy_eq_matrix (α : ℝ) (ρ σ : MState d) : + D̃_ α(ρ‖σ) = + open Classical in + if hα : 0 < α then + if h : σ.M.ker ≤ ρ.M.ker + then (.ofNNReal ⟨if α = 1 then + ⟪ρ.M, ρ.M.log - σ.M.log⟫ + else + ((ρ.M.conj (σ.M ^ ((1 - α)/(2 * α))).mat) ^ α).trace.log / (α - 1), + sandwichedRelRentropy_nonneg_M hα h⟩) + else ⊤ + else 0 := + _root_.sandwichedRelRentropy_eq_matrix α ρ σ + /-- The Sandwiched Renyi Relative entropy is additive for α=1 (standard relative entropy). -/ private theorem sandwichedRelRentropy_additive_alpha_one (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂) : D̃_ 1(ρ₁ ⊗ᴹ ρ₂‖σ₁ ⊗ᴹ σ₂) = D̃_ 1(ρ₁‖σ₁) + D̃_ 1(ρ₂‖σ₂) := by + have hker : (σ₁ ⊗ᴹ σ₂).M.ker ≤ (ρ₁ ⊗ᴹ ρ₂).M.ker ↔ + σ₁.M.ker ≤ ρ₁.M.ker ∧ σ₂.M.ker ≤ ρ₂.M.ker := by + simpa using ker_prod_le_iff ρ₁ σ₁ ρ₂ σ₂ by_cases h1 : σ₁.M.ker ≤ ρ₁.M.ker <;> by_cases h2 : σ₂.M.ker ≤ ρ₂.M.ker - · simp only [SandwichedRelRentropy, ↓reduceIte, ↓reduceDIte, h1, h2] - split_ifs <;> simp_all [ ker_prod_le_iff ]; - simp only [sandwichedRelRentropy_additive_alpha_one_aux ρ₁ σ₁ ρ₂ σ₂ h1 h2] - rfl - · simp only [SandwichedRelRentropy, zero_lt_one, ↓reduceDIte, ↓reduceIte, h1, h2, - add_top, dite_eq_right_iff, ENNReal.coe_ne_top, imp_false] - have := ker_prod_le_iff ρ₁ σ₁ ρ₂ σ₂ + · simp only [MState.sandwichedRelRentropy_eq_matrix, zero_lt_one, ↓reduceDIte, ↓reduceIte, h1, h2, + hker.mpr ⟨h1, h2⟩] + rw [← ENNReal.coe_add, ENNReal.coe_inj] + apply NNReal.eq + push_cast + exact sandwichedRelRentropy_additive_alpha_one_aux ρ₁ σ₁ ρ₂ σ₂ h1 h2 + all_goals + simp only [MState.sandwichedRelRentropy_eq_matrix, zero_lt_one, ↓reduceDIte, ↓reduceIte, h1, h2, + add_top, top_add, dite_eq_right_iff, ENNReal.coe_ne_top, imp_false] + rw [hker] tauto - · simp only [SandwichedRelRentropy, zero_lt_one, ↓reduceDIte, ↓reduceIte, h1, h2, - top_add, dite_eq_right_iff, ENNReal.coe_ne_top, imp_false] - contrapose! h1 - exact (ker_le_of_ker_kron_le_left ρ₁ σ₁ ρ₂ σ₂) h1 - · simp only [SandwichedRelRentropy, zero_lt_one, ↓reduceDIte, ↓reduceIte, h1, h2, - add_top, dite_eq_right_iff, ENNReal.coe_ne_top, imp_false] - contrapose! h1 - exact (ker_le_of_ker_kron_le_left ρ₁ σ₁ ρ₂ σ₂) h1 lemma sandwiched_term_product (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂) (α β : ℝ) : (((ρ₁ ⊗ᴹ ρ₂).M.conj ((σ₁ ⊗ᴹ σ₂).M ^ β).mat) ^ α).trace = ((ρ₁.M.conj (σ₁.M ^ β).mat) ^ α).trace * ((ρ₂.M.conj (σ₂.M ^ β).mat) ^ α).trace := by - simp only [MState.prod] + simp only [MState.prod_M] rw [← HermitianMat.trace_kronecker] rw [← HermitianMat.rpow_kron α ?_ ?_, ← HermitianMat.conj_kron, HermitianMat.rpow_kron β σ₁.nonneg σ₂.nonneg, HermitianMat.kronecker_mat] @@ -1542,9 +1695,9 @@ The Sandwiched Renyi Relative entropy is additive for alpha != 1. theorem sandwichedRelRentropy_additive_alpha_ne_one {α : ℝ} (hα : α ≠ 1) (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState d₂) : D̃_ α(ρ₁ ⊗ᴹ ρ₂‖σ₁ ⊗ᴹ σ₂) = D̃_ α(ρ₁‖σ₁) + D̃_ α(ρ₂‖σ₂) := by by_cases hα0 : 0 < α; swap - · simp [SandwichedRelRentropy, hα0] + · simp [MState.sandwichedRelRentropy_eq_matrix, hα0] by_cases h_ker : σ₁.M.ker ≤ ρ₁.M.ker ∧ σ₂.M.ker ≤ ρ₂.M.ker - · simp_all [SandwichedRelRentropy] + · simp_all [MState.sandwichedRelRentropy_eq_matrix] -- Apply the additivity of the trace term to split the logarithm into the sum of the logarithms. have h_trace_add : Real.log ((ρ₁ ⊗ᴹ ρ₂).M.conj ((σ₁ ⊗ᴹ σ₂).M ^ ((1 - α) / (2 * α))).mat ^ α).trace = Real.log ((ρ₁.M.conj (σ₁.M ^ ((1 - α) / (2 * α))).mat) ^ α).trace + Real.log ((ρ₂.M.conj (σ₂.M ^ ((1 - α) / (2 * α))).mat) ^ α).trace := by rw [ sandwiched_term_product, Real.log_mul ]; @@ -1553,13 +1706,13 @@ theorem sandwichedRelRentropy_additive_alpha_ne_one {α : ℝ} (hα : α ≠ 1) split_ifs <;> simp_all · norm_num [ add_div ]; exact rfl; - · exact False.elim ( ‹¬ ( σ₁ ⊗ᴹ σ₂ |> MState.M |> HermitianMat.ker ) ≤ ( ρ₁ ⊗ᴹ ρ₂ |> MState.M |> HermitianMat.ker ) › ( by simpa [ HermitianMat.ker ] using ker_prod_le_iff _ _ _ _ |>.2 h_ker ) ); - · have h_ker_prod : ¬((σ₁ ⊗ᴹ σ₂).M.ker ≤ (ρ₁ ⊗ᴹ ρ₂).M.ker) := by + · exact absurd ((ker_prod_le_iff ρ₁ σ₁ ρ₂ σ₂).2 h_ker) ‹_› + · have h_ker_prod : ¬((σ₁.M ⊗ₖ σ₂.M).ker ≤ (ρ₁.M ⊗ₖ ρ₂.M).ker) := by simp_all [ ker_prod_le_iff ] rw [not_and_or] at h_ker rcases h_ker with h_ker | h_ker - · simp [SandwichedRelRentropy, h_ker_prod, h_ker, hα0] - · simp [SandwichedRelRentropy, h_ker_prod, h_ker, hα0] + · simp [MState.sandwichedRelRentropy_eq_matrix, h_ker_prod, h_ker, hα0] + · simp [MState.sandwichedRelRentropy_eq_matrix, h_ker_prod, h_ker, hα0] end additivity @@ -1578,23 +1731,25 @@ theorem qRelativeEnt_additive (ρ₁ σ₁ : MState d₁) (ρ₂ σ₂ : MState --or `simp [SandwichedRelRentropy]`. exact sandwichedRelRentropy_additive_alpha_one ρ₁ σ₁ ρ₂ σ₂ -set_option backward.isDefEq.respectTransparency false in @[simp] theorem sandwichedRelRentropy_relabel (ρ σ : MState d) (e : d₂ ≃ d) : D̃_ α(ρ.relabel e‖σ.relabel e) = D̃_ α(ρ‖σ) := by - simp only [SandwichedRelRentropy, MState.relabel_M] - split_ifs <;> simp_all [HermitianMat.conj_submatrix] <;> - exact (HermitianMat.ker_reindex_le_iff σ.M ρ.M e.symm).mp ‹_› + simp only [MState.sandwichedRelRentropy_eq_matrix] + rw! [MState.relabel_M, MState.relabel_M, HermitianMat.ker_reindex_le_iff] + --Why doesn't this `simp`? Because it's an if condition, I'm guessing + simp [HermitianMat.conj_submatrix] -set_option backward.isDefEq.respectTransparency false in @[simp] theorem sandwichedRelRentropy_self (hα : 0 < α) (ρ : MState d) : --Technically this holds for all α except for `-1` and `0`. But those are stupid. --TODO: Maybe SandwichedRelRentropy should actually be defined differently for α = 0? D̃_ α(ρ‖ρ) = 0 := by - simp only [SandwichedRelRentropy, hα, ↓reduceDIte, Std.le_refl, sub_self, inner_zero_right, - ENNReal.coe_eq_zero, NNReal.eq_iff, NNReal.coe_zero] - simp [NNReal.toReal] + rw [MState.sandwichedRelRentropy_eq_matrix] + simp only [hα, ↓reduceDIte, le_refl, sub_self, inner_zero_right, ENNReal.coe_eq_zero] + apply NNReal.coe_injective + show (if α = 1 then (0 : ℝ) + else ((ρ.M.conj ((ρ.M ^ ((1 - α) / (2 * α))).mat)) ^ α).trace.log / (α - 1)) = 0 + simp only [ite_eq_left_iff, div_eq_zero_iff, Real.log_eq_zero] intro hα left; right; left rw [HermitianMat.rpow_eq_cfc, HermitianMat.rpow_eq_cfc] @@ -1614,12 +1769,19 @@ theorem sandwichedRelRentropy_self (hα : 0 < α) (ρ : MState d) : · field_simp; ring_nf; positivity simp -set_option backward.isDefEq.respectTransparency false in +/-- The Sandwiched Renyi Relative entropy is infinite exactly when the support condition fails. -/ +theorem sandwichedRelRentropy_eq_top (hα : 0 < α) {ρ σ : MState d} (h : ¬ σ.M.ker ≤ ρ.M.ker) : + D̃_ α(ρ‖σ) = ⊤ := by + rw [MState.sandwichedRelRentropy_eq_matrix, dif_pos hα, dif_neg h] + +theorem qRelativeEnt_eq_top {ρ σ : MState d} (h : ¬ σ.M.ker ≤ ρ.M.ker) : 𝐃(ρ‖σ) = ⊤ := + sandwichedRelRentropy_eq_top zero_lt_one h + @[aesop (rule_sets := [finiteness]) unsafe apply] theorem sandwichedRelEntropy_ne_top {ρ σ : MState d} [σ.M.NonSingular] : D̃_ α(ρ‖σ) ≠ ⊤ := by by_cases 0 < α - · simp [SandwichedRelRentropy, HermitianMat.nonSingular_ker_bot, *] - · simp [SandwichedRelRentropy, *] + · simp [MState.sandwichedRelRentropy_eq_matrix, HermitianMat.nonSingular_ker_bot, *] + · simp [MState.sandwichedRelRentropy_eq_matrix, *] @[fun_prop] lemma continuousOn_exponent : ContinuousOn (fun α : ℝ => (1 - α) / (2 * α)) (Set.Ioi 0) := by @@ -1669,7 +1831,7 @@ private theorem sandwichedRelRentropy.continuousOn_Ioi_1_aux (ρ σ : MState d) private theorem sandwichedRelRentropy.continuousOn_Ioi_1 (ρ σ : MState d) : ContinuousOn (fun α => D̃_ α(ρ‖σ)) (Set.Ioi 1) := by - dsimp [SandwichedRelRentropy] + simp only [MState.sandwichedRelRentropy_eq_matrix] split_ifs with hρ · rw [continuousOn_congr (f := fun α ↦ ENNReal.ofReal (Real.log ((HermitianMat.conj (σ.M ^ ((1 - α) / (2 * α))).mat) ρ.M ^ α).trace / (α - 1)))] @@ -1716,7 +1878,7 @@ private theorem sandwichedRelRentropy.continuousOn_Ioo_0_1_aux (ρ σ : MState d /-- Continuity on (0,1): the sandwich relative Rényi entropy is continuous in α on (0,1). -/ private theorem sandwichedRelRentropy.continuousOn_Ioo_0_1 (ρ σ : MState d) : ContinuousOn (fun α => D̃_ α(ρ‖σ)) (Set.Ioo 0 1) := by - dsimp [SandwichedRelRentropy] + simp only [MState.sandwichedRelRentropy_eq_matrix] split_ifs with hρ · rw [continuousOn_congr (f := fun α ↦ ENNReal.ofReal (Real.log ((HermitianMat.conj (σ.M ^ ((1 - α) / (2 * α))).mat) ρ.M ^ α).trace / (α - 1)))] @@ -1743,12 +1905,12 @@ private theorem sandwichedRelRentropy.continuousOn_Ioo_0_1 (ρ σ : MState d) : dsimp only simp [hx.1] -set_option backward.isDefEq.respectTransparency false in /-- Continuity at 1: the sandwich relative Rényi entropy is continuous at α = 1. -/ private theorem sandwichedRelRentropy.continuousAt_1 (ρ σ : MState d) : ContinuousWithinAt (fun α => D̃_ α(ρ‖σ)) (Set.Ioi 0) 1 := by by_cases h : σ.M.ker ≤ ρ.M.ker - · simp only [ContinuousWithinAt, SandwichedRelRentropy, dif_pos h, zero_lt_one, if_true] + · simp only [ContinuousWithinAt, MState.sandwichedRelRentropy_eq_matrix, dif_pos h, zero_lt_one, + if_true] -- Use the fact that the limit of the real-valued function is the inner product. have h_real_limit : Filter.Tendsto (fun α : ℝ => if α = 1 then ⟪ρ.M, ρ.M.log - σ.M.log⟫ else Real.log ((HermitianMat.conj (σ.M ^ ((1 - α) / (2 * α))).mat) ρ.M ^ α).trace / (α - 1)) (nhdsWithin 1 (Set.Ioi 0)) (nhds ⟪ρ.M, ρ.M.log - σ.M.log⟫) := by have h_real_limit : Filter.Tendsto (fun α : ℝ => Real.log ((HermitianMat.conj (σ.M ^ ((1 - α) / (2 * α))).mat) ρ.M ^ α).trace / (α - 1)) (nhdsWithin 1 (Set.Ioi 0 \ {1})) (nhds ⟪ρ.M, ρ.M.log - σ.M.log⟫) := by @@ -1770,7 +1932,7 @@ private theorem sandwichedRelRentropy.continuousAt_1 (ρ σ : MState d) : exact Real.toNNReal_of_nonneg _ · apply tendsto_const_nhds.congr' filter_upwards [self_mem_nhdsWithin] with α hα - simp only [SandwichedRelRentropy, Set.mem_Ioi.mp hα, zero_lt_one, dif_neg h] + simp only [MState.sandwichedRelRentropy_eq_matrix, Set.mem_Ioi.mp hα, zero_lt_one, dif_neg h] @[fun_prop] theorem sandwichedRelRentropy.continuousOn (ρ σ : MState d) : @@ -1788,20 +1950,8 @@ theorem sandwichedRelRentropy.continuousOn (ρ σ : MState d) : /-- Quantum relative entropy as `Tr[ρ (log ρ - log σ)]` when supports are contained. -/ theorem qRelativeEnt_ker {ρ σ : MState d} (h : σ.M.ker ≤ ρ.M.ker) : 𝐃(ρ‖σ).toEReal = ⟪ρ.M, ρ.M.log - σ.M.log⟫ := by - simp [qRelativeEnt, SandwichedRelRentropy, h] - norm_cast - -set_option backward.isDefEq.respectTransparency false in -/-- The quantum relative entropy is finite exactly when the support condition -`σ.M.ker ≤ ρ.M.ker` holds. -/ -theorem qRelativeEnt_ne_top_iff {ρ σ : MState d} : 𝐃(ρ‖σ) ≠ ⊤ ↔ σ.M.ker ≤ ρ.M.ker := by - rw [qRelativeEnt, SandwichedRelRentropy] - simp only [zero_lt_one, ↓reduceDIte] - split_ifs with h <;> simp [h] - -/-- The quantum relative entropy is `⊤` exactly when the support condition fails. -/ -theorem qRelativeEnt_eq_top_iff {ρ σ : MState d} : 𝐃(ρ‖σ) = ⊤ ↔ ¬ σ.M.ker ≤ ρ.M.ker := by - simpa using (not_congr (qRelativeEnt_ne_top_iff (ρ := ρ) (σ := σ))) + simp [qRelativeEnt, MState.sandwichedRelRentropy_eq_matrix, h, EReal.coe_nnreal_eq_coe_real] + rfl open Classical in theorem qRelativeEnt_eq_neg_Sᵥₙ_add (ρ σ : MState d) : @@ -1810,7 +1960,7 @@ theorem qRelativeEnt_eq_neg_Sᵥₙ_add (ρ σ : MState d) : by_cases h : σ.M.ker ≤ ρ.M.ker · simp [h, Sᵥₙ_eq_neg_trace_log, qRelativeEnt_ker, inner_sub_right] rw [real_inner_comm, sub_eq_add_neg] - · simp [h, qRelativeEnt, SandwichedRelRentropy] + · simp [h, qRelativeEnt, MState.sandwichedRelRentropy_eq_matrix] /-- The quantum relative entropy is unchanged by `MState.relabel` -/ @[simp] @@ -1823,7 +1973,7 @@ theorem sandwichedRelRentropy_of_unique [Unique d] (ρ σ : MState d) : D̃_α(ρ‖σ) = 0 := by rcases Subsingleton.allEq ρ default rcases Subsingleton.allEq σ default - simp [SandwichedRelRentropy] + simp [MState.sandwichedRelRentropy_eq_matrix] intro rfl @@ -1960,24 +2110,21 @@ private lemma eigenWeight_eq_zero_iff (ρ x : MState d) (i : d) : have h_real : star v ⬝ᵥ (ρ.M.mat.mulVec v) = star (star v ⬝ᵥ (ρ.M.mat.mulVec v)) := by have h_inner : ∀ (v w : d → ℂ), star v ⬝ᵥ (ρ.M.mat.mulVec w) = star (star w ⬝ᵥ (ρ.M.mat.mulVec v)) := by intro v w - have h_inner : star v ⬝ᵥ (ρ.M.mat.mulVec w) = star (star w ⬝ᵥ (ρ.M.mat.mulVec v)) := by - have h_inner : star v ⬝ᵥ (ρ.M.mat.mulVec w) = star (star w ⬝ᵥ (ρ.M.mat.mulVec v)) := by - have h_inner : ρ.M.mat = star ρ.M.mat := by - exact ρ.M.2.symm ▸ rfl - conv_rhs => rw [ h_inner ] - simp [ Matrix.mulVec, dotProduct ] - ring_nf - simp [Finset.mul_sum, mul_comm, mul_left_comm ]; - rw [ Finset.sum_comm ] ; congr ; ext ; congr ; ext ; ring!; - exact h_inner - exact h_inner + have hH : ∀ a b, star (ρ.M.mat b a) = ρ.M.mat a b := fun a b ↦ + congrFun (congrFun ρ.M.2 a) b + simp only [dotProduct, Matrix.mulVec, Pi.star_apply, star_sum, star_mul', star_star, + Finset.mul_sum] + rw [Finset.sum_comm] + refine Finset.sum_congr rfl fun b _ ↦ Finset.sum_congr rfl fun a _ ↦ ?_ + rw [hH a b] + ring exact h_inner v v ▸ by simp [ Matrix.mulVec, dotProduct ] ; exact h_real.trans ( by simp [] ) have h_real : ∀ (v : d → ℂ), star v ⬝ᵥ (ρ.M.mat.mulVec v) = RCLike.re (star v ⬝ᵥ (ρ.M.mat.mulVec v)) := by intro v; specialize h_real v; rw [ eq_comm ] at h_real; simp_all [ Complex.ext_iff ] ; linarith! [ h_real ] ; rw [ h_real ] ; norm_cast; simp [Matrix.dotProduct_mulVec ] - exact HermitianMat.mem_ker_of_inner_mulVec_zero ρ.2 _ h_inner + exact HermitianMat.mem_ker_of_inner_mulVec_zero ρ.nonneg _ h_inner refine ⟨h_forward, fun h ↦ ?_⟩ -- Since ρ e_i = 0, we have e_i^* ρ e_i = 0. have h_zero : (Matrix.vecMul (star (x.M.H.eigenvectorBasis i : d → ℂ)) ρ.M.mat) ⬝ᵥ (x.M.H.eigenvectorBasis i : d → ℂ) = 0 := by @@ -1989,7 +2136,6 @@ private lemma eigenWeight_eq_zero_iff (ρ x : MState d) (i : d) : simp [ dotProduct ] exact congr_arg Complex.re h_zero -set_option backward.isDefEq.respectTransparency false in private lemma ker_le_iff_eigenWeight_zero (ρ x : MState d) : x.M.ker ≤ ρ.M.ker ↔ ∀ i, x.M.H.eigenvalues i = 0 → eigenWeight ρ x i = 0 := by constructor @@ -2009,7 +2155,7 @@ private lemma ker_le_iff_eigenWeight_zero (ρ x : MState d) : exact Finset.sum_congr rfl fun i _ => by rw [ Matrix.mulVec_smul, h_eigenvalue i, SMulCommClass.smul_comm ] have h_eigenvalue_zero : ∑ i, (x.M.H.eigenvalues i) • w i • x.M.H.eigenvectorBasis i = 0 := by replace h_eigenvalue := congr(WithLp.toLp 2 $h_eigenvalue) - simp only [HermitianMat.val_eq_coe, MState.mat_M, WithLp.ofLp_sum, WithLp.ofLp_smul, + simp only [HermitianMat.val_eq_coe, DensityOp.mat_M, WithLp.ofLp_sum, WithLp.ofLp_smul, WithLp.toLp_sum, WithLp.toLp_smul, WithLp.toLp_ofLp] at h_eigenvalue rw [← h_eigenvalue, ← hv] rfl @@ -2094,10 +2240,9 @@ Relative entropy is lower semicontinuous (in each argument, actually, but we onl latter here). Will need the fact that all the cfc / eigenvalue stuff is continuous, plus carefully handling what happens with the kernel subspace, which will make this a pain. -/ -set_option backward.isDefEq.respectTransparency false in @[fun_prop] theorem qRelativeEnt.lowerSemicontinuous (ρ : MState d) : LowerSemicontinuous fun σ => 𝐃(ρ‖σ) := by - simp_rw [qRelativeEnt, SandwichedRelRentropy, if_true, lowerSemicontinuous_iff] + simp_rw [qRelativeEnt, MState.sandwichedRelRentropy_eq_matrix, if_true, lowerSemicontinuous_iff] simp only [zero_lt_one, ↓reduceDIte] intro x by_cases hx : x.M.ker ≤ ρ.M.ker @@ -2135,171 +2280,98 @@ theorem qRelativeEnt_ne_top {ρ σ : MState d} [σ.M.NonSingular] : 𝐃(ρ‖σ rw [qRelativeEnt] finiteness -omit [DecidableEq dA] in -open HermitianMat in -private lemma inner_kron_one_eq_inner_traceRight - (A : HermitianMat dA ℂ) (M : HermitianMat (dA × dB) ℂ) : - ⟪A ⊗ₖ (1 : HermitianMat dB ℂ), M⟫ = ⟪A, M.traceRight⟫ := by - rw [inner_comm, inner_eq_re_trace, inner_eq_re_trace] - exact (congrArg Complex.re (Matrix.trace_mul_kron_one_right M.mat A.mat)).trans <| by - simpa using congrArg Complex.re (Matrix.trace_mul_comm M.traceRight.mat A.mat) - -omit [DecidableEq dB] in -open HermitianMat in -private lemma inner_one_kron_eq_inner_traceLeft - (B : HermitianMat dB ℂ) (M : HermitianMat (dA × dB) ℂ) : - ⟪(1 : HermitianMat dA ℂ) ⊗ₖ B, M⟫ = ⟪B, M.traceLeft⟫ := by - rw [inner_comm, inner_eq_re_trace, inner_eq_re_trace] - exact (congrArg Complex.re (Matrix.trace_mul_one_kron_right M.mat B.mat)).trans <| by - simpa using congrArg Complex.re (Matrix.trace_mul_comm M.traceLeft.mat B.mat) - -private lemma fixed_support_kron_right (ρ : MState (dA × dB)) - {x : EuclideanSpace ℂ (dA × dB)} (hx : x ∈ ρ.M.support) : - (ρ.traceRight.M.supportProj ⊗ₖ (1 : HermitianMat dB ℂ)).lin x = x := by - set K : HermitianMat (dA × dB) ℂ := ρ.traceRight.M.kerProj ⊗ₖ (1 : HermitianMat dB ℂ) with hKdef - have hKx : x ∈ K.ker := by - refine (HermitianMat.inner_zero_iff ρ.nonneg (HermitianMat.kronecker_nonneg - (by simpa [HermitianMat.kerProj] using - (HermitianMat.projector_nonneg (S := ρ.traceRight.M.ker))) - (by rw [HermitianMat.zero_le_iff]; exact Matrix.PosSemidef.one))).1 ?_ hx - rw [HermitianMat.inner_comm, inner_kron_one_eq_inner_traceRight, HermitianMat.inner_comm] - simpa [hKdef, MState.exp_val] using - (ρ.traceRight.exp_val_eq_zero_iff (by simpa [HermitianMat.kerProj] using - (HermitianMat.projector_nonneg (S := ρ.traceRight.M.ker)))).2 (by simp) - have hsum : K + ρ.traceRight.M.supportProj ⊗ₖ (1 : HermitianMat dB ℂ) = 1 := by - rw [hKdef, ← HermitianMat.add_kronecker, ρ.traceRight.M.kerProj_add_supportProj, - HermitianMat.kronecker_one_one] - have key := congrArg (fun T : HermitianMat (dA × dB) ℂ => T.lin x) hsum - simpa [HermitianMat.lin, HermitianMat.mat_add, map_add, LinearMap.add_apply, - Matrix.toLpLin_apply, (K.mem_ker_iff_mulVec_zero x).1 hKx] using key - -private lemma fixed_support_kron_left (ρ : MState (dA × dB)) - {x : EuclideanSpace ℂ (dA × dB)} (hx : x ∈ ρ.M.support) : - ((1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.supportProj).lin x = x := by - let K : HermitianMat (dA × dB) ℂ := (1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.kerProj - let P : HermitianMat (dA × dB) ℂ := (1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.supportProj - have hK_nonneg : 0 ≤ K := by - dsimp [K] - exact HermitianMat.kronecker_nonneg - (by rw [HermitianMat.zero_le_iff]; exact Matrix.PosSemidef.one) - (by simpa [HermitianMat.kerProj] using - (HermitianMat.projector_nonneg (S := ρ.traceLeft.M.ker))) - have hsum : K + P = 1 := by - simp only [K, P, ← HermitianMat.kronecker_add, ρ.traceLeft.M.kerProj_add_supportProj, - HermitianMat.kronecker_one_one] - have hKx : x ∈ K.ker := by - refine (HermitianMat.inner_zero_iff ρ.nonneg hK_nonneg).1 ?_ hx - rw [HermitianMat.inner_comm, inner_one_kron_eq_inner_traceLeft, HermitianMat.inner_comm] - simpa [K, MState.exp_val] using - (ρ.traceLeft.exp_val_eq_zero_iff (by simpa [HermitianMat.kerProj] using - (HermitianMat.projector_nonneg (S := ρ.traceLeft.M.ker)))).2 (by simp) - have key := congrArg (fun T : HermitianMat (dA × dB) ℂ => T.lin x) hsum - simpa [P, HermitianMat.lin, HermitianMat.mat_add, map_add, LinearMap.add_apply, - Matrix.toLpLin_apply, (K.mem_ker_iff_mulVec_zero x).1 hKx] using key +section mutualInfo + +open HermitianMat + +private lemma inner_kerProj_kron_one (ρ : MState (dA × dB)) : + ⟪ρ.M, ρ.traceRight.M.kerProj ⊗ₖ (1 : HermitianMat dB ℂ)⟫ = 0 := by + rw [inner_comm, inner_kron_one, ← MState.traceRight_M, + eq_sub_of_add_eq (kerProj_add_supportProj ρ.traceRight.M), inner_sub_left, one_inner, + inner_comm, inner_supportProj_self, sub_self] + +private lemma inner_one_kron_kerProj (ρ : MState (dA × dB)) : + ⟪ρ.M, (1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.kerProj⟫ = 0 := by + rw [inner_comm, inner_one_kron, ← MState.traceLeft_M, + eq_sub_of_add_eq (kerProj_add_supportProj ρ.traceLeft.M), inner_sub_left, one_inner, + inner_comm, inner_supportProj_self, sub_self] + +private lemma mul_kerProj_kron_one (ρ : MState (dA × dB)) : + ρ.M.mat * (ρ.traceRight.M.kerProj ⊗ₖ (1 : HermitianMat dB ℂ)).mat = 0 := by + refine mul_eq_zero_of_inner_eq_zero ρ.nonneg ?_ (inner_kerProj_kron_one ρ) + rw [kronecker_mat, mat_one, ← Matrix.mul_kronecker_mul, one_mul, kerProj_sq] + +private lemma mul_one_kron_kerProj (ρ : MState (dA × dB)) : + ρ.M.mat * ((1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.kerProj).mat = 0 := by + refine mul_eq_zero_of_inner_eq_zero ρ.nonneg ?_ (inner_one_kron_kerProj ρ) + rw [kronecker_mat, mat_one, ← Matrix.mul_kronecker_mul, one_mul, kerProj_sq] + +private lemma mul_kerProj_kron (ρ : MState (dA × dB)) (Y : HermitianMat dB ℂ) : + ρ.M.mat * (ρ.traceRight.M.kerProj ⊗ₖ Y).mat = 0 := by + have h : (ρ.traceRight.M.kerProj ⊗ₖ Y).mat + = (ρ.traceRight.M.kerProj ⊗ₖ (1 : HermitianMat dB ℂ)).mat + * ((1 : HermitianMat dA ℂ) ⊗ₖ Y).mat := by + rw [kronecker_mat, kronecker_mat, kronecker_mat, mat_one, mat_one, ← Matrix.mul_kronecker_mul, + mul_one, one_mul] + rw [h, ← mul_assoc, mul_kerProj_kron_one, Matrix.zero_mul] + +private lemma mul_kron_kerProj (ρ : MState (dA × dB)) (X : HermitianMat dA ℂ) : + ρ.M.mat * (X ⊗ₖ ρ.traceLeft.M.kerProj).mat = 0 := by + have h : (X ⊗ₖ ρ.traceLeft.M.kerProj).mat + = ((1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.kerProj).mat + * (X ⊗ₖ (1 : HermitianMat dB ℂ)).mat := by + rw [kronecker_mat, kronecker_mat, kronecker_mat, mat_one, mat_one, ← Matrix.mul_kronecker_mul, + mul_one, one_mul] + rw [h, ← mul_assoc, mul_one_kron_kerProj, Matrix.zero_mul] + +/-- A bipartite state is unchanged by projecting onto the support of the product of its +marginals. -/ +private lemma mul_supportProj_prod (ρ : MState (dA × dB)) : + ρ.M.mat * (ρ.traceRight ⊗ᴹ ρ.traceLeft).M.supportProj.mat = ρ.M.mat := by + have hA : ρ.traceRight.M.supportProj = 1 - ρ.traceRight.M.kerProj := + eq_sub_of_add_eq' (kerProj_add_supportProj ρ.traceRight.M) + have hB : ρ.traceLeft.M.supportProj = 1 - ρ.traceLeft.M.kerProj := + eq_sub_of_add_eq' (kerProj_add_supportProj ρ.traceLeft.M) + rw [MState.prod_M, supportProj_kron, hA, hB, sub_kronecker, kronecker_sub, kronecker_sub] + simp only [mat_sub, Matrix.mul_sub, mul_kron_kerProj, mul_kerProj_kron] + simp + +private lemma ker_prod_le_ker (ρ : MState (dA × dB)) : + (ρ.traceRight ⊗ᴹ ρ.traceLeft).M.ker ≤ ρ.M.ker := by + intro v hv + have hPv := (mem_ker_iff_mulVec_zero _ v).mp (ker_le_ker_supportProj _ hv) + rw [mem_ker_iff_mulVec_zero] at hv ⊢ + calc ρ.M.mat.mulVec v + = (ρ.M.mat * (ρ.traceRight ⊗ᴹ ρ.traceLeft).M.supportProj.mat).mulVec v := by + rw [mul_supportProj_prod] + _ = ρ.M.mat.mulVec ((ρ.traceRight ⊗ᴹ ρ.traceLeft).M.supportProj.mat.mulVec v) := by + rw [Matrix.mulVec_mulVec] + _ = 0 := by rw [hPv, Matrix.mulVec_zero] + +private lemma inner_log_prod (ρ : MState (dA × dB)) : + ⟪ρ.M, (ρ.traceRight ⊗ᴹ ρ.traceLeft).M.log⟫ = -Sᵥₙ ρ.traceRight - Sᵥₙ ρ.traceLeft := by + rw [MState.prod_M, log_kron_with_proj, inner_add_right] + have h1 : ⟪ρ.M, ρ.traceRight.M.log ⊗ₖ ρ.traceLeft.M.supportProj⟫ = -Sᵥₙ ρ.traceRight := by + rw [eq_sub_of_add_eq' (kerProj_add_supportProj ρ.traceLeft.M), kronecker_sub, inner_sub_right, + inner_eq_zero_of_mul_eq_zero (mul_kron_kerProj ρ _), sub_zero, inner_comm, inner_kron_one, + ← MState.traceRight_M, Sᵥₙ_eq_neg_trace_log, neg_neg] + have h2 : ⟪ρ.M, ρ.traceRight.M.supportProj ⊗ₖ ρ.traceLeft.M.log⟫ = -Sᵥₙ ρ.traceLeft := by + rw [eq_sub_of_add_eq' (kerProj_add_supportProj ρ.traceRight.M), sub_kronecker, inner_sub_right, + inner_eq_zero_of_mul_eq_zero (mul_kerProj_kron ρ _), sub_zero, inner_comm, inner_one_kron, + ← MState.traceLeft_M, Sᵥₙ_eq_neg_trace_log, neg_neg] + rw [h1, h2] + ring /-- `I(A:B) = 𝐃(ρᴬᴮ‖ρᴬ ⊗ ρᴮ)` -/ theorem qMutualInfo_as_qRelativeEnt (ρ : MState (dA × dB)) : qMutualInfo ρ = (𝐃(ρ‖ρ.traceRight ⊗ᴹ ρ.traceLeft) : EReal) := by - have fixed_support_kron_prod : ∀ {x : EuclideanSpace ℂ (dA × dB)}, - x ∈ ρ.M.support → - (ρ.traceRight.M.supportProj ⊗ₖ ρ.traceLeft.M.supportProj).lin x = x := by - intro x hx - let A : Matrix (dA × dB) (dA × dB) ℂ := - Matrix.kroneckerMap (· * ·) ρ.traceRight.M.supportProj.mat (1 : Matrix dB dB ℂ) - let B : Matrix (dA × dB) (dA × dB) ℂ := - Matrix.kroneckerMap (· * ·) (1 : Matrix dA dA ℂ) ρ.traceLeft.M.supportProj.mat - have hxA' : A.toEuclideanLin x = x := by - simpa [A, HermitianMat.lin, Matrix.toEuclideanLin] using fixed_support_kron_right ρ hx - have hxB' : B.toEuclideanLin x = x := by - simpa [B, HermitianMat.lin, Matrix.toEuclideanLin] using fixed_support_kron_left ρ hx - have hmul : (A * B).toEuclideanLin x = x := by - simpa [A, B, Matrix.toEuclideanLin, Matrix.mulVec_mulVec] using - show A.toEuclideanLin (B.toEuclideanLin x) = x by rw [hxB', hxA'] - simpa [HermitianMat.lin, Matrix.toEuclideanLin, A, B, ← Matrix.mul_kronecker_mul] using hmul - have prod_marginals_ker_le : (ρ.traceRight ⊗ᴹ ρ.traceLeft).M.ker ≤ ρ.M.ker := by - let P : HermitianMat (dA × dB) ℂ := ρ.traceRight.M.supportProj ⊗ₖ ρ.traceLeft.M.supportProj - have hP : ρ.M.support ≤ P.support := by - intro x hx - exact ⟨x, by simpa [P] using fixed_support_kron_prod hx⟩ - have hkerP : P.ker ≤ ρ.M.ker := by - simpa [HermitianMat.support_orthogonal_eq_range] using Submodule.orthogonal_le hP - exact (show (ρ.traceRight ⊗ᴹ ρ.traceLeft).M.ker ≤ P.ker by - change LinearMap.ker - ((Matrix.kroneckerMap (· * ·) ρ.traceRight.M.mat ρ.traceLeft.M.mat).toEuclideanLin) - ≤ LinearMap.ker - ((Matrix.kroneckerMap (· * ·) ρ.traceRight.M.supportProj.mat - ρ.traceLeft.M.supportProj.mat).toEuclideanLin) - exact ker_kron_le_of_le _ _ _ _ - (by - simpa [HermitianMat.ker, HermitianMat.lin] using - (show ρ.traceRight.M.ker ≤ ρ.traceRight.M.supportProj.ker by simp)) - (by - simpa [HermitianMat.ker, HermitianMat.lin] using - (show ρ.traceLeft.M.ker ≤ ρ.traceLeft.M.supportProj.ker by simp))).trans hkerP - have right_mul_eq_of_fixed_support {Q ρM : HermitianMat (dA × dB) ℂ} - (hfix : ∀ x : EuclideanSpace ℂ (dA × dB), x ∈ ρM.support → Q.lin x = x) : - ρM.mat * Q.mat = ρM.mat := by - have hleft : Q.mat * ρM.mat = ρM.mat := by - rw [Matrix.ext_iff_mulVec] - intro v - have hv : WithLp.toLp 2 (ρM.mat.mulVec v) ∈ ρM.support := - Set.mem_range_self (WithLp.toLp 2 v) - simpa [Matrix.mulVec_mulVec, HermitianMat.lin, Matrix.toEuclideanLin] using hfix _ hv - simpa [Matrix.conjTranspose_mul, HermitianMat.conjTranspose_mat] using - congrArg Matrix.conjTranspose hleft - have inner_kron_support_right_eq (A : HermitianMat dA ℂ) : - ⟪ρ.M, A ⊗ₖ ρ.traceLeft.M.supportProj⟫ = ⟪ρ.M, A ⊗ₖ (1 : HermitianMat dB ℂ)⟫ := by - let Q : HermitianMat (dA × dB) ℂ := (1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.supportProj - have hQ : ρ.M.mat * Q.mat = ρ.M.mat := by - apply right_mul_eq_of_fixed_support - intro x hx - simpa [Q] using fixed_support_kron_left ρ hx - have hmat : Q.mat * (A ⊗ₖ (1 : HermitianMat dB ℂ)).mat = - (A ⊗ₖ ρ.traceLeft.M.supportProj).mat := by - simp [Q, HermitianMat.kronecker_mat, ← Matrix.mul_kronecker_mul] - rw [HermitianMat.inner_eq_re_trace, HermitianMat.inner_eq_re_trace] - apply congrArg Complex.re - rw [← hmat, ← Matrix.mul_assoc, hQ] - have inner_support_kron_left_eq (B : HermitianMat dB ℂ) : - ⟪ρ.M, ρ.traceRight.M.supportProj ⊗ₖ B⟫ = ⟪ρ.M, (1 : HermitianMat dA ℂ) ⊗ₖ B⟫ := by - let Q : HermitianMat (dA × dB) ℂ := ρ.traceRight.M.supportProj ⊗ₖ (1 : HermitianMat dB ℂ) - have hQ : ρ.M.mat * Q.mat = ρ.M.mat := by - apply right_mul_eq_of_fixed_support - intro x hx - simpa [Q] using fixed_support_kron_right ρ hx - have hmat : Q.mat * ((1 : HermitianMat dA ℂ) ⊗ₖ B).mat = - (ρ.traceRight.M.supportProj ⊗ₖ B).mat := by - simp [Q, HermitianMat.kronecker_mat, ← Matrix.mul_kronecker_mul] - rw [HermitianMat.inner_eq_re_trace, HermitianMat.inner_eq_re_trace] - apply congrArg Complex.re - rw [← hmat, ← Matrix.mul_assoc, hQ] - rw [qRelativeEnt_ker prod_marginals_ker_le, qMutualInfo, - Sᵥₙ_eq_neg_trace_log, Sᵥₙ_eq_neg_trace_log, Sᵥₙ_eq_neg_trace_log] - rw [show (ρ.traceRight ⊗ᴹ ρ.traceLeft).M.log = - ρ.traceRight.M.log ⊗ₖ ρ.traceLeft.M.supportProj + - ρ.traceRight.M.supportProj ⊗ₖ ρ.traceLeft.M.log by - simpa [MState.prod] using - (HermitianMat.log_kron_with_proj (A := ρ.traceRight.M) (B := ρ.traceLeft.M)), - inner_sub_right, inner_add_right] - rw [show ⟪ρ.M.log, ρ.M⟫ = ⟪ρ.M, ρ.M.log⟫ by rw [HermitianMat.inner_comm]] - exact congrArg (fun x : ℝ => (x : EReal)) <| by - rw [show ⟪ρ.M, ρ.traceRight.M.log ⊗ₖ ρ.traceLeft.M.supportProj⟫ = - ⟪ρ.traceRight.M.log, ρ.traceRight.M⟫ by - calc - _ = ⟪ρ.M, ρ.traceRight.M.log ⊗ₖ (1 : HermitianMat dB ℂ)⟫ := - inner_kron_support_right_eq ρ.traceRight.M.log - _ = ⟪ρ.traceRight.M.log, ρ.traceRight.M⟫ := by - simpa [HermitianMat.inner_comm] using - inner_kron_one_eq_inner_traceRight ρ.traceRight.M.log ρ.M, - show ⟪ρ.M, ρ.traceRight.M.supportProj ⊗ₖ ρ.traceLeft.M.log⟫ = - ⟪ρ.traceLeft.M.log, ρ.traceLeft.M⟫ by - calc - _ = ⟪ρ.M, (1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.log⟫ := - inner_support_kron_left_eq ρ.traceLeft.M.log - _ = ⟪ρ.traceLeft.M.log, ρ.traceLeft.M⟫ := by - simpa [HermitianMat.inner_comm] using - inner_one_kron_eq_inner_traceLeft ρ.traceLeft.M.log ρ.M] - ring + rw [qRelativeEnt_ker (ker_prod_le_ker ρ), inner_sub_right, inner_log_prod] + rw [show ⟪ρ.M, ρ.M.log⟫ = -Sᵥₙ ρ by rw [Sᵥₙ_eq_neg_trace_log, neg_neg, inner_comm]] + rw [qMutualInfo] + norm_cast + ring + +end mutualInfo /- Helper: If σ₂ ≤ α • σ₁ for density matrices, then α > 0. @@ -2454,8 +2526,113 @@ theorem qRelEntropy_le_add_of_le_smul (ρ : MState d) {σ₁ σ₂ : MState d} ( simpa using hσ · positivity exact hker₁ (hker_le.trans hker) - · simp [hker, SandwichedRelRentropy, qRelativeEnt] + · simp [hker, MState.sandwichedRelRentropy_eq_matrix, qRelativeEnt] theorem qRelativeEnt_op_le {ρ σ : MState d} (h : ρ.M ≤ α • σ.M) : 𝐃(ρ‖σ) ≤ .ofReal (Real.log α) := by simpa using qRelEntropy_le_add_of_le_smul ρ h + +/-! ## Joint convexity + +Joint convexity of the relative entropy follows from monotonicity under the partial trace, which +in turn is the operator strong subadditivity inequality `inner_log_traceRight_le` integrated +against `ρ`. Given the partial trace bound, one forms the *flag state* +`p ρ₁ ⊗ |0⟩⟨0| + (1-p) ρ₂ ⊗ |1⟩⟨1|` on `d × Fin 2`, whose right marginal is the mixture +`p [ρ₁ ↔ ρ₂]` and whose relative entropy against the corresponding flag state for the `σᵢ` is +exactly the convex combination `p 𝐃(ρ₁‖σ₁) + (1-p) 𝐃(ρ₂‖σ₂)`, because relative entropy of +block-diagonal states decomposes blockwise. -/ + +open scoped Topology in +private theorem tendsto_mul_const_nhdsGT {c : ℝ} (hc : 0 < c) : + Filter.Tendsto (fun ε : ℝ ↦ ε * c) (𝓝[>] 0) (𝓝[>] 0) := by + refine tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within _ ?_ ?_ + · have h : Filter.Tendsto (fun ε : ℝ ↦ ε * c) (𝓝 0) (𝓝 (0 * c)) := + (continuous_mul_const c).tendsto 0 + rw [zero_mul] at h + exact h.mono_left nhdsWithin_le_nhds + · filter_upwards [self_mem_nhdsWithin] with ε hε + exact mul_pos hε hc + +open scoped Topology in +/-- Monotonicity of the quantum relative entropy under the partial trace. -/ +theorem qRelativeEnt_traceRight_le (ρ σ : MState (dA × dB)) : + 𝐃(ρ.traceRight‖σ.traceRight) ≤ 𝐃(ρ‖σ) := by + by_cases hker : σ.M.ker ≤ ρ.M.ker + swap + · rw [qRelativeEnt_eq_top hker] + exact le_top + have hkerR : σ.traceRight.M.ker ≤ ρ.traceRight.M.ker := by + rw [MState.traceRight_M, MState.traceRight_M] + exact HermitianMat.ker_traceRight_le σ.nonneg hker + obtain ⟨_, _⟩ := nonempty_prod.mp ρ.nonempty + have hcard : (0 : ℝ) < Fintype.card dB := by + exact_mod_cast Fintype.card_pos + have key : ⟪ρ.M, σ.M.log⟫ + Sᵥₙ ρ ≤ ⟪ρ.traceRight.M, σ.traceRight.M.log⟫ + Sᵥₙ ρ.traceRight := by + have h_ev : ∀ᶠ ε in 𝓝[>] (0 : ℝ), + ⟪ρ.M, (σ.M + ε • 1).log⟫ + Sᵥₙ ρ + ≤ ⟪ρ.traceRight.M, (σ.traceRight.M + (ε * (Fintype.card dB : ℝ)) • 1).log⟫ + + Sᵥₙ ρ.traceRight := by + refine eventually_nhdsWithin_of_forall fun ε hε ↦ ?_ + have h := inner_log_traceRight_le ρ (posDef_add_eps σ.nonneg hε) + rwa [HermitianMat.traceRight_add_smul_one, ← MState.traceRight_M] at h + refine le_of_tendsto_of_tendsto ?_ ?_ h_ev + · exact (inner_log_shift_tendsto hker).add tendsto_const_nhds + · exact ((inner_log_shift_tendsto hkerR).comp + (tendsto_mul_const_nhdsGT hcard)).add tendsto_const_nhds + rw [← EReal.coe_ennreal_le_coe_ennreal_iff, qRelativeEnt_ker hkerR, qRelativeEnt_ker hker, + EReal.coe_le_coe_iff, HermitianMat.inner_sub_left, HermitianMat.inner_sub_left] + rw [Sᵥₙ_eq_neg_trace_log ρ, Sᵥₙ_eq_neg_trace_log ρ.traceRight, + HermitianMat.inner_comm ρ.M.log, HermitianMat.inner_comm ρ.traceRight.M.log] at key + linarith + +/-- The inner product of two block-diagonal matrices is the sum of the blockwise inner products. -/ +theorem HermitianMat.inner_sum_kron_basisProj {ι : Type*} [Fintype ι] [DecidableEq ι] + (A B : ι → HermitianMat d ℂ) : + ⟪∑ i, A i ⊗ₖ HermitianMat.basisProj ℂ i, ∑ j, B j ⊗ₖ HermitianMat.basisProj ℂ j⟫ + = ∑ i, ⟪A i, B i⟫ := by + rw [sum_inner] + refine Finset.sum_congr rfl fun i _ ↦ ?_ + rw [inner_sum, Finset.sum_eq_single i] + · rw [HermitianMat.inner_kron, HermitianMat.inner_basisProj, if_pos rfl, mul_one] + · intro j _ hj + rw [HermitianMat.inner_kron, HermitianMat.inner_basisProj, if_neg (Ne.symm hj), mul_zero] + · intro h + exact absurd (Finset.mem_univ i) h + +/-- The relative entropy inner product of two block-diagonal states decomposes as the +weighted sum of the blockwise relative entropies. -/ +theorem inner_blocks_log_sub {ι : Type*} [Fintype ι] [DecidableEq ι] + (c : ι → ℝ) (hc : ∀ i, c i ≠ 0) (A B : ι → HermitianMat d ℂ) (hA : ∀ i, (A i).trace = 1) + (hker : ∀ i, (B i).ker ≤ (A i).ker) : + ⟪∑ i, (c i • A i) ⊗ₖ HermitianMat.basisProj ℂ i, + (∑ i, (c i • A i) ⊗ₖ HermitianMat.basisProj ℂ i).log + - (∑ i, (c i • B i) ⊗ₖ HermitianMat.basisProj ℂ i).log⟫ + = ∑ i, c i * ⟪A i, (A i).log - (B i).log⟫ := by + rw [HermitianMat.inner_sub_left, HermitianMat.log_sum_kron_basisProj, + HermitianMat.log_sum_kron_basisProj, HermitianMat.inner_sum_kron_basisProj, + HermitianMat.inner_sum_kron_basisProj, ← Finset.sum_sub_distrib] + refine Finset.sum_congr rfl fun i _ ↦ ?_ + rw [HermitianMat.log_smul_of_pos (A i) (hc i), HermitianMat.log_smul_of_pos (B i) (hc i), + HermitianMat.inner_smul_left, HermitianMat.inner_smul_left, HermitianMat.inner_add_right, + HermitianMat.inner_add_right, HermitianMat.inner_smul_right, HermitianMat.inner_smul_right, + HermitianMat.inner_supportProj_self, HermitianMat.inner_supportProj_of_ker_le (hker i), + HermitianMat.inner_sub_left, hA i] + ring + +private theorem qRelativeEnt_ne_top_of_ker {ρ σ : MState d} (h : σ.M.ker ≤ ρ.M.ker) : + 𝐃(ρ‖σ) ≠ ⊤ := by + intro htop + have h2 := qRelativeEnt_ker h + rw [htop] at h2 + simp at h2 + +/-- The quantum relative entropy is finite exactly when the support condition +`σ.M.ker ≤ ρ.M.ker` holds. -/ +theorem qRelativeEnt_ne_top_iff {ρ σ : MState d} : 𝐃(ρ‖σ) ≠ ⊤ ↔ σ.M.ker ≤ ρ.M.ker := by + refine ⟨fun h ↦ ?_, qRelativeEnt_ne_top_of_ker⟩ + by_contra hk + exact h (qRelativeEnt_eq_top hk) + +/-- The quantum relative entropy is `⊤` exactly when the support condition fails. -/ +theorem qRelativeEnt_eq_top_iff {ρ σ : MState d} : 𝐃(ρ‖σ) = ⊤ ↔ ¬ σ.M.ker ≤ ρ.M.ker := by + simpa using (not_congr (qRelativeEnt_ne_top_iff (ρ := ρ) (σ := σ))) diff --git a/QuantumInfo/Entropy/SSA.lean b/QuantumInfo/Entropy/SSA.lean index a1fc3e2c3..5b2d5f246 100644 --- a/QuantumInfo/Entropy/SSA.lean +++ b/QuantumInfo/Entropy/SSA.lean @@ -15,6 +15,8 @@ entropy, and generalizations such as sandwiched Rényi relative entropy. @[expose] public section +set_option backward.isDefEq.respectTransparency false + noncomputable section variable {d d₁ d₂ d₃ m n : Type*} @@ -134,7 +136,6 @@ lemma V_rho_conj_mul_self_eq (ρAB : HermitianMat (dA × dB) ℂ) (hρ : ρAB.ma simp_all [ mul_assoc, Matrix.mul_assoc ]; simp [ ← Matrix.mul_assoc, ← map_to_tensor_MES_prop ] -set_option backward.isDefEq.respectTransparency false in /-- The partial trace (left) of a positive definite matrix is positive definite. -/ @@ -543,7 +544,6 @@ private lemma T₁_isometry [Nonempty dB] rw [T₁_mat, Matrix.reindex_apply, Matrix.conjTranspose_submatrix, Matrix.submatrix_mul_equiv, h_kron, Matrix.submatrix_one_equiv] -set_option maxHeartbeats 400000 in private lemma T₂_sq_le_one [Nonempty dB] (σBC : HermitianMat (dB × dC) ℂ) (hσ : σBC.mat.PosDef) : (T₂_mat dA dB dC σBC)ᴴ * (T₂_mat dA dB dC σBC) ≤ 1 := by @@ -851,34 +851,6 @@ open scoped InnerProductSpace RealInnerProductSpace /-! ### Weak monotonicity and SSA proof infrastructure -/ section SSA_proof -omit [DecidableEq d₁] in -open HermitianMat in -private lemma inner_kron_one_eq_inner_traceRight - (A : HermitianMat d₁ ℂ) (M : HermitianMat (d₁ × d₂) ℂ) : - ⟪A ⊗ₖ (1 : HermitianMat d₂ ℂ), M⟫ = ⟪A, M.traceRight⟫ := by - rw [inner_comm]; - -- By definition of partial trace, we have that the trace of M multiplied by (A ⊗ I) is equal to the trace of A multiplied by the partial trace of M. - have h_partial_trace : Matrix.trace (M.mat * (A.mat ⊗ₖ 1 : Matrix (d₁ × d₂) (d₁ × d₂) ℂ)) = Matrix.trace (A.mat * M.traceRight.mat) := by - simp [ Matrix.trace, Matrix.mul_apply ]; - simp [ Matrix.traceRight, Matrix.one_apply, mul_comm ]; - simp only [Finset.sum_sigma', Finset.mul_sum _ _ _]; - rw [ ← Finset.sum_filter ]; - refine' Finset.sum_bij ( fun x _ => ⟨ x.snd.1, x.fst.1, x.fst.2 ⟩ ) _ _ _ _ <;> - aesop (add simp [Finset.mem_filter, Sigma.ext_iff, Prod.ext_iff]); - exact congr_arg Complex.re h_partial_trace - -omit [DecidableEq d₂] in -open HermitianMat in -private lemma inner_one_kron_eq_inner_traceLeft - (B : HermitianMat d₂ ℂ) (M : HermitianMat (d₁ × d₂) ℂ) : - ⟪(1 : HermitianMat d₁ ℂ) ⊗ₖ B, M⟫ = ⟪B, M.traceLeft⟫ := by - convert! inner_kron_one_eq_inner_traceRight B ( M.reindex ( Equiv.prodComm d₁ d₂ ) ) using 1; - refine' congr_arg ( fun x : ℂ => x.re ) _; - refine' Finset.sum_bij ( fun x y => ( x.2, x.1 ) ) _ _ _ _ <;> simp [ Matrix.mul_apply ]; - intro a b; rw [ ← Equiv.sum_comp ( Equiv.prodComm d₁ d₂ ) ] - simp [mul_comm] - rfl - open HermitianMat in private lemma hermitianMat_log_inv_eq_neg (A : HermitianMat d₁ ℂ) [A.NonSingular] : A⁻¹.log = -A.log := by @@ -897,38 +869,53 @@ private lemma PosDef_assoc'_traceRight (ρ : MState (d₁ × d₂ × d₃)) (hρ : ρ.M.mat.PosDef) : ρ.assoc'.traceRight.M.mat.PosDef := by have _ := ρ.nonempty |> nonempty_prod.mp |>.right |> nonempty_prod.mp |>.right - apply PosDef_traceRight - convert! hρ.reindex (Equiv.prodAssoc d₁ d₂ d₃).symm + rw [MState.traceRight_M, MState.assoc'_M] + exact PosDef_traceRight _ (HermitianMat.PosDef_reindex _ _ hρ) + +/-- The version of `inner_marginal_logs` where the first marginal's log is replaced by the log of +an arbitrary Hermitian matrix `X`. -/ +private lemma inner_marginal_logs' [Nonempty dA] [Nonempty dB] (ρ : MState (dA × dB)) + (X : HermitianMat dA ℂ) : + ⟪(-X.log) ⊗ₖ (1 : HermitianMat dB ℂ) + + (1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.log, ρ.M⟫ = + -⟪X.log, ρ.traceRight.M⟫ - Sᵥₙ ρ.traceLeft := by + rw [inner_add_left, HermitianMat.inner_kron_one, HermitianMat.inner_one_kron, + Sᵥₙ_eq_neg_trace_log, inner_neg_left, MState.traceRight_M, MState.traceLeft_M] + ring + +private lemma inner_marginal_logs [Nonempty dA] [Nonempty dB] (ρ : MState (dA × dB)) : + ⟪(-ρ.traceRight.M.log) ⊗ₖ (1 : HermitianMat dB ℂ) + + (1 : HermitianMat dA ℂ) ⊗ₖ ρ.traceLeft.M.log, ρ.M⟫ = + Sᵥₙ ρ.traceRight - Sᵥₙ ρ.traceLeft := by + rw [inner_marginal_logs' ρ ρ.traceRight.M, Sᵥₙ_eq_neg_trace_log ρ.traceRight] + +private lemma inner_marginal_logs_assoc' [Nonempty d₁] [Nonempty d₂] [Nonempty d₃] + (ρ : MState (d₁ × d₂ × d₃)) (X : HermitianMat (d₁ × d₂) ℂ) : + ⟪((-X.log) ⊗ₖ (1 : HermitianMat d₃ ℂ) + + (1 : HermitianMat (d₁ × d₂) ℂ) ⊗ₖ ρ.traceLeft.M.traceLeft.log).reindex + (Equiv.prodAssoc d₁ d₂ d₃), ρ.M⟫ = + -⟪X.log, ρ.assoc'.traceRight.M⟫ - Sᵥₙ ρ.traceLeft.traceLeft := by + rw [HermitianMat.reindex_inner, ← MState.assoc'_M, ← MState.traceLeft_M, + ← MState.traceLeft_assoc'] + exact inner_marginal_logs' ρ.assoc' X -set_option backward.isDefEq.respectTransparency false in private lemma wm_inner_lhs [Nonempty d₁] [Nonempty d₂] [Nonempty d₃] (ρ : MState (d₁ × d₂ × d₃)) : ⟪(-ρ.assoc'.traceRight.M.traceRight.log) ⊗ₖ (1 : HermitianMat (d₂ × d₃) ℂ) + (1 : HermitianMat d₁ ℂ) ⊗ₖ ρ.traceLeft.M.log, ρ.M⟫ = Sᵥₙ ρ.traceRight - Sᵥₙ ρ.traceLeft := by - convert! congr_arg₂ ( · + · ) _ _ using 1; - convert! inner_add_left _ _ _ using 1; - · rw [ Sᵥₙ_eq_neg_trace_log ]; - convert! inner_kron_one_eq_inner_traceRight _ _ using 1; - simp [ HermitianMat.traceRight ]; - congr! 2; - ext i j; simp [ Matrix.traceRight ] ; - exact Fintype.sum_prod_type fun x => ρ.m (i, x) (j, x) - · rw [ Sᵥₙ_eq_neg_trace_log ]; - simp [ inner_one_kron_eq_inner_traceLeft ] - -set_option backward.isDefEq.respectTransparency false in + rw [← MState.traceRight_M, MState.traceRight_right_assoc'] + exact inner_marginal_logs ρ + private lemma wm_inner_rhs [Nonempty d₁] [Nonempty d₂] [Nonempty d₃] (ρ : MState (d₁ × d₂ × d₃)) : ⟪((-ρ.assoc'.traceRight.M.log) ⊗ₖ (1 : HermitianMat d₃ ℂ) + (1 : HermitianMat (d₁ × d₂) ℂ) ⊗ₖ ρ.traceLeft.M.traceLeft.log).reindex (Equiv.prodAssoc d₁ d₂ d₃), ρ.M⟫ = Sᵥₙ ρ.assoc'.traceRight - Sᵥₙ ρ.traceLeft.traceLeft := by - simp [ HermitianMat.traceLeft, HermitianMat.traceRight, Sᵥₙ_eq_neg_trace_log ]; - simp [ inner_add_left, inner_one_kron_eq_inner_traceLeft, inner_kron_one_eq_inner_traceRight ]; - congr! 2; - convert MState.traceLeft_assoc' ρ using 1; - unfold MState.assoc' MState.traceLeft; aesop; + rw [HermitianMat.reindex_inner, ← MState.assoc'_M, ← MState.traceLeft_M, + ← MState.traceLeft_assoc'] + exact inner_marginal_logs ρ.assoc' /-- Weak monotonicity (form 2) for positive definite states: S(ρ₁₂) + S(ρ₂₃) ≥ S(ρ₁) + S(ρ₃). @@ -939,7 +926,9 @@ private lemma Sᵥₙ_wm_pd [Nonempty d₁] [Nonempty d₂] [Nonempty d₃] Sᵥₙ ρ.assoc'.traceRight + Sᵥₙ ρ.traceLeft := by -- Set up marginals and their PD properties have h₁₂ := PosDef_assoc'_traceRight ρ hρ - have h₂₃ := PosDef_traceLeft ρ.M hρ + have h₂₃ : ρ.traceLeft.M.mat.PosDef := by + rw [MState.traceLeft_M] + exact PosDef_traceLeft ρ.M hρ have : ρ.assoc'.traceRight.M.NonSingular := nonSingular_of_posDef h₁₂ have : ρ.traceLeft.M.NonSingular := nonSingular_of_posDef h₂₃ have : ρ.assoc'.traceRight.M.traceRight.NonSingular := @@ -962,8 +951,7 @@ private lemma Sᵥₙ_wm_pd [Nonempty d₁] [Nonempty d₂] [Nonempty d₃] rw [wm_inner_lhs ρ, wm_inner_rhs ρ] at h_inner linarith -set_option backward.isDefEq.respectTransparency false in -private lemma MState.approx_by_pd +lemma MState.approx_by_pd (ρ : MState d₁) : ∃ (ρn : ℕ → MState d₁), (∀ n, (ρn n).M.mat.PosDef) ∧ Filter.Tendsto ρn Filter.atTop (nhds ρ) := by @@ -975,9 +963,8 @@ private lemma MState.approx_by_pd refine' ⟨ ρn, _, _ ⟩; · intro n have h_pos_def : (ρn n).M = (1 - εn n) • ρ.M + εn n • (MState.uniform (d := d₁)).M := by - refine' add_comm _ _ |> Eq.trans <| _; - congr! 1 - aesop; + simp only [ρn, MState.mix_M] + exact add_comm _ _ have h_pos_def : ∀ (A : Matrix d₁ d₁ ℂ), A.PosSemidef → ∀ (B : Matrix d₁ d₁ ℂ), B.PosDef → ∀ (ε : ℝ), 0 < ε ∧ ε < 1 → (1 - ε) • A + ε • B ∈ {M : Matrix d₁ d₁ ℂ | M.PosDef} := by intro A hA B hB ε hε simp only [ Matrix.posSemidef_iff_dotProduct_mulVec, Matrix.posDef_iff_dotProduct_mulVec ] at * @@ -990,21 +977,23 @@ private lemma MState.approx_by_pd simp [ Matrix.mulVec, dotProduct, Finset.mul_sum _ _ _, mul_assoc, mul_left_comm, sub_mul, mul_sub ] ; ring!; convert! h_pos_def _ _ _ _ _ ⟨ _, _ ⟩ <;> norm_num [ * ]; congr! 1 - exact psd ρ + exact ρ.psd · exact uniform_posDef; · exact one_div_pos.mpr ( by linarith ); · exact div_lt_one ( by positivity ) |>.2 ( by linarith ) · -- Show that the sequence ρn converges to ρ. have h_conv : Filter.Tendsto (fun n => εn n • (MState.uniform : MState d₁).M + (1 - εn n) • ρ.M) Filter.atTop (nhds ρ.M) := by exact le_trans ( Filter.Tendsto.add ( Filter.Tendsto.smul ( tendsto_const_nhds.div_atTop <| Filter.tendsto_atTop_add_const_right _ _ tendsto_natCast_atTop_atTop ) tendsto_const_nhds ) ( Filter.Tendsto.smul ( tendsto_const_nhds.sub <| tendsto_const_nhds.div_atTop <| Filter.tendsto_atTop_add_const_right _ _ tendsto_natCast_atTop_atTop ) tendsto_const_nhds ) ) ( by simp ); - rw [ tendsto_iff_dist_tendsto_zero ] at *; - convert h_conv using 1; - ext n; simp [ρn, Mixable.mix]; - congr! 1 + rw [MState.toMat_IsEmbedding.toIsInducing.tendsto_nhds_iff] + have h_comp : (DensityOp.M ∘ ρn) + = fun n => εn n • (MState.uniform : MState d₁).M + (1 - εn n) • ρ.M := by + funext n + simp [ρn, MState.mix_M] + rw [h_comp] + exact h_conv -set_option backward.isDefEq.respectTransparency false in @[fun_prop] -private lemma MState.traceLeft_continuous : +lemma MState.traceLeft_continuous : Continuous (MState.traceLeft : MState (d₁ × d₂) → MState d₂) := by -- Since the matrix traceLeft is continuous, the function that maps a state to its partial trace is also continuous. have h_traceLeft_cont : Continuous (fun ρ : HermitianMat (d₁ × d₂) ℂ => ρ.traceLeft) := by @@ -1015,9 +1004,8 @@ private lemma MState.traceLeft_continuous : · fun_prop; exact continuous_induced_rng.mpr ( by continuity ) -set_option backward.isDefEq.respectTransparency false in @[fun_prop] -private lemma MState.traceRight_continuous : +lemma MState.traceRight_continuous : Continuous (MState.traceRight : MState (d₁ × d₂) → MState d₁) := by rw [ continuous_iff_continuousAt ]; intro ρ @@ -1039,18 +1027,24 @@ private lemma MState.traceRight_continuous : convert f.continuous_of_finiteDimensional; exact funext fun A => hf A ▸ rfl; have := h_traceRight_cont.isOpen_preimage t ht; - exact Filter.mem_of_superset ( this.preimage ( continuous_induced_dom ) |> IsOpen.mem_nhds <| by simpa using hρs ) fun x hx => hx + refine Filter.mem_of_superset (this.preimage continuous_induced_dom |> IsOpen.mem_nhds <| ?_) + fun x hx => ?_ + · simpa [MState.traceRight_M] using hρs + · simpa [MState.traceRight_M] using hx @[fun_prop] -private lemma MState.assoc'_continuous : +lemma MState.assoc'_continuous : Continuous (MState.assoc' : MState (d₁ × d₂ × d₃) → MState ((d₁ × d₂) × d₃)) := by - apply continuous_induced_rng.mpr; -- The reindex function is continuous because it is a composition of continuous functions (permutations). have h_reindex_cont : Continuous (fun ρ : HermitianMat (d₁ × d₂ × d₃) ℂ => ρ.reindex (Equiv.prodAssoc d₁ d₂ d₃).symm) := by apply continuous_induced_rng.mpr; fun_prop (disch := norm_num); - convert! h_reindex_cont.comp _ using 2; - exact Continuous_HermitianMat + rw [continuous_induced_rng] + have h : (DensityOp.M ∘ (MState.assoc' : MState (d₁ × d₂ × d₃) → MState ((d₁ × d₂) × d₃))) + = (fun ρ : HermitianMat (d₁ × d₂ × d₃) ℂ => ρ.reindex (Equiv.prodAssoc d₁ d₂ d₃).symm) + ∘ DensityOp.M := funext fun ρ ↦ MState.assoc'_M ρ + rw [h] + exact h_reindex_cont.comp Continuous_HermitianMat /-- Weak monotonicity, version with partial traces. -/ lemma Sᵥₙ_wm (ρ : MState (d₁ × d₂ × d₃)) : @@ -1081,7 +1075,6 @@ private def perm_A_BCR' (dA dB dC : Type*) : private def ρBCR (ρ : MState (dA × dB × dC)) : MState (dB × dC × (dA × dB × dC)) := ((MState.pure ρ.purify).relabel (perm_A_BCR' dA dB dC).symm).traceLeft -set_option backward.isDefEq.respectTransparency false in private lemma S_BC_of_BCR_eq (ρ : MState (dA × dB × dC)) : Sᵥₙ (ρBCR ρ).assoc'.traceRight = Sᵥₙ ρ.traceLeft := by -- By definition of ρBCR, we know that its BC-marginal is equal to the BC-marginal of ρ. @@ -1105,41 +1098,33 @@ private def perm_AB_CR' (dA dB dC : Type*) : left_inv := by intro x; simp right_inv := by intro x; simp -set_option backward.isDefEq.respectTransparency false in /- The CR-marginal of ρBCR equals the traceLeft of the AB|CR-relabeled purification. -/ private lemma BCR_traceLeft_eq_purify_traceLeft (ρ : MState (dA × dB × dC)) : (ρBCR ρ).traceLeft = ((MState.pure ρ.purify).relabel (perm_AB_CR' dA dB dC).symm).traceLeft := by - convert MState.ext ?_; - convert MState.ext ?_; - any_goals exact ρ.traceLeft.traceLeft; - · simp [ MState.traceLeft, MState.relabel, perm_AB_CR' ]; - simp [ MState.traceLeft, MState.relabel, ρBCR ]; - unfold HermitianMat.traceLeft - simp only [mat_reindex, MState.mat_M, Matrix.reindex_apply, mat_mk, Equiv.coe_fn_symm_mk] - unfold Matrix.traceLeft - simp - congr! 2 - ext i₁ j₁ - rw [ ← Finset.sum_product' ] - simp [ perm_A_BCR' ] - exact Finset.sum_bij ( fun x _ => ( x.2, x.1 ) ) ( by simp ) ( by simp ) ( by simp ) ( by simp ); - · rfl + ext ⟨c, r⟩ ⟨c', r'⟩ + simp [ρBCR, MState.traceLeft_M, MState.relabel_M, HermitianMat.traceLeft_apply, + HermitianMat.reindex_apply, perm_A_BCR', perm_AB_CR', Fintype.sum_prod_type] + exact Finset.sum_comm /- The traceRight of the AB|CR-relabeled purification has same entropy as ρ.assoc'.traceRight. -/ private lemma purify_AB_traceRight_eq (ρ : MState (dA × dB × dC)) : Sᵥₙ ((MState.pure ρ.purify).relabel (perm_AB_CR' dA dB dC).symm).traceRight = Sᵥₙ ρ.assoc'.traceRight := by - have h_traceRight : ((MState.pure ρ.purify).relabel (perm_AB_CR' dA dB dC).symm).traceRight = ρ.assoc'.traceRight := by - have h_traceRight : (MState.pure ρ.purify).traceRight = ρ := by - exact MState.purify_spec ρ; - convert congr_arg ( fun m => m.assoc'.traceRight ) h_traceRight using 1; - ext i j; simp [ MState.traceRight, MState.assoc' ] ; - simp [ HermitianMat.traceRight, MState.SWAP, MState.assoc ]; - simp [ Matrix.traceRight, Matrix.submatrix ]; - congr! 2; - ext i j; simp [ perm_AB_CR' ] ; - exact Fintype.sum_prod_type _ + have h_purify : ∀ (i j : dA × dB × dC), + ρ.M i j = ∑ r : dA × dB × dC, ρ.purify (i, r) * (starRingEnd ℂ) (ρ.purify (j, r)) := by + intro i j + conv_lhs => rw [← ρ.purify_spec] + simp only [MState.traceRight_M, HermitianMat.traceRight_apply, MState.pure_M_apply] + have h_traceRight : ((MState.pure ρ.purify).relabel (perm_AB_CR' dA dB dC).symm).traceRight + = ρ.assoc'.traceRight := by + ext ⟨a, b⟩ ⟨a', b'⟩ + simp [MState.traceRight_M, MState.relabel_M, MState.assoc'_M, HermitianMat.traceRight_apply, + HermitianMat.reindex_apply, perm_AB_CR', Fintype.sum_prod_type, Matrix.vecMulVec_apply, + Bra.eq_conj] + refine Finset.sum_congr rfl fun x _ ↦ ?_ + rw [h_purify] + simp [Fintype.sum_prod_type] rw [h_traceRight] /-- The CR-marginal of ρBCR has the same entropy as the AB-marginal of ρ. -/ @@ -1149,7 +1134,6 @@ private lemma S_CR_of_BCR_eq (ρ : MState (dA × dB × dC)) : rw [Sᵥₙ_pure_complement ρ.purify (perm_AB_CR' dA dB dC).symm] exact purify_AB_traceRight_eq ρ -set_option backward.isDefEq.respectTransparency false in private lemma S_B_of_BCR_eq (ρ : MState (dA × dB × dC)) : Sᵥₙ (ρBCR ρ).traceRight = Sᵥₙ ρ.traceLeft.traceRight := by unfold ρBCR; @@ -1182,9 +1166,7 @@ private lemma S_R_of_BCR_eq (ρ : MState (dA × dB × dC)) : simp [ Finset.sum_sigma' ]; refine' Finset.sum_bij ( fun x _ => ( x.snd.snd, x.snd.fst, x.fst ) ) _ _ _ _ <;> simp grind; - convert Sᵥₙ_of_partial_eq ρ.purify using 1; - · rw [h_trace]; - · rw [ ρ.purify_spec ] + rw [h_trace, Sᵥₙ_of_partial_eq, ρ.purify_spec] /-- Strong subadditivity on a tripartite system -/ theorem Sᵥₙ_strong_subadditivity (ρ₁₂₃ : MState (d₁ × d₂ × d₃)) : @@ -1201,15 +1183,22 @@ theorem Sᵥₙ_strong_subadditivity (ρ₁₂₃ : MState (d₁ × d₂ × d₃ /-- "Ordinary" subadditivity of von Neumann entropy -/ theorem Sᵥₙ_subadditivity (ρ : MState (d₁ × d₂)) : Sᵥₙ ρ ≤ Sᵥₙ ρ.traceRight + Sᵥₙ ρ.traceLeft := by - have := Sᵥₙ_strong_subadditivity (ρ.relabel (d₂ := d₁ × Unit × d₂) - ⟨fun x ↦ (x.1, x.2.2), fun x ↦ (x.1, ⟨(), x.2⟩), fun x ↦ by simp, fun x ↦ by simp⟩) - simp [Sᵥₙ_relabel] at this - convert this using 1 - congr 1 - · convert Sᵥₙ_relabel _ (Equiv.prodPUnit _).symm - exact rfl - · convert Sᵥₙ_relabel _ (Equiv.punitProd _).symm - exact rfl + -- Pad the bipartite state with a trivial middle factor and apply strong subadditivity. + let e : d₁ × Unit × d₂ ≃ d₁ × d₂ := + ⟨fun x ↦ (x.1, x.2.2), fun x ↦ (x.1, ⟨(), x.2⟩), fun x ↦ by simp, fun x ↦ by simp⟩ + have h := Sᵥₙ_strong_subadditivity (ρ.relabel e) + dsimp only at h + have h₃ : (ρ.relabel e).assoc'.traceRight = ρ.traceRight.relabel (Equiv.prodPUnit d₁) := by + ext ⟨a, u⟩ ⟨a', u'⟩ + simp [MState.traceRight_M, MState.assoc'_M, MState.relabel_M, HermitianMat.traceRight_apply, + HermitianMat.reindex_apply, e] + have h₄ : (ρ.relabel e).traceLeft = ρ.traceLeft.relabel (Equiv.punitProd d₂) := by + ext ⟨u, c⟩ ⟨u', c'⟩ + simp [MState.traceLeft_M, MState.relabel_M, HermitianMat.traceLeft_apply, + HermitianMat.reindex_apply, e] + rw [Sᵥₙ_relabel, Sᵥₙ_unit_zero (ρ.relabel e).traceLeft.traceRight, h₃, h₄, + Sᵥₙ_relabel, Sᵥₙ_relabel] at h + linarith /-- Triangle inequality for pure tripartite states: S(A) ≤ S(B) + S(C). -/ theorem Sᵥₙ_pure_tripartite_triangle (ψ : Ket ((d₁ × d₂) × d₃)) : @@ -1243,13 +1232,28 @@ theorem Sᵥₙ_weak_monotonicity (ρ : MState (dA × dB × dC)) : let ρAB := ρ.assoc'.traceRight let ρAC := ρ.SWAP.assoc.traceLeft.SWAP 0 ≤ qConditionalEnt ρAB + qConditionalEnt ρAC := by - simp only [qConditionalEnt, MState.traceRight_left_assoc', Sᵥₙ_of_SWAP_eq] - rw [add_sub, sub_add_eq_add_sub, le_sub_iff_add_le, le_sub_iff_add_le, zero_add] - nth_rw 2 [add_comm] - have := Sᵥₙ_wm ρ.SWAP.assoc.SWAP.assoc - simp_all only [MState.traceRight_assoc, MState.traceRight_SWAP, MState.traceLeft_right_assoc, - MState.traceLeft_left_assoc, MState.traceLeft_SWAP, MState.assoc'_assoc, ge_iff_le] - exact this + show (0 : ℝ) ≤ qConditionalEnt ρ.assoc'.traceRight + + qConditionalEnt ρ.SWAP.assoc.traceLeft.SWAP + have hwm := Sᵥₙ_wm ρ.SWAP.assoc.SWAP.assoc + have h1 : ρ.SWAP.assoc.SWAP.assoc.traceRight = ρ.traceLeft.traceLeft := by + ext i j + simp [HermitianMat.traceLeft_apply] + have h2 : ρ.SWAP.assoc.SWAP.assoc.traceLeft.traceLeft = ρ.traceLeft.traceRight := by + ext i j + simp [HermitianMat.traceLeft_apply, HermitianMat.traceRight_apply] + have h3 : ρ.SWAP.assoc.SWAP.assoc.traceLeft = ρ.assoc'.traceRight := by + ext i j + simp [HermitianMat.traceLeft_apply, HermitianMat.traceRight_apply, + HermitianMat.reindex_apply] + have h4 : ρ.SWAP.assoc.SWAP.assoc.assoc'.traceRight = ρ.SWAP.assoc.traceLeft := by + ext i j + simp [HermitianMat.traceLeft_apply, HermitianMat.reindex_apply] + have h6 : ρ.SWAP.assoc.traceLeft.SWAP.traceLeft = ρ.traceLeft.traceLeft := by + ext i j + simp [HermitianMat.traceLeft_apply] + rw [h1, h2, h3, h4] at hwm + simp only [qConditionalEnt, MState.traceRight_left_assoc', h6, Sᵥₙ_of_SWAP_eq] + linarith /-- Strong subadditivity, stated in terms of conditional entropies. Also called the data processing inequality. H(A|BC) ≤ H(A|B). -/ @@ -1281,7 +1285,9 @@ theorem qcmi_le_2_log_dim (ρ : MState (dA × dB × dC)) : qcmi ρ ≤ 2 * Real.log (Fintype.card dA) := by have := Sᵥₙ_subadditivity ρ.assoc'.traceRight have := abs_le.mp (Sᵥₙ_triangle_subaddivity ρ) - grind [qcmi, qConditionalEnt, Sᵥₙ_nonneg, Sᵥₙ_le_log_d] + have := Sᵥₙ_le_log_d ρ.traceRight + have := Sᵥₙ_le_log_d ρ.assoc'.traceRight.traceRight + grind [qcmi, qConditionalEnt, Sᵥₙ_nonneg] /-- The quantum conditional mutual information `QCMI ρABC` is at most 2 log dC. -/ theorem qcmi_le_2_log_dim' (ρ : MState (dA × dB × dC)) : @@ -1291,7 +1297,214 @@ theorem qcmi_le_2_log_dim' (ρ : MState (dA × dB × dC)) : rw [← ρ.traceLeft_assoc', ← Sᵥₙ_of_assoc'_eq ρ] exact Sᵥₙ_triangle_subaddivity ρ.assoc' have := Sᵥₙ_subadditivity ρ.traceLeft - grind [qcmi, qConditionalEnt, Sᵥₙ_le_log_d, MState.traceRight_left_assoc'] + have := Sᵥₙ_le_log_d ρ.traceLeft.traceLeft + grind [qcmi, qConditionalEnt, MState.traceRight_left_assoc'] + +/-! ### The operator form of data processing for the partial trace + +Strong subadditivity, in the operator form `operator_ineq_SSA`, compares `ρABC` with the +marginals of `ρ`. Replacing the marginal `ρAB` there by an arbitrary positive definite `τ` and +pairing against `ρ` gives an inequality that only involves `τ` through `log τ`; taking `ρ` to be +the purification of a state on `dA × dB` collapses the three-party statement into a two-party one. +The result, `inner_log_traceRight_le`, is what makes the relative entropy monotone under the +partial trace. -/ + +/-- Strong subadditivity, paired against a positive definite `τ` on the first two factors, in +the special case that the underlying state is positive definite. -/ +theorem inner_log_le_pd [Nonempty d₁] [Nonempty d₂] [Nonempty d₃] + (ρ : MState (d₁ × d₂ × d₃)) (hρ : ρ.M.mat.PosDef) + {τ : HermitianMat (d₁ × d₂) ℂ} (hτ : τ.mat.PosDef) : + -⟪τ.traceRight.log, ρ.traceRight.M⟫ - Sᵥₙ ρ.traceLeft ≤ + -⟪τ.log, ρ.assoc'.traceRight.M⟫ - Sᵥₙ ρ.traceLeft.traceLeft := by + have h₂₃ : ρ.traceLeft.M.mat.PosDef := by + rw [MState.traceLeft_M] + exact PosDef_traceLeft ρ.M hρ + have : τ.NonSingular := nonSingular_of_posDef hτ + have : ρ.traceLeft.M.NonSingular := nonSingular_of_posDef h₂₃ + have : τ.traceRight.NonSingular := nonSingular_of_posDef (PosDef_traceRight _ hτ) + have : ρ.traceLeft.M.traceLeft.NonSingular := nonSingular_of_posDef (PosDef_traceLeft _ h₂₃) + have h_op := operator_ineq_SSA τ ρ.traceLeft.M hτ h₂₃ + have h_lhs_pd : (τ.traceRight⁻¹ ⊗ₖ ρ.traceLeft.M).mat.PosDef := + HermitianMat.PosDef_kronecker _ _ (PosDef_traceRight _ hτ).inv h₂₃ + have h_log := HermitianMat.log_mono h_lhs_pd h_op + rw [HermitianMat.log_kron, hermitianMat_log_inv_eq_neg] at h_log + rw [HermitianMat.reindex_log, HermitianMat.log_kron, hermitianMat_log_inv_eq_neg] at h_log + have h_inner := HermitianMat.inner_mono ρ.nonneg h_log + rw [HermitianMat.inner_comm, HermitianMat.inner_comm ρ.M] at h_inner + rw [inner_marginal_logs' ρ τ.traceRight, inner_marginal_logs_assoc' ρ τ] at h_inner + linarith + +/-- Strong subadditivity, paired against a positive definite `τ` on the first two factors. -/ +theorem inner_log_le (ρ : MState (d₁ × d₂ × d₃)) + {τ : HermitianMat (d₁ × d₂) ℂ} (hτ : τ.mat.PosDef) : + -⟪τ.traceRight.log, ρ.traceRight.M⟫ - Sᵥₙ ρ.traceLeft ≤ + -⟪τ.log, ρ.assoc'.traceRight.M⟫ - Sᵥₙ ρ.traceLeft.traceLeft := by + have h_ne123 := ρ.nonempty + have ⟨_, hn23⟩ := nonempty_prod.mp h_ne123 + have ⟨_, _⟩ := nonempty_prod.mp hn23 + obtain ⟨ρn, hρn_pos, hρn⟩ := MState.approx_by_pd ρ + have hL : Continuous fun σ : MState (d₁ × d₂ × d₃) ↦ + -⟪τ.traceRight.log, σ.traceRight.M⟫ - Sᵥₙ σ.traceLeft := by + fun_prop + have hR : Continuous fun σ : MState (d₁ × d₂ × d₃) ↦ + -⟪τ.log, σ.assoc'.traceRight.M⟫ - Sᵥₙ σ.traceLeft.traceLeft := by + fun_prop + exact le_of_tendsto_of_tendsto' (hL.continuousAt.tendsto.comp hρn) + (hR.continuousAt.tendsto.comp hρn) fun n => inner_log_le_pd _ (hρn_pos n) hτ + +/-- A monotonicity inequality for the partial trace: for any positive definite `τ`, the +quantity `⟪ρ, log τ⟫ + Sᵥₙ ρ` does not decrease under taking the marginal. This is the +operator form of the data processing inequality for the partial trace. -/ +theorem inner_log_traceRight_le (ρ : MState (dA × dB)) {τ : HermitianMat (dA × dB) ℂ} + (hτ : τ.mat.PosDef) : + ⟪ρ.M, τ.log⟫ + Sᵥₙ ρ ≤ ⟪ρ.traceRight.M, τ.traceRight.log⟫ + Sᵥₙ ρ.traceRight := by + obtain ⟨_, _⟩ := nonempty_prod.mp ρ.nonempty + set Ψ : MState ((dA × dB) × (dA × dB)) := MState.pure ρ.purify with hΨ + set ω : MState (dA × dB × (dA × dB)) := + Ψ.relabel (Equiv.prodAssoc dA dB (dA × dB)).symm with hω + have hassoc : ω.assoc' = Ψ := by + ext i j + simp [hω, MState.assoc'_M, MState.relabel_M] + have h := inner_log_le ω hτ + rw [hassoc, hΨ, ρ.purify_spec] at h + have h1 : ω.traceRight = ρ.traceRight := by + rw [← MState.traceRight_right_assoc' ω, hassoc, hΨ, ρ.purify_spec] + have h2 : Sᵥₙ ω.traceLeft = Sᵥₙ ρ.traceRight := by + rw [hω, hΨ, Sᵥₙ_pure_complement] + rw [← hΨ, ← hω, h1] + have h3 : Sᵥₙ ω.traceLeft.traceLeft = Sᵥₙ ρ := by + rw [← MState.traceLeft_assoc', hassoc, hΨ, Sᵥₙ_of_partial_eq, ρ.purify_spec] + rw [h1, h2, h3] at h + rw [HermitianMat.inner_comm ρ.M, HermitianMat.inner_comm ρ.traceRight.M] + linarith + +section basis_free + +/-! ### Basis-free forms + +The results above are stated for a system whose index type is a product. These are their +counterparts for a state on an honest tensor product of Hilbert spaces. Each is obtained from the +index-level statement by picking an arbitrary preferred basis on each factor (`StdBasis.some`) and +transporting; the index-level quantity is insensitive to that choice, so nothing is lost. -/ + +open scoped TensorProduct + +variable {E F G : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] +variable [NormedAddCommGroup G] [InnerProductSpace ℂ G] [FiniteDimensional ℂ G] + +namespace DensityOp + +/-- Strong subadditivity on a tripartite system. -/ +theorem Sᵥₙ_strong_subadditivity (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : + Sᵥₙ ρ + Sᵥₙ ρ.traceLeft.traceRight ≤ + Sᵥₙ ((ρ.congr (TensorProduct.assocIsometry ℂ E F G).symm).traceRight) + Sᵥₙ ρ.traceLeft := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + let := StdBasis.some ℂ G + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [← MState.assoc'_transport, MState.traceRight_transport, MState.traceLeft_transport, + MState.traceRight_transport, Sᵥₙ_transport, Sᵥₙ_transport, Sᵥₙ_transport, Sᵥₙ_transport] + have := _root_.Sᵥₙ_strong_subadditivity μ + dsimp only at this + linarith + +/-- "Ordinary" subadditivity of von Neumann entropy. -/ +theorem Sᵥₙ_subadditivity (ρ : DensityOp (E ⊗[ℂ] F)) : + Sᵥₙ ρ ≤ Sᵥₙ ρ.traceRight + Sᵥₙ ρ.traceLeft := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [MState.traceRight_transport, MState.traceLeft_transport, Sᵥₙ_transport, Sᵥₙ_transport, + Sᵥₙ_transport] + exact _root_.Sᵥₙ_subadditivity μ + +/-- Araki-Lieb triangle inequality on von Neumann entropy. -/ +theorem Sᵥₙ_triangle_subaddivity (ρ : DensityOp (E ⊗[ℂ] F)) : + |Sᵥₙ ρ.traceRight - Sᵥₙ ρ.traceLeft| ≤ Sᵥₙ ρ := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [MState.traceRight_transport, MState.traceLeft_transport, Sᵥₙ_transport, Sᵥₙ_transport, + Sᵥₙ_transport] + exact _root_.Sᵥₙ_triangle_subaddivity μ + +/-- Weak monotonicity of quantum conditional entropy: `S(E|F) + S(E|G) ≥ 0`. -/ +theorem Sᵥₙ_weak_monotonicity (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : + 0 ≤ ((ρ.congr (TensorProduct.assocIsometry ℂ E F G).symm).traceRight).qConditionalEnt + + ((((ρ.congr (TensorProduct.commIsometry ℂ E (F ⊗[ℂ] G))).congr + (TensorProduct.assocIsometry ℂ F G E)).traceLeft).congr + (TensorProduct.commIsometry ℂ G E)).qConditionalEnt := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + let := StdBasis.some ℂ G + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [← MState.assoc'_transport, ← MState.SWAP_transport, ← MState.assoc_transport, + MState.traceLeft_transport, ← MState.SWAP_transport, MState.traceRight_transport, + qConditionalEnt_transport, qConditionalEnt_transport] + have := _root_.Sᵥₙ_weak_monotonicity μ + dsimp only at this + linarith + +/-- Strong subadditivity, stated in terms of conditional entropies: +`S(E|FG) ≤ S(E|F)`. -/ +theorem qConditionalEnt_strong_subadditivity (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : + ρ.qConditionalEnt ≤ + ((ρ.congr (TensorProduct.assocIsometry ℂ E F G).symm).traceRight).qConditionalEnt := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + let := StdBasis.some ℂ G + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [← MState.assoc'_transport, MState.traceRight_transport, qConditionalEnt_transport, + qConditionalEnt_transport] + exact _root_.qConditionalEnt_strong_subadditivity μ + +/-- Strong subadditivity, stated in terms of quantum mutual information: +`I(E:FG) ≥ I(E:F)`. -/ +theorem qMutualInfo_strong_subadditivity (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : + ((ρ.congr (TensorProduct.assocIsometry ℂ E F G).symm).traceRight).qMutualInfo ≤ + ρ.qMutualInfo := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + let := StdBasis.some ℂ G + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [← MState.assoc'_transport, MState.traceRight_transport, qMutualInfo_transport, + qMutualInfo_transport] + exact _root_.qMutualInfo_strong_subadditivity μ + +/-- The quantum conditional mutual information is nonnegative. -/ +theorem qcmi_nonneg (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : 0 ≤ ρ.qcmi := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + let := StdBasis.some ℂ G + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [qcmi_transport] + exact _root_.qcmi_nonneg μ + +/-- The quantum conditional mutual information `I(E;G|F)` is at most `2 log (dim E)`. -/ +theorem qcmi_le_2_log_dim (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : + ρ.qcmi ≤ 2 * Real.log (Module.finrank ℂ E) := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + let := StdBasis.some ℂ G + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [qcmi_transport] + simpa using _root_.qcmi_le_2_log_dim μ + +/-- The quantum conditional mutual information `I(E;G|F)` is at most `2 log (dim G)`. -/ +theorem qcmi_le_2_log_dim' (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : + ρ.qcmi ≤ 2 * Real.log (Module.finrank ℂ G) := by + let := StdBasis.some ℂ E + let := StdBasis.some ℂ F + let := StdBasis.some ℂ G + obtain ⟨μ, rfl⟩ := ρ.exists_transport_eq + rw [qcmi_transport] + simpa using _root_.qcmi_le_2_log_dim' μ + +end DensityOp + +end basis_free /- The chain rule for quantum conditional mutual information: `I(A₁A₂ : C | B) = I(A₁:C|B) + I(A₂:C|BA₁)`. @@ -1300,7 +1513,7 @@ It should be something like this, but it's hard to track the indices correctly: theorem qcmi_chain_rule (ρ : MState ((dA₁ × dA₂) × dB × dC)) : let ρA₁BC := ρ.assoc.SWAP.assoc.traceLeft.SWAP; let ρA₂BA₁C : MState (dA₂ × (dA₁ × dB) × dC) := - ((CPTPMap.id ⊗ₖ CPTPMap.assoc').compose (CPTPMap.assoc.compose (CPTPMap.SWAP ⊗ₖ CPTPMap.id))) ρ; + ((CPTPOp.id ⊗ₖ CPTPOp.assoc').compose (CPTPOp.assoc.compose (CPTPOp.SWAP ⊗ₖ CPTPOp.id))) ρ; qcmi ρ = qcmi ρA₁BC + qcmi ρA₂BA₁C := by admit diff --git a/QuantumInfo/Entropy/VonNeumann.lean b/QuantumInfo/Entropy/VonNeumann.lean index 59e5e7717..8f2e41b8f 100644 --- a/QuantumInfo/Entropy/VonNeumann.lean +++ b/QuantumInfo/Entropy/VonNeumann.lean @@ -72,9 +72,50 @@ open scoped InnerProductSpace RealInnerProductSpace section entropy -/-- Von Neumann entropy of a mixed state. -/ -def Sᵥₙ (ρ : MState d) : ℝ := - Hₛ ρ.spectrum +section operator + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + +/-- Von Neumann entropy of a mixed state: the trace of `-ρ log ρ`, where the operator function is +given by the continuous functional calculus. + +This makes no reference to a basis. `Sᵥₙ_eq_Hₛ_spectrum` identifies it with the Shannon entropy of +the spectrum, and `Sᵥₙ_eq_trace_cfc_negMulLog` is the matrix analogue. -/ +def Sᵥₙ (ρ : DensityOp E) : ℝ := + (ρ.op.cfc Real.negMulLog).trace + +/-- **Matrix analogue of `Sᵥₙ`**: the von Neumann entropy is the trace of `x ↦ -x * log x` applied +to the density matrix through the continuous functional calculus. -/ +theorem Sᵥₙ_eq_trace_cfc_negMulLog (ρ : DensityOp E) : + Sᵥₙ ρ = ((ρ.M : HermitianMat ι ℂ).cfc Real.negMulLog).trace := by + simp only [Sᵥₙ, ← HermitianOp.trace_toMat (ι := ι), HermitianOp.toMat_cfc] + rfl + +/-- **Matrix analogue of `Sᵥₙ`**, phrased with the continuous functional calculus on the raw +density matrix rather than on a `HermitianMat`. -/ +theorem Sᵥₙ_eq_re_trace_matrix_cfc (ρ : DensityOp E) : + Sᵥₙ ρ = (cfc Real.negMulLog (ρ.m : Matrix ι ι ℂ)).trace.re := by + rw [Sᵥₙ_eq_trace_cfc_negMulLog (ι := ι), HermitianMat.trace, HermitianMat.mat_cfc, + IsMaximalSelfAdjoint.RCLike_selfadjMap, RCLike.re_to_complex, DensityOp.mat_M] + +/-- The von Neumann entropy is unchanged by reading a state on a different space with the same +preferred index type. -/ +@[simp] +theorem Sᵥₙ_transport {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℂ F] [StdBasis ℂ F ι] + (ρ : DensityOp E) : Sᵥₙ (ρ.transport F) = Sᵥₙ ρ := by + rw [Sᵥₙ_eq_trace_cfc_negMulLog (ι := ι), Sᵥₙ_eq_trace_cfc_negMulLog (ι := ι), + DensityOp.M_transport] + +end operator + +/-- The von Neumann entropy is the Shannon entropy of the spectrum. -/ +theorem Sᵥₙ_eq_Hₛ_spectrum (ρ : MState d) : Sᵥₙ ρ = Hₛ ρ.spectrum := by + rw [Sᵥₙ_eq_trace_cfc_negMulLog (ι := d), ← HermitianMat.sum_eigenvalues_eq_trace, Hₛ] + obtain ⟨e, he⟩ := ρ.M.cfc_eigenvalues (f := Real.negMulLog) + refine Finset.sum_equiv e (by simp) fun i _ ↦ ?_ + rw [congrFun he i] + simp [H₁, MState.spectrum, ProbDistribution.mk', ProbDistribution.prob] /-- The Quantum Conditional Entropy S(ρᴬ|ρᴮ) is given by S(ρᴬᴮ) - S(ρᴮ). -/ def qConditionalEnt (ρ : MState (dA × dB)) : ℝ := @@ -88,26 +129,94 @@ def qMutualInfo (ρ : MState (dA × dB)) : ℝ := entropy of the image under Λ of the purification of ρ. -/ def coherentInfo (ρ : MState d₁) (Λ : CPTPMap d₁ d₂) : ℝ := let ρPure : MState (d₁ × d₁) := MState.pure ρ.purify - let ρImg : MState (d₂ × d₁) := Λ.prod (CPTPMap.id (dIn := d₁)) ρPure + let ρImg : MState (d₂ × d₁) := Λ.prod (CPTPOp.id (dIn := d₁)) ρPure (- qConditionalEnt ρImg) /-- The Quantum Conditional Mutual Information, I(A;C|B) = S(A|B) - S(A|BC). -/ def qcmi (ρ : MState (dA × dB × dC)) : ℝ := qConditionalEnt ρ.assoc'.traceRight - qConditionalEnt ρ +section bipartite + +/-! ### Basis-free forms of the composite-system entropies + +The definitions above are stated for a system whose index type is a product; these are their +counterparts for a state on an honest tensor product, together with the "matrix analogue" +theorems identifying the two through `DensityOp.transport`. -/ + +open scoped TensorProduct + +variable {E F G : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] +variable [NormedAddCommGroup G] [InnerProductSpace ℂ G] + +namespace DensityOp + +section Defs + +variable [FiniteDimensional ℂ E] [FiniteDimensional ℂ F] [FiniteDimensional ℂ G] + +/-- The quantum conditional entropy `S(E|F) = S(EF) - S(F)` of a state on `E ⊗ F`. -/ +def qConditionalEnt (ρ : DensityOp (E ⊗[ℂ] F)) : ℝ := + Sᵥₙ ρ - Sᵥₙ ρ.traceLeft + +/-- The quantum mutual information `I(E:F) = S(E) + S(F) - S(EF)` of a state on `E ⊗ F`. -/ +def qMutualInfo (ρ : DensityOp (E ⊗[ℂ] F)) : ℝ := + Sᵥₙ ρ.traceLeft + Sᵥₙ ρ.traceRight - Sᵥₙ ρ + +/-- The quantum conditional mutual information `I(E;G|F) = S(E|F) - S(E|FG)` of a state on +`E ⊗ (F ⊗ G)`. -/ +def qcmi (ρ : DensityOp (E ⊗[ℂ] (F ⊗[ℂ] G))) : ℝ := + ((ρ.congr (TensorProduct.assocIsometry ℂ E F G).symm).traceRight).qConditionalEnt - + ρ.qConditionalEnt + +end Defs + +end DensityOp + +section Analogue + +variable [StdBasis ℂ E dA] [StdBasis ℂ F dB] [StdBasis ℂ G dC] + +/-- **Matrix analogue of `DensityOp.qConditionalEnt`.** -/ +@[simp] +theorem qConditionalEnt_transport (ρ : MState (dA × dB)) : + (ρ.transport (E ⊗[ℂ] F)).qConditionalEnt = qConditionalEnt ρ := by + rw [DensityOp.qConditionalEnt, qConditionalEnt, MState.traceLeft_transport, Sᵥₙ_transport, + Sᵥₙ_transport] + +/-- **Matrix analogue of `DensityOp.qMutualInfo`.** -/ +@[simp] +theorem qMutualInfo_transport (ρ : MState (dA × dB)) : + (ρ.transport (E ⊗[ℂ] F)).qMutualInfo = qMutualInfo ρ := by + rw [DensityOp.qMutualInfo, qMutualInfo, MState.traceLeft_transport, MState.traceRight_transport, + Sᵥₙ_transport, Sᵥₙ_transport, Sᵥₙ_transport] + +/-- **Matrix analogue of `DensityOp.qcmi`.** -/ +@[simp] +theorem qcmi_transport (ρ : MState (dA × dB × dC)) : + (ρ.transport (E ⊗[ℂ] (F ⊗[ℂ] G))).qcmi = qcmi ρ := by + rw [DensityOp.qcmi, qcmi, ← MState.assoc'_transport, MState.traceRight_transport, + qConditionalEnt_transport, qConditionalEnt_transport] + +end Analogue + +end bipartite + /-- von Neumman entropy is nonnegative. -/ theorem Sᵥₙ_nonneg (ρ : MState d) : 0 ≤ Sᵥₙ ρ := - Hₛ_nonneg _ + (Hₛ_nonneg _).trans (Sᵥₙ_eq_Hₛ_spectrum ρ).ge /-- von Neumman entropy is at most log d. -/ theorem Sᵥₙ_le_log_d (ρ : MState d) : Sᵥₙ ρ ≤ Real.log (Finset.card Finset.univ (α := d)):= - Hₛ_le_log_d _ + (Sᵥₙ_eq_Hₛ_spectrum ρ).trans_le (Hₛ_le_log_d _) /-- von Neumman entropy of pure states is zero. -/ @[simp] theorem Sᵥₙ_of_pure_zero (ψ : Ket d) : Sᵥₙ (MState.pure ψ) = 0 := by obtain ⟨i, hi⟩ := MState.spectrum_pure_eq_constant ψ - rw [Sᵥₙ, hi, Hₛ_constant_eq_zero] + rw [Sᵥₙ_eq_Hₛ_spectrum, hi, Hₛ_constant_eq_zero] set_option backward.isDefEq.respectTransparency false in theorem Sᵥₙ_eq_neg_trace_log (ρ : MState d) : Sᵥₙ ρ = -⟪ρ.M.log, ρ.M⟫ := by @@ -115,24 +224,12 @@ theorem Sᵥₙ_eq_neg_trace_log (ρ : MState d) : Sᵥₙ ρ = -⟪ρ.M.log, ρ rw [log, inner_eq_re_trace] nth_rw 2 [← cfc_id ρ.M] rw [← mat_cfc_mul] - simp only [Sᵥₙ, Hₛ, H₁, Real.negMulLog, neg_mul, Finset.sum_neg_distrib, neg_inj] + simp only [Sᵥₙ_eq_Hₛ_spectrum, Hₛ, H₁, Real.negMulLog, neg_mul, Finset.sum_neg_distrib, neg_inj] rw [← trace_eq_re_trace, ← sum_eigenvalues_eq_trace] obtain ⟨e, he⟩ := ρ.M.cfc_eigenvalues (Real.log * id) apply Finset.sum_equiv e.symm (by simp) simp [MState.spectrum, ProbDistribution.mk', he, mul_comm] -/-- Von Neumann entropy is the trace of the matrix function `x ↦ -x log x`. -/ -theorem Sᵥₙ_eq_trace_cfc_negMulLog (ρ : MState d) : - Sᵥₙ ρ = (ρ.M.cfc Real.negMulLog).trace := by - open HermitianMat in - unfold Real.negMulLog - rw [Sᵥₙ_eq_neg_trace_log, trace, log, inner_eq_re_trace, IsMaximalSelfAdjoint.RCLike_selfadjMap] - nth_rw 2 [← cfc_id ρ.M] - rw [← mat_cfc_mul, RCLike.re_to_complex, ← Complex.neg_re, ← Matrix.trace_neg] - rw [← mat_neg, ← ρ.M.cfc_neg] - congr! 5 - simp [mul_comm] - @[simp] theorem Sᵥₙ_unit_zero [Unique d] (ρ : MState d) : Sᵥₙ ρ = 0 := by refine le_antisymm ?_ (Sᵥₙ_nonneg ρ) @@ -147,12 +244,14 @@ theorem Sᵥₙ_relabel (ρ : MState d₁) (e : d₂ ≃ d₁) : /-- Von Neumann entropy is unchanged under SWAP. TODO: All unitaries-/ @[simp] theorem Sᵥₙ_of_SWAP_eq (ρ : MState (d₁ × d₂)) : Sᵥₙ ρ.SWAP = Sᵥₙ ρ := by + rw [Sᵥₙ_eq_Hₛ_spectrum, Sᵥₙ_eq_Hₛ_spectrum] apply Hₛ_eq_of_multiset_map_eq exact ρ.multiset_spectrum_relabel_eq (Equiv.prodComm d₁ d₂).symm /-- Von Neumann entropy is unchanged under assoc. -/ @[simp] theorem Sᵥₙ_of_assoc_eq (ρ : MState ((d₁ × d₂) × d₃)) : Sᵥₙ ρ.assoc = Sᵥₙ ρ := by + rw [Sᵥₙ_eq_Hₛ_spectrum, Sᵥₙ_eq_Hₛ_spectrum] apply Hₛ_eq_of_multiset_map_eq apply ρ.multiset_spectrum_relabel_eq @@ -168,14 +267,8 @@ theorem selfAdjointMap_Continuous {𝕜 : Type*} [RCLike 𝕜] : fun_prop @[fun_prop] -theorem HermitianMat.trace_Continuous {d 𝕜 : Type*} [Fintype d] [RCLike 𝕜] : - Continuous (HermitianMat.trace : HermitianMat d 𝕜 → ℝ) := by - rw [funext HermitianMat.trace_eq_re_trace] - fun_prop - -@[fun_prop] -theorem Sᵥₙ_continuous : Continuous (Sᵥₙ (d := d)) := by - rw [funext Sᵥₙ_eq_trace_cfc_negMulLog] +theorem Sᵥₙ_continuous : Continuous (Sᵥₙ (E := EuclideanSpace ℂ d)) := by + rw [funext (Sᵥₙ_eq_trace_cfc_negMulLog (ι := d))] fun_prop section partial_trace_pure @@ -209,8 +302,11 @@ The left partial trace of `vecToMat` is the transpose of M^H * M. private lemma traceLeft_eq_transpose_conjTranspose_mul (ψ : Ket (d₁ × d₂)) : (MState.pure ψ).traceLeft.M.val = ((vecToMat ψ.vec).conjTranspose * (vecToMat ψ.vec)).transpose := by - norm_num +zetaDelta at *; - convert! traceLeft_eq_transpose_mul_conj ψ using 1 + rw [traceLeft_eq_transpose_mul_conj] + ext i j + simp only [Matrix.transpose_apply, Matrix.mul_apply, Matrix.conjTranspose_apply, + Matrix.map_apply] + exact Finset.sum_congr rfl fun k _ ↦ mul_comm _ _ /-- Shannon entropy is determined by the multiset of non-zero probabilities. @@ -258,6 +354,7 @@ private lemma Sᵥₙ_eq_of_nonzero_eigenvalues_eq (ρ₁ : MState d₁) (ρ₂ (h : (Finset.univ.val.map ρ₁.spectrum.prob).filter (· ≠ 0) = (Finset.univ.val.map ρ₂.spectrum.prob).filter (· ≠ 0)) : Sᵥₙ ρ₁ = Sᵥₙ ρ₂ := by + rw [Sᵥₙ_eq_Hₛ_spectrum, Sᵥₙ_eq_Hₛ_spectrum] exact Hₛ_eq_of_nonzero_multiset_eq _ _ h /-- diff --git a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/GeneralizedPerspectiveFunction.lean b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/GeneralizedPerspectiveFunction.lean index e5bd68b95..aa8215969 100644 --- a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/GeneralizedPerspectiveFunction.lean +++ b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/GeneralizedPerspectiveFunction.lean @@ -255,7 +255,6 @@ private lemma conj_le_conj {X Y T : L ℋ} (hXY : X ≤ Y) (hT : IsSelfAdjoint T simp [sub_eq_add_neg, mul_add, add_mul, mul_assoc] exact sub_nonneg.mp (by simpa [hsub] using hconj) -set_option maxHeartbeats 800000 in -- The generalized-perspective normalization expands several nested CFC products. private theorem theorem_2_5_forward_jointlyConvexOn_psd_pd_of_condV {f h : ℝ → ℝ} diff --git a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequality.lean b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequality.lean index 09d181d54..49f693c94 100644 --- a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequality.lean +++ b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequality.lean @@ -192,7 +192,6 @@ private lemma blockDiagonal_le_left_wrap {A0 A1 B0 B1 : L ℋ} exact sub_nonneg.mp ((ContinuousLinearMap.nonneg_iff_isPositive _).2 hleftPos) -- Theorem 2.5.2 `(iv) → (v)`. -set_option maxHeartbeats 3000000 in -- Block-matrix normalization in this wrapper needs a larger local heartbeat budget. theorem theorem_2_5_2_iv_imp_v {f : ℝ → ℝ} (hiv : CondIVAll.{u} f) (hcont : ContinuousOn f Set.univ) : @@ -304,7 +303,6 @@ theorem theorem_2_5_2_i_all_imp_v {f : ℝ → ℝ} (hf : CondIAll.{u} f) : exact theorem_2_5_2_i_all_imp_iv (ℋ := K) (f := f) hf -- Uniform localized consequence of Theorem 2.5.2: `(i) → (v)` on `Set.Ici 0`. -set_option maxHeartbeats 3000000 in -- The localized wrapper repeats the same block-operator normalization as -- `theorem_2_5_2_iv_imp_v`. theorem theorem_2_5_2_i_ici_all_imp_v {f : ℝ → ℝ} diff --git a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIImpIV.lean b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIImpIV.lean index 894f03e6b..938296abe 100644 --- a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIImpIV.lean +++ b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIImpIV.lean @@ -299,7 +299,6 @@ private theorem nontrivial_hsumL [Nontrivial ℋ] : Nontrivial (L (HSum ℋ)) := set_option synthInstance.maxHeartbeats 100000 in -- `CFC.sqrt` on block-diagonal operators triggers expensive instance search through the product map. set_option linter.unusedSectionVars false in -set_option maxHeartbeats 400000 in private lemma sqrt_blockDiagonal_of_nonneg [Nontrivial ℋ] {A B : L ℋ} (hA : IsSelfAdjoint A) (hB : IsSelfAdjoint B) diff --git a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIVtoV.lean b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIVtoV.lean index 02e3168cf..33b4c0e50 100644 --- a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIVtoV.lean +++ b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/JensenOperatorInequalityIVtoV.lean @@ -63,12 +63,12 @@ omit [CompleteSpace ℋ] in private theorem nontrivial_hsumL : Nontrivial (L (HSum ℋ)) := by have h_not_sub : ¬ Subsingleton ℋ := by intro hsub - letI : Subsingleton ℋ := hsub - letI : Subsingleton (L ℋ) := by infer_instance + let : Subsingleton ℋ := hsub + let : Subsingleton (L ℋ) := by infer_instance exact (not_nontrivial_iff_subsingleton.mpr (by infer_instance)) (inferInstance : Nontrivial (L ℋ)) have hH_nontriv : Nontrivial ℋ := (not_subsingleton_iff_nontrivial.mp h_not_sub) - letI : Nontrivial ℋ := hH_nontriv + let : Nontrivial ℋ := hH_nontriv rcases exists_pair_ne ℋ with ⟨x, y, hxy⟩ let w : ℋ := x - y have hw : w ≠ 0 := sub_ne_zero.mpr hxy @@ -114,7 +114,6 @@ private lemma blockOp_mul (A00 A01 A10 A11 B00 B01 B10 B11 : L ℋ) : simp [ContinuousLinearMap.mul_def, add_left_comm, add_comm] set_option synthInstance.maxHeartbeats 400000 in -set_option maxHeartbeats 800000 in omit [Nontrivial ℋ] in private lemma cfcR_blockDiagonal (f : ℝ → ℝ) (A B : L ℋ) (hA : IsSelfAdjoint A) (hB : IsSelfAdjoint B) @@ -184,7 +183,6 @@ private lemma blockDiagonal_le_left {A0 A1 B0 B1 : L ℋ} -- Scratch theorem for fast feedback while formalizing Theorem 2.5.2 `(iv) → (v)`. -- This file intentionally avoids importing the heavy `(i) → (iv)` proof. set_option synthInstance.maxHeartbeats 400000 in -set_option maxHeartbeats 3000000 in -- The block-matrix reduction creates large normalization goals in this scratch file. theorem theorem_2_5_2_iv_imp_v {f : ℝ → ℝ} (hiv : CondIVAll.{u} f) (hcont : ContinuousOn f Set.univ) : @@ -200,7 +198,7 @@ theorem theorem_2_5_2_iv_imp_v {f : ℝ → ℝ} (hiv : CondIVAll.{u} f) simpa [Set.Ici] using hBs hx let Atilde : L (HSum ℋ) := blockDiagonal (ℋ := ℋ) A B let Xtilde : L (HSum ℋ) := blockOp (ℋ := ℋ) X 0 Y 0 - letI : Nontrivial (L (HSum ℋ)) := nontrivial_hsumL (ℋ := ℋ) + let : Nontrivial (L (HSum ℋ)) := nontrivial_hsumL (ℋ := ℋ) have hAtilde_sa : IsSelfAdjoint Atilde := by simpa [Atilde] using blockDiagonal_selfAdjoint (ℋ := ℋ) hA hB have hAtilde0 : (0 : L (HSum ℋ)) ≤ Atilde := by diff --git a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/LiebAndoTrace.lean b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/LiebAndoTrace.lean index f87313c3e..83144aebb 100644 --- a/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/LiebAndoTrace.lean +++ b/QuantumInfo/ForMathlib/HayataGroup/TraceInequality/LiebAndoTrace.lean @@ -240,7 +240,6 @@ private lemma aeval_apply_of_mem_eigenspace_realpoly (f := T.toLinearMap) (p := p.map (algebraMap ℝ ℂ)) (μ := (r : ℂ)) (x := x) ⟨hx, hx0⟩) -- The interpolation-based `cfcR`-on-eigenspace lemma is elaboration-heavy. -set_option maxHeartbeats 400000 in private lemma cfcR_apply_of_mem_eigenspace_real {𝓚 : Type*} [NormedAddCommGroup 𝓚] [InnerProductSpace ℂ 𝓚] [CompleteSpace 𝓚] [FiniteDimensional ℂ 𝓚] @@ -298,7 +297,6 @@ private lemma cfcR_apply_of_mem_eigenspace_real simpa using congrArg (fun t : ℝ => (t : ℂ)) (hq_spec hr_spec).symm -- This proof is isolated because the joint eigenspace decomposition is heartbeat-heavy. -set_option maxHeartbeats 800000 in private lemma hmiddle_leftMul_rightMul {s : ℝ} {A B : L ℋ} (hA : A ∈ pdSet (ℋ := ℋ)) (hB : B ∈ pdSet (ℋ := ℋ)) : @@ -551,7 +549,6 @@ private lemma hmiddle_leftMul_rightMul -- The bridge lemma expands a large `HSOp`-valued generalized perspective term. set_option backward.isDefEq.respectTransparency false in -set_option maxHeartbeats 800000 in private lemma phiK_operatorPowerMean_eq_liebTraceMap {s : ℝ} (K A B : L ℋ) (hA : A ∈ pdSet (ℋ := ℋ)) (hB : B ∈ pdSet (ℋ := ℋ)) : phiK (ℋ := ℋ) K @@ -835,7 +832,6 @@ private lemma phiK_leftMul_rightMul_eq_traceRe (K C D : L ℋ) : simpa [X, mul_assoc] using htrace'.trans hcycle omit [FiniteDimensional ℂ ℋ] in -set_option maxHeartbeats 400000 in private lemma pdSet_rpow_of_mem_Icc_zero_one {p : ℝ} (hp : p ∈ Set.Icc (0 : ℝ) 1) {A : L ℋ} (hA : A ∈ pdSet (ℋ := ℋ)) : A ^ p ∈ pdSet (ℋ := ℋ) := by @@ -877,7 +873,6 @@ private lemma pdSet_rpow_of_mem_Icc_zero_one ⟨r ^ p, Real.rpow_pos_of_pos hr p, hbound⟩ omit [Nontrivial ℋ] in -set_option maxHeartbeats 400000 in private lemma liebTraceMap_mono_right {s : ℝ} (hs : 1 - s ∈ Set.Icc (0 : ℝ) 1) (K A B₁ B₂ : L ℋ) @@ -948,7 +943,6 @@ private lemma liebTraceMap_mono_right linarith [hrewrite ▸ hphi] omit [Nontrivial ℋ] in -set_option maxHeartbeats 400000 in private lemma liebTraceMap_antitone_right {s : ℝ} (hs : 1 - s ∈ Set.Icc (-1 : ℝ) 0) (K A B₁ B₂ : L ℋ) @@ -1180,7 +1174,6 @@ theorem liebTrace_jointlyConvexOn_pdSet hA₁ hA₂ hB₁ hB₂] at hphi_mono simpa [add_comm, add_left_comm, add_assoc] using hphi_mono -set_option maxHeartbeats 600000 in theorem liebExtensionTrace_jointlyConcaveOn_pdSet {p q : ℝ} (hp : 0 < p) (hq : 0 < q) (hpq : p + q ≤ 1) (K : L ℋ) : JointlyConcaveOn (pdSet (ℋ := ℋ)) (pdSet (ℋ := ℋ)) @@ -1297,7 +1290,6 @@ theorem liebExtensionTrace_jointlyConcaveOn_pdSet simpa [hpow_rewrite hB₁_mem, hpow_rewrite hB₂_mem, hpow_rewrite hB_combo_mem] using (le_trans hconc hmono) -set_option maxHeartbeats 600000 in theorem andoTrace_jointlyConvexOn_pdSet {q r : ℝ} (hq1 : 1 ≤ q) (hq2 : q ≤ 2) (hr0 : 0 ≤ r) (hr1 : r ≤ 1) (hqr : 1 ≤ q - r) (K : L ℋ) : diff --git a/QuantumInfo/ForMathlib/HermitianMat.lean b/QuantumInfo/ForMathlib/HermitianMat.lean index 8c322e822..5bf97f7e5 100644 --- a/QuantumInfo/ForMathlib/HermitianMat.lean +++ b/QuantumInfo/ForMathlib/HermitianMat.lean @@ -6,6 +6,7 @@ Authors: Alex Meiburg module public import QuantumInfo.ForMathlib.HermitianMat.Basic +public import QuantumInfo.ForMathlib.HermitianMat.BlockDiag public import QuantumInfo.ForMathlib.HermitianMat.CFC public import QuantumInfo.ForMathlib.HermitianMat.Inner public import QuantumInfo.ForMathlib.HermitianMat.LiebConcavity @@ -18,4 +19,5 @@ public import QuantumInfo.ForMathlib.HermitianMat.Reindex public import QuantumInfo.ForMathlib.HermitianMat.Schatten public import QuantumInfo.ForMathlib.HermitianMat.Sqrt public import QuantumInfo.ForMathlib.HermitianMat.Trace +public import QuantumInfo.ForMathlib.HermitianMat.TraceNorm public import QuantumInfo.ForMathlib.HermitianMat.Unitary diff --git a/QuantumInfo/ForMathlib/HermitianMat/Basic.lean b/QuantumInfo/ForMathlib/HermitianMat/Basic.lean index 185a3b15b..1efbbbea6 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Basic.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Basic.lean @@ -64,6 +64,11 @@ instance instFun : FunLike (HermitianMat n α) n (n → α) where theorem mat_apply {A : HermitianMat n α} {i j : n} : A.mat i j = A i j := by rfl +@[simp] +theorem mk_apply (x : Matrix n n α) (h) (i j : n) : + DFunLike.coe (F := HermitianMat n α) ⟨x, h⟩ i j = x i j := by + rfl + @[simp] theorem conjTranspose_mat (A : HermitianMat n α) : A.mat.conjTranspose = A.mat := @@ -524,6 +529,10 @@ variable (f g : n → ℝ) theorem diagonal_mat : (diagonal 𝕜 f).mat = Matrix.diagonal (f · : n → 𝕜) := by rfl +@[simp] +theorem diagonal_apply (i j : n) : diagonal 𝕜 f i j = if i = j then (f i : 𝕜) else 0 := by + rw [← mat_apply, diagonal_mat, Matrix.diagonal_apply] + @[simp] theorem diagonal_zero : (diagonal 𝕜 0) = (0 : HermitianMat n 𝕜) := by ext1; simp @@ -581,6 +590,10 @@ theorem kronecker_mat (A : HermitianMat m α) (B : HermitianMat n α) : (A ⊗ₖ B).mat = A.mat ⊗ₖ B.mat := by rfl +theorem kronecker_apply (A : HermitianMat m α) (B : HermitianMat n α) (i j : m × n) : + (A ⊗ₖ B) i j = A i.1 j.1 * B i.2 j.2 := by + rfl + @[simp] theorem zero_kronecker (A : HermitianMat m α) : (0 : HermitianMat n α) ⊗ₖ A = 0 := by ext1; simp @@ -602,6 +615,20 @@ variable (A : HermitianMat m α) (B C : HermitianMat n α) in theorem kronecker_add : A ⊗ₖ (B + C) = A ⊗ₖ B + A ⊗ₖ C := by ext1; simp [Matrix.kronecker_add] +variable (A B : HermitianMat m α) (C : HermitianMat n α) in +theorem sub_kronecker : (A - B) ⊗ₖ C = A ⊗ₖ C - B ⊗ₖ C := by + ext1 + simp only [kronecker_mat, mat_sub] + ext i j + simp [Matrix.kroneckerMap_apply, Matrix.sub_apply, sub_mul] + +variable (A : HermitianMat m α) (B C : HermitianMat n α) in +theorem kronecker_sub : A ⊗ₖ (B - C) = A ⊗ₖ B - A ⊗ₖ C := by + ext1 + simp only [kronecker_mat, mat_sub] + ext i j + simp [Matrix.kroneckerMap_apply, Matrix.sub_apply, mul_sub] + lemma kronecker_diagonal [DecidableEq m] [DecidableEq n] (d₁ : m → ℝ) (d₂ : n → ℝ) : (diagonal 𝕜 d₁ ⊗ₖ diagonal 𝕜 d₂) = diagonal 𝕜 (fun (i : m × n) => d₁ i.1 * d₂ i.2) := by ext1 diff --git a/QuantumInfo/ForMathlib/HermitianMat/BlockDiag.lean b/QuantumInfo/ForMathlib/HermitianMat/BlockDiag.lean new file mode 100644 index 000000000..8d9958c13 --- /dev/null +++ b/QuantumInfo/ForMathlib/HermitianMat/BlockDiag.lean @@ -0,0 +1,194 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import QuantumInfo.ForMathlib.HermitianMat.LogExp +public import QuantumInfo.ForMathlib.HermitianMat.Trace + +/-! # Block-diagonal Hermitian matrices + +A Hermitian matrix on `d × ι` that is *block diagonal* with respect to the second factor can be +written as `∑ i, X i ⊗ₖ basisProj 𝕜 i`, where `basisProj 𝕜 i` is the rank-one projector onto the +`i`-th standard basis vector of `ι`. Such a matrix behaves blockwise under all of the operations +we care about: its continuous functional calculus (and hence its logarithm) is applied to each +block separately, its kernel is the product of the blockwise kernels, and inner products of two +such matrices decompose as the sum of the blockwise inner products. +-/ + +@[expose] public section + +noncomputable section + +open scoped Kronecker Matrix RealInnerProductSpace + +namespace HermitianMat + +variable {d ι 𝕜 : Type*} [Fintype d] [DecidableEq d] [Fintype ι] [DecidableEq ι] [RCLike 𝕜] + +variable (𝕜) in +/-- The rank-one projector onto the `i`-th standard basis vector. -/ +def basisProj (i : ι) : HermitianMat ι 𝕜 := + diagonal 𝕜 (fun j ↦ if j = i then 1 else 0) + +variable (𝕜) in +private def P (i : ι) : Matrix ι ι 𝕜 := + Matrix.diagonal (fun j ↦ if j = i then 1 else 0) + +omit [Fintype ι] in +private theorem basisProj_mat_P (i : ι) : (basisProj 𝕜 i).mat = P 𝕜 i := by + simp only [basisProj, diagonal_mat, P] + congr 1 + funext j + split_ifs <;> simp + +omit [Fintype ι] in +@[simp] +theorem basisProj_mat (i : ι) : + (basisProj 𝕜 i).mat = Matrix.diagonal (fun j ↦ if j = i then (1 : 𝕜) else 0) := + basisProj_mat_P i + +private theorem P_mul_P (i j : ι) : P 𝕜 i * P 𝕜 j = if i = j then P 𝕜 i else 0 := by + by_cases h : i = j + · subst h + simp only [P, Matrix.diagonal_mul_diagonal] + congr 1 + funext k + by_cases hk : k = i <;> simp [hk] + · simp only [P, Matrix.diagonal_mul_diagonal, if_neg h, ← Matrix.diagonal_zero] + congr 1 + funext k + by_cases hk : k = i <;> simp [hk, h] + +omit [Fintype ι] in +private theorem P_conjTranspose (i : ι) : (P 𝕜 i)ᴴ = P 𝕜 i := by + simp only [P, Matrix.diagonal_conjTranspose] + congr 1 + funext j + by_cases hj : j = i <;> simp [hj] + +omit [DecidableEq d] in +private theorem block_mul (A B : ι → Matrix d d 𝕜) : + (∑ i, A i ⊗ₖ P 𝕜 i) * (∑ j, B j ⊗ₖ P 𝕜 j) = ∑ i, (A i * B i) ⊗ₖ P 𝕜 i := by + rw [Finset.sum_mul] + refine Finset.sum_congr rfl fun i _ ↦ ?_ + rw [Finset.mul_sum, Finset.sum_eq_single i] + · rw [← Matrix.mul_kronecker_mul, P_mul_P, if_pos rfl] + · intro j _ hj + rw [← Matrix.mul_kronecker_mul, P_mul_P, if_neg (Ne.symm hj)] + simp + · intro h + exact absurd (Finset.mem_univ i) h + +omit [DecidableEq d] [Fintype d] in +private theorem block_conjTranspose (A : ι → Matrix d d 𝕜) : + (∑ i, A i ⊗ₖ P 𝕜 i)ᴴ = ∑ i, (A i)ᴴ ⊗ₖ P 𝕜 i := by + rw [Matrix.conjTranspose_sum] + exact Finset.sum_congr rfl fun i _ ↦ by + rw [Matrix.kroneckerMap_conjTranspose, P_conjTranspose] + +omit [Fintype d] in +private theorem sum_one_kron_P : ∑ i : ι, (1 : Matrix d d 𝕜) ⊗ₖ P 𝕜 i = 1 := by + ext ⟨a, i⟩ ⟨b, j⟩ + rw [Matrix.sum_apply] + simp only [Matrix.kroneckerMap_apply, Matrix.one_apply, P, Matrix.diagonal_apply, Prod.mk.injEq] + by_cases hab : a = b <;> by_cases hij : i = j <;> simp [hab, hij] + +private theorem block_unitary (U : ι → Matrix.unitaryGroup d 𝕜) : + (∑ i, (U i).val ⊗ₖ P 𝕜 i) ∈ Matrix.unitaryGroup (d × ι) 𝕜 := by + have h1 : ∀ i, (U i).val * ((U i).val)ᴴ = 1 := fun i ↦ by + have := Matrix.mem_unitaryGroup_iff.mp (U i).2 + rwa [Matrix.star_eq_conjTranspose] at this + rw [Matrix.mem_unitaryGroup_iff, Matrix.star_eq_conjTranspose, block_conjTranspose, block_mul] + simp only [h1] + exact sum_one_kron_P + +omit [Fintype d] in +private theorem diagonal_block (E : ι → d → ℝ) : + (diagonal 𝕜 (fun x : d × ι ↦ E x.2 x.1)).mat = ∑ i, (diagonal 𝕜 (E i)).mat ⊗ₖ P 𝕜 i := by + ext ⟨a, i⟩ ⟨b, j⟩ + rw [Matrix.sum_apply] + simp only [diagonal_mat, Matrix.diagonal_apply, Matrix.kroneckerMap_apply, P, Prod.mk.injEq] + by_cases hab : a = b <;> by_cases hij : i = j <;> simp [hab, hij] + +private theorem conj_block (E : ι → d → ℝ) (U : ι → Matrix.unitaryGroup d 𝕜) : + ∑ i, ((diagonal 𝕜 (E i)).conj (U i).val) ⊗ₖ basisProj 𝕜 i + = (diagonal 𝕜 (fun x : d × ι ↦ E x.2 x.1)).conj (∑ i, (U i).val ⊗ₖ P 𝕜 i) := by + ext1 + rw [mat_finset_sum, conj_apply_mat, diagonal_block, block_conjTranspose, block_mul, block_mul] + exact Finset.sum_congr rfl fun i _ ↦ by + rw [kronecker_mat, conj_apply_mat, basisProj_mat_P] + +theorem mulVec_sum_kron_basisProj (X : ι → HermitianMat d 𝕜) (v : d × ι → 𝕜) (a : d) (i : ι) : + ((∑ j, X j ⊗ₖ basisProj 𝕜 j).mat *ᵥ v) (a, i) = ((X i).mat *ᵥ (fun b ↦ v (b, i))) a := by + simp only [mat_finset_sum, kronecker_mat, basisProj_mat, Matrix.mulVec, dotProduct, + Matrix.sum_apply, Matrix.kroneckerMap_apply, Matrix.diagonal_apply, Fintype.sum_prod_type, + Finset.sum_mul, mul_ite, mul_zero, ite_mul, zero_mul] + simp + +theorem ker_sum_kron_basisProj_le {X Y : ι → HermitianMat d 𝕜} + (h : ∀ i, (X i).ker ≤ (Y i).ker) : + (∑ i, X i ⊗ₖ basisProj 𝕜 i).ker ≤ (∑ i, Y i ⊗ₖ basisProj 𝕜 i).ker := by + intro v hv + rw [mem_ker_iff_mulVec_zero] at hv ⊢ + funext p + obtain ⟨a, i⟩ := p + have hvi : (WithLp.toLp 2 (fun b ↦ v (b, i)) : EuclideanSpace 𝕜 d) ∈ (X i).ker := by + rw [mem_ker_iff_mulVec_zero] + funext b + rw [← mulVec_sum_kron_basisProj X v b i, hv] + rfl + have hY := (mem_ker_iff_mulVec_zero _ _).mp (h i hvi) + rw [mulVec_sum_kron_basisProj Y v a i] + exact congrFun hY a + +/-- The continuous functional calculus acts blockwise on a block-diagonal matrix. -/ +theorem cfc_sum_kron_basisProj (X : ι → HermitianMat d 𝕜) (f : ℝ → ℝ) : + (∑ i, X i ⊗ₖ basisProj 𝕜 i).cfc f = ∑ i, (X i).cfc f ⊗ₖ basisProj 𝕜 i := by + set U : ι → Matrix.unitaryGroup d 𝕜 := fun i ↦ (X i).H.eigenvectorUnitary with hU + set E : ι → d → ℝ := fun i ↦ (X i).H.eigenvalues with hE + have hX : ∀ i, X i = (diagonal 𝕜 (E i)).conj (U i).val := fun i ↦ eq_conj_diagonal (X i) + set V : Matrix.unitaryGroup (d × ι) 𝕜 := ⟨_, block_unitary U⟩ with hV + have key : ∀ E' : ι → d → ℝ, ∑ i, ((diagonal 𝕜 (E' i)).conj (U i).val) ⊗ₖ basisProj 𝕜 i + = (diagonal 𝕜 (fun x : d × ι ↦ E' x.2 x.1)).conj V.val := fun E' ↦ conj_block E' U + have h1 : ∑ i, X i ⊗ₖ basisProj 𝕜 i + = (diagonal 𝕜 (fun x : d × ι ↦ E x.2 x.1)).conj V.val := by + rw [← key E] + exact Finset.sum_congr rfl fun i _ ↦ by rw [← hX i] + have h2 : ∀ i, (X i).cfc f = (diagonal 𝕜 (f ∘ E i)).conj (U i).val := by + intro i + rw [hX i, cfc_conj_unitary, cfc_diagonal] + rw [h1, cfc_conj_unitary, cfc_diagonal, + show (f ∘ fun x : d × ι ↦ E x.2 x.1) = (fun x : d × ι ↦ (f ∘ E x.2) x.1) from rfl, + ← key (fun i ↦ f ∘ E i)] + exact Finset.sum_congr rfl fun i _ ↦ by rw [h2 i] + +/-- The matrix logarithm acts blockwise on a block-diagonal matrix. -/ +theorem log_sum_kron_basisProj (X : ι → HermitianMat d 𝕜) : + (∑ i, X i ⊗ₖ basisProj 𝕜 i).log = ∑ i, (X i).log ⊗ₖ basisProj 𝕜 i := by + simp only [HermitianMat.log] + exact cfc_sum_kron_basisProj X Real.log + +omit [Fintype d] [DecidableEq d] [Fintype ι] [DecidableEq ι] in +theorem smul_kronecker (r : ℝ) (A : HermitianMat d 𝕜) (B : HermitianMat ι 𝕜) : + (r • A) ⊗ₖ B = r • (A ⊗ₖ B) := by + ext1 + rw [kronecker_mat, mat_smul, mat_smul, kronecker_mat, Matrix.smul_kronecker] + +@[simp] +theorem trace_basisProj (i : ι) : (basisProj 𝕜 i).trace = 1 := by + rw [basisProj, trace_diagonal] + simp + +theorem inner_basisProj (i j : ι) : + ⟪basisProj 𝕜 i, basisProj 𝕜 j⟫ = if i = j then 1 else 0 := by + rw [inner_eq_re_trace, basisProj_mat_P, basisProj_mat_P, P_mul_P] + split_ifs with h + · simp [P, Matrix.trace_diagonal] + · simp + +end HermitianMat + +end diff --git a/QuantumInfo/ForMathlib/HermitianMat/CFC.lean b/QuantumInfo/ForMathlib/HermitianMat/CFC.lean index 473106385..1626aa78e 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/CFC.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/CFC.lean @@ -634,7 +634,6 @@ The proof uses the resolvent approach and compactness. 5. For B with ‖B - A₀‖ < δ: spectrum ℝ B.mat ⊆ Metric.closedBall 0 M (by step 1) and spectrum ℝ B.mat ∩ K = ∅ (by step 4). So spectrum ℝ B.mat ⊆ Metric.closedBall 0 M \ K ⊆ U. Note: we need to connect spectrum ℝ B.mat (the real spectrum) to IsUnit in the complex matrix ring. Use that for self-adjoint elements, t ∈ spectrum ℝ A.mat iff algebraMap ℝ (Matrix d d ℂ) t ∈ spectrum ℂ A.mat, and the resolvent set is open. We can use spectrum.isOpen_resolventSet or the characterization via IsUnit. -/ -set_option maxHeartbeats 400000 in set_option backward.isDefEq.respectTransparency false in lemma spectrum_subset_of_isOpen (A₀ : HermitianMat d ℂ) (U : Set ℝ) (hU : IsOpen U) (hAU : spectrum ℝ A₀.mat ⊆ U) : @@ -733,7 +732,6 @@ Both terms → 0, so the map is ContinuousWithinAt. Use `Metric.continuousWithinAt_iff` and an ε/2 argument. -/ set_option backward.isDefEq.respectTransparency false in -set_option maxHeartbeats 800000 in lemma continuousWithinAt_cfc_of_continuousOn {T : Set ℝ} {g : ℝ → ℝ} {A₀ : HermitianMat d ℂ} (hg : ContinuousOn g T) (hA₀ : spectrum ℝ A₀.mat ⊆ T) : @@ -836,7 +834,6 @@ Use `by_contra` and arrive at contradiction, or construct the neighborhood direc IMPORTANT: To get the open sets V_i, use `ContinuousWithinAt` of f at (x₀, λᵢ) which gives an eventually filter statement, then extract using `mem_nhdsWithin_iff_exists_mem_nhds_inter` and `mem_nhds_prod_iff`. For the continuity of A composed with spectrum_subset_of_isOpen: use `ContinuousWithinAt.eventually` or compose the filter. Specifically: `(hA₂ x₀ hx₀).eventually (spectrum_subset_of_isOpen (A x₀) W hW_open hW_contains)` gives `∀ᶠ y in nhdsWithin x₀ S, spectrum(A y) ⊆ W`. Then use `Filter.Eventually.exists_mem` to get U'. -/ -set_option maxHeartbeats 800000 in lemma dist_lt_of_continuous_spectrum {X : Type*} [TopologicalSpace X] {f : X → ℝ → ℝ} {A : X → HermitianMat d ℂ} {S : Set X} {T : Set ℝ} (hf : ContinuousOn (fun (p : X × ℝ) ↦ f p.1 p.2) (S ×ˢ T)) diff --git a/QuantumInfo/ForMathlib/HermitianMat/Inner.lean b/QuantumInfo/ForMathlib/HermitianMat/Inner.lean index 913c71be2..b5c2a072e 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Inner.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Inner.lean @@ -515,3 +515,27 @@ lemma inner_eq_doubly_stochastic_sum {d : Type*} [Fintype d] [DecidableEq d] simp only [RCLike.ofReal_eq_complex_ofReal]; ring rw [inner_eq_re_trace, key, Matrix.trace_mul_cycle, hUU, one_mul, hC_trace] exact Complex.ofReal_re _ + +section kronecker + +open Kronecker + +variable {n m 𝕜 : Type*} [Fintype n] [Fintype m] [RCLike 𝕜] + +/-- Pairing against `A ⊗ₖ 1` is pairing the right partial trace against `A`. -/ +theorem inner_kron_one [DecidableEq m] (A : HermitianMat n 𝕜) (M : HermitianMat (n × m) 𝕜) : + ⟪A ⊗ₖ (1 : HermitianMat m 𝕜), M⟫ = ⟪A, M.traceRight⟫ := by + rw [inner_comm, inner_comm A, inner_eq_re_trace, inner_eq_re_trace, kronecker_mat, mat_one, + Matrix.trace_mul_kron_one_right, traceRight_mat] + +/-- Pairing against `1 ⊗ₖ B` is pairing the left partial trace against `B`. -/ +theorem inner_one_kron [DecidableEq n] (B : HermitianMat m 𝕜) (M : HermitianMat (n × m) 𝕜) : + ⟪(1 : HermitianMat n 𝕜) ⊗ₖ B, M⟫ = ⟪B, M.traceLeft⟫ := by + rw [inner_comm, inner_comm B, inner_eq_re_trace, inner_eq_re_trace, kronecker_mat, mat_one, + Matrix.trace_mul_one_kron_right, traceLeft_mat] + +end kronecker + +theorem inner_eq_zero_of_mul_eq_zero {n 𝕜 : Type*} [Fintype n] [RCLike 𝕜] + {A B : HermitianMat n 𝕜} (h : A.mat * B.mat = 0) : ⟪A, B⟫ = 0 := by + rw [inner_eq_re_trace, h, Matrix.trace_zero, map_zero] diff --git a/QuantumInfo/ForMathlib/HermitianMat/LiebConcavity.lean b/QuantumInfo/ForMathlib/HermitianMat/LiebConcavity.lean index 9fb765b01..386715776 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/LiebConcavity.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/LiebConcavity.lean @@ -189,7 +189,6 @@ private lemma tendsto_add_eps (σ : HermitianMat d ℂ) : /-! ### Helper lemmas for the core concavity proof -/ -set_option maxHeartbeats 800000 in /-- **AB/BA trace identity for rpow**: `Tr[(C^*C)^p] = Tr[(CC^*)^p]` for any square C. -/ private lemma trace_rpow_conjTranspose_mul_comm [Nonempty d] (C : Matrix d d ℂ) (p : ℝ) : @@ -270,7 +269,6 @@ private lemma variational_eq_optimizer Joint concavity of the Lieb extension trace map on HermitianMat. This bridges `liebExtensionTrace_jointlyConcaveOn_pdSet` to HermitianMat. -/ -set_option maxHeartbeats 1600000 in private lemma liebExtension_bridge [Nonempty d] {q r : ℝ} (hq : 0 < q) (hr : 0 < r) (hqr : q + r ≤ 1) (K : HermitianMat d ℂ) @@ -398,7 +396,6 @@ private lemma liebExtension_bridge_psd [Nonempty d] refine' Continuous.tendsto' _ _ _ _ <;> norm_num fun_prop -set_option maxHeartbeats 1600000 in /-- Core concavity inequality on positive definite matrices. -/ private lemma trace_conj_rpow_concave_pd [Nonempty d] {α : ℝ} (hα : 1 < α) (H : HermitianMat d ℂ) (hH : 0 ≤ H) diff --git a/QuantumInfo/ForMathlib/HermitianMat/LogExp.lean b/QuantumInfo/ForMathlib/HermitianMat/LogExp.lean index 10916a389..8065c88cf 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/LogExp.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/LogExp.lean @@ -204,7 +204,6 @@ lemma Real.tendsto_log_div_add_atTop (x : ℝ) : refine h_div.congr' ( by filter_upwards [ Filter.eventually_gt_atTop 0 ] with b hb using by rw [ show ( 1 + b ) / ( x + b ) = ( 1 / b + 1 ) / ( x / b + 1 ) by rw [ div_add_one, div_add_one, div_div_div_cancel_right₀ ] <;> positivity ] ); exact le_trans ( Filter.Tendsto.log ( Filter.Tendsto.div ( Filter.Tendsto.add ( tendsto_const_nhds.div_atTop Filter.tendsto_id ) tendsto_const_nhds ) ( Filter.Tendsto.add ( tendsto_const_nhds.div_atTop Filter.tendsto_id ) tendsto_const_nhds ) ( by positivity ) ) ( by positivity ) ) ( by norm_num ) -set_option maxHeartbeats 1000000 in set_option backward.isDefEq.respectTransparency false in open ComplexOrder MeasureTheory intervalIntegral in /-- diff --git a/QuantumInfo/ForMathlib/HermitianMat/Order.lean b/QuantumInfo/ForMathlib/HermitianMat/Order.lean index 543d126d0..1127a07dd 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Order.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Order.lean @@ -538,6 +538,65 @@ theorem eigenvalues_le_imp_le_smul_one [DecidableEq n] (A : HermitianMat n ℂ) exact (Matrix.PosSemidef.le_smul_one_of_eigenvalues_iff A.H M).mp h +/-- Containment of kernels is preserved by the partial trace, provided the smaller one is PSD. + +Given `x` in the kernel of `A.traceRight`, each of the vectors `v b := x ⊗ eᵦ` has +`⟪v b, A v b⟫ ≥ 0`, and these terms sum to `⟪x, A.traceRight x⟫ = 0`; so each one vanishes and +each `v b` lies in `ker A ≤ ker B`. Summing `B *ᵥ v b` over `b` recovers `B.traceRight *ᵥ x`. -/ +theorem ker_traceRight_le {dA dB : Type*} [Fintype dA] [Fintype dB] [DecidableEq dA] + [DecidableEq dB] {A B : HermitianMat (dA × dB) 𝕜} (hA : 0 ≤ A) (h : A.ker ≤ B.ker) : + A.traceRight.ker ≤ B.traceRight.ker := by + intro x hx + rw [mem_ker_iff_mulVec_zero] at hx ⊢ + set v : dB → EuclideanSpace 𝕜 (dA × dB) := fun b ↦ + WithLp.toLp 2 fun p ↦ if p.2 = b then x.ofLp p.1 else 0 with hv + have key : ∀ (M : HermitianMat (dA × dB) 𝕜) (a : dA), + (M.traceRight.mat *ᵥ x.ofLp) a = ∑ b, (M.mat *ᵥ (v b).ofLp) (a, b) := by + intro M a + have hR : ∀ j : dB, (M.mat *ᵥ (v j).ofLp) (a, j) = ∑ b, M.mat (a, j) (b, j) * x.ofLp b := by + intro j + simp only [hv, WithLp.ofLp_toLp, Matrix.mulVec, dotProduct, Fintype.sum_prod_type, + mul_ite, mul_zero] + refine Finset.sum_congr rfl fun b _ ↦ ?_ + rw [Finset.sum_eq_single j] + · rw [if_pos rfl] + · intro k _ hk + rw [if_neg hk] + · intro hj + exact absurd (Finset.mem_univ j) hj + rw [show (∑ b, (M.mat *ᵥ (v b).ofLp) (a, b)) = ∑ j, ∑ b, M.mat (a, j) (b, j) * x.ofLp b from + Finset.sum_congr rfl fun j _ ↦ hR j] + simp only [Matrix.mulVec, dotProduct, traceRight_mat, Matrix.traceRight, + Matrix.of_apply, Finset.sum_mul] + exact Finset.sum_comm + have hnn : ∀ b, 0 ≤ star (v b).ofLp ⬝ᵥ A.mat *ᵥ (v b).ofLp := + fun b ↦ inner_mulVec_nonneg hA _ + have hsum0 : ∑ b, star (v b).ofLp ⬝ᵥ A.mat *ᵥ (v b).ofLp = 0 := by + have h1 : ∀ b, star (v b).ofLp ⬝ᵥ A.mat *ᵥ (v b).ofLp + = ∑ a, star (x.ofLp a) * (A.mat *ᵥ (v b).ofLp) (a, b) := by + intro b + simp only [dotProduct, hv, WithLp.ofLp_toLp, Fintype.sum_prod_type, Pi.star_apply] + refine Finset.sum_congr rfl fun a _ ↦ ?_ + rw [Finset.sum_eq_single b] + · rw [if_pos rfl] + · intro k _ hk + simp [hk] + · intro hb + exact absurd (Finset.mem_univ b) hb + simp only [h1] + rw [Finset.sum_comm] + simp only [← Finset.mul_sum, ← key A, hx, Pi.zero_apply, mul_zero, Finset.sum_const_zero] + have hker : ∀ b, v b ∈ A.ker := by + intro b + refine mem_ker_of_inner_mulVec_zero hA _ ?_ + exact le_antisymm (hsum0 ▸ Finset.single_le_sum (fun j _ ↦ hnn j) (Finset.mem_univ b)) (hnn b) + funext a + rw [key B a] + refine Finset.sum_eq_zero fun b _ ↦ ?_ + have := (mem_ker_iff_mulVec_zero _ _).mp (h (hker b)) + rw [this] + rfl + --TODO: Positivity extensions for traceLeft, traceRight, rpow, nat powers, inverse function, -- the various `proj` function (in Proj.lean), and the inner product. diff --git a/QuantumInfo/ForMathlib/HermitianMat/Peierls.lean b/QuantumInfo/ForMathlib/HermitianMat/Peierls.lean index 08d48b5c1..16e685c4f 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Peierls.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Peierls.lean @@ -129,21 +129,23 @@ theorem trace_function_convex_univ (g : ℝ → ℝ) (hg : ConvexOn ℝ Set.univ have h_eigenvalue : C.H.eigenvalues i = a * ((A.conj (star C.H.eigenvectorUnitary.val)).mat i i).re + b * ((B.conj (star C.H.eigenvectorUnitary.val)).mat i i).re := by - have h_eigenvalue : (C.conj (star C.H.eigenvectorUnitary.val)).mat i i = + have h_add : (C.conj (star C.H.eigenvectorUnitary.val)).mat i i = a * (A.conj (star C.H.eigenvectorUnitary.val)).mat i i + b * (B.conj (star C.H.eigenvectorUnitary.val)).mat i i := by - simp +zetaDelta at * - simp [conj] - exact Complex.ext rfl rfl - have h_eigenvalue : (C.conj (star C.H.eigenvectorUnitary.val)) = + simp +zetaDelta only [mat_add, mat_smul, map_add, mat_apply] + simp only [conj, AddMonoidHom.coe_mk, ZeroHom.coe_mk, mat_smul, Algebra.mul_smul_comm, + Algebra.smul_mul_assoc] + rfl + have h_eig2 : (C.conj (star C.H.eigenvectorUnitary.val)) = (diagonal ℂ C.H.eigenvalues).conj 1 := by convert congr_arg (conj (star C.H.eigenvectorUnitary.val) ·) (eq_conj_diagonal C) using 1 simp [conj_conj] - simp_all [conj] - convert congr_arg Complex.re ‹ (diagonal ℂ _) i i = _ › using 1 - · exact Eq.symm (by erw [show (diagonal ℂ _ : HermitianMat d ℂ) i i = - (C.H.eigenvalues i : ℂ) by exact if_pos rfl]; norm_cast) - · norm_num [Complex.ext_iff] + -- In the eigenbasis of `C`, the `i`-th diagonal entry is exactly the `i`-th eigenvalue. + have h_diag : (C.conj (star C.H.eigenvectorUnitary.val)).mat i i = + (C.H.eigenvalues i : ℂ) := by + rw [h_eig2, conj_one, mat_apply, diagonal_apply, if_pos rfl] + rfl + simpa using congr_arg Complex.re (h_diag.symm.trans h_add) rw [h_eigenvalue] exact hg.2 trivial trivial ha hb hab simpa only [Finset.mul_sum, Finset.sum_add_distrib] using Finset.sum_le_sum fun i _ => h_sum i @@ -199,11 +201,12 @@ theorem trace_function_convex_ici {g : ℝ → ℝ} (hg : ConvexOn ℝ (Set.Ici (diagonal ℂ C.H.eigenvalues).conj 1 := by convert congr_arg (conj (star C.H.eigenvectorUnitary.val) ·) (eq_conj_diagonal C) using 1 simp [conj_conj] - simp_all [conj] - convert congr_arg Complex.re h_eigenvalue using 1 - · exact Eq.symm (by erw [show (diagonal ℂ _ : HermitianMat d ℂ) i i = - (C.H.eigenvalues i : ℂ) by exact if_pos rfl]; norm_cast) - · norm_num [Complex.ext_iff] + -- In the eigenbasis of `C`, the `i`-th diagonal entry is exactly the `i`-th eigenvalue. + have h_diag : (C.conj (star C.H.eigenvectorUnitary.val)).mat i i = + (C.H.eigenvalues i : ℂ) := by + rw [h_eig2, conj_one, mat_apply, diagonal_apply, if_pos rfl] + rfl + simpa using congr_arg Complex.re (h_diag.symm.trans h_eigenvalue) rw [h_eigenvalue] refine hg.2 ?_ ?_ ha hb hab · simp diff --git a/QuantumInfo/ForMathlib/HermitianMat/Proj.lean b/QuantumInfo/ForMathlib/HermitianMat/Proj.lean index f3969253f..071aa6beb 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Proj.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Proj.lean @@ -209,6 +209,25 @@ theorem supportProj_eq_cfc : A.supportProj = A.cfc (if · = 0 then 0 else 1) := simp [ Finset.sum_ite, Finset.filter_eq, Finset.filter_and ]; rw [ Finset.sum_eq_single i ] <;> aesop +/- +`HermitianMat.kerProj` as a cfc. +-/ +theorem kerProj_eq_cfc : A.kerProj = A.cfc (if · = 0 then 1 else 0) := by + have h : A.cfc (fun x ↦ (1 : ℝ) - (if x = 0 then 0 else 1)) = A.cfc (if · = 0 then 1 else 0) := by + congr 1 + funext x + split_ifs <;> norm_num + rw [← h, cfc_sub_apply, cfc_const, one_smul, ← supportProj_eq_cfc, eq_sub_iff_add_eq, + kerProj_add_supportProj] + +/-- `HermitianMat.kerProj` is idempotent. -/ +theorem kerProj_sq : A.kerProj.mat * A.kerProj.mat = A.kerProj.mat := by + have hf : (fun x : ℝ ↦ (if x = 0 then (1 : ℝ) else 0) * (if x = 0 then 1 else 0)) + = (if · = 0 then 1 else 0) := by + funext x + split_ifs <;> norm_num + rw [kerProj_eq_cfc, ← mat_cfc_mul_apply, hf] + /-- Projector onto the non-negative eigenspace of `B - A`. Accessible by the notation `{A ≤ₚ B}`, which is scoped to `HermitianMat`. This is the unique maximum operator `P` such that `P^2 = P` and `P * A * P ≤ P * B * P` in the Loewner order. -/ @@ -536,3 +555,37 @@ theorem nonneg_iff_inner_nonneg (A : HermitianMat n 𝕜) : classical use A⁻, negPart_nonneg A rwa [inner_negPart_neg_iff] + +section kronecker + +open Kronecker + +variable {m : Type*} [Fintype m] [DecidableEq m] + +theorem ker_le_ker_supportProj (A : HermitianMat n ℂ) : A.ker ≤ A.supportProj.ker := by + rw [supportProj_eq_cfc] + exact ker_le_ker_cfc (fun i hi ↦ by simp [hi]) + +lemma supportProj_kron_diagonal (f : n → ℝ) (g : m → ℝ) : + (diagonal 𝕜 f ⊗ₖ diagonal 𝕜 g).supportProj = + (diagonal 𝕜 f).supportProj ⊗ₖ (diagonal 𝕜 g).supportProj := by + rw [supportProj_eq_cfc, supportProj_eq_cfc, supportProj_eq_cfc, kronecker_diagonal, + cfc_diagonal, cfc_diagonal, cfc_diagonal, kronecker_diagonal] + congr 1 + funext i + by_cases h1 : f i.1 = 0 <;> by_cases h2 : g i.2 = 0 <;> simp [h1, h2] + +/-- The support projector of a Kronecker product is the Kronecker product of the support +projectors. -/ +theorem supportProj_kron (A : HermitianMat n 𝕜) (B : HermitianMat m 𝕜) : + (A ⊗ₖ B).supportProj = A.supportProj ⊗ₖ B.supportProj := by + obtain ⟨UA, DA, rfl⟩ : ∃ UA : Matrix.unitaryGroup n 𝕜, ∃ DA, A = (diagonal 𝕜 DA).conj UA.val := + ⟨_, _, eq_conj_diagonal A⟩ + obtain ⟨UB, DB, rfl⟩ : ∃ UB : Matrix.unitaryGroup m 𝕜, ∃ DB, B = (diagonal 𝕜 DB).conj UB.val := + ⟨_, _, eq_conj_diagonal B⟩ + rw [← kronecker_conj, supportProj_eq_cfc, + cfc_conj_unitary _ _ ⟨_, Matrix.kronecker_mem_unitary UA.2 UB.2⟩, ← supportProj_eq_cfc, + supportProj_kron_diagonal, kronecker_conj] + congr 1 <;> rw [supportProj_eq_cfc, supportProj_eq_cfc, cfc_conj_unitary] + +end kronecker diff --git a/QuantumInfo/ForMathlib/HermitianMat/Reindex.lean b/QuantumInfo/ForMathlib/HermitianMat/Reindex.lean index 87709dde3..d0518000c 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Reindex.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Reindex.lean @@ -31,6 +31,9 @@ def reindex (e : d ≃ d₂) : HermitianMat d₂ 𝕜 := theorem mat_reindex : (A.reindex e).mat = A.mat.reindex e e := by rfl +theorem reindex_apply (i j : d₂) : A.reindex e i j = A (e.symm i) (e.symm j) := by + rfl + /-! Our simp-normal form for expressions involving `HermitianMat.reindex` is that we try to push the reindexing as far out as possible, so that it can be absorbed by `HermitianMat.trace`, or cancelled our in a `HermitianMat.inner`. In places where it commutes (like `HermitianMat.inner`) diff --git a/QuantumInfo/ForMathlib/HermitianMat/Sqrt.lean b/QuantumInfo/ForMathlib/HermitianMat/Sqrt.lean index 1804aeee8..1a0ca867f 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Sqrt.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Sqrt.lean @@ -6,6 +6,7 @@ Authors: Alex Meiburg module public import QuantumInfo.ForMathlib.HermitianMat.Proj +public import QuantumInfo.ForMathlib.MatrixNorm.TraceNorm @[expose] public section @@ -62,6 +63,31 @@ theorem sqrt_nonneg (A : HermitianMat d 𝕜) : 0 ≤ A.sqrt := by rw [sqrt, cfc_nonneg_iff] intro; positivity +/-- On nonnegative matrices, `HermitianMat.sqrt` is the C⋆-algebra square root `CFC.sqrt` of the +underlying matrix. -/ +theorem mat_sqrt (hA : 0 ≤ A) : A.sqrt.mat = CFC.sqrt A.mat := + (CFC.sqrt_unique (b := A.sqrt.mat) (sqrt_sq hA) + (Matrix.nonneg_iff_posSemidef.mpr (zero_le_iff.mp A.sqrt_nonneg))).symm + +theorem trace_sqrt (hA : 0 ≤ A) : A.sqrt.trace = RCLike.re (CFC.sqrt A.mat).trace := by + rw [trace_eq_re_trace, mat_sqrt hA] + +/-- A nonnegative idempotent, that is, an orthogonal projector, is its own square root. -/ +theorem sqrt_eq_self (hA : 0 ≤ A) (h : A.mat * A.mat = A.mat) : A.sqrt = A := by + ext1 + rw [mat_sqrt hA] + exact CFC.sqrt_unique h (Matrix.nonneg_iff_posSemidef.mpr (zero_le_iff.mp hA)) + +/-- Scaling a nonnegative matrix by `c ≥ 0` scales its square root by `√c`. -/ +theorem sqrt_smul (hA : 0 ≤ A) {c : ℝ} (hc : 0 ≤ c) : (c • A).sqrt = Real.sqrt c • A.sqrt := by + have h₁ : (0 : HermitianMat d 𝕜) ≤ c • A := smul_nonneg hc hA + have h₂ : (0 : HermitianMat d 𝕜) ≤ Real.sqrt c • A.sqrt := + smul_nonneg (Real.sqrt_nonneg c) (sqrt_nonneg A) + ext1 + rw [mat_sqrt h₁] + refine CFC.sqrt_unique ?_ (Matrix.nonneg_iff_posSemidef.mpr (zero_le_iff.mp h₂)) + rw [mat_smul, mat_smul, smul_mul_smul_comm, sqrt_sq hA, Real.mul_self_sqrt hc] + theorem sqrt_pos (h : 0 < A) : 0 < A.sqrt := by rw [sqrt] apply cfc_pos_of_pos h (by intros; positivity) (by simp) @@ -86,6 +112,115 @@ meta def evalHermitianMatSqrt : PositivityExt where eval {_u _α} _zα _pα? e : catch _ => pure (.nonnegative (← mkAppM ``HermitianMat.sqrt_nonneg #[A])) +open RealInnerProductSpace in +/-- If a positive semidefinite matrix `A` has zero inner product with an idempotent `X`, then +their product vanishes. -/ +theorem mul_eq_zero_of_inner_eq_zero {A X : HermitianMat d 𝕜} (hA : 0 ≤ A) + (hX : X.mat * X.mat = X.mat) (h : ⟪A, X⟫ = 0) : A.mat * X.mat = 0 := by + have hY : A.sqrt.mat * X.mat = 0 := by + apply Matrix.eq_zero_of_re_trace_conjTranspose_mul_self_eq_zero + rw [Matrix.conjTranspose_mul, A.sqrt.conjTranspose_mat, X.conjTranspose_mat] + have h1 : X.mat * A.sqrt.mat * (A.sqrt.mat * X.mat) = X.mat * (A.mat * X.mat) := by + rw [mul_assoc, ← mul_assoc A.sqrt.mat, sqrt_sq hA] + rw [h1, Matrix.trace_mul_comm, mul_assoc, hX, ← inner_eq_re_trace] + exact h + calc A.mat * X.mat = A.sqrt.mat * (A.sqrt.mat * X.mat) := by rw [← mul_assoc, sqrt_sq hA] + _ = 0 := by rw [hY, Matrix.mul_zero] + +section traceNorm + +/-- Whenever `A` is a Gram matrix `XᴴX`, the trace of `√A` is the trace norm of `X`. -/ +theorem trace_sqrt_eq_traceNorm {X : Matrix d d 𝕜} (hAX : A.mat = Xᴴ * X) : + A.sqrt.trace = X.traceNorm := by + have h0 : 0 ≤ A := by + rw [zero_le_iff, hAX] + exact Matrix.posSemidef_conjTranspose_mul_self X + rw [trace_sqrt h0, hAX] + rfl + +/-- The Gram matrix `XᴴX` written in terms of the square roots of `A` and `B`, where +`X = √A √B`: this is the matrix whose square root's trace is the fidelity of `A` and `B`. -/ +private theorem conj_sqrt_eq_gram (hA : 0 ≤ A) : + (A.conj B.sqrt.mat).mat = (A.sqrt.mat * B.sqrt.mat)ᴴ * (A.sqrt.mat * B.sqrt.mat) := by + rw [conj_apply_mat, B.sqrt.conjTranspose_mat, Matrix.conjTranspose_mul, + A.sqrt.conjTranspose_mat, B.sqrt.conjTranspose_mat, ← sqrt_sq hA] + simp [mul_assoc] + +/-- The trace of `√(√B A √B)`, that is, the fidelity of `A` and `B`, is the trace norm of +`√A √B`. -/ +theorem trace_sqrt_conj_sqrt_eq_traceNorm (hA : 0 ≤ A) : + (A.conj B.sqrt.mat).sqrt.trace = (A.sqrt.mat * B.sqrt.mat).traceNorm := + trace_sqrt_eq_traceNorm (conj_sqrt_eq_gram hA) + +/-- The **fidelity is symmetric**: the trace of `√(√B A √B)` is unchanged when `A` and `B` are +swapped, because the two matrices are `XᴴX` and `XXᴴ` for `X = √A √B`. -/ +theorem trace_sqrt_conj_sqrt_comm (hA : 0 ≤ A) (hB : 0 ≤ B) : + (A.conj B.sqrt.mat).sqrt.trace = (B.conj A.sqrt.mat).sqrt.trace := by + have h2 : (B.conj A.sqrt.mat).mat + = ((A.sqrt.mat * B.sqrt.mat)ᴴ)ᴴ * (A.sqrt.mat * B.sqrt.mat)ᴴ := by + rw [Matrix.conjTranspose_conjTranspose, conj_apply_mat, A.sqrt.conjTranspose_mat, + Matrix.conjTranspose_mul, A.sqrt.conjTranspose_mat, B.sqrt.conjTranspose_mat, ← sqrt_sq hB] + simp [mul_assoc] + rw [trace_sqrt_eq_traceNorm (conj_sqrt_eq_gram hA), trace_sqrt_eq_traceNorm h2, + Matrix.traceNorm_conjTranspose] + +/-- The **fidelity is at most one** for states: the trace of `√(√B A √B)` is bounded by +`√(Tr A) * √(Tr B)`, which is Cauchy–Schwarz applied to the Hilbert–Schmidt inner product. -/ +theorem trace_sqrt_conj_sqrt_le (hA : 0 ≤ A) (hB : 0 ≤ B) : + (A.conj B.sqrt.mat).sqrt.trace ≤ Real.sqrt A.trace * Real.sqrt B.trace := by + rw [trace_sqrt_eq_traceNorm (conj_sqrt_eq_gram hA)] + obtain ⟨U, hU1, hU⟩ := + Matrix.exists_unitary_re_trace_eq_traceNorm (A.sqrt.mat * B.sqrt.mat) + have hswap : (U * (A.sqrt.mat * B.sqrt.mat)).trace + = ((A.sqrt.mat * Uᴴ)ᴴ * B.sqrt.mat).trace := by + rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, A.sqrt.conjTranspose_mat, + mul_assoc] + have e1 : RCLike.re ((A.sqrt.mat * Uᴴ)ᴴ * (A.sqrt.mat * Uᴴ)).trace = A.trace := by + rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, A.sqrt.conjTranspose_mat, + show U * A.sqrt.mat * (A.sqrt.mat * Uᴴ) = U * (A.sqrt.mat * A.sqrt.mat) * Uᴴ by + simp [mul_assoc], + sqrt_sq hA, Matrix.trace_mul_comm, hU1, ← trace_eq_re_trace] + have e2 : RCLike.re ((B.sqrt.mat)ᴴ * B.sqrt.mat).trace = B.trace := by + rw [B.sqrt.conjTranspose_mat, sqrt_sq hB, ← trace_eq_re_trace] + calc (A.sqrt.mat * B.sqrt.mat).traceNorm + = RCLike.re ((A.sqrt.mat * Uᴴ)ᴴ * B.sqrt.mat).trace := by rw [← hU, hswap] + _ ≤ Real.sqrt (RCLike.re ((A.sqrt.mat * Uᴴ)ᴴ * (A.sqrt.mat * Uᴴ)).trace) * + Real.sqrt (RCLike.re ((B.sqrt.mat)ᴴ * B.sqrt.mat).trace) := + Matrix.re_trace_conjTranspose_mul_le _ _ + _ = Real.sqrt A.trace * Real.sqrt B.trace := by rw [e1, e2] + +/-- The **fidelity is one only for equal states**: if the trace of `√(√B A √B)` attains the bound +of `trace_sqrt_conj_sqrt_le` for unit-trace `A` and `B`, then `A = B`. This is the equality case of +Cauchy–Schwarz. -/ +theorem eq_of_trace_sqrt_conj_sqrt_eq_one (hA : 0 ≤ A) (hB : 0 ≤ B) (hA1 : A.trace = 1) + (hB1 : B.trace = 1) (h : (A.conj B.sqrt.mat).sqrt.trace = 1) : A = B := by + rw [trace_sqrt_eq_traceNorm (conj_sqrt_eq_gram hA)] at h + obtain ⟨U, hU1, hU⟩ := + Matrix.exists_unitary_re_trace_eq_traceNorm (A.sqrt.mat * B.sqrt.mat) + have e1 : RCLike.re ((A.sqrt.mat * Uᴴ)ᴴ * (A.sqrt.mat * Uᴴ)).trace = A.trace := by + rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, A.sqrt.conjTranspose_mat, + show U * A.sqrt.mat * (A.sqrt.mat * Uᴴ) = U * (A.sqrt.mat * A.sqrt.mat) * Uᴴ by + simp [mul_assoc], + sqrt_sq hA, Matrix.trace_mul_comm, hU1, ← trace_eq_re_trace] + have e2 : RCLike.re ((B.sqrt.mat)ᴴ * B.sqrt.mat).trace = B.trace := by + rw [B.sqrt.conjTranspose_mat, sqrt_sq hB, ← trace_eq_re_trace] + have e3 : RCLike.re ((A.sqrt.mat * Uᴴ)ᴴ * B.sqrt.mat).trace = 1 := by + rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, A.sqrt.conjTranspose_mat, + mul_assoc, hU, h] + have key : A.sqrt.mat * Uᴴ = B.sqrt.mat := + Matrix.eq_of_re_trace_conjTranspose_mul_eq_one (e1.trans hA1) (e2.trans hB1) e3 + apply HermitianMat.ext + calc A.mat = A.sqrt.mat * A.sqrt.mat := (sqrt_sq hA).symm + _ = A.sqrt.mat * (Uᴴ * (U * A.sqrt.mat)) := by rw [hU1] + _ = (A.sqrt.mat * Uᴴ) * (A.sqrt.mat * Uᴴ)ᴴ := by + rw [Matrix.conjTranspose_mul, Matrix.conjTranspose_conjTranspose, + A.sqrt.conjTranspose_mat] + simp [mul_assoc] + _ = B.sqrt.mat * (B.sqrt.mat)ᴴ := by rw [key] + _ = B.mat := by rw [B.sqrt.conjTranspose_mat, sqrt_sq hB] + +end traceNorm + example {A : HermitianMat d ℂ} : 0 ≤ A.sqrt := by positivity diff --git a/QuantumInfo/ForMathlib/HermitianMat/Trace.lean b/QuantumInfo/ForMathlib/HermitianMat/Trace.lean index 19e22b46d..89ed3b652 100644 --- a/QuantumInfo/ForMathlib/HermitianMat/Trace.lean +++ b/QuantumInfo/ForMathlib/HermitianMat/Trace.lean @@ -64,6 +64,13 @@ theorem trace_zero : (0 : HermitianMat n α).trace = 0 := by theorem trace_add (A B : HermitianMat n α) : (A + B).trace = A.trace + B.trace := by simp [trace] +theorem trace_sum {ι : Type*} (s : Finset ι) (A : ι → HermitianMat n α) : + (∑ i ∈ s, A i).trace = ∑ i ∈ s, (A i).trace := by + classical + induction s using Finset.induction with + | empty => simp + | insert i s hi ih => rw [Finset.sum_insert hi, Finset.sum_insert hi, trace_add, ih] + end semiring section ring @@ -120,6 +127,11 @@ variable {n m 𝕜 : Type*} [Fintype n] [Fintype m] [RCLike 𝕜] theorem trace_eq_re_trace (A : HermitianMat n 𝕜) : A.trace = RCLike.re A.mat.trace := by rfl +@[fun_prop] +theorem trace_Continuous : Continuous (HermitianMat.trace : HermitianMat n 𝕜 → ℝ) := by + rw [funext (trace_eq_re_trace (n := n) (𝕜 := 𝕜))] + fun_prop + @[simp] theorem trace_one [DecidableEq n] : (1 : HermitianMat n 𝕜).trace = Fintype.card n := by simp [trace_eq_re_trace] @@ -176,6 +188,9 @@ variable (A B : HermitianMat (m × n) α) theorem traceLeft_mat : A.traceLeft.mat = A.mat.traceLeft := by rfl +theorem traceLeft_apply (i j : n) : A.traceLeft i j = ∑ k, A (k, i) (k, j) := by + rfl + @[simp] theorem traceLeft_add : (A + B).traceLeft = A.traceLeft + B.traceLeft := by ext1; simp @@ -195,6 +210,9 @@ theorem traceRight_mat : (traceRight A).mat = A.mat.traceRight := by rfl +theorem traceRight_apply (i j : n) : A.traceRight i j = ∑ k, A (i, k) (j, k) := by + rfl + @[simp] theorem traceRight_add : (A + B).traceRight = A.traceRight + B.traceRight := by ext1; simp @@ -249,5 +267,16 @@ theorem traceRight_kron [Fintype n] : (A ⊗ₖ B).traceRight = B.trace • A := simp only [HermitianMat.traceRight, Matrix.traceRight, kronecker_mat, mat_mk] simp [Matrix.trace, RCLike.real_smul_eq_coe_mul, ← Finset.mul_sum, mul_comm] +variable [DecidableEq m] [DecidableEq n] + +theorem one_traceRight [Fintype n] : + (1 : HermitianMat (m × n) 𝕜).traceRight = (Fintype.card n : ℝ) • 1 := by + conv_lhs => rw [← kronecker_one_one] + rw [traceRight_kron, trace_one] + +theorem traceRight_add_smul_one [Fintype n] (A : HermitianMat (m × n) 𝕜) (ε : ℝ) : + (A + ε • 1).traceRight = A.traceRight + (ε * Fintype.card n) • 1 := by + rw [traceRight_add, traceRight_smul, one_traceRight, smul_smul] + end kron end partialTrace diff --git a/QuantumInfo/ForMathlib/HermitianMat/TraceNorm.lean b/QuantumInfo/ForMathlib/HermitianMat/TraceNorm.lean new file mode 100644 index 000000000..0e9fa8b9a --- /dev/null +++ b/QuantumInfo/ForMathlib/HermitianMat/TraceNorm.lean @@ -0,0 +1,32 @@ +/- +Copyright (c) 2026 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import QuantumInfo.ForMathlib.HermitianMat.Proj +public import QuantumInfo.ForMathlib.MatrixNorm.TraceNorm + +/-! # The trace norm of a Hermitian matrix + +The Jordan decomposition `A = A⁺ - A⁻` splits a Hermitian matrix into two positive semidefinite +pieces with orthogonal supports, so the trace norm — the sum of the absolute values of the +eigenvalues — is the sum of their traces. +-/ + +@[expose] public section + +namespace HermitianMat + +variable {d : Type*} [Fintype d] [DecidableEq d] + +/-- The trace norm of a Hermitian matrix is the sum of the traces of its positive and negative +parts. -/ +theorem traceNorm_eq_trace_posPart_add_negPart (A : HermitianMat d ℂ) : + A.mat.traceNorm = A⁺.trace + A⁻.trace := by + rw [Matrix.traceNorm_Hermitian_eq_sum_abs_eigenvalues A.H, posPart_eq_cfc_max, + negPart_eq_cfc_min, trace_cfc_eq, trace_cfc_eq, ← Finset.sum_add_distrib] + exact Finset.sum_congr rfl fun i _ ↦ (max_zero_add_max_neg_zero_eq_abs_self _).symm + +end HermitianMat diff --git a/QuantumInfo/ForMathlib/HermitianOp.lean b/QuantumInfo/ForMathlib/HermitianOp.lean new file mode 100644 index 000000000..6d17c01a6 --- /dev/null +++ b/QuantumInfo/ForMathlib/HermitianOp.lean @@ -0,0 +1,549 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import QuantumInfo.ForMathlib.HermitianMat.Inner +public import QuantumInfo.ForMathlib.HermitianMat.Rpow +public import QuantumInfo.ForMathlib.HermitianMat.Unitary +public import QuantumInfo.ForMathlib.MatrixNorm.TraceNorm +public import QuantumInfo.ForMathlib.StdBasis + +public import Mathlib.Analysis.CStarAlgebra.ContinuousLinearMap + +/-! +# Self-adjoint operators on a Hilbert space + +`HermitianOp E` is the type of self-adjoint continuous linear operators on a complex inner product +space `E`. It is the basis-free counterpart of `HermitianMat ι ℂ`, and it is the type in which +observables, density operators and POVM elements naturally live. + +Given a preferred orthonormal basis (a `StdBasis ℂ E ι` instance), `HermitianOp.toMat` identifies +`HermitianOp E` with `HermitianMat ι ℂ`. Because `StdBasis.toMat` is a ⋆-algebra equivalence, the +identification is compatible with everything of interest: the additive and `ℝ`-module structures, +the Loewner order, the trace, the spectrum, and the continuous functional calculus. The lemmas +transporting those are the *matrix analogues* of the operator-level statements, and they are what +lets an existing matrix definition be reused verbatim on operators. + +## Main definitions + +* `HermitianOp E`: self-adjoint operators on `E`. +* `HermitianOp.toMat`, `HermitianOp.ofMat`: the mutually inverse maps to and from + `HermitianMat ι ℂ` determined by the preferred basis. +* `HermitianOp.trace`, `HermitianOp.cfc`: the trace and the continuous functional calculus, + defined operator-side. +* `HermitianOp.sqrt`, `HermitianOp.rpow`, `HermitianOp.log`, `HermitianOp.exp`, + `HermitianOp.abs`: the standard operator functions, all instances of `HermitianOp.cfc`. +* `HermitianOp.conj`: conjugation `A ↦ B A B⋆`. +* `HermitianOp.traceNorm`: the Schatten 1-norm, i.e. the sum of the absolute values of the + eigenvalues. + +## Main results + +* `HermitianOp.matEquiv`: the identification with `HermitianMat ι ℂ` as an `ℝ`-linear equivalence. +* `HermitianOp.toMat_cfc`, `HermitianOp.trace_toMat`, `HermitianOp.toMat_le_toMat`: the matrix + analogues of the operator-level `cfc`, `trace` and order. +-/ + +@[expose] public section + +open scoped ComplexOrder + +/-- The type of self-adjoint continuous linear operators on `E`, as a `Subtype`. + +This is the basis-free analogue of `HermitianMat`; see `HermitianOp.matEquiv` for the +identification of the two given a preferred orthonormal basis. -/ +def HermitianOp (E : Type*) [NormedAddCommGroup E] [InnerProductSpace ℂ E] [CompleteSpace E] := + (selfAdjoint (E →L[ℂ] E) : Type _) + +namespace HermitianOp + +variable {E ι : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [CompleteSpace E] + +/-- The underlying operator of a `HermitianOp`. -/ +@[coe] def op : HermitianOp E → (E →L[ℂ] E) := + Subtype.val + +instance : Coe (HermitianOp E) (E →L[ℂ] E) := ⟨op⟩ + +/-- The underlying operator of a `HermitianOp` is self-adjoint. -/ +theorem H (A : HermitianOp E) : IsSelfAdjoint A.op := + A.2 + +@[simp] +theorem op_mk (A : E →L[ℂ] E) (h) : op ⟨A, h⟩ = A := + rfl + +@[ext] protected theorem ext {A B : HermitianOp E} : A.op = B.op → A = B := + Subtype.ext + +theorem op_injective : Function.Injective (op (E := E)) := + fun _ _ ↦ HermitianOp.ext + +noncomputable instance : AddCommGroup (HermitianOp E) := + inferInstanceAs (AddCommGroup (selfAdjoint (E →L[ℂ] E))) + +noncomputable instance : Module ℝ (HermitianOp E) := + inferInstanceAs (Module ℝ (selfAdjoint (E →L[ℂ] E))) + +noncomputable instance : PartialOrder (HermitianOp E) := + inferInstanceAs (PartialOrder (selfAdjoint (E →L[ℂ] E))) + +@[simp] theorem op_zero : (0 : HermitianOp E).op = 0 := rfl + +@[simp] theorem op_add (A B : HermitianOp E) : (A + B).op = A.op + B.op := rfl + +@[simp] theorem op_neg (A : HermitianOp E) : (-A).op = -A.op := rfl + +@[simp] theorem op_sub (A B : HermitianOp E) : (A - B).op = A.op - B.op := rfl + +@[simp] theorem op_smul (r : ℝ) (A : HermitianOp E) : (r • A).op = r • A.op := rfl + +theorem le_def {A B : HermitianOp E} : A ≤ B ↔ A.op ≤ B.op := + Iff.rfl + +/-- A self-adjoint operator is nonnegative exactly when it is a positive operator. -/ +theorem zero_le_iff {A : HermitianOp E} : 0 ≤ A ↔ A.op.IsPositive := + ContinuousLinearMap.nonneg_iff_isPositive A.op + +/-- The trace of a self-adjoint operator. It is real because the operator is self-adjoint. -/ +noncomputable def trace [FiniteDimensional ℂ E] (A : HermitianOp E) : ℝ := + RCLike.re (LinearMap.trace ℂ E (A.op : E →ₗ[ℂ] E)) + +/-- The continuous functional calculus applied to a self-adjoint operator. -/ +noncomputable def cfc (A : HermitianOp E) (f : ℝ → ℝ) : HermitianOp E := + ⟨_root_.cfc f A.op, cfc_predicate _ _⟩ + +@[simp] +theorem op_cfc (A : HermitianOp E) (f : ℝ → ℝ) : (A.cfc f).op = _root_.cfc f A.op := + rfl + +/-- The square root of a self-adjoint operator. Negative spectrum is mapped to zero. -/ +noncomputable def sqrt (A : HermitianOp E) : HermitianOp E := + A.cfc Real.sqrt + +/-- Real powers of a self-adjoint operator. This carries the usual `Real.rpow` caveats: for +instance the zero operator to the power `-1` is zero. -/ +noncomputable def rpow (A : HermitianOp E) (r : ℝ) : HermitianOp E := + A.cfc (Real.rpow · r) + +noncomputable instance instRPow : Pow (HermitianOp E) ℝ := + ⟨rpow⟩ + +theorem pow_eq_rpow (A : HermitianOp E) (r : ℝ) : A ^ r = A.rpow r := + rfl + +/-- The logarithm of a self-adjoint operator. Nonpositive spectrum is mapped to zero, following +the convention of `Real.log`. -/ +noncomputable def log (A : HermitianOp E) : HermitianOp E := + A.cfc Real.log + +/-- The exponential of a self-adjoint operator. -/ +noncomputable def exp (A : HermitianOp E) : HermitianOp E := + A.cfc Real.exp + +/-- The absolute value of a self-adjoint operator, i.e. `|·|` applied to its spectrum. -/ +noncomputable def abs (A : HermitianOp E) : HermitianOp E := + A.cfc (|·|) + +/-- Conjugation of a self-adjoint operator, `A ↦ B A B⋆`. -/ +noncomputable def conj (A : HermitianOp E) (B : E →L[ℂ] E) : HermitianOp E := + ⟨B * A.op * star B, A.H.conjugate B⟩ + +@[simp] +theorem op_conj (A : HermitianOp E) (B : E →L[ℂ] E) : (A.conj B).op = B * A.op * star B := + rfl + +@[simp] +theorem conj_one (A : HermitianOp E) : A.conj 1 = A := + op_injective <| by rw [op_conj, star_one, mul_one, one_mul] + +theorem conj_conj (A : HermitianOp E) (B C : E →L[ℂ] E) : (A.conj B).conj C = A.conj (C * B) := + op_injective <| by simp only [op_conj, star_mul, mul_assoc] + +/-- Conjugation preserves nonnegativity. -/ +theorem conj_nonneg {A : HermitianOp E} (h : 0 ≤ A) (B : E →L[ℂ] E) : 0 ≤ A.conj B := by + rw [zero_le_iff, op_conj, mul_assoc] + exact (zero_le_iff.1 h).conj_adjoint B + +/-- The Hilbert–Schmidt inner product `Tr[A B]` of two self-adjoint operators. It is real +because both operators are. -/ +noncomputable instance [FiniteDimensional ℂ E] : Inner ℝ (HermitianOp E) where + inner A B := RCLike.re (LinearMap.trace ℂ E ((A.op * B.op : E →L[ℂ] E) : E →ₗ[ℂ] E)) + +theorem inner_def [FiniteDimensional ℂ E] (A B : HermitianOp E) : + (inner ℝ A B : ℝ) = RCLike.re (LinearMap.trace ℂ E ((A.op * B.op : E →L[ℂ] E) : E →ₗ[ℂ] E)) := + rfl + +/-- The kernel of a self-adjoint operator, equivalently its zero eigenspace. -/ +def ker (A : HermitianOp E) : Submodule ℂ E := + LinearMap.ker (A.op : E →ₗ[ℂ] E) + +theorem mem_ker_iff {A : HermitianOp E} {x : E} : x ∈ A.ker ↔ A.op x = 0 := + Iff.rfl + +/-- The support of a self-adjoint operator: the span of its nonzero eigenspaces. -/ +def support (A : HermitianOp E) : Submodule ℂ E := + LinearMap.range (A.op : E →ₗ[ℂ] E) + +@[simp] +theorem ker_zero : (0 : HermitianOp E).ker = ⊤ := by + simp [ker] + +@[simp] +theorem support_zero : (0 : HermitianOp E).support = ⊥ := by + simp [support] + +/-- The trace norm (Schatten 1-norm) of a self-adjoint operator: the sum of the absolute values +of its eigenvalues. -/ +noncomputable def traceNorm [FiniteDimensional ℂ E] (A : HermitianOp E) : ℝ := + A.abs.trace + +section Congr + +variable {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℂ F] [CompleteSpace F] + +/-- Transport a self-adjoint operator along a linear isometry equivalence. -/ +noncomputable def congr (A : HermitianOp E) (e : E ≃ₗᵢ[ℂ] F) : HermitianOp F := + ⟨e.conjStarAlgEquiv A.op, by + show star (e.conjStarAlgEquiv A.op) = _ + rw [← map_star, A.H.star_eq]⟩ + +@[simp] +theorem op_congr (A : HermitianOp E) (e : E ≃ₗᵢ[ℂ] F) : + (A.congr e).op = e.conjStarAlgEquiv A.op := + rfl + +/-- Transport along a linear isometry equivalence preserves nonnegativity. -/ +theorem congr_nonneg {A : HermitianOp E} (h : 0 ≤ A) (e : E ≃ₗᵢ[ℂ] F) : 0 ≤ A.congr e := + zero_le_iff.2 ((zero_le_iff.1 h).conjStarAlgEquiv e) + +/-- Transport along a linear isometry equivalence preserves the trace. -/ +@[simp] +theorem trace_congr [FiniteDimensional ℂ E] [FiniteDimensional ℂ F] (A : HermitianOp E) + (e : E ≃ₗᵢ[ℂ] F) : (A.congr e).trace = A.trace := by + have hb := stdOrthonormalBasis ℂ E + rw [trace, trace, LinearMap.trace_eq_sum_inner _ (hb.map e), + LinearMap.trace_eq_sum_inner _ hb] + refine congrArg _ (Finset.sum_congr rfl fun i _ ↦ ?_) + rw [ContinuousLinearMap.coe_coe, op_congr, OrthonormalBasis.map_apply, + LinearIsometryEquiv.conjStarAlgEquiv_apply_apply, LinearIsometryEquiv.symm_apply_apply, + e.inner_map_map] + rfl + +end Congr + +section StdBasis + +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + +/-- The matrix of a self-adjoint operator in the preferred basis. -/ +noncomputable def toMat (A : HermitianOp E) : HermitianMat ι ℂ := + ⟨StdBasis.toMat ℂ E ι A.op, (StdBasis.isHermitian_toMat_iff _).2 A.H⟩ + +/-- The self-adjoint operator with a given matrix in the preferred basis. -/ +noncomputable def ofMat (M : HermitianMat ι ℂ) : HermitianOp E := + ⟨(StdBasis.toMat ℂ E ι).symm M.mat, by + show star ((StdBasis.toMat ℂ E ι).symm M.mat) = _ + rw [← map_star, Matrix.star_eq_conjTranspose, M.H]⟩ + +@[simp] +theorem toMat_mat (A : HermitianOp E) : (toMat (ι := ι) A).mat = StdBasis.toMat ℂ E ι A.op := + rfl + +@[simp] +theorem ofMat_op (M : HermitianMat ι ℂ) : + (ofMat (E := E) M).op = (StdBasis.toMat ℂ E ι).symm M.mat := + rfl + +@[simp] +theorem toMat_ofMat (M : HermitianMat ι ℂ) : toMat (ofMat (E := E) M) = M := by + ext1 + simp + +@[simp] +theorem ofMat_toMat (A : HermitianOp E) : ofMat (toMat (ι := ι) A) = A := by + ext1 + simp + +theorem toMat_injective : Function.Injective (toMat (E := E) (ι := ι)) := + Function.LeftInverse.injective ofMat_toMat + +@[simp] theorem toMat_zero : toMat (0 : HermitianOp E) = (0 : HermitianMat ι ℂ) := by + ext1; simp + +@[simp] theorem toMat_add (A B : HermitianOp E) : + toMat (ι := ι) (A + B) = toMat A + toMat B := by + ext1; simp + +@[simp] theorem toMat_neg (A : HermitianOp E) : toMat (ι := ι) (-A) = -toMat A := by + ext1; simp + +@[simp] theorem toMat_sub (A B : HermitianOp E) : + toMat (ι := ι) (A - B) = toMat A - toMat B := by + ext1; simp + +@[simp] theorem toMat_smul (r : ℝ) (A : HermitianOp E) : + toMat (ι := ι) (r • A) = r • toMat A := by + ext1 + show StdBasis.toMat ℂ E ι ((r : ℂ) • A.op) = (r : ℂ) • StdBasis.toMat ℂ E ι A.op + exact map_smul _ _ _ + +/-- **Matrix analogue of the operator order.** An inequality of self-adjoint operators is exactly +the Loewner inequality of their matrices in the preferred basis. -/ +@[simp] +theorem toMat_le_toMat {A B : HermitianOp E} : toMat (ι := ι) A ≤ toMat B ↔ A ≤ B := by + rw [HermitianMat.le_iff, le_def, ContinuousLinearMap.le_def, + ← StdBasis.posSemidef_toMat_iff (ι := ι)] + congr! 1 + show (toMat (ι := ι) B - toMat A).mat = _ + rw [HermitianMat.mat_sub, toMat_mat, toMat_mat, ← map_sub] + +/-- The identification of self-adjoint operators with Hermitian matrices determined by the +preferred basis, as an `ℝ`-linear equivalence. -/ +@[simps apply symm_apply] +noncomputable def matEquiv : HermitianOp E ≃ₗ[ℝ] HermitianMat ι ℂ where + toFun := toMat + invFun := ofMat + left_inv := ofMat_toMat + right_inv := toMat_ofMat + map_add' := toMat_add + map_smul' := toMat_smul + +/-- **Matrix analogue of the operator trace.** -/ +@[simp] +theorem trace_toMat (A : HermitianOp E) : (toMat (ι := ι) A).trace = A.trace := by + rw [HermitianMat.trace_eq_re_trace, trace, toMat_mat, StdBasis.trace_toMat] + +/-- The spectrum of a self-adjoint operator is the spectrum of its matrix. -/ +theorem spectrum_toMat (A : HermitianOp E) : + spectrum ℝ (toMat (ι := ι) A).mat = spectrum ℝ A.op := + AlgEquiv.spectrum_eq ((StdBasis.toMat ℂ E ι).toAlgEquiv.restrictScalars ℝ) A.op + +/-- The spectrum of an operator on a space with a preferred basis is finite: it is the spectrum of +a matrix. -/ +instance finite_spectrum (A : E →L[ℂ] E) : Finite (spectrum ℝ A) := by + rw [← AlgEquiv.spectrum_eq ((StdBasis.toMat ℂ E ι).toAlgEquiv.restrictScalars ℝ) A] + infer_instance + +/-- **Matrix analogue of the operator continuous functional calculus.** -/ +@[simp] +theorem toMat_cfc (A : HermitianOp E) (f : ℝ → ℝ) : + toMat (ι := ι) (A.cfc f) = (toMat A).cfc f := by + ext1 + rw [toMat_mat, op_cfc, HermitianMat.mat_cfc, toMat_mat] + refine StarAlgHomClass.map_cfc (S := ℂ) _ f A.op + (HermitianMat.continuousOn_finite f _) ?_ A.H ((StdBasis.isHermitian_toMat_iff _).2 A.H) + exact (StdBasis.toMat ℂ E ι).toAlgEquiv.toLinearMap.continuous_of_finiteDimensional + +/-- **Matrix analogue of `HermitianOp.sqrt`.** -/ +@[simp] +theorem toMat_sqrt (A : HermitianOp E) : toMat (ι := ι) A.sqrt = (toMat A).sqrt := + toMat_cfc A _ + +/-- **Matrix analogue of `HermitianOp.rpow`.** -/ +@[simp] +theorem toMat_rpow (A : HermitianOp E) (r : ℝ) : toMat (ι := ι) (A ^ r) = (toMat A) ^ r := + toMat_cfc A _ + +/-- **Matrix analogue of `HermitianOp.log`.** -/ +@[simp] +theorem toMat_log (A : HermitianOp E) : toMat (ι := ι) A.log = (toMat A).log := + toMat_cfc A _ + +/-- **Matrix analogue of `HermitianOp.exp`.** -/ +@[simp] +theorem toMat_exp (A : HermitianOp E) : toMat (ι := ι) A.exp = (toMat A).exp := + toMat_cfc A _ + +/-- **Matrix analogue of `HermitianOp.abs`.** -/ +@[simp] +theorem toMat_abs (A : HermitianOp E) : toMat (ι := ι) A.abs = (toMat A).cfc (|·|) := + toMat_cfc A _ + +/-- **Matrix analogue of `HermitianOp.conj`.** -/ +@[simp] +theorem toMat_conj (A : HermitianOp E) (B : E →L[ℂ] E) : + toMat (ι := ι) (A.conj B) = (toMat A).conj (StdBasis.toMat ℂ E ι B) := by + ext1 + rw [toMat_mat, op_conj, HermitianMat.conj_apply_mat, map_mul, map_mul, toMat_mat, + ← Matrix.star_eq_conjTranspose, ← map_star] + +/-- **Matrix analogue of the Hilbert–Schmidt inner product.** -/ +@[simp] +theorem inner_toMat (A B : HermitianOp E) : + (inner ℝ (toMat (ι := ι) A) (toMat B) : ℝ) = inner ℝ A B := by + rw [HermitianMat.inner_def, IsMaximalSelfAdjoint.RCLike_selfadjMap, inner_def, toMat_mat, + toMat_mat, ← map_mul, StdBasis.trace_toMat] + +/-- **Matrix analogue of `HermitianOp.traceNorm`.** -/ +theorem traceNorm_toMat (A : HermitianOp E) : + A.traceNorm = Matrix.traceNorm (HermitianMat.mat (toMat (ι := ι) A)) := by + rw [Matrix.traceNorm_Hermitian_eq_sum_abs_eigenvalues (toMat (ι := ι) A).H, traceNorm, abs, + ← trace_toMat (ι := ι), toMat_cfc, ← HermitianMat.sum_eigenvalues_eq_trace] + obtain ⟨e, he⟩ := (toMat (ι := ι) A).cfc_eigenvalues (f := (|·|)) + refine Finset.sum_equiv e (by simp) fun i _ ↦ ?_ + simp only [he, Function.comp_apply] + congr! + +/-- The matrix of `A` in the preferred basis acts on `EuclideanSpace ℂ ι` as `A` does on `E`, +transported along the coordinate isometry. -/ +theorem lin_toMat_apply (A : HermitianOp E) (x : EuclideanSpace ℂ ι) : + (toMat (ι := ι) A).lin x = + (stdBasis (𝕜 := ℂ) (E := E)).repr (A.op ((stdBasis (𝕜 := ℂ) (E := E)).repr.symm x)) := by + have h : Matrix.toEuclideanCLM (𝕜 := ℂ) (n := ι) (toMat (ι := ι) A).mat = + (stdBasis (𝕜 := ℂ) (E := E)).repr.conjStarAlgEquiv A.op := by + rw [toMat_mat, StdBasis.toMat_def, StdBasis.toMatOf, StarAlgEquiv.trans_apply, + StarAlgEquiv.apply_symm_apply] + have hx : (toMat (ι := ι) A).lin x = Matrix.toEuclideanCLM (𝕜 := ℂ) (n := ι) + (toMat (ι := ι) A).mat x := by + rw [← ContinuousLinearMap.coe_coe (Matrix.toEuclideanCLM (𝕜 := ℂ) (n := ι) _), + Matrix.coe_toEuclideanCLM_eq_toEuclideanLin] + rfl + rw [hx, h, LinearIsometryEquiv.conjStarAlgEquiv_apply_apply] + +/-- **Matrix analogue of `HermitianOp.ker`**: the kernel of the matrix in the preferred basis is +the kernel of the operator, read in coordinates. -/ +theorem mem_ker_toMat_iff (A : HermitianOp E) (x : EuclideanSpace ℂ ι) : + x ∈ (toMat (ι := ι) A).ker ↔ (stdBasis (𝕜 := ℂ) (E := E)).repr.symm x ∈ A.ker := by + rw [HermitianMat.ker, LinearMap.mem_ker, mem_ker_iff] + show (toMat (ι := ι) A).lin x = 0 ↔ _ + rw [lin_toMat_apply, map_eq_zero_iff _ (stdBasis (𝕜 := ℂ) (E := E)).repr.injective] + +theorem ker_toMat_le_ker_toMat {A B : HermitianOp E} : + (toMat (ι := ι) A).ker ≤ (toMat (ι := ι) B).ker ↔ A.ker ≤ B.ker := by + constructor + · intro h y hy + have hy' : (stdBasis (𝕜 := ℂ) (E := E)).repr y ∈ (toMat (ι := ι) A).ker := by + rw [mem_ker_toMat_iff] + simpa using hy + simpa using (mem_ker_toMat_iff (ι := ι) B _).1 (h hy') + · intro h x hx + rw [mem_ker_toMat_iff] at hx ⊢ + exact h hx + +/-- **Matrix analogue of `HermitianOp.conj` by a unitary**: conjugation by the unitary matrix of +the operator in the preferred basis. -/ +@[simp] +theorem toMat_conj_unitary (A : HermitianOp E) (U : unitary (E →L[ℂ] E)) : + toMat (ι := ι) (A.conj U.val) = (toMat A).conj (StdBasis.toMatUnitary (ι := ι) U).val := + toMat_conj A U.val + +end StdBasis + +section StdBasisCongr + +variable {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℂ F] [CompleteSpace F] +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + +/-- **Matrix analogue of `HermitianOp.congr`** along an isometry that carries the preferred basis +of `E` to that of `F` up to a relabelling `σ` of the index: the matrix is relabelled along `σ`. -/ +theorem toMat_congr_of_stdBasis {κ : Type*} [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] + (A : HermitianOp E) (e : E ≃ₗᵢ[ℂ] F) (σ : ι ≃ κ) + (he : ∀ i, e (stdBasis (𝕜 := ℂ) (E := E) i) = stdBasis (𝕜 := ℂ) (E := F) (σ i)) : + toMat (ι := κ) (A.congr e) = (toMat (ι := ι) A).reindex σ := by + ext1 + rw [toMat_mat, op_congr, StdBasis.toMat_conjStarAlgEquiv_of_stdBasis _ σ he, + HermitianMat.mat_reindex, toMat_mat] + rfl + +variable [StdBasis ℂ F ι] + +/-- **Matrix analogue of `HermitianOp.congr`** along `StdBasis.equiv`: the matrix is unchanged. -/ +@[simp] +theorem toMat_congr_equiv (A : HermitianOp E) : + toMat (A.congr (StdBasis.equiv ℂ E F ι)) = toMat (ι := ι) A := by + ext1 + rw [toMat_mat, op_congr, StdBasis.toMat_conjStarAlgEquiv_equiv, toMat_mat] + +end StdBasisCongr + +section FiniteDimensional + +variable [FiniteDimensional ℂ E] {A B : HermitianOp E} + +/-- The index type of the basis `StdBasis.some ℂ E` picks out. + +Each fact in this section is basis-free, so it may be proved by choosing an arbitrary basis and +appealing to the matrix analogue; `local notation` keeps those proofs readable. -/ +local notation "ι₀" => Fin (Module.finrank ℂ E) + +theorem cfc_nonneg (A : HermitianOp E) {f : ℝ → ℝ} (hf : ∀ x, 0 ≤ f x) : 0 ≤ A.cfc f := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + rw [← toMat_le_toMat (ι := ι₀), toMat_zero, toMat_cfc] + exact (HermitianMat.cfc_nonneg_iff _ f).2 fun i ↦ hf _ + +theorem sqrt_nonneg (A : HermitianOp E) : 0 ≤ A.sqrt := + A.cfc_nonneg Real.sqrt_nonneg + +theorem abs_nonneg (A : HermitianOp E) : 0 ≤ A.abs := + A.cfc_nonneg _root_.abs_nonneg + +theorem trace_nonneg (h : 0 ≤ A) : 0 ≤ A.trace := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + rw [← trace_toMat (ι := ι₀)] + exact HermitianMat.trace_nonneg (by simpa using toMat_le_toMat.2 h) + +theorem traceNorm_nonneg (A : HermitianOp E) : 0 ≤ A.traceNorm := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + rw [traceNorm_toMat (ι := ι₀)] + exact Matrix.traceNorm_nonneg _ + +@[simp] +theorem traceNorm_neg (A : HermitianOp E) : (-A).traceNorm = A.traceNorm := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + simp only [traceNorm_toMat (ι := ι₀), toMat_neg, HermitianMat.mat_neg] + exact Matrix.traceNorm_neg _ + +theorem traceNorm_add_le (A B : HermitianOp E) : + (A + B).traceNorm ≤ A.traceNorm + B.traceNorm := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + simp only [traceNorm_toMat (ι := ι₀), toMat_add, HermitianMat.mat_add] + exact Matrix.traceNorm_add_le _ _ + +theorem traceNorm_sub_le (A B : HermitianOp E) : + (A - B).traceNorm ≤ A.traceNorm + B.traceNorm := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + simp only [traceNorm_toMat (ι := ι₀), toMat_sub, HermitianMat.mat_sub] + exact Matrix.traceNorm_sub_le _ _ + +/-- On a nonnegative operator the trace norm is just the trace. -/ +theorem traceNorm_of_nonneg (h : 0 ≤ A) : A.traceNorm = A.trace := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + have hpsd : (HermitianMat.mat (toMat (ι := ι₀) A)).PosSemidef := by + rw [← HermitianMat.zero_le_iff] + simpa using toMat_le_toMat.2 h + rw [traceNorm_toMat (ι := ι₀), ← trace_toMat (ι := ι₀), HermitianMat.trace_eq_re_trace, + ← hpsd.traceNorm_eq_trace] + simp + +section Unitary + +variable (U : unitary (E →L[ℂ] E)) + +@[simp] +theorem trace_conj_unitary (A : HermitianOp E) : (A.conj U.val).trace = A.trace := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + rw [← trace_toMat (ι := ι₀), ← trace_toMat (ι := ι₀), toMat_conj_unitary] + exact HermitianMat.trace_conj_unitary _ _ + +@[simp] +theorem conj_unitary_le_conj_unitary : A.conj U.val ≤ B.conj U.val ↔ A ≤ B := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + rw [← toMat_le_toMat (ι := ι₀), toMat_conj_unitary, toMat_conj_unitary, + HermitianMat.le_conj_unitary, toMat_le_toMat] + +@[simp] +theorem inner_conj_unitary (A B : HermitianOp E) : + (inner ℝ (A.conj U.val) (B.conj U.val) : ℝ) = inner ℝ A B := by + let _ : StdBasis ℂ E ι₀ := StdBasis.some ℂ E + rw [← inner_toMat (ι := ι₀), ← inner_toMat (ι := ι₀), toMat_conj_unitary, toMat_conj_unitary] + exact HermitianMat.inner_conj_unitary _ _ _ + +end Unitary + +end FiniteDimensional + +end HermitianOp diff --git a/QuantumInfo/ForMathlib/Isometry.lean b/QuantumInfo/ForMathlib/Isometry.lean index b45bb8581..ae853c652 100644 --- a/QuantumInfo/ForMathlib/Isometry.lean +++ b/QuantumInfo/ForMathlib/Isometry.lean @@ -522,7 +522,6 @@ theorem mulVec_sharedEigenbasisB (j : d) : PROVIDED SOLUTION This is exactly analogous to star_shared_mul_B_mul_IsDiag (which is proved below in this file), but for A instead of B. Use the same proof structure: rw isDiag_iff_diagonal_diag, apply toEuclideanLin.injective, ext with basis, simp, then use mulVec_sharedEigenbasisA (instead of mulVec_sharedEigenbasisB), sharedEigenvectorUnitary_mulVec, h_simp2 (orthogonality/unit property), and by_cases on index equality, simplifying with simp +decide. Reference the B version's proof approach for the exact tactic sequence. -/ -set_option maxHeartbeats 0 in theorem star_shared_mul_A_mul_IsDiag : IsDiag ((star (sharedEigenvectorUnitary hA hB hAB : Matrix d d 𝕜)) * A * diff --git a/QuantumInfo/ForMathlib/Majorization.lean b/QuantumInfo/ForMathlib/Majorization.lean index 9bb257922..6354eaad3 100644 --- a/QuantumInfo/ForMathlib/Majorization.lean +++ b/QuantumInfo/ForMathlib/Majorization.lean @@ -142,7 +142,6 @@ noncomputable def compoundMatrix (M : Matrix d d ℂ) (k : ℕ) : @Matrix.det (Fin k) _ _ ℂ _ (M.submatrix (fun i => S.1.orderEmbOfFin S.2 i) (fun j => T.1.orderEmbOfFin T.2 j)) -set_option maxHeartbeats 400000 in /-- **Cauchy–Binet formula** for rectangular matrices: if `A` is `m × n` and `B` is `n × m`, then `det(A * B) = ∑_S det(A[:,S]) * det(B[S,:])` where the sum is over `m`-element subsets `S` of the column/row index. -/ @@ -537,7 +536,6 @@ lemma prod_singularValues_subset_le_sorted_prod (M : Matrix d d ℂ) (k : ℕ) intro i j hij simpa [g] using congr_arg σ hij -set_option maxHeartbeats 800000 in set_option backward.isDefEq.respectTransparency false in lemma exists_subset_prod_eq_sorted_prod (M : Matrix d d ℂ) (k : ℕ) (hk : k ≤ Fintype.card d) : diff --git a/QuantumInfo/ForMathlib/Matrix.lean b/QuantumInfo/ForMathlib/Matrix.lean index 9e3d16ede..23dd3b5c0 100644 --- a/QuantumInfo/ForMathlib/Matrix.lean +++ b/QuantumInfo/ForMathlib/Matrix.lean @@ -790,7 +790,6 @@ theorem IsHermitian.cfc_eigenvalues {M : Matrix d d 𝕜} (hM : M.IsHermitian) ( simp_all only [Function.comp_apply, Equiv.apply_symm_apply, algebraMap.coe_inj] -set_option maxHeartbeats 0 in --Should be combined the above...? TODO Cleanup /-- If a Hermitian matrix A is unitarily similar to a diagonal matrix with real entries f, then the eigenvalues of A are a permutation of f. @@ -912,6 +911,184 @@ theorem PosSemidef.pow_mul {A : Matrix d d 𝕜} {x y : ℝ} (hA : A.PosSemidef) end more_cfc +section polar + +open scoped ComplexOrder MatrixOrder + +variable {n 𝕜 : Type*} [Fintype n] [DecidableEq n] [RCLike 𝕜] + +theorem toEuclideanLin_mul (M N : Matrix n n 𝕜) : + toEuclideanLin (M * N) = (toEuclideanLin M) ∘ₗ (toEuclideanLin N) := by + ext x i + simp [Matrix.mulVec_mulVec] + +theorem adjoint_toEuclideanLin_of_isHermitian {M : Matrix n n 𝕜} (hM : Mᴴ = M) : + LinearMap.adjoint M.toEuclideanLin = M.toEuclideanLin := by + rw [← toEuclideanLin_conjTranspose_eq_adjoint, hM] + +/-- The trace of `M`, as the sum of the diagonal inner products `⟪eᵢ, M eᵢ⟫`. -/ +theorem trace_eq_sum_inner (M : Matrix n n 𝕜) : M.trace = ∑ i, inner (𝕜 := 𝕜) + (EuclideanSpace.single i (1 : 𝕜)) (M.toEuclideanLin (EuclideanSpace.single i 1)) := by + rw [Matrix.trace] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [EuclideanSpace.inner_single_left] + simp [Matrix.mulVec_single] + +theorem inner_toEuclideanLin_self (A : Matrix n n 𝕜) (x : EuclideanSpace 𝕜 n) : + (inner (𝕜 := 𝕜) (A.toEuclideanLin x) (A.toEuclideanLin x)) = + inner (𝕜 := 𝕜) x ((Aᴴ * A).toEuclideanLin x) := by + rw [toEuclideanLin_mul, toEuclideanLin_conjTranspose_eq_adjoint, LinearMap.comp_apply, + LinearMap.adjoint_inner_right] + +/-- Two matrices with the same `Aᴴ * A` act with the same norms on every vector. -/ +theorem norm_toEuclideanLin_eq_of_conjTranspose_mul_self_eq {A B : Matrix n n 𝕜} + (h : Aᴴ * A = Bᴴ * B) (x : EuclideanSpace 𝕜 n) : + ‖A.toEuclideanLin x‖ = ‖B.toEuclideanLin x‖ := by + have h₂ : (inner (𝕜 := 𝕜) (A.toEuclideanLin x) (A.toEuclideanLin x)) = + inner (𝕜 := 𝕜) (B.toEuclideanLin x) (B.toEuclideanLin x) := by + rw [inner_toEuclideanLin_self, inner_toEuclideanLin_self, h] + rw [inner_self_eq_norm_sq_to_K, inner_self_eq_norm_sq_to_K] at h₂ + simpa using congrArg RCLike.re h₂ + +/-- **Polar decomposition** of a square matrix: every `A` factors as a unitary times the +positive semidefinite matrix `√(Aᴴ * A)`. The unitary is built by extending the isometry +`√(Aᴴ * A) x ↦ A x`, which is well defined since the two sides have equal kernels. -/ +theorem exists_unitary_mul_sqrt_conjTranspose_mul_self (A : Matrix n n 𝕜) : + ∃ U ∈ unitaryGroup n 𝕜, A = U * CFC.sqrt (Aᴴ * A) := by + set P := CFC.sqrt (Aᴴ * A) with hP + have hPnn : (0 : Matrix n n 𝕜) ≤ P := CFC.sqrt_nonneg _ + have hPsa : Pᴴ = P := (Matrix.nonneg_iff_posSemidef.mp hPnn).isHermitian + have hkey : Pᴴ * P = Aᴴ * A := by + rw [hPsa, hP, CFC.sqrt_mul_sqrt_self _ + (Matrix.nonneg_iff_posSemidef.mpr A.posSemidef_conjTranspose_mul_self)] + set p := P.toEuclideanLin with hp + set a := A.toEuclideanLin with ha + have hnorm : ∀ x, ‖p x‖ = ‖a x‖ := + norm_toEuclideanLin_eq_of_conjTranspose_mul_self_eq hkey + have hker : LinearMap.ker p = LinearMap.ker a := by + ext x + simp only [LinearMap.mem_ker] + rw [← norm_eq_zero, hnorm x, norm_eq_zero] + set L₀ : (LinearMap.range p) →ₗ[𝕜] EuclideanSpace 𝕜 n := + (Submodule.liftQ (LinearMap.ker p) a hker.le) ∘ₗ + ((LinearMap.quotKerEquivRange p).symm : (LinearMap.range p) →ₗ[𝕜] _) with hL₀ + have hL₀_apply : ∀ x, L₀ ⟨p x, LinearMap.mem_range_self p x⟩ = a x := by + intro x + have h₁ : (LinearMap.quotKerEquivRange p) (Submodule.Quotient.mk x) = + ⟨p x, LinearMap.mem_range_self p x⟩ := rfl + rw [hL₀, LinearMap.comp_apply, LinearEquiv.coe_coe, ← h₁, LinearEquiv.symm_apply_apply] + rfl + have hL₀_norm : ∀ y : LinearMap.range p, ‖L₀ y‖ = ‖y‖ := by + rintro ⟨y, x, rfl⟩ + rw [hL₀_apply x] + exact (hnorm x).symm + set L : (LinearMap.range p) →ₗᵢ[𝕜] EuclideanSpace 𝕜 n := ⟨L₀, hL₀_norm⟩ with hL + set u := L.extend with hu + have hu_apply : ∀ x, u (p x) = a x := by + intro x + rw [hu, ← hL₀_apply x] + exact L.extend_apply ⟨p x, LinearMap.mem_range_self p x⟩ + refine ⟨toEuclideanLin.symm u.toLinearMap, ?_, ?_⟩ + · rw [Matrix.mem_unitaryGroup_iff', star_eq_conjTranspose] + apply toEuclideanLin.injective + rw [toEuclideanLin_mul, toEuclideanLin_conjTranspose_eq_adjoint, toEuclideanLin_one, + LinearEquiv.apply_symm_apply] + refine LinearMap.ext fun x => ?_ + apply ext_inner_left 𝕜 + intro v + rw [LinearMap.comp_apply, LinearMap.adjoint_inner_right] + exact u.inner_map_map v x + · apply toEuclideanLin.injective + rw [toEuclideanLin_mul, LinearEquiv.apply_symm_apply] + exact LinearMap.ext fun x => (hu_apply x).symm + +end polar + +section hilbertSchmidt + +variable {n 𝕜 : Type*} [Fintype n] [RCLike 𝕜] + +/-- A square matrix viewed as a vector in Euclidean space indexed by pairs, so that the Euclidean +inner product is the Hilbert–Schmidt inner product `Tr[AᴴB]`. -/ +def toEuclideanHS (A : Matrix n n 𝕜) : EuclideanSpace 𝕜 (n × n) := + WithLp.toLp 2 fun p ↦ A p.1 p.2 + +omit [Fintype n] [RCLike 𝕜] in +theorem toEuclideanHS_injective : + Function.Injective (toEuclideanHS (n := n) (𝕜 := 𝕜)) := by + intro A B h + ext i j + exact congrFun congr(WithLp.ofLp $h) (i, j) + +/-- The Euclidean inner product of `toEuclideanHS` is the Hilbert–Schmidt inner product. -/ +theorem inner_toEuclideanHS (A B : Matrix n n 𝕜) : + inner 𝕜 (toEuclideanHS A) (toEuclideanHS B) = (Aᴴ * B).trace := by + simp only [toEuclideanHS, PiLp.inner_apply, RCLike.inner_apply, + Matrix.trace, Matrix.diag_apply, Matrix.mul_apply, Matrix.conjTranspose_apply, + Fintype.sum_prod_type] + rw [Finset.sum_comm] + exact Finset.sum_congr rfl fun i _ ↦ Finset.sum_congr rfl fun j _ ↦ mul_comm _ _ + +/-- The squared Hilbert–Schmidt norm of `A` is `Tr[AᴴA]`. -/ +theorem norm_toEuclideanHS_sq (A : Matrix n n 𝕜) : + ‖toEuclideanHS A‖ ^ 2 = RCLike.re (Aᴴ * A).trace := by + rw [← inner_toEuclideanHS A A, inner_self_eq_norm_sq_to_K, ← RCLike.ofReal_pow, + RCLike.ofReal_re] + +/-- **Cauchy–Schwarz for the Hilbert–Schmidt inner product**: `Re Tr[AᴴB]` is at most the +product of the Hilbert–Schmidt norms `√(Tr[AᴴA])` and `√(Tr[BᴴB])`. -/ +theorem re_trace_conjTranspose_mul_le (A B : Matrix n n 𝕜) : + RCLike.re (Aᴴ * B).trace ≤ + Real.sqrt (RCLike.re (Aᴴ * A).trace) * Real.sqrt (RCLike.re (Bᴴ * B).trace) := by + rw [← inner_toEuclideanHS, ← norm_toEuclideanHS_sq, ← norm_toEuclideanHS_sq, + Real.sqrt_sq (norm_nonneg _), Real.sqrt_sq (norm_nonneg _)] + exact re_inner_le_norm _ _ + +/-- Equality case of `re_trace_conjTranspose_mul_le` for matrices of unit Hilbert–Schmidt norm. -/ +theorem eq_of_re_trace_conjTranspose_mul_eq_one {A B : Matrix n n 𝕜} + (hA : RCLike.re (Aᴴ * A).trace = 1) (hB : RCLike.re (Bᴴ * B).trace = 1) + (h : RCLike.re (Aᴴ * B).trace = 1) : A = B := by + set x := toEuclideanHS A with hx + set y := toEuclideanHS B with hy + have hxn : ‖x‖ = 1 := by + have h' := norm_toEuclideanHS_sq A + rw [hA, ← hx] at h' + nlinarith [norm_nonneg x] + have hyn : ‖y‖ = 1 := by + have h' := norm_toEuclideanHS_sq B + rw [hB, ← hy] at h' + nlinarith [norm_nonneg y] + have hxy : inner 𝕜 x y = 1 := by + have h1 : RCLike.re (inner 𝕜 x y) = 1 := by rw [hx, hy, inner_toEuclideanHS]; exact h + have h2 : ‖inner 𝕜 x y‖ ≤ 1 := by + simpa [hxn, hyn] using norm_inner_le_norm (𝕜 := 𝕜) x y + have h3 : ‖inner 𝕜 x y‖ ^ 2 = + RCLike.re (inner 𝕜 x y) * RCLike.re (inner 𝕜 x y) + + RCLike.im (inner 𝕜 x y) * RCLike.im (inner 𝕜 x y) := + RCLike.norm_sq_eq_def + apply RCLike.ext + · simpa using h1 + · have : RCLike.im (inner 𝕜 x y) = 0 := by nlinarith [norm_nonneg (inner 𝕜 x y)] + simpa using this + apply toEuclideanHS_injective + have := (inner_eq_norm_mul_iff (𝕜 := 𝕜) (x := x) (y := y)).mp (by simp [hxy, hxn, hyn]) + simpa [hxn, hyn] using this + +/-- A matrix with vanishing Hilbert–Schmidt norm is zero. -/ +theorem eq_zero_of_re_trace_conjTranspose_mul_self_eq_zero {X : Matrix n n 𝕜} + (h : RCLike.re (Xᴴ * X).trace = 0) : X = 0 := by + have h0 : ‖toEuclideanHS X‖ = 0 := by + have h1 := norm_toEuclideanHS_sq X + rw [h] at h1 + nlinarith [norm_nonneg (toEuclideanHS X)] + apply toEuclideanHS_injective + rw [norm_eq_zero] at h0 + rw [h0] + ext p + simp [toEuclideanHS] + +end hilbertSchmidt + section subm variable {α : Type*} [AddCommMonoid α] @@ -928,7 +1105,6 @@ end subm section spectrum_kron --This is really really ugly, and already *after* trying to clean it up a bit. -set_option maxHeartbeats 7200000 open Kronecker open scoped Pointwise @@ -1075,7 +1251,6 @@ private lemma spectrum_prod_le {d d₂ : Type*} simp only [RCLike.algebraMap_eq_ofReal] at h₁ exact_mod_cast h₁ -set_option maxHeartbeats 800000 open Kronecker in open scoped Pointwise in theorem spectrum_prod {d d₂ : Type*} @@ -1513,13 +1688,65 @@ theorem unitaryGroup_row_norm [Fintype n] (U : Matrix.unitaryGroup n ℂ) (i : n simpa [Matrix.mul_apply, Complex.sq_norm, Complex.normSq_eq_conj_mul_self] using congr($(U.prop.left) i i) +/-- The coordinates of a matrix in `Matrix.stdBasis` are just its entries. -/ +@[simp] +theorem stdBasis_repr_apply {m n R : Type*} [Fintype m] [Fintype n] [CommSemiring R] + (Y : Matrix m n R) (p : m × n) : (Matrix.stdBasis R m n).repr Y p = Y p.1 p.2 := by + simp [Matrix.stdBasis] + section finprod -variable {ι : Type*} {d : ι → Type*} [fι : Fintype ι] +variable {ι : Type*} {d d' d'' : ι → Type*} [fι : Fintype ι] variable {R : Type*} -def piProd [CommMonoid R] (A : ∀ i, Matrix (d i) (d i) R) : Matrix (∀ i, d i) (∀ i, d i) R := - Matrix.of (fun j k : (∀ i, d i) ↦ ∏ i, A i (j i) (k i)) +/-- The Kronecker product of an `ι`-indexed family of matrices, as a matrix indexed by the +corresponding Pi types. -/ +def piProd [CommMonoid R] (A : ∀ i, Matrix (d i) (d' i) R) : Matrix (∀ i, d i) (∀ i, d' i) R := + Matrix.of (fun j k ↦ ∏ i, A i (j i) (k i)) + +theorem piProd_apply [CommMonoid R] (A : ∀ i, Matrix (d i) (d' i) R) (j : ∀ i, d i) + (k : ∀ i, d' i) : piProd A j k = ∏ i, A i (j i) (k i) := + rfl + +theorem piProd_mul [DecidableEq ι] [CommSemiring R] [∀ i, Fintype (d' i)] + (A : ∀ i, Matrix (d i) (d' i) R) (B : ∀ i, Matrix (d' i) (d'' i) R) : + piProd A * piProd B = piProd (fun i ↦ A i * B i) := by + ext j k + simp only [Matrix.mul_apply, piProd, Matrix.of_apply] + rw [Finset.prod_univ_sum] + exact (Fintype.sum_equiv (Equiv.refl _) _ _ fun x ↦ by simp [Finset.prod_mul_distrib]).symm + +theorem conjTranspose_piProd [CommSemiring R] [StarRing R] (A : ∀ i, Matrix (d i) (d' i) R) : + (piProd A)ᴴ = piProd (fun i ↦ (A i)ᴴ) := by + ext j k + simp [piProd, star_prod] + +/-- A Kronecker product of matrix units is the corresponding matrix unit. -/ +theorem piProd_single [CommSemiring R] [∀ i, DecidableEq (d i)] [∀ i, DecidableEq (d' i)] + (a : ∀ i, d i) (b : ∀ i, d' i) : + piProd (fun i ↦ Matrix.single (a i) (b i) (1 : R)) = Matrix.single a b 1 := by + classical + ext j k + simp only [piProd, Matrix.of_apply, Matrix.single_apply] + by_cases h : a = j ∧ b = k + · obtain ⟨rfl, rfl⟩ := h + simp + · rw [if_neg h] + obtain ⟨i, hi⟩ : ∃ i, ¬(a i = j i ∧ b i = k i) := by + by_contra hc + push Not at hc + exact h ⟨funext fun i ↦ (hc i).1, funext fun i ↦ (hc i).2⟩ + exact Finset.prod_eq_zero (Finset.mem_univ i) (if_neg hi) + +/-- Kronecker products distribute over sums in each factor: summing the Kronecker products over +all tuples of indices is the Kronecker product of the summed factors. -/ +theorem sum_piProd [DecidableEq ι] [CommSemiring R] {κ : ι → Type*} [∀ i, Fintype (κ i)] + (A : ∀ i, κ i → Matrix (d i) (d' i) R) : + ∑ k : (∀ i, κ i), piProd (fun i ↦ A i (k i)) = piProd (fun i ↦ ∑ x, A i x) := by + ext j l + simp only [Matrix.sum_apply, piProd, Matrix.of_apply] + rw [Finset.prod_univ_sum] + rfl variable {A : ∀ i, Matrix (d i) (d i) R} @@ -1583,6 +1810,28 @@ theorem submatrix_eq_mul_mul {d d₂ d₃ R : Type*} [DecidableEq d] [Fintype d] rw [show id = Equiv.refl d by rfl, Matrix.mul_submatrix_one, Matrix.one_submatrix_mul] simp +section stack + +variable {k n R : Type*} + +/-- Stack a family of matrices, all with the same shape, on top of each other to make one tall +matrix, with rows indexed by `κ × k`. -/ +def stack {κ : Type*} (A : κ → Matrix k n R) : Matrix (κ × k) n R := + Matrix.of fun p j ↦ A p.1 p.2 j + +@[simp] +theorem stack_apply {κ : Type*} (A : κ → Matrix k n R) (p : κ × k) (j : n) : + stack A p j = A p.1 p.2 j := + rfl + +/-- The Gram matrix of a stack of matrices is the sum of their Gram matrices. -/ +theorem conjTranspose_stack_mul_stack {κ : Type*} [Semiring R] [StarRing R] [Fintype κ] [Fintype k] + (A B : κ → Matrix k n R) : (stack A)ᴴ * (stack B) = ∑ i, (A i)ᴴ * (B i) := by + ext j j' + simp [Matrix.mul_apply, Fintype.sum_prod_type, Matrix.sum_apply, Finset.sum_comm (γ := κ)] + +end stack + open scoped Matrix Kronecker in /-- The conjugate of a Kronecker product by a Kronecker product is the Kronecker product of the conjugates (for matrices). diff --git a/QuantumInfo/ForMathlib/MatrixNorm/TraceNorm.lean b/QuantumInfo/ForMathlib/MatrixNorm/TraceNorm.lean index ec2ec3f49..e392b2cda 100644 --- a/QuantumInfo/ForMathlib/MatrixNorm/TraceNorm.lean +++ b/QuantumInfo/ForMathlib/MatrixNorm/TraceNorm.lean @@ -44,6 +44,18 @@ theorem traceNorm_neg (A : Matrix m n R) : traceNorm (-A) = traceNorm A := by open MatrixOrder Isometry +/-- Conjugating a PSD matrix by an isometry commutes with taking the square root: since +`uᴴ * u = 1`, the conjugate `u * √A * uᴴ` is PSD and squares to `u * A * uᴴ`. -/ +lemma cfc_sqrt_isometry_conj {A : Matrix n n R} (hA : 0 ≤ A) {u : Matrix m n R} + (hu₁ : u.Isometry) : CFC.sqrt (u * A * uᴴ) = u * CFC.sqrt A * uᴴ := by + have h_conj (B : Matrix n n R) (hB : 0 ≤ B) : 0 ≤ u * B * uᴴ := by + rw [Matrix.nonneg_iff_posSemidef] at hB ⊢ + exact hB.mul_mul_conjTranspose_same u + apply (CFC.sqrt_eq_iff _ _ (h_conj A hA) (h_conj _ (CFC.sqrt_nonneg A))).mpr + rw [Matrix.mul_assoc, ← Matrix.mul_assoc uᴴ, ← Matrix.mul_assoc uᴴ] + simp [show uᴴ * u = 1 from hu₁] + rw [← Matrix.mul_assoc, Matrix.mul_assoc u, CFC.sqrt_mul_sqrt_self A hA] + /-- The trace norm is invariant under left multiplication by an isometry. -/ theorem traceNorm_isometry_left [Fintype k] {A : Matrix n m R} {u : Matrix k n R} (hu₁ : u.Isometry) : traceNorm (u * A) = traceNorm A := by @@ -61,15 +73,7 @@ theorem traceNorm_isometry_right [Fintype k] {A : Matrix n m R} {u : Matrix k m simp [← Matrix.mul_assoc] nth_rw 2 [Matrix.mul_assoc] have hA := (Matrix.posSemidef_conjTranspose_mul_self A).nonneg - have hsqrt : CFC.sqrt (u * (Aᴴ * A) * uᴴ) = u * CFC.sqrt (Aᴴ * A) * uᴴ := by - have h_conj (B : Matrix m m R) (hB : 0 ≤ B) : 0 ≤ u * B * uᴴ := by - rw [Matrix.nonneg_iff_posSemidef] at hB ⊢ - exact hB.mul_mul_conjTranspose_same u - apply (CFC.sqrt_eq_iff _ _ (h_conj _ hA) (h_conj _ (CFC.sqrt_nonneg _))).mpr - rw [Matrix.mul_assoc, ← Matrix.mul_assoc uᴴ, ← Matrix.mul_assoc uᴴ] - simp [show uᴴ * u = 1 from hu₁] - rw [← Matrix.mul_assoc, Matrix.mul_assoc u, CFC.sqrt_mul_sqrt_self _ hA] - rw [hsqrt, Matrix.trace_mul_comm, ← Matrix.mul_assoc] + rw [cfc_sqrt_isometry_conj hA hu₁, Matrix.trace_mul_comm, ← Matrix.mul_assoc] simp [show uᴴ * u = 1 by exact hu₁] private theorem traceNorm_isometry_conj {A : Matrix n n R} {u : Matrix m n R} @@ -166,6 +170,88 @@ theorem traceNorm_smul (A : Matrix m n R) (c : R) : (c • A).traceNorm = ‖c · exact le_trans ( by norm_num ) ( smul_le_smul_of_nonneg_left ( show 0 ≤ CFC.sqrt M from by exact (CFC.sqrt_nonneg M) ) ( norm_nonneg c ) ); +/-- For square matrices, the trace norm is the largest value of `Re Tr[U * A]` over unitaries `U`. +The maximum is attained at `U = W⁻¹`, where `A = W√(AᴴA)` is the polar decomposition. -/ +theorem traceNorm_eq_max_re_tr_U (A : Matrix n n R) : + IsGreatest {x : ℝ | ∃ U : unitaryGroup n R, RCLike.re (U.1 * A).trace = x} A.traceNorm := by + obtain ⟨W, hW, hA⟩ := A.exists_unitary_mul_sqrt_conjTranspose_mul_self + set P := CFC.sqrt (Aᴴ * A) with hP + have hPnn : (0 : Matrix n n R) ≤ P := CFC.sqrt_nonneg _ + constructor + · refine ⟨⟨star W, Unitary.star_mem hW⟩, ?_⟩ + show RCLike.re (star W * A).trace = _ + simp only [traceNorm, ← hP] + congr 1 + rw [hA, ← mul_assoc, mem_unitaryGroup_iff'.mp hW, one_mul] + · rintro x ⟨U, rfl⟩ + -- Writing `√(AᴴA) = S * S` and cycling the trace, `Tr[UA] = ∑ᵢ ⟪S eᵢ, U W S eᵢ⟫`, which by + -- Cauchy-Schwarz is at most `∑ᵢ ‖S eᵢ‖² = Tr[√(AᴴA)]`. + set S := CFC.sqrt P with hS + have hSnn : (0 : Matrix n n R) ≤ S := CFC.sqrt_nonneg _ + have hSS : S * S = P := CFC.sqrt_mul_sqrt_self P hPnn + have hSH : Sᴴ = S := (Matrix.nonneg_iff_posSemidef.mp hSnn).isHermitian + have hs_adj := adjoint_toEuclideanLin_of_isHermitian hSH + set V := (U : Matrix n n R) * W with hV + have hVU : Vᴴ * V = 1 := by + rw [← star_eq_conjTranspose] + exact mem_unitaryGroup_iff'.mp (mul_mem U.2 hW) + have hViso : ∀ y : EuclideanSpace R n, ‖V.toEuclideanLin y‖ = ‖y‖ := by + intro y + have := norm_toEuclideanLin_eq_of_conjTranspose_mul_self_eq + (A := V) (B := (1 : Matrix n n R)) (by simp [hVU]) y + simpa [toEuclideanLin_one] using this + set v : n → EuclideanSpace R n := fun i => S.toEuclideanLin (EuclideanSpace.single i 1) with hv + have htr : (U.1 * A).trace = (S * V * S).trace := by + rw [hA, hV, ← hSS] + rw [show U.1 * (W * (S * S)) = (U.1 * W * S) * S by simp [mul_assoc]] + rw [Matrix.trace_mul_comm] + congr 1 + simp [mul_assoc] + have hsum : (S * V * S).trace = ∑ i, inner (𝕜 := R) (v i) (V.toEuclideanLin (v i)) := by + rw [trace_eq_sum_inner] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [toEuclideanLin_mul, toEuclideanLin_mul, LinearMap.comp_apply, LinearMap.comp_apply, + ← LinearMap.adjoint_inner_left, hs_adj] + have hsumP : (∑ i, ‖v i‖ ^ 2) = A.traceNorm := by + rw [traceNorm, ← hP, ← hSS, trace_eq_sum_inner, map_sum] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [toEuclideanLin_mul, LinearMap.comp_apply, ← LinearMap.adjoint_inner_left, hs_adj, + inner_self_eq_norm_sq_to_K] + simp [hv] + rw [htr, hsum, map_sum, ← hsumP] + refine Finset.sum_le_sum fun i _ => ?_ + calc RCLike.re (inner (𝕜 := R) (v i) (V.toEuclideanLin (v i))) + ≤ ‖v i‖ * ‖V.toEuclideanLin (v i)‖ := re_inner_le_norm _ _ + _ = ‖v i‖ ^ 2 := by rw [hViso]; ring + +/-- The trace norm is invariant under the conjugate transpose (for square matrices). -/ +theorem traceNorm_conjTranspose (A : Matrix n n R) : Aᴴ.traceNorm = A.traceNorm := by + have key : ∀ U : Matrix n n R, RCLike.re (U * Aᴴ).trace = RCLike.re (Uᴴ * A).trace := by + intro U + rw [show U * Aᴴ = (A * Uᴴ)ᴴ by simp, Matrix.trace_conjTranspose, Matrix.trace_mul_comm] + simp + have hset : {x : ℝ | ∃ U : unitaryGroup n R, RCLike.re (U.1 * Aᴴ).trace = x} + = {x : ℝ | ∃ U : unitaryGroup n R, RCLike.re (U.1 * A).trace = x} := by + ext x + constructor + · rintro ⟨U, rfl⟩ + exact ⟨⟨star U.1, Unitary.star_mem U.2⟩, (key U.1).symm⟩ + · rintro ⟨U, rfl⟩ + refine ⟨⟨star U.1, Unitary.star_mem U.2⟩, ?_⟩ + rw [key] + simp [star_eq_conjTranspose] + exact IsGreatest.unique (hset ▸ traceNorm_eq_max_re_tr_U Aᴴ) (traceNorm_eq_max_re_tr_U A) + +/-- The maximizing unitary of `traceNorm_eq_max_re_tr_U`, stated without reference to +`Matrix.unitaryGroup` or to the identity matrix, so that it can be used in contexts carrying a +different `DecidableEq` instance than the classical one in scope here. -/ +theorem exists_unitary_re_trace_eq_traceNorm (A : Matrix n n R) : + ∃ U : Matrix n n R, (∀ X : Matrix n n R, Uᴴ * (U * X) = X) ∧ + RCLike.re (U * A).trace = A.traceNorm := by + obtain ⟨U, hU⟩ := (traceNorm_eq_max_re_tr_U A).left + refine ⟨U.1, fun X ↦ ?_, hU⟩ + rw [← mul_assoc, ← star_eq_conjTranspose, mem_unitaryGroup_iff'.mp U.2, one_mul] + section complexTraceNorm variable [DecidableEq n] @@ -332,29 +418,6 @@ theorem traceNorm_mul_le_opNorm_traceNorm [DecidableEq n] (A B : Matrix n n ℂ) _ = ‖A‖ * B.traceNorm := by rw [traceNorm_eq_sum_singularValuesSorted] -omit [DecidableEq n] in -/-- The trace norm is invariant under conjugate transpose. -/ -theorem traceNorm_conjTranspose (A : Matrix n n ℂ) : - Aᴴ.traceNorm = A.traceNorm := by - classical - let : DecidableEq n := Classical.decEq n - have hH : (Aᴴ * A).IsHermitian := Matrix.isHermitian_conjTranspose_mul_self A - obtain ⟨V, W, hA⟩ := Matrix.exists_svd_sqrt_eigenvalues A - set D : Matrix n n ℂ := - Matrix.diagonal (fun i => (Real.sqrt (hH.eigenvalues i) : ℂ)) - have hDH : Dᴴ = D := by simp [D, Matrix.diagonal_conjTranspose] - calc - Aᴴ.traceNorm = (W.val * D * V.valᴴ).traceNorm := by - rw [hA, Matrix.conjTranspose_mul, Matrix.conjTranspose_mul, - Matrix.conjTranspose_conjTranspose, hDH, ← Matrix.mul_assoc] - _ = D.traceNorm := traceNorm_isometry_conj - ((Matrix.mem_unitaryGroup_iff_isometry W.val).mp W.prop).1 - ((Matrix.mem_unitaryGroup_iff_isometry V.val).mp V.prop).1 - _ = (V.val * D * W.valᴴ).traceNorm := (traceNorm_isometry_conj - ((Matrix.mem_unitaryGroup_iff_isometry V.val).mp V.prop).1 - ((Matrix.mem_unitaryGroup_iff_isometry W.val).mp W.prop).1).symm - _ = A.traceNorm := by rw [hA] - omit [Fintype m] [RCLike R] [DecidableEq n] in /-- The trace norm is bounded by trace norm on the left times operator norm on the right. -/ theorem traceNorm_mul_le_traceNorm_opNorm [DecidableEq n] (A B : Matrix n n ℂ) : @@ -409,57 +472,10 @@ theorem abs_trace_le_traceNorm (A : Matrix n n ℂ) : end complexTraceNorm -/-- For square complex matrices, the trace norm is the maximum of `re (Tr[U * A])` -over unitaries `U`. -/ -theorem traceNorm_eq_max_re_tr_U (A : Matrix n n ℂ) : - IsGreatest {x : ℝ | ∃ U : unitaryGroup n ℂ, Complex.re ((U.val * A).trace) = x} A.traceNorm := by - classical - let hH : (Aᴴ * A).IsHermitian := by - simpa using (Matrix.isHermitian_mul_conjTranspose_self A.conjTranspose) - obtain ⟨V, W, hA⟩ : - ∃ V W : Matrix.unitaryGroup n ℂ, - A = V.val * Matrix.diagonal (fun i => (Real.sqrt (hH.eigenvalues i) : ℂ)) * W.valᴴ := by - simpa [hH] using exists_svd_sqrt_eigenvalues A - have htraceNorm : A.traceNorm = ∑ i, Real.sqrt (hH.eigenvalues i) := by - simpa [hH] using traceNorm_eq_sum_sqrt_eigenvalues A - set D : Matrix n n ℂ := Matrix.diagonal (fun i => (Real.sqrt (hH.eigenvalues i) : ℂ)) - have hVu : V.valᴴ * V.val = 1 := (Matrix.mem_unitaryGroup_iff_isometry V.val).mp V.prop |>.1 - have hWu : W.valᴴ * W.val = 1 := (Matrix.mem_unitaryGroup_iff_isometry W.val).mp W.prop |>.1 - refine ⟨⟨W * star V, ?_⟩, ?_⟩ - · calc Complex.re (((W * star V).val * A).trace) - = Complex.re (D.trace) := by - rw [hA]; congr 1 - change (W.val * V.valᴴ * (V.val * D * W.valᴴ)).trace = D.trace - simp [Matrix.mul_assoc, hVu, Matrix.trace_mul_comm, hWu] - _ = A.traceNorm := by simp [D, Matrix.trace, htraceNorm] - · rintro _ ⟨U, rfl⟩ - set C : Matrix.unitaryGroup n ℂ := star W * U * V - rw [show Complex.re ((U.val * A).trace) = - ∑ i, Real.sqrt (hH.eigenvalues i) * Complex.re (C.val i i) by - conv_lhs => rw [hA] - have h1 : (U.val * (V.val * D * W.valᴴ)).trace = (C.val * D).trace := by - change _ = (W.valᴴ * U.val * V.val * D).trace - rw [show (U.val * (V.val * D * W.valᴴ)).trace = - (((U.val * V.val) * D) * W.valᴴ).trace by simp [Matrix.mul_assoc], - Matrix.trace_mul_comm _ W.valᴴ] - simp [Matrix.mul_assoc] - rw [h1] - simp [D, Matrix.trace, Matrix.mul_apply, Matrix.diagonal, Complex.mul_re, mul_comm], - htraceNorm] - have hdiag_le : ∀ i, Complex.re (C.val i i) ≤ 1 := fun i => - (Complex.re_le_norm _).trans (by - have hsq : ‖C.val i i‖ ^ 2 ≤ 1 := by - linarith [(Finset.single_le_sum (f := fun j => ‖C.val i j‖ ^ 2) - (fun j _ => by positivity) (Finset.mem_univ i)).trans_eq - (Matrix.unitary_row_sum_norm_sq C.val (Matrix.mem_unitaryGroup_iff.mp C.prop) i)] - nlinarith [norm_nonneg (C.val i i), hsq]) - exact Finset.sum_le_sum fun i _ => by - nlinarith [hdiag_le i, Real.sqrt_nonneg (hH.eigenvalues i)] - /-- The trace norm satisfies the triangle inequality for square complex matrices. -/ theorem traceNorm_add_le (A B : Matrix n n ℂ) : (A + B).traceNorm ≤ A.traceNorm + B.traceNorm := by obtain ⟨Uab, h₁⟩ := (traceNorm_eq_max_re_tr_U (A + B)).left - rw [Matrix.mul_add, Matrix.trace_add, Complex.add_re] at h₁ + rw [Matrix.mul_add, Matrix.trace_add, map_add] at h₁ obtain h₂ := (traceNorm_eq_max_re_tr_U A).right obtain h₃ := (traceNorm_eq_max_re_tr_U B).right simp only [upperBounds, Set.mem_ofPred_eq] at h₂ h₃ @@ -473,6 +489,67 @@ theorem traceNorm_add_le (A B : Matrix n n ℂ) : (A + B).traceNorm ≤ A.traceN simpa [add_comm] using add_le_add_left (h₃ (a := RCLike.re ((Uab.1 * B).trace)) ⟨Uab, rfl⟩) (traceNorm A) +/-- The trace norm of a difference is bounded by the sum of trace norms. -/ +theorem traceNorm_sub_le (A B : Matrix n n ℂ) : (A - B).traceNorm ≤ A.traceNorm + B.traceNorm := by + rw [sub_eq_add_neg A B, ← traceNorm_neg B] + exact traceNorm_add_le A (-B) + +/-- **Cauchy-Schwarz for the trace pairing**: for square matrices, `Re Tr[Pᴴ Q]` is at most the +trace norm of `√(QᴴQ) √(PᴴP)`. Writing the polar decompositions `P = W₁ √(PᴴP)` and +`Q = W₂ √(QᴴQ)` turns the left side into `Re Tr[(W₁ᴴW₂) √(QᴴQ) √(PᴴP)]`, and the right side is the +largest such value by `traceNorm_eq_max_re_tr_U`. -/ +theorem re_trace_conjTranspose_mul_le_traceNorm (P Q : Matrix n n ℂ) : + RCLike.re (Pᴴ * Q).trace ≤ (CFC.sqrt (Qᴴ * Q) * CFC.sqrt (Pᴴ * P)).traceNorm := by + obtain ⟨W₁, hW₁, hP⟩ := P.exists_unitary_mul_sqrt_conjTranspose_mul_self + obtain ⟨W₂, hW₂, hQ⟩ := Q.exists_unitary_mul_sqrt_conjTranspose_mul_self + set S := CFC.sqrt (Pᴴ * P) with hS + set T := CFC.sqrt (Qᴴ * Q) with hT + have hSH : Sᴴ = S := (Matrix.nonneg_iff_posSemidef.mp (hS ▸ CFC.sqrt_nonneg _)).isHermitian + have key : (Pᴴ * Q).trace = ((star W₁ * W₂) * (T * S)).trace := by + rw [hP, hQ, Matrix.conjTranspose_mul, hSH, star_eq_conjTranspose, + show S * W₁ᴴ * (W₂ * T) = S * (W₁ᴴ * W₂ * T) by simp [Matrix.mul_assoc], + Matrix.trace_mul_comm] + congr 1 + simp [Matrix.mul_assoc] + rw [key] + exact (traceNorm_eq_max_re_tr_U (T * S)).2 + ⟨⟨star W₁ * W₂, mul_mem (Unitary.star_mem hW₁) hW₂⟩, rfl⟩ + +/-- The rectangular version of `re_trace_conjTranspose_mul_le_traceNorm`, obtained by padding `P` +and `Q` out to square matrices along an isometry `E`. -/ +theorem re_trace_conjTranspose_mul_le_traceNorm_of_isometry (P Q : Matrix m n ℂ) + {E : Matrix m n ℂ} (hE : E.Isometry) : + RCLike.re (Pᴴ * Q).trace ≤ (CFC.sqrt (Qᴴ * Q) * CFC.sqrt (Pᴴ * P)).traceNorm := by + have hE' : Eᴴ * E = 1 := hE + have hgram (X Y : Matrix m n ℂ) : (X * Eᴴ)ᴴ * (Y * Eᴴ) = E * (Xᴴ * Y) * Eᴴ := by + simp [Matrix.conjTranspose_mul, Matrix.mul_assoc] + have htr : ((P * Eᴴ)ᴴ * (Q * Eᴴ)).trace = (Pᴴ * Q).trace := by + rw [hgram, Matrix.trace_mul_comm, ← Matrix.mul_assoc, hE', Matrix.one_mul] + have h := re_trace_conjTranspose_mul_le_traceNorm (P * Eᴴ) (Q * Eᴴ) + rw [htr, hgram, hgram, + cfc_sqrt_isometry_conj (Matrix.posSemidef_conjTranspose_mul_self Q).nonneg hE, + cfc_sqrt_isometry_conj (Matrix.posSemidef_conjTranspose_mul_self P).nonneg hE, + show E * CFC.sqrt (Qᴴ * Q) * Eᴴ * (E * CFC.sqrt (Pᴴ * P) * Eᴴ) + = E * (CFC.sqrt (Qᴴ * Q) * CFC.sqrt (Pᴴ * P)) * Eᴴ by + simp only [Matrix.mul_assoc] + rw [← Matrix.mul_assoc Eᴴ, hE', Matrix.one_mul], + traceNorm_isometry_conj hE hE] at h + exact h + +/-- An isometry `Matrix m n R` exists whenever `n` embeds into `m`. -/ +theorem exists_isometry_of_card_le (h : Fintype.card n ≤ Fintype.card m) : + ∃ E : Matrix m n R, E.Isometry := by + obtain ⟨f⟩ := Function.Embedding.nonempty_of_card_le h + exact ⟨Matrix.submatrix 1 id f, Matrix.submatrix_one_isometry Function.bijective_id f.injective⟩ + +/-- The rectangular version of `re_trace_conjTranspose_mul_le_traceNorm`, for matrices with at +least as many rows as columns. -/ +theorem re_trace_conjTranspose_mul_le_traceNorm' (P Q : Matrix m n ℂ) + (h : Fintype.card n ≤ Fintype.card m) : + RCLike.re (Pᴴ * Q).trace ≤ (CFC.sqrt (Qᴴ * Q) * CFC.sqrt (Pᴴ * P)).traceNorm := by + obtain ⟨E, hE⟩ := exists_isometry_of_card_le (R := ℂ) h + exact re_trace_conjTranspose_mul_le_traceNorm_of_isometry P Q hE + /-- A positive semidefinite matrix has trace norm equal to its trace. -/ theorem PosSemidef.traceNorm_eq_trace {A : Matrix m m R} (hA : A.PosSemidef) : A.traceNorm = A.trace := by diff --git a/QuantumInfo/ForMathlib/PartialTrace.lean b/QuantumInfo/ForMathlib/PartialTrace.lean new file mode 100644 index 000000000..e38858ba7 --- /dev/null +++ b/QuantumInfo/ForMathlib/PartialTrace.lean @@ -0,0 +1,409 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import QuantumInfo.ForMathlib.HermitianMat.Trace +public import QuantumInfo.ForMathlib.HermitianOp + +public import Mathlib.LinearAlgebra.Contraction + +/-! +# Partial traces + +Mathlib knows the trace of an operator, but not the *partial* trace: the map that takes an operator +on `E ⊗[𝕜] F` and traces out one of the two factors. This file defines it. + +The partial trace over the left factor of `A : (E ⊗[𝕜] F) →L[𝕜] E ⊗[𝕜] F` is characterised by + + `⟪y, A.traceLeft y'⟫ = ∑ i, ⟪b i ⊗ₜ y, A (b i ⊗ₜ y')⟫` + +for any orthonormal basis `b` of `E`. Taking this as the *definition* would make it depend on a +basis, so instead it is defined using `stdOrthonormalBasis`, and the displayed formula is proved +for an arbitrary orthonormal basis. The proof that the right-hand side does not depend on `b` is +the only real content here: both sides are linear in `A`, and every operator on `E ⊗[𝕜] F` is a +sum of operators of the form `TensorProduct.map f g`, for which the sum evaluates to +`(LinearMap.trace f) * ⟪y, g y'⟫`. + +## Main definitions + +* `TensorProduct.tmulLeftL`, `TensorProduct.tmulRightL`: tensoring with a fixed vector, as a + continuous linear map. +* `ContinuousLinearMap.traceLeft`, `ContinuousLinearMap.traceRight`: the partial traces. +* `HermitianOp.traceLeft`, `HermitianOp.traceRight`: the partial traces of a self-adjoint operator. + +## Main results + +* `ContinuousLinearMap.inner_traceLeft`, `ContinuousLinearMap.inner_traceRight`: the defining + formula, for an arbitrary orthonormal basis of the factor being traced out. +* `ContinuousLinearMap.trace_traceLeft`, `ContinuousLinearMap.trace_traceRight`: the partial trace + preserves the trace. +* `ContinuousLinearMap.IsPositive.traceLeft`, `ContinuousLinearMap.IsPositive.traceRight`: the + partial trace preserves positivity. +* `HermitianOp.toMat_traceLeft`, `HermitianOp.toMat_traceRight`: the **matrix analogues**, saying + that in the preferred basis the partial trace is `HermitianMat.traceLeft` / `traceRight`. +-/ + +@[expose] public section + +open scoped ComplexOrder InnerProductSpace TensorProduct + +namespace TensorProduct + +variable {𝕜 E F : Type*} [RCLike 𝕜] +variable [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] +variable [NormedAddCommGroup F] [InnerProductSpace 𝕜 F] + +variable (𝕜) in +/-- Tensoring with a fixed vector on the left, `y ↦ x ⊗ₜ y`, as a continuous linear map. -/ +noncomputable def tmulLeftL (x : E) : F →L[𝕜] E ⊗[𝕜] F := + (TensorProduct.mk 𝕜 E F x).mkContinuous ‖x‖ fun y ↦ le_of_eq (by simp) + +variable (𝕜) in +/-- Tensoring with a fixed vector on the right, `x ↦ x ⊗ₜ y`, as a continuous linear map. -/ +noncomputable def tmulRightL (y : F) : E →L[𝕜] E ⊗[𝕜] F := + ((TensorProduct.mk 𝕜 E F).flip y).mkContinuous ‖y‖ fun x ↦ le_of_eq (by simp [mul_comm]) + +@[simp] +theorem tmulLeftL_apply (x : E) (y : F) : tmulLeftL 𝕜 x y = x ⊗ₜ[𝕜] y := + rfl + +@[simp] +theorem tmulRightL_apply (x : E) (y : F) : tmulRightL 𝕜 y x = x ⊗ₜ[𝕜] y := + rfl + +end TensorProduct + +namespace ContinuousLinearMap + +open TensorProduct + +variable {𝕜 E F ι κ : Type*} [RCLike 𝕜] +variable [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] [FiniteDimensional 𝕜 E] +variable [NormedAddCommGroup F] [InnerProductSpace 𝕜 F] [FiniteDimensional 𝕜 F] +variable [Fintype ι] [Fintype κ] + +section BasisIndependence + +/-- The matrix elements of `A` summed over an orthonormal basis of the left factor do not depend +on the choice of that basis. This is what makes `ContinuousLinearMap.traceLeft` well defined. -/ +private theorem sum_inner_tmulLeft_congr (b : OrthonormalBasis ι 𝕜 E) + (c : OrthonormalBasis κ 𝕜 E) (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F) (y y' : F) : + ∑ i, ⟪b i ⊗ₜ[𝕜] y, A (b i ⊗ₜ[𝕜] y')⟫_𝕜 = ∑ j, ⟪c j ⊗ₜ[𝕜] y, A (c j ⊗ₜ[𝕜] y')⟫_𝕜 := by + obtain ⟨t, rfl⟩ := (homTensorHomEquiv 𝕜 E F E F).surjective A + induction t using TensorProduct.induction_on with + | zero => simp + | tmul f g => + simp only [homTensorHomEquiv_apply, homTensorHomMap_apply, map_tmul, inner_tmul] + rw [← Finset.sum_mul, ← Finset.sum_mul, ← LinearMap.trace_eq_sum_inner f b, + ← LinearMap.trace_eq_sum_inner f c] + | add t₁ t₂ h₁ h₂ => + simp only [map_add, LinearMap.add_apply, inner_add_right, Finset.sum_add_distrib, h₁, h₂] + +/-- The mirror image of `sum_inner_tmulLeft_congr`, for the right factor. -/ +private theorem sum_inner_tmulRight_congr (b : OrthonormalBasis ι 𝕜 F) + (c : OrthonormalBasis κ 𝕜 F) (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F) (x x' : E) : + ∑ i, ⟪x ⊗ₜ[𝕜] b i, A (x' ⊗ₜ[𝕜] b i)⟫_𝕜 = ∑ j, ⟪x ⊗ₜ[𝕜] c j, A (x' ⊗ₜ[𝕜] c j)⟫_𝕜 := by + obtain ⟨t, rfl⟩ := (homTensorHomEquiv 𝕜 E F E F).surjective A + induction t using TensorProduct.induction_on with + | zero => simp + | tmul f g => + simp only [homTensorHomEquiv_apply, homTensorHomMap_apply, map_tmul, inner_tmul] + rw [← Finset.mul_sum, ← Finset.mul_sum, ← LinearMap.trace_eq_sum_inner g b, + ← LinearMap.trace_eq_sum_inner g c] + | add t₁ t₂ h₁ h₂ => + simp only [map_add, LinearMap.add_apply, inner_add_right, Finset.sum_add_distrib, h₁, h₂] + +end BasisIndependence + +section Ext + +variable {G : Type*} [NormedAddCommGroup G] [InnerProductSpace 𝕜 G] + +/-- Two operators that have the same inner products against all pairs of vectors are equal. -/ +private theorem ext_of_inner {S T : G →L[𝕜] G} (h : ∀ y y', ⟪y, S y'⟫_𝕜 = ⟪y, T y'⟫_𝕜) : S = T := + ContinuousLinearMap.ext fun y' ↦ ext_inner_left 𝕜 fun y ↦ h y y' + +end Ext + +section Left + +variable [CompleteSpace F] + +/-- The **partial trace** of an operator on `E ⊗[𝕜] F` over the left factor. -/ +noncomputable def traceLeft (A : (E ⊗[𝕜] F) →L[𝕜] E ⊗[𝕜] F) : F →L[𝕜] F := + ∑ i, adjoint (tmulLeftL 𝕜 (stdOrthonormalBasis 𝕜 E i)) ∘L A ∘L + tmulLeftL 𝕜 (stdOrthonormalBasis 𝕜 E i) + +variable (A B : (E ⊗[𝕜] F) →L[𝕜] E ⊗[𝕜] F) + +/-- The defining property of the partial trace over the left factor, for an arbitrary orthonormal +basis of the factor being traced out. -/ +theorem inner_traceLeft (b : OrthonormalBasis ι 𝕜 E) (y y' : F) : + ⟪y, A.traceLeft y'⟫_𝕜 = ∑ i, ⟪b i ⊗ₜ[𝕜] y, A (b i ⊗ₜ[𝕜] y')⟫_𝕜 := by + rw [traceLeft, _root_.sum_apply, inner_sum] + simp only [ContinuousLinearMap.comp_apply, adjoint_inner_right, tmulLeftL_apply] + exact sum_inner_tmulLeft_congr _ b (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F) y y' + +@[simp] +theorem traceLeft_zero : traceLeft (0 : (E ⊗[𝕜] F) →L[𝕜] E ⊗[𝕜] F) = 0 := + ext_of_inner fun y y' ↦ by + rw [inner_traceLeft _ (stdOrthonormalBasis 𝕜 E)]; simp + +@[simp] +theorem traceLeft_add : (A + B).traceLeft = A.traceLeft + B.traceLeft := + ext_of_inner fun y y' ↦ by + simp only [inner_traceLeft _ (stdOrthonormalBasis 𝕜 E), _root_.add_apply, + inner_add_right, Finset.sum_add_distrib] + +@[simp] +theorem traceLeft_smul (r : 𝕜) : (r • A).traceLeft = r • A.traceLeft := + ext_of_inner fun y y' ↦ by + simp only [inner_traceLeft _ (stdOrthonormalBasis 𝕜 E), _root_.smul_apply, + inner_smul_right, Finset.mul_sum] + +@[simp] +theorem traceLeft_neg : (-A).traceLeft = -A.traceLeft := + ext_of_inner fun y y' ↦ by + simp only [inner_traceLeft _ (stdOrthonormalBasis 𝕜 E), _root_.neg_apply, + inner_neg_right, Finset.sum_neg_distrib] + +@[simp] +theorem traceLeft_sub : (A - B).traceLeft = A.traceLeft - B.traceLeft := by + rw [sub_eq_add_neg, traceLeft_add, traceLeft_neg, ← sub_eq_add_neg] + +/-- The partial trace over the left factor preserves symmetry. -/ +theorem IsSymmetric.traceLeft (hA : (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F).IsSymmetric) : + (A.traceLeft : F →ₗ[𝕜] F).IsSymmetric := by + intro y y' + rw [ContinuousLinearMap.coe_coe, ← inner_conj_symm (A.traceLeft y) y', + inner_traceLeft _ (stdOrthonormalBasis 𝕜 E), inner_traceLeft _ (stdOrthonormalBasis 𝕜 E), + map_sum] + exact Finset.sum_congr rfl fun i _ ↦ by rw [inner_conj_symm]; exact hA _ _ + +/-- The partial trace over the left factor preserves positivity. -/ +theorem IsPositive.traceLeft (hA : A.IsPositive) : A.traceLeft.IsPositive := by + refine ⟨IsSymmetric.traceLeft A hA.1, fun y ↦ ?_⟩ + rw [ContinuousLinearMap.reApplyInnerSelf, ← RCLike.conj_re (⟪A.traceLeft y, y⟫_𝕜), + inner_conj_symm, inner_traceLeft _ (stdOrthonormalBasis 𝕜 E), map_sum] + refine Finset.sum_nonneg fun i _ ↦ ?_ + have h := hA.2 (stdOrthonormalBasis 𝕜 E i ⊗ₜ[𝕜] y) + rwa [ContinuousLinearMap.reApplyInnerSelf, ← inner_conj_symm, RCLike.conj_re] at h + +/-- The partial trace over the left factor preserves the trace. -/ +@[simp] +theorem trace_traceLeft : + LinearMap.trace 𝕜 F (A.traceLeft : F →ₗ[𝕜] F) = + LinearMap.trace 𝕜 (E ⊗[𝕜] F) (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F) := by + classical + set b := stdOrthonormalBasis 𝕜 E + set c := stdOrthonormalBasis 𝕜 F + rw [LinearMap.trace_eq_sum_inner _ c, LinearMap.trace_eq_sum_inner _ (b.tensorProduct c), + Fintype.sum_prod_type_right] + exact Finset.sum_congr rfl fun j _ ↦ by + simpa using inner_traceLeft A b (c j) (c j) + +end Left + +section Right + +variable [CompleteSpace E] + +/-- The **partial trace** of an operator on `E ⊗[𝕜] F` over the right factor. -/ +noncomputable def traceRight (A : (E ⊗[𝕜] F) →L[𝕜] E ⊗[𝕜] F) : E →L[𝕜] E := + ∑ i, adjoint (tmulRightL 𝕜 (stdOrthonormalBasis 𝕜 F i)) ∘L A ∘L + tmulRightL 𝕜 (stdOrthonormalBasis 𝕜 F i) + +variable (A B : (E ⊗[𝕜] F) →L[𝕜] E ⊗[𝕜] F) + +/-- The defining property of the partial trace over the right factor, for an arbitrary orthonormal +basis of the factor being traced out. -/ +theorem inner_traceRight (b : OrthonormalBasis ι 𝕜 F) (x x' : E) : + ⟪x, A.traceRight x'⟫_𝕜 = ∑ i, ⟪x ⊗ₜ[𝕜] b i, A (x' ⊗ₜ[𝕜] b i)⟫_𝕜 := by + rw [traceRight, _root_.sum_apply, inner_sum] + simp only [ContinuousLinearMap.comp_apply, adjoint_inner_right, tmulRightL_apply] + exact sum_inner_tmulRight_congr _ b (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F) x x' + +@[simp] +theorem traceRight_zero : traceRight (0 : (E ⊗[𝕜] F) →L[𝕜] E ⊗[𝕜] F) = 0 := + ext_of_inner fun x x' ↦ by + rw [inner_traceRight _ (stdOrthonormalBasis 𝕜 F)]; simp + +@[simp] +theorem traceRight_add : (A + B).traceRight = A.traceRight + B.traceRight := + ext_of_inner fun x x' ↦ by + simp only [inner_traceRight _ (stdOrthonormalBasis 𝕜 F), _root_.add_apply, + inner_add_right, Finset.sum_add_distrib] + +@[simp] +theorem traceRight_smul (r : 𝕜) : (r • A).traceRight = r • A.traceRight := + ext_of_inner fun x x' ↦ by + simp only [inner_traceRight _ (stdOrthonormalBasis 𝕜 F), _root_.smul_apply, + inner_smul_right, Finset.mul_sum] + +@[simp] +theorem traceRight_neg : (-A).traceRight = -A.traceRight := + ext_of_inner fun x x' ↦ by + simp only [inner_traceRight _ (stdOrthonormalBasis 𝕜 F), _root_.neg_apply, + inner_neg_right, Finset.sum_neg_distrib] + +@[simp] +theorem traceRight_sub : (A - B).traceRight = A.traceRight - B.traceRight := by + rw [sub_eq_add_neg, traceRight_add, traceRight_neg, ← sub_eq_add_neg] + +/-- The partial trace over the right factor preserves symmetry. -/ +theorem IsSymmetric.traceRight (hA : (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F).IsSymmetric) : + (A.traceRight : E →ₗ[𝕜] E).IsSymmetric := by + intro x x' + rw [ContinuousLinearMap.coe_coe, ← inner_conj_symm (A.traceRight x) x', + inner_traceRight _ (stdOrthonormalBasis 𝕜 F), inner_traceRight _ (stdOrthonormalBasis 𝕜 F), + map_sum] + exact Finset.sum_congr rfl fun i _ ↦ by rw [inner_conj_symm]; exact hA _ _ + +/-- The partial trace over the right factor preserves positivity. -/ +theorem IsPositive.traceRight (hA : A.IsPositive) : A.traceRight.IsPositive := by + refine ⟨IsSymmetric.traceRight A hA.1, fun x ↦ ?_⟩ + rw [ContinuousLinearMap.reApplyInnerSelf, ← RCLike.conj_re (⟪A.traceRight x, x⟫_𝕜), + inner_conj_symm, inner_traceRight _ (stdOrthonormalBasis 𝕜 F), map_sum] + refine Finset.sum_nonneg fun i _ ↦ ?_ + have h := hA.2 (x ⊗ₜ[𝕜] stdOrthonormalBasis 𝕜 F i) + rwa [ContinuousLinearMap.reApplyInnerSelf, ← inner_conj_symm, RCLike.conj_re] at h + +/-- The partial trace over the right factor preserves the trace. -/ +@[simp] +theorem trace_traceRight : + LinearMap.trace 𝕜 E (A.traceRight : E →ₗ[𝕜] E) = + LinearMap.trace 𝕜 (E ⊗[𝕜] F) (A : (E ⊗[𝕜] F) →ₗ[𝕜] E ⊗[𝕜] F) := by + classical + set b := stdOrthonormalBasis 𝕜 E + set c := stdOrthonormalBasis 𝕜 F + rw [LinearMap.trace_eq_sum_inner _ b, LinearMap.trace_eq_sum_inner _ (b.tensorProduct c), + Fintype.sum_prod_type] + exact Finset.sum_congr rfl fun i _ ↦ by + simpa using inner_traceRight A c (b i) (b i) + +end Right + +end ContinuousLinearMap + +namespace HermitianOp + +open TensorProduct + +variable {E F ι κ : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] + +section Left + +/-- The partial trace of a self-adjoint operator over the left factor. -/ +noncomputable def traceLeft (A : HermitianOp (E ⊗[ℂ] F)) : HermitianOp F := + ⟨A.op.traceLeft, ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric.2 + (ContinuousLinearMap.IsSymmetric.traceLeft A.op + (ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric.1 A.H))⟩ + +@[simp] +theorem op_traceLeft (A : HermitianOp (E ⊗[ℂ] F)) : A.traceLeft.op = A.op.traceLeft := + rfl + +variable (A B : HermitianOp (E ⊗[ℂ] F)) + +@[simp] theorem traceLeft_zero : (0 : HermitianOp (E ⊗[ℂ] F)).traceLeft = 0 := + HermitianOp.ext <| by simp + +@[simp] theorem traceLeft_add : (A + B).traceLeft = A.traceLeft + B.traceLeft := + HermitianOp.ext <| by simp + +@[simp] theorem traceLeft_neg : (-A).traceLeft = -A.traceLeft := + HermitianOp.ext <| by simp + +@[simp] theorem traceLeft_sub : (A - B).traceLeft = A.traceLeft - B.traceLeft := + HermitianOp.ext <| by simp + +@[simp] theorem traceLeft_smul (r : ℝ) : (r • A).traceLeft = r • A.traceLeft := + HermitianOp.ext <| by + show ((r : ℂ) • A.op).traceLeft = (r : ℂ) • A.op.traceLeft + exact ContinuousLinearMap.traceLeft_smul A.op r + +/-- The partial trace over the left factor preserves nonnegativity. -/ +theorem traceLeft_nonneg (h : 0 ≤ A) : 0 ≤ A.traceLeft := + zero_le_iff.2 (ContinuousLinearMap.IsPositive.traceLeft A.op (zero_le_iff.1 h)) + +@[simp] +theorem trace_traceLeft : A.traceLeft.trace = A.trace := by + rw [trace, trace, op_traceLeft, ContinuousLinearMap.trace_traceLeft] + +/-- **Matrix analogue of `HermitianOp.traceLeft`.** In the preferred basis, the partial trace over +the left factor is `HermitianMat.traceLeft`. -/ +@[simp] +theorem toMat_traceLeft [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] : + toMat (ι := κ) A.traceLeft = (toMat (ι := ι × κ) A).traceLeft := by + apply HermitianMat.ext + ext j j' + simp only [HermitianMat.traceLeft_mat, Matrix.traceLeft, Matrix.of_apply, toMat_mat, + op_traceLeft, StdBasis.toMat_apply] + rw [ContinuousLinearMap.inner_traceLeft _ (stdBasis (𝕜 := ℂ) (E := E))] + exact Finset.sum_congr rfl fun i _ ↦ by + simp [OrthonormalBasis.tensorProduct_apply] + +end Left + +section Right + +/-- The partial trace of a self-adjoint operator over the right factor. -/ +noncomputable def traceRight (A : HermitianOp (E ⊗[ℂ] F)) : HermitianOp E := + ⟨A.op.traceRight, ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric.2 + (ContinuousLinearMap.IsSymmetric.traceRight A.op + (ContinuousLinearMap.isSelfAdjoint_iff_isSymmetric.1 A.H))⟩ + +@[simp] +theorem op_traceRight (A : HermitianOp (E ⊗[ℂ] F)) : A.traceRight.op = A.op.traceRight := + rfl + +variable (A B : HermitianOp (E ⊗[ℂ] F)) + +@[simp] theorem traceRight_zero : (0 : HermitianOp (E ⊗[ℂ] F)).traceRight = 0 := + HermitianOp.ext <| by simp + +@[simp] theorem traceRight_add : (A + B).traceRight = A.traceRight + B.traceRight := + HermitianOp.ext <| by simp + +@[simp] theorem traceRight_neg : (-A).traceRight = -A.traceRight := + HermitianOp.ext <| by simp + +@[simp] theorem traceRight_sub : (A - B).traceRight = A.traceRight - B.traceRight := + HermitianOp.ext <| by simp + +@[simp] theorem traceRight_smul (r : ℝ) : (r • A).traceRight = r • A.traceRight := + HermitianOp.ext <| by + show ((r : ℂ) • A.op).traceRight = (r : ℂ) • A.op.traceRight + exact ContinuousLinearMap.traceRight_smul A.op r + +/-- The partial trace over the right factor preserves nonnegativity. -/ +theorem traceRight_nonneg (h : 0 ≤ A) : 0 ≤ A.traceRight := + zero_le_iff.2 (ContinuousLinearMap.IsPositive.traceRight A.op (zero_le_iff.1 h)) + +@[simp] +theorem trace_traceRight : A.traceRight.trace = A.trace := by + rw [trace, trace, op_traceRight, ContinuousLinearMap.trace_traceRight] + +/-- **Matrix analogue of `HermitianOp.traceRight`.** In the preferred basis, the partial trace over +the right factor is `HermitianMat.traceRight`. -/ +@[simp] +theorem toMat_traceRight [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] : + toMat (ι := ι) A.traceRight = (toMat (ι := ι × κ) A).traceRight := by + apply HermitianMat.ext + ext i i' + simp only [HermitianMat.traceRight_mat, Matrix.traceRight, Matrix.of_apply, toMat_mat, + op_traceRight, StdBasis.toMat_apply] + rw [ContinuousLinearMap.inner_traceRight _ (stdBasis (𝕜 := ℂ) (E := F))] + exact Finset.sum_congr rfl fun j _ ↦ by + simp [OrthonormalBasis.tensorProduct_apply] + +end Right + +end HermitianOp diff --git a/QuantumInfo/ForMathlib/StdBasis.lean b/QuantumInfo/ForMathlib/StdBasis.lean new file mode 100644 index 000000000..07de03146 --- /dev/null +++ b/QuantumInfo/ForMathlib/StdBasis.lean @@ -0,0 +1,588 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import Mathlib.Analysis.CStarAlgebra.Matrix +public import Mathlib.Analysis.InnerProductSpace.Positive +public import Mathlib.Analysis.InnerProductSpace.TensorProduct +public import Mathlib.Analysis.InnerProductSpace.Trace + +/-! +# Preferred orthonormal bases + +Quantum information theory is usually written down twice: once in a basis-free language of +operators on a Hilbert space, and once in the language of matrices. The two are equivalent, but +only after a choice of orthonormal basis, and a handful of notions -- computational-basis states, +Pauli and Clifford operators, controlled gates, stabilizer entropies -- are genuinely defined +*relative to* such a choice. + +This file introduces `StdBasis 𝕜 E ι`, a class carrying a preferred `OrthonormalBasis ι 𝕜 E`, so +that the choice can be propagated by typeclass inference instead of being threaded through every +definition by hand. Since `OrthonormalBasis ι 𝕜 E` unfolds to a `LinearIsometryEquiv` onto +`EuclideanSpace 𝕜 ι`, an instance of `StdBasis 𝕜 E ι` is exactly the data identifying `E` with a +labelled copy of `EuclideanSpace 𝕜 ι`. + +## Main definitions + +* `StdBasis 𝕜 E ι`: the class of a preferred orthonormal basis of `E` indexed by `ι`. +* `StdBasis.toMatOf`: the ⋆-algebra equivalence `(E →L[𝕜] E) ≃⋆ₐ[𝕜] Matrix ι ι 𝕜` determined by an + explicit orthonormal basis. +* `StdBasis.toMat`: the same, using the preferred basis. +* `StdBasis.toMatUnitary`, `StdBasis.unitaryOfMat`: the resulting bijection between unitary + operators on `E` and unitary matrices indexed by `ι`. +* `StdBasis.changeOfBasis`: the unitary matrix relating the matrices of an operator in two + different orthonormal bases. + +## Main results + +* `StdBasis.toMatOf_conj`: changing the orthonormal basis conjugates the matrix by a unitary. +* `StdBasis.congr_of_unitaryInvariant`: any quantity computed from the matrix of an operator that + is invariant under unitary conjugation does not depend on the choice of orthonormal basis. This + is the workhorse for showing that a matrix-level definition descends to operators. +* `StdBasis.posSemidef_toMatOf_iff`, `StdBasis.trace_toMatOf`: the matrix positivity and trace + agree with their basis-free counterparts. + +## Design notes + +The basis is required to be *orthonormal* rather than a bare `Module.Basis`. This is not a +convenience: with a bare basis the change-of-basis matrix ranges over all of `GL`, and the +quantities of interest in quantum information (eigenvalues, entropies, Schatten norms, positivity) +are *not* invariant under general similarity. Orthonormality is exactly what makes the +change-of-basis matrix unitary, and it is also what makes `E →L[𝕜] E ≃ Matrix ι ι 𝕜` a +⋆-isomorphism rather than merely an algebra isomorphism, so that adjoints, self-adjointness, +unitarity, spectra and the continuous functional calculus all transport. + +The index type `ι` is an `outParam`: a type carries at most one preferred basis, and the index type +is part of that choice. Consequently `StdBasis.reindex` and `StdBasis.transport` are *definitions* +rather than instances; making them instances would both loop and silently install non-canonical +bases. +-/ + +@[expose] public section + +open scoped ComplexOrder InnerProductSpace Matrix TensorProduct + +/-- A preferred orthonormal basis of `E`, indexed by `ι`. + +This is the data used by notions that are genuinely basis-dependent (computational-basis states, +Pauli operators, controlled gates), and by the matrix representation of operators on `E`. -/ +class StdBasis (𝕜 : Type*) (E : Type*) (ι : outParam Type*) [RCLike 𝕜] + [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] [Fintype ι] where + /-- The preferred orthonormal basis. -/ + stdBasis : OrthonormalBasis ι 𝕜 E + +export StdBasis (stdBasis) + +namespace OrthonormalBasis + +variable {𝕜 E ι κ : Type*} [RCLike 𝕜] [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] +variable [Fintype ι] [Fintype κ] + +@[simp] +theorem reindex_reindex (b : OrthonormalBasis ι 𝕜 E) (e : ι ≃ κ) {ν : Type*} [Fintype ν] + (f : κ ≃ ν) : (b.reindex e).reindex f = b.reindex (e.trans f) := + DFunLike.ext _ _ fun _ ↦ by simp + +@[simp] +theorem reindex_refl (b : OrthonormalBasis ι 𝕜 E) : b.reindex (Equiv.refl ι) = b := + DFunLike.ext _ _ fun _ ↦ by simp + +theorem reindex_reindex_symm (b : OrthonormalBasis ι 𝕜 E) (e : ι ≃ κ) : + (b.reindex e).reindex e.symm = b := by + simp + +end OrthonormalBasis + +/-- The standard basis of `EuclideanSpace 𝕜 d` is `EuclideanSpace.basisFun`. This is the instance +that makes existing matrix-indexed definitions a special case of the abstract ones. -/ +noncomputable instance EuclideanSpace.instStdBasis (𝕜 : Type*) [RCLike 𝕜] (d : Type*) + [Fintype d] : StdBasis 𝕜 (EuclideanSpace 𝕜 d) d where + stdBasis := EuclideanSpace.basisFun d 𝕜 + +namespace StdBasis + +variable {𝕜 E F ι κ : Type*} [RCLike 𝕜] +variable [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] +variable [NormedAddCommGroup F] [InnerProductSpace 𝕜 F] + +section Instances + +variable [Fintype ι] [Fintype κ] + +/-- A space with a preferred (necessarily finite) orthonormal basis is finite-dimensional. -/ +instance (priority := 100) toFiniteDimensional [StdBasis 𝕜 E ι] : FiniteDimensional 𝕜 E := + Module.Basis.finiteDimensional_of_finite (stdBasis (𝕜 := 𝕜) (E := E) (ι := ι)).toBasis + +/-- A space with a preferred orthonormal basis is complete. + +Mathlib keeps `FiniteDimensional.complete` a theorem rather than an instance because the scalar +field cannot be recovered from the goal `CompleteSpace E`. Here it can: a `StdBasis 𝕜 E ι` instance +pins down `𝕜`, so the search terminates. Having this available means the operator ⋆-algebra +structure on `E →L[𝕜] E` -- adjoints, the continuous functional calculus, `StdBasis.toMat` -- is +usable from a `StdBasis` instance alone, without carrying `[CompleteSpace E]` in every signature. -/ +instance (priority := 100) toCompleteSpace [StdBasis 𝕜 E ι] : CompleteSpace E := + FiniteDimensional.complete 𝕜 E + +/-- A finite-dimensional complex normed space is complete. + +Mathlib registers `FiniteDimensional.proper` as an instance only for `𝕜 = ℝ`, so a space that is +finite-dimensional over `ℂ` -- but not known to be a `ℝ`-normed space -- does not pick up +completeness by inference. Registering it here means `[FiniteDimensional ℂ E]` alone is enough to +use the operator ⋆-algebra on `E →L[ℂ] E`, and `[CompleteSpace E]` never has to appear next to it. +`CompleteSpace` is a `Prop`, so the extra route to it creates no diamond. -/ +instance (priority := 100) _root_.FiniteDimensional.toCompleteSpaceComplex {E : Type*} + [NormedAddCommGroup E] [NormedSpace ℂ E] [FiniteDimensional ℂ E] : CompleteSpace E := + FiniteDimensional.complete ℂ E + +/-- A tensor product of finite-dimensional inner product spaces is complete. This is needed for the +`StdBasis` instance on a tensor product to be usable, since the operator ⋆-algebra structure on +`E ⊗[𝕜] F →L[𝕜] E ⊗[𝕜] F` requires completeness. -/ +instance _root_.TensorProduct.instCompleteSpaceOfFiniteDimensional + [FiniteDimensional 𝕜 E] [FiniteDimensional 𝕜 F] : CompleteSpace (E ⊗[𝕜] F) := + FiniteDimensional.complete 𝕜 _ + +/-- The preferred basis of a tensor product is the tensor product of the preferred bases, indexed +by the product of the index types. -/ +noncomputable instance instTensorProduct [StdBasis 𝕜 E ι] [StdBasis 𝕜 F κ] : + StdBasis 𝕜 (E ⊗[𝕜] F) (ι × κ) where + stdBasis := (stdBasis (𝕜 := 𝕜) (E := E)).tensorProduct (stdBasis (𝕜 := 𝕜) (E := F)) + +@[simp] +theorem stdBasis_euclideanSpace (d : Type*) [Fintype d] : + stdBasis (𝕜 := 𝕜) (E := EuclideanSpace 𝕜 d) = EuclideanSpace.basisFun d 𝕜 := + rfl + +@[simp] +theorem stdBasis_tensorProduct [StdBasis 𝕜 E ι] [StdBasis 𝕜 F κ] : + stdBasis (𝕜 := 𝕜) (E := E ⊗[𝕜] F) = + (stdBasis (𝕜 := 𝕜) (E := E)).tensorProduct (stdBasis (𝕜 := 𝕜) (E := F)) := + rfl + +/-- Relabel the preferred basis of `E` along an equivalence of index types. + +This is deliberately not an instance: a type has at most one preferred basis, and there is no +canonical `ι ≃ κ` to relabel along. -/ +@[instance_reducible] +noncomputable def reindex [StdBasis 𝕜 E ι] (e : ι ≃ κ) : StdBasis 𝕜 E κ := + ⟨(stdBasis (𝕜 := 𝕜) (E := E)).reindex e⟩ + +/-- Transport the preferred basis of `E` to `F` along a linear isometry equivalence. + +This is deliberately not an instance, for the same reason as `StdBasis.reindex`. -/ +@[instance_reducible] +noncomputable def transport [StdBasis 𝕜 E ι] (f : E ≃ₗᵢ[𝕜] F) : StdBasis 𝕜 F ι := + ⟨(stdBasis (𝕜 := 𝕜) (E := E)).map f⟩ + +/-- An arbitrary preferred basis on a finite-dimensional space, indexed by `Fin (finrank 𝕜 E)`. + +This is deliberately not an instance: a space that already has a preferred basis must not silently +acquire a second one. It is meant to be introduced locally (`let _ := StdBasis.some 𝕜 E`) inside +the proof of a basis-free statement, so that the statement can be discharged by its matrix +analogue. -/ +@[instance_reducible] +noncomputable def some (𝕜 E : Type*) [RCLike 𝕜] [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] + [FiniteDimensional 𝕜 E] : StdBasis 𝕜 E (Fin (Module.finrank 𝕜 E)) := + ⟨stdOrthonormalBasis 𝕜 E⟩ + +end Instances + +section ToMat + +variable [Fintype ι] [DecidableEq ι] [CompleteSpace E] + +/-- The matrix of an operator in a given orthonormal basis, as a ⋆-algebra equivalence. + +Because this is an equivalence of ⋆-algebras, it automatically transports products, adjoints, +self-adjointness, unitarity, spectra, and the continuous functional calculus. -/ +noncomputable def toMatOf (b : OrthonormalBasis ι 𝕜 E) : (E →L[𝕜] E) ≃⋆ₐ[𝕜] Matrix ι ι 𝕜 := + b.repr.conjStarAlgEquiv.trans (Matrix.toEuclideanCLM (𝕜 := 𝕜) (n := ι)).symm + +/-- The matrix of an operator in the preferred basis of `E`. -/ +noncomputable def toMat (𝕜 E ι : Type*) [RCLike 𝕜] [NormedAddCommGroup E] + [InnerProductSpace 𝕜 E] [Fintype ι] [DecidableEq ι] [StdBasis 𝕜 E ι] : + (E →L[𝕜] E) ≃⋆ₐ[𝕜] Matrix ι ι 𝕜 := + toMatOf (stdBasis (𝕜 := 𝕜) (E := E)) + +theorem toMat_def [StdBasis 𝕜 E ι] : toMat 𝕜 E ι = toMatOf (stdBasis (𝕜 := 𝕜) (E := E)) := + rfl + +/-- `StdBasis.toMat` for the basis provided by an explicitly given instance is `toMatOf` of that +basis. This lets statements about a *change of instance* be reduced to statements about a change of +orthonormal basis. -/ +theorem toMat_mk (b : OrthonormalBasis ι 𝕜 E) : + @toMat 𝕜 E ι _ _ _ _ _ ⟨b⟩ = toMatOf b := + rfl + +@[simp] +theorem toMatOf_apply (b : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) (i j : ι) : + toMatOf b A i j = ⟪b i, A (b j)⟫_𝕜 := by + show LinearMap.toMatrixOrthonormal (EuclideanSpace.basisFun ι 𝕜) + ((b.repr.conjStarAlgEquiv A : EuclideanSpace 𝕜 ι →L[𝕜] EuclideanSpace 𝕜 ι) : + EuclideanSpace 𝕜 ι →ₗ[𝕜] EuclideanSpace 𝕜 ι) i j = _ + rw [LinearMap.toMatrixOrthonormal_apply_apply] + simp only [ContinuousLinearMap.coe_coe, LinearIsometryEquiv.conjStarAlgEquiv_apply_apply, + EuclideanSpace.basisFun_apply, OrthonormalBasis.repr_symm_single] + rw [← b.repr_self i, LinearIsometryEquiv.inner_map_map] + +variable [FiniteDimensional 𝕜 E] + +/-- `StdBasis.toMatOf` agrees with Mathlib's `LinearMap.toMatrixOrthonormal`. -/ +theorem toMatOf_eq_toMatrixOrthonormal (b : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) : + toMatOf b A = LinearMap.toMatrixOrthonormal b (A : E →ₗ[𝕜] E) := by + ext i j + rw [toMatOf_apply, LinearMap.toMatrixOrthonormal_apply_apply, ContinuousLinearMap.coe_coe] + +/-- On `EuclideanSpace 𝕜 d` the preferred matrix representation is the identity, in the sense that +it is Mathlib's `Matrix.toEuclideanCLM`. Existing matrix-level definitions are therefore literally +the special case `E := EuclideanSpace 𝕜 d` of the abstract ones. -/ +@[simp] +theorem toMat_euclideanSpace (d : Type*) [Fintype d] [DecidableEq d] : + toMat 𝕜 (EuclideanSpace 𝕜 d) d = (Matrix.toEuclideanCLM (𝕜 := 𝕜) (n := d)).symm := + rfl + +@[simp] +theorem toMatOf_reindex (b : OrthonormalBasis ι 𝕜 E) [Fintype κ] [DecidableEq κ] (e : ι ≃ κ) + (A : E →L[𝕜] E) : toMatOf (b.reindex e) A = (toMatOf b A).reindex e e := by + rw [toMatOf_eq_toMatrixOrthonormal, toMatOf_eq_toMatrixOrthonormal, + LinearMap.toMatrixOrthonormal_reindex] + +@[simp] +theorem toMatOf_symm_reindex (b : OrthonormalBasis ι 𝕜 E) [Fintype κ] [DecidableEq κ] (e : ι ≃ κ) + (M : Matrix κ κ 𝕜) : (toMatOf (b.reindex e)).symm M = (toMatOf b).symm (M.submatrix e e) := by + apply EquivLike.injective (toMatOf (b.reindex e)) + rw [StarAlgEquiv.apply_symm_apply, toMatOf_reindex, StarAlgEquiv.apply_symm_apply] + ext i j + simp + +section Unitary + +omit [FiniteDimensional 𝕜 E] + +variable [StdBasis 𝕜 E ι] + +/-- The matrix of a unitary operator in the preferred basis, as a unitary matrix. -/ +noncomputable def toMatUnitary (U : unitary (E →L[𝕜] E)) : Matrix.unitaryGroup ι 𝕜 := + ⟨toMat 𝕜 E ι U.val, + ⟨by rw [← map_star, ← map_mul, U.2.1, map_one], by rw [← map_star, ← map_mul, U.2.2, map_one]⟩⟩ + +@[simp] +theorem toMatUnitary_coe (U : unitary (E →L[𝕜] E)) : + (toMatUnitary (ι := ι) U : Matrix ι ι 𝕜) = toMat 𝕜 E ι U.val := + rfl + +/-- The unitary operator whose matrix in the preferred basis is a given unitary matrix. -/ +noncomputable def unitaryOfMat (U : Matrix.unitaryGroup ι 𝕜) : unitary (E →L[𝕜] E) := + ⟨(toMat 𝕜 E ι).symm U.val, + ⟨by rw [← map_star, ← map_mul, U.2.1, map_one], by rw [← map_star, ← map_mul, U.2.2, map_one]⟩⟩ + +@[simp] +theorem unitaryOfMat_coe (U : Matrix.unitaryGroup ι 𝕜) : + (unitaryOfMat (E := E) U : E →L[𝕜] E) = (toMat 𝕜 E ι).symm U.val := + rfl + +@[simp] +theorem toMatUnitary_unitaryOfMat (U : Matrix.unitaryGroup ι 𝕜) : + toMatUnitary (E := E) (ι := ι) (unitaryOfMat U) = U := + Subtype.ext <| by simp + +@[simp] +theorem unitaryOfMat_toMatUnitary (U : unitary (E →L[𝕜] E)) : + unitaryOfMat (toMatUnitary (ι := ι) U) = U := + Subtype.ext <| by simp + +end Unitary + +end ToMat + +section ChangeOfBasis + +variable [Fintype ι] [DecidableEq ι] + +/-- The unitary change-of-basis matrix taking the matrix of an operator in the basis `b` to its +matrix in the basis `b'`. -/ +noncomputable def changeOfBasis (b b' : OrthonormalBasis ι 𝕜 E) : Matrix.unitaryGroup ι 𝕜 := + ⟨b.toBasis.toMatrix b'.toBasis, b.toMatrix_orthonormalBasis_mem_unitary b'⟩ + +@[simp] +theorem changeOfBasis_coe (b b' : OrthonormalBasis ι 𝕜 E) : + (changeOfBasis b b' : Matrix ι ι 𝕜) = b.toBasis.toMatrix b'.toBasis := + rfl + +theorem changeOfBasis_star (b b' : OrthonormalBasis ι 𝕜 E) : + (star (changeOfBasis b b') : Matrix ι ι 𝕜) = b'.toBasis.toMatrix b.toBasis := by + have h₁ : b'.toBasis.toMatrix b.toBasis * b.toBasis.toMatrix b'.toBasis = 1 := + Module.Basis.toMatrix_mul_toMatrix_flip _ _ + have h₂ : b.toBasis.toMatrix b'.toBasis * (b.toBasis.toMatrix b'.toBasis)ᴴ = 1 := + b.toMatrix_orthonormalBasis_self_mul_conjTranspose b' + calc (star (changeOfBasis b b') : Matrix ι ι 𝕜) + = 1 * (b.toBasis.toMatrix b'.toBasis)ᴴ := (one_mul _).symm + _ = b'.toBasis.toMatrix b.toBasis * + (b.toBasis.toMatrix b'.toBasis * (b.toBasis.toMatrix b'.toBasis)ᴴ) := by + rw [← h₁, mul_assoc] + _ = b'.toBasis.toMatrix b.toBasis := by rw [h₂, mul_one] + +variable [FiniteDimensional 𝕜 E] [CompleteSpace E] + +/-- Changing the orthonormal basis conjugates the matrix of an operator by a unitary. -/ +theorem toMatOf_conj (b b' : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) : + toMatOf b' A = + (star (changeOfBasis b b') : Matrix ι ι 𝕜) * toMatOf b A * + (changeOfBasis b b' : Matrix ι ι 𝕜) := by + rw [changeOfBasis_star, changeOfBasis_coe, toMatOf_eq_toMatrixOrthonormal, + toMatOf_eq_toMatrixOrthonormal] + exact (basis_toMatrix_mul_linearMap_toMatrix_mul_basis_toMatrix _ _ _ _ _).symm + +/-- The inverse form of `StdBasis.toMatOf_conj`: reading a matrix as an operator in a different +orthonormal basis conjugates it by a unitary. -/ +theorem toMatOf_symm_conj (b b' : OrthonormalBasis ι 𝕜 E) (M : Matrix ι ι 𝕜) : + (toMatOf b').symm M = (toMatOf b).symm ((changeOfBasis b b' : Matrix ι ι 𝕜) * M * + (star (changeOfBasis b b') : Matrix ι ι 𝕜)) := by + set C := changeOfBasis b b' with hC + have h₁ : (star C : Matrix ι ι 𝕜) * (C : Matrix ι ι 𝕜) = 1 := + Matrix.UnitaryGroup.star_mul_self C + apply EquivLike.injective (toMatOf b') + rw [StarAlgEquiv.apply_symm_apply, toMatOf_conj b b', StarAlgEquiv.apply_symm_apply, ← hC] + calc M = ((star C : Matrix ι ι 𝕜) * (C : Matrix ι ι 𝕜)) * M * + ((star C : Matrix ι ι 𝕜) * (C : Matrix ι ι 𝕜)) := by rw [h₁, one_mul, mul_one] + _ = (star C : Matrix ι ι 𝕜) * ((C : Matrix ι ι 𝕜) * M * (star C : Matrix ι ι 𝕜)) * + (C : Matrix ι ι 𝕜) := by noncomm_ring + +/-- **Basis insensitivity.** A quantity extracted from the matrix of an operator is independent of +the choice of orthonormal basis as soon as it is invariant under unitary conjugation. + +This reduces "prove the matrix definition is basis-independent" to the single unitary-invariance +fact that is usually already available (for instance `HermitianMat.eigenvalues_conj` or +`HermitianMat.trace_conj_unitary`). -/ +theorem congr_of_unitaryInvariant {X : Type*} (f : Matrix ι ι 𝕜 → X) + (hf : ∀ (U : Matrix.unitaryGroup ι 𝕜) (M : Matrix ι ι 𝕜), + f ((star U : Matrix ι ι 𝕜) * M * (U : Matrix ι ι 𝕜)) = f M) + (b b' : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) : + f (toMatOf b' A) = f (toMatOf b A) := by + rw [toMatOf_conj b b', hf] + +/-- The version of `StdBasis.congr_of_unitaryInvariant` for two `StdBasis` instances on the same +type. -/ +theorem toMat_congr_of_unitaryInvariant {X : Type*} (f : Matrix ι ι 𝕜 → X) + (hf : ∀ (U : Matrix.unitaryGroup ι 𝕜) (M : Matrix ι ι 𝕜), + f ((star U : Matrix ι ι 𝕜) * M * (U : Matrix ι ι 𝕜)) = f M) + (inst inst' : StdBasis 𝕜 E ι) (A : E →L[𝕜] E) : + f (@toMat 𝕜 E ι _ _ _ _ _ inst' A) = f (@toMat 𝕜 E ι _ _ _ _ _ inst A) := + congr_of_unitaryInvariant f hf inst.stdBasis inst'.stdBasis A + +end ChangeOfBasis + +section Conjugation + +variable [CompleteSpace E] [CompleteSpace F] + +/-- Conjugating by a linear isometry equivalence preserves positivity of operators. -/ +theorem _root_.ContinuousLinearMap.IsPositive.conjStarAlgEquiv + (e : E ≃ₗᵢ[𝕜] F) {A : E →L[𝕜] E} (hA : A.IsPositive) : + (e.conjStarAlgEquiv A).IsPositive := by + refine ⟨fun x y ↦ ?_, fun x ↦ ?_⟩ + · calc ⟪(e.conjStarAlgEquiv A) x, y⟫_𝕜 = ⟪e (A (e.symm x)), e (e.symm y)⟫_𝕜 := by simp + _ = ⟪A (e.symm x), e.symm y⟫_𝕜 := e.inner_map_map _ _ + _ = ⟪e.symm x, A (e.symm y)⟫_𝕜 := hA.1 _ _ + _ = ⟪e (e.symm x), e (A (e.symm y))⟫_𝕜 := (e.inner_map_map _ _).symm + _ = ⟪x, (e.conjStarAlgEquiv A) y⟫_𝕜 := by simp + · have h := hA.2 (e.symm x) + rw [ContinuousLinearMap.reApplyInnerSelf] at h ⊢ + rw [LinearIsometryEquiv.conjStarAlgEquiv_apply_apply] + have hx : ⟪e (A (e.symm x)), x⟫_𝕜 = ⟪A (e.symm x), e.symm x⟫_𝕜 := by + rw [← e.inner_map_map (A (e.symm x)) (e.symm x), e.apply_symm_apply] + rwa [hx] + +/-- An operator is positive exactly when its conjugate by a linear isometry equivalence is. -/ +theorem _root_.ContinuousLinearMap.isPositive_conjStarAlgEquiv_iff + (e : E ≃ₗᵢ[𝕜] F) (A : E →L[𝕜] E) : + (e.conjStarAlgEquiv A).IsPositive ↔ A.IsPositive := by + refine ⟨fun h ↦ ?_, fun h ↦ h.conjStarAlgEquiv e⟩ + have := h.conjStarAlgEquiv e.symm + rwa [← LinearIsometryEquiv.symm_conjStarAlgEquiv, StarAlgEquiv.symm_apply_apply] at this + +end Conjugation + +section Transfer + +variable [Fintype ι] [DecidableEq ι] [CompleteSpace E] + +/-- The matrix of an operator is positive semidefinite exactly when the operator is positive. -/ +theorem posSemidef_toMatOf_iff (b : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) : + (toMatOf b A).PosSemidef ↔ A.IsPositive := by + rw [← Matrix.isPositive_toEuclideanLin_iff] + have hcoe : (toMatOf b A).toEuclideanLin = + ((b.repr.conjStarAlgEquiv A : EuclideanSpace 𝕜 ι →L[𝕜] EuclideanSpace 𝕜 ι) : + EuclideanSpace 𝕜 ι →ₗ[𝕜] EuclideanSpace 𝕜 ι) := by + rw [← Matrix.coe_toEuclideanCLM_eq_toEuclideanLin] + congr 1 + exact (Matrix.toEuclideanCLM (𝕜 := 𝕜) (n := ι)).apply_symm_apply _ + rw [hcoe, ContinuousLinearMap.isPositive_toLinearMap_iff, + ContinuousLinearMap.isPositive_conjStarAlgEquiv_iff] + +/-- The matrix of an operator is positive semidefinite exactly when the operator is nonnegative in +the Loewner order. -/ +theorem posSemidef_toMatOf_iff_nonneg (b : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) : + (toMatOf b A).PosSemidef ↔ 0 ≤ A := + (posSemidef_toMatOf_iff b A).trans (ContinuousLinearMap.nonneg_iff_isPositive A).symm + +/-- The matrix of an operator is Hermitian exactly when the operator is self-adjoint. -/ +theorem isHermitian_toMatOf_iff (b : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) : + (toMatOf b A).IsHermitian ↔ IsSelfAdjoint A := by + have hst : toMatOf b (star A) = star (toMatOf b A) := (toMatOf b).map_star' A + refine ⟨fun h ↦ EquivLike.injective (toMatOf b) ?_, fun h ↦ ?_⟩ + · rw [hst] + exact h + · show (toMatOf b A)ᴴ = toMatOf b A + rw [← Matrix.star_eq_conjTranspose, ← hst, h.star_eq] + +variable [FiniteDimensional 𝕜 E] + +/-- The matrix of an operator has the same trace as the operator. -/ +theorem trace_toMatOf (b : OrthonormalBasis ι 𝕜 E) (A : E →L[𝕜] E) : + (toMatOf b A).trace = LinearMap.trace 𝕜 E (A : E →ₗ[𝕜] E) := by + rw [toMatOf_eq_toMatrixOrthonormal, LinearMap.trace_eq_matrix_trace 𝕜 b.toBasis] + rfl + +end Transfer + +section Preferred + +variable [Fintype ι] [DecidableEq ι] [StdBasis 𝕜 E ι] + +@[simp] +theorem toMat_apply (A : E →L[𝕜] E) (i j : ι) : + toMat 𝕜 E ι A i j = ⟪stdBasis (𝕜 := 𝕜) (E := E) i, A (stdBasis (𝕜 := 𝕜) (E := E) j)⟫_𝕜 := + toMatOf_apply _ A i j + +variable (𝕜 E ι) in +/-- `StdBasis.toMat` as a `𝕜`-linear equivalence, for use where only the linear structure is +needed -- for instance in transporting a linear map of operators to a linear map of matrices. -/ +noncomputable def toMatₗ : (E →L[𝕜] E) ≃ₗ[𝕜] Matrix ι ι 𝕜 := + (toMat 𝕜 E ι).toAlgEquiv.toLinearEquiv + +@[simp] +theorem toMatₗ_apply (A : E →L[𝕜] E) : toMatₗ 𝕜 E ι A = toMat 𝕜 E ι A := + rfl + +@[simp] +theorem toMatₗ_symm_apply (M : Matrix ι ι 𝕜) : (toMatₗ 𝕜 E ι).symm M = (toMat 𝕜 E ι).symm M := + rfl + +theorem toMat_eq_toMatrixOrthonormal (A : E →L[𝕜] E) : + toMat 𝕜 E ι A = LinearMap.toMatrixOrthonormal (stdBasis (𝕜 := 𝕜) (E := E)) (A : E →ₗ[𝕜] E) := + toMatOf_eq_toMatrixOrthonormal _ A + +/-- The matrix of an operator in the preferred basis is positive semidefinite exactly when the +operator is positive. -/ +theorem posSemidef_toMat_iff (A : E →L[𝕜] E) : (toMat 𝕜 E ι A).PosSemidef ↔ A.IsPositive := + posSemidef_toMatOf_iff _ A + +/-- The matrix of an operator in the preferred basis is positive semidefinite exactly when the +operator is nonnegative in the Loewner order. -/ +theorem posSemidef_toMat_iff_nonneg (A : E →L[𝕜] E) : (toMat 𝕜 E ι A).PosSemidef ↔ 0 ≤ A := + posSemidef_toMatOf_iff_nonneg _ A + +/-- The matrix of an operator in the preferred basis is Hermitian exactly when the operator is +self-adjoint. -/ +theorem isHermitian_toMat_iff (A : E →L[𝕜] E) : (toMat 𝕜 E ι A).IsHermitian ↔ IsSelfAdjoint A := + isHermitian_toMatOf_iff _ A + +/-- The matrix of an operator in the preferred basis has the same trace as the operator. -/ +@[simp] +theorem trace_toMat (A : E →L[𝕜] E) : + (toMat 𝕜 E ι A).trace = LinearMap.trace 𝕜 E (A : E →ₗ[𝕜] E) := + trace_toMatOf _ A + +end Preferred + +section Equiv + +variable [Fintype ι] [DecidableEq ι] [StdBasis 𝕜 E ι] [StdBasis 𝕜 F ι] + +variable (𝕜 E F ι) in +/-- The linear isometry equivalence between two spaces carrying preferred orthonormal bases with +the same index type: the one matching up the two preferred bases. + +This is how a space whose preferred basis happens to be indexed by a product `ι × κ` -- for +instance `EuclideanSpace 𝕜 (ι × κ)` -- gets identified with an actual tensor product. -/ +noncomputable def equiv : E ≃ₗᵢ[𝕜] F := + (stdBasis (𝕜 := 𝕜) (E := E)).repr.trans (stdBasis (𝕜 := 𝕜) (E := F)).repr.symm + +@[simp] +theorem equiv_stdBasis (i : ι) : + equiv 𝕜 E F ι (stdBasis (𝕜 := 𝕜) (E := E) i) = stdBasis (𝕜 := 𝕜) (E := F) i := by + rw [equiv, LinearIsometryEquiv.trans_apply, OrthonormalBasis.repr_self, + OrthonormalBasis.repr_symm_single] + +@[simp] +theorem equiv_symm_stdBasis (i : ι) : + (equiv 𝕜 E F ι).symm (stdBasis (𝕜 := 𝕜) (E := F) i) = stdBasis (𝕜 := 𝕜) (E := E) i := by + rw [← equiv_stdBasis (𝕜 := 𝕜) (E := E) (F := F) i, LinearIsometryEquiv.symm_apply_apply] + +variable [CompleteSpace E] [CompleteSpace F] + +/-- Transporting an operator along `StdBasis.equiv` leaves its matrix unchanged: that is exactly +what it means for `StdBasis.equiv` to identify the two preferred bases. -/ +@[simp] +theorem toMat_conjStarAlgEquiv_equiv (A : E →L[𝕜] E) : + toMat 𝕜 F ι ((equiv 𝕜 E F ι).conjStarAlgEquiv A) = toMat 𝕜 E ι A := by + ext i j + rw [toMat_apply, toMat_apply, LinearIsometryEquiv.conjStarAlgEquiv_apply_apply, + equiv_symm_stdBasis, ← equiv_stdBasis (𝕜 := 𝕜) (E := E) (F := F) i, + LinearIsometryEquiv.inner_map_map] + +end Equiv + +section Relabel + +variable [Fintype ι] [DecidableEq ι] [StdBasis 𝕜 E ι] [Fintype κ] [DecidableEq κ] [StdBasis 𝕜 F κ] + +/-- A linear isometry equivalence that carries the preferred basis of `E` to the preferred basis of +`F`, relabelling indices along `σ`, relabels matrices along `σ` as well. + +`toMat_conjStarAlgEquiv_equiv` is the case `σ = Equiv.refl`; the case of interest with `σ ≠ refl` +is an isometry that rearranges tensor factors, such as `TensorProduct.assocIsometry`. -/ +theorem toMat_conjStarAlgEquiv_of_stdBasis (e : E ≃ₗᵢ[𝕜] F) (σ : ι ≃ κ) + (he : ∀ i, e (stdBasis (𝕜 := 𝕜) (E := E) i) = stdBasis (𝕜 := 𝕜) (E := F) (σ i)) + (A : E →L[𝕜] E) : + toMat 𝕜 F κ (e.conjStarAlgEquiv A) = (toMat 𝕜 E ι A).submatrix σ.symm σ.symm := by + have he' (k : κ) : stdBasis (𝕜 := 𝕜) (E := F) k = e (stdBasis (𝕜 := 𝕜) (E := E) (σ.symm k)) := by + rw [he, Equiv.apply_symm_apply] + have hsymm (k : κ) : + e.symm (stdBasis (𝕜 := 𝕜) (E := F) k) = stdBasis (𝕜 := 𝕜) (E := E) (σ.symm k) := by + rw [he', LinearIsometryEquiv.symm_apply_apply] + ext k l + rw [Matrix.submatrix_apply, toMat_apply, toMat_apply, + LinearIsometryEquiv.conjStarAlgEquiv_apply_apply, hsymm, he' k, + LinearIsometryEquiv.inner_map_map] + +end Relabel + +section TensorRearrange + +variable {G μ : Type*} [NormedAddCommGroup G] [InnerProductSpace 𝕜 G] +variable [Fintype ι] [Fintype κ] [Fintype μ] +variable [StdBasis 𝕜 E ι] [StdBasis 𝕜 F κ] [StdBasis 𝕜 G μ] + +/-- Reassociating a triple tensor product carries preferred basis vectors to preferred basis +vectors, relabelling the index along `Equiv.prodAssoc`. -/ +@[simp] +theorem assocIsometry_stdBasis (p : (ι × κ) × μ) : + TensorProduct.assocIsometry 𝕜 E F G (stdBasis (𝕜 := 𝕜) (E := (E ⊗[𝕜] F) ⊗[𝕜] G) p) = + stdBasis (𝕜 := 𝕜) (E := E ⊗[𝕜] (F ⊗[𝕜] G)) (Equiv.prodAssoc ι κ μ p) := by + simp [OrthonormalBasis.tensorProduct_apply'] + +/-- Swapping the two factors of a tensor product carries preferred basis vectors to preferred +basis vectors, relabelling the index along `Equiv.prodComm`. -/ +@[simp] +theorem commIsometry_stdBasis (p : ι × κ) : + TensorProduct.commIsometry 𝕜 E F (stdBasis (𝕜 := 𝕜) (E := E ⊗[𝕜] F) p) = + stdBasis (𝕜 := 𝕜) (E := F ⊗[𝕜] E) (Equiv.prodComm ι κ p) := by + simp [OrthonormalBasis.tensorProduct_apply'] + +end TensorRearrange + +end StdBasis diff --git a/QuantumInfo/ForMathlib/Superadditive.lean b/QuantumInfo/ForMathlib/Superadditive.lean index b077353d2..742eb2ff7 100644 --- a/QuantumInfo/ForMathlib/Superadditive.lean +++ b/QuantumInfo/ForMathlib/Superadditive.lean @@ -33,7 +33,7 @@ theorem tendsto_lim (hbdd : BddAbove (Set.range fun n => u n / n)) : ) convert this.neg using 1 · ext; rw [neg_div', neg_neg] - · simp only [lim, Subadditive.lim, Real.sInf_def, neg_neg, nhds_eq_nhds_iff, + · simp only [lim, Subadditive.lim, Real.sInf_def, neg_neg, ← Set.image_neg_eq_neg, Set.image_image, neg_div', neg_neg] end Superadditive diff --git a/QuantumInfo/Measurements/POVM.lean b/QuantumInfo/Measurements/POVM.lean index 19ef5695c..ac4a6307e 100644 --- a/QuantumInfo/Measurements/POVM.lean +++ b/QuantumInfo/Measurements/POVM.lean @@ -51,66 +51,80 @@ namespace POVM variable {X : Type*} {d : Type*} [Fintype X] [Fintype d] [DecidableEq d] [DecidableEq X] +open Kronecker in +/-- The matrix of the measurement channel `POVM.measurementMap`. -/ +def measurementMatrixMap (Λ : POVM X d) : MatrixMap d (d × X) ℂ := + ∑ (x : X), { + toFun := fun ρ ↦ ((((Λ.mats x) ^ (1/2:ℝ)).mat * ρ * ((Λ.mats x)^(1/2:ℝ)).mat) ⊗ₖ Matrix.single x x 1) + map_add' := by simp [mul_add, add_mul, Matrix.kroneckerMap_add_left] + map_smul' := by simp [Matrix.smul_kronecker] + } + +theorem measurementMatrixMap_cp (Λ : POVM X d) : + MatrixMap.IsCompletelyPositive Λ.measurementMatrixMap := by + rw [measurementMatrixMap] + apply Finset.sum_induction + · exact fun _ _ ha ↦ ha.add + · exact MatrixMap.IsCompletelyPositive.zero _ _ + · intro x _ + --Note: this map M₁ would do as well as an object on its own, it's "measure and forget the result". + let M₁ : MatrixMap d d ℂ := ⟨⟨ + fun ρ ↦ ((Λ.mats x) ^ (1/2:ℝ)).mat * ρ * ((Λ.mats x)^(1/2:ℝ)).mat, + by simp [mul_add, add_mul]⟩, + by simp⟩ + let M₂ : MatrixMap d (d × X) ℂ := ⟨⟨ + fun ρ ↦ (ρ.kronecker (Matrix.single x x 1)), + by simp [add_mul, Matrix.kroneckerMap_add_left]⟩, + by simp [Matrix.smul_kronecker]⟩ + set M₃ := LinearMap.comp M₂ M₁ with hM₃ + simp only [M₁, M₂, LinearMap.comp, kronecker, LinearMap.coe_mk, AddHom.coe_mk] at hM₃ + rw [← hM₃] + apply MatrixMap.IsCompletelyPositive.comp + · dsimp [M₁] + conv => + enter [1, 1, 1, ρ, 2] + rw [← HermitianMat.conjTranspose_mat] + exact MatrixMap.conj_isCompletelyPositive (Λ.mats x ^ (1 / 2)).mat + · apply MatrixMap.kron_kronecker_const + exact (Matrix.PosSemidef.stdBasisMatrix_iff_eq x x (zero_lt_one' ℂ)).2 rfl + +theorem measurementMatrixMap_TP (Λ : POVM X d) : + MatrixMap.IsTracePreserving Λ.measurementMatrixMap := by + intro x + rw [measurementMatrixMap, LinearMap.sum_apply, trace_sum] + dsimp + simp only [Matrix.trace_kronecker, Matrix.trace_mul_cycle (B := x), + Matrix.trace_single_eq_same, mul_one] + rw [← trace_sum, ← Finset.sum_mul] + congr + convert one_mul x + rw [show (1 : Matrix d d ℂ) = (1 : HermitianMat d ℂ).mat by rfl, ← Λ.normalized] + rw [HermitianMat.mat_finset_sum] + congr! with i _ + exact HermitianMat.pow_half_mul (Λ.nonneg i) + /-- The act of measuring is a quantum channel, that maps a `d`-dimensional quantum state to an `d × X`-dimensional quantum-classical state. -/ -def measurementMap (Λ : POVM X d) : CPTPMap d (d × X) where - toLinearMap := - ∑ (x : X), open Kronecker in { - toFun := fun ρ ↦ ((((Λ.mats x) ^ (1/2:ℝ)).mat * ρ * ((Λ.mats x)^(1/2:ℝ)).mat) ⊗ₖ Matrix.single x x 1) - map_add' := by simp [mul_add, add_mul, Matrix.kroneckerMap_add_left] - map_smul' := by simp [Matrix.smul_kronecker] - } - cp := by - apply Finset.sum_induction - · exact fun _ _ ha ↦ ha.add - · exact MatrixMap.IsCompletelyPositive.zero _ _ - · intro x _ - --Note: this map M₁ would do as well as an object on its own, it's "measure and forget the result". - let M₁ : MatrixMap d d ℂ := ⟨⟨ - fun ρ ↦ ((Λ.mats x) ^ (1/2:ℝ)).mat * ρ * ((Λ.mats x)^(1/2:ℝ)).mat, - by simp [mul_add, add_mul]⟩, - by simp⟩ - let M₂ : MatrixMap d (d × X) ℂ := ⟨⟨ - fun ρ ↦ (ρ.kronecker (Matrix.single x x 1)), - by simp [add_mul, Matrix.kroneckerMap_add_left]⟩, - by simp [Matrix.smul_kronecker]⟩ - set M₃ := LinearMap.comp M₂ M₁ with hM₃ - simp only [M₁, M₂, LinearMap.comp, kronecker, LinearMap.coe_mk, AddHom.coe_mk] at hM₃ - rw [← hM₃] - apply MatrixMap.IsCompletelyPositive.comp - · dsimp [M₁] - conv => - enter [1, 1, 1, ρ, 2] - rw [← HermitianMat.conjTranspose_mat] - exact MatrixMap.conj_isCompletelyPositive (Λ.mats x ^ (1 / 2)).mat - · apply MatrixMap.kron_kronecker_const - exact (Matrix.PosSemidef.stdBasisMatrix_iff_eq x x (zero_lt_one' ℂ)).2 rfl - TP := by - intro x - rw [LinearMap.sum_apply, trace_sum] - dsimp - simp only [Matrix.trace_kronecker, Matrix.trace_mul_cycle (B := x), - Matrix.trace_single_eq_same, mul_one] - rw [← trace_sum, ← Finset.sum_mul] - congr - convert one_mul x - rw [show (1 : Matrix d d ℂ) = (1 : HermitianMat d ℂ).mat by rfl, ← Λ.normalized] - rw [HermitianMat.mat_finset_sum] - congr! with i _ - exact HermitianMat.pow_half_mul (Λ.nonneg i) +def measurementMap (Λ : POVM X d) : CPTPMap d (d × X) := + CPTPOp.ofMat Λ.measurementMatrixMap Λ.measurementMatrixMap_cp Λ.measurementMatrixMap_TP + +/-- **Matrix analogue of the measurement channel**. -/ +@[simp] +theorem measurementMap_map (Λ : POVM X d) : + Λ.measurementMap.map = Λ.measurementMatrixMap := + CPTPOp.map_ofMat _ _ _ open Kronecker in theorem measurementMap_apply_matrix (Λ : POVM X d) (m : Matrix d d ℂ) : Λ.measurementMap.map m = ∑ x : X, ((((Λ.mats x) ^ (1/2:ℝ)).mat * m * ((Λ.mats x)^(1/2:ℝ)).mat) ⊗ₖ Matrix.single x x 1) := by - dsimp [measurementMap, HPMap.map] - rw [LinearMap.sum_apply] + rw [measurementMap_map, measurementMatrixMap, LinearMap.sum_apply] rfl set_option backward.isDefEq.respectTransparency false in open HermitianMat in theorem measurementMap_apply_hermitianMat (Λ : POVM X d) (m : HermitianMat d ℂ) : - Λ.measurementMap.toHPMap m = ∑ x : X, + Λ.measurementMap.toHPOp m = ∑ x : X, --TODO: Something like `HermitianMat.single` to make this better ((m.conj ((Λ.mats x)^(1/2:ℝ)).mat : HermitianMat d ℂ) ⊗ₖ HermitianMat.diagonal ℂ (fun y ↦ ite (x = y) 1 0)) := by ext1 @@ -119,7 +133,7 @@ theorem measurementMap_apply_hermitianMat (Λ : POVM X d) (m : HermitianMat d kronecker_mat, mat_mk] congr! ext i j - simp only [HermitianMat.diagonal, mat_mk, diagonal_apply, single, of_apply] + simp only [HermitianMat.diagonal, mat_mk, Matrix.diagonal_apply, single, of_apply] split_ifs <;> grind only [= map_zero, = map_one] /-- A POVM leads to a distribution of outcomes on any given mixed state ρ. -/ @@ -132,16 +146,16 @@ def measure (Λ : POVM X d) (ρ : MState d) : ProbDistribution X := .mk' /-- The quantum-classical `POVM.measurement_map`, gives a marginal on the right equal to `POVM.measure`.-/ theorem traceLeft_measurementMap_eq_measure (Λ : POVM X d) (ρ : MState d) : - (Λ.measurementMap ρ).traceLeft = MState.ofClassical (Λ.measure ρ) := by + MState.traceLeft (Λ.measurementMap ρ) = MState.ofClassical (Λ.measure ρ) := by open Kronecker in + apply DensityOp.ext_m + rw [MState.traceLeft_m, CPTPOp.mat_coe_eq_apply_mat, MState.ofClassical, DensityOp.m_ofMat, + measurementMap_apply_matrix] ext i j - rcases ρ with ⟨⟨ρ, ρH⟩, hρ0, hρ1⟩ - change (Matrix.traceLeft (Λ.measurementMap.map ρ)) i j = _ - rw [measurementMap_apply_matrix] --TODO: a lemma for Matrix.traceLeft (∑ x, _) = ∑ x, (Matrix.traceLeft _) simp_rw [Matrix.traceLeft, Matrix.of_apply, Matrix.sum_apply] rw [Finset.sum_comm] - simp only [kroneckerMap_apply, MState.coe_ofClassical] + simp only [kroneckerMap_apply] simp only [single, of_apply, mul_ite, mul_one, mul_zero, Finset.sum_ite_irrel, Finset.sum_const_zero] simp only [HermitianMat.diagonal, HermitianMat.mat_mk, diagonal_apply] @@ -159,7 +173,7 @@ theorem traceLeft_measurementMap_eq_measure (Λ : POVM X d) (ρ : MState d) : the mixed state recording the outcome. This resulting state is purely diagonal, as given in `POVM.measureDiscard_apply`. -/ noncomputable def measureDiscard (Λ : POVM X d) : CPTPMap d X := - CPTPMap.traceLeft ∘ₘ Λ.measurementMap + CPTPOp.traceLeft ∘ₘ Λ.measurementMap theorem measureDiscard_apply (Λ : POVM X d) (ρ : MState d) : Λ.measureDiscard ρ = MState.ofClassical (Λ.measure ρ) := by @@ -168,22 +182,20 @@ theorem measureDiscard_apply (Λ : POVM X d) (ρ : MState d) : /-- The action of measuring a state with the POVM `Λ`, forgetting the measurement outcome, and keeping the disturbed state. -/ noncomputable def measureForget (Λ : POVM X d) : CPTPMap d d := - CPTPMap.traceRight ∘ₘ Λ.measurementMap + CPTPOp.traceRight ∘ₘ Λ.measurementMap theorem measureForget_eq_kraus (Λ : POVM X d) : - Λ.measureForget = CPTPMap.of_kraus_CPTPMap (fun i ↦ (Λ.mats i) ^ (1/2 : ℝ)) (by + Λ.measureForget = CPTPOp.of_kraus_CPTPMap (fun i ↦ (Λ.mats i) ^ (1/2 : ℝ)) (by simpa [-one_div, fun x ↦ HermitianMat.pow_half_mul (Λ.nonneg x), HermitianMat.ext_iff] using Λ.normalized ) := by - apply CPTPMap.funext + rw [CPTPOp.funext_iff] intro ρ - apply MState.ext_m - rw [CPTPMap.mat_coe_eq_apply_mat (Λ := Λ.measureForget) (ρ := ρ)] + apply DensityOp.ext_m + rw [measureForget, CPTPOp.compose_eq, CPTPOp.traceRight_eq_MState_traceRight, + MState.traceRight_m, CPTPOp.mat_coe_eq_apply_mat, CPTPOp.mat_coe_eq_apply_mat, + CPTPOp.map_of_kraus_CPTPMap, measurementMap_apply_matrix] ext i j - change (Matrix.traceRight (Λ.measurementMap.map ρ.m)) i j = - (MatrixMap.of_kraus (fun i ↦ ((Λ.mats i) ^ (1 / 2 : ℝ)).mat) - (fun i ↦ ((Λ.mats i) ^ (1 / 2 : ℝ)).mat) ρ.m) i j - rw [measurementMap_apply_matrix] simp [Matrix.traceRight, MatrixMap.of_kraus] simp_rw [Matrix.sum_apply] refine Finset.sum_congr rfl fun x _ ↦ ?_ diff --git a/QuantumInfo/Operators/Unitary.lean b/QuantumInfo/Operators/Unitary.lean index b3794ad31..1d06b8738 100644 --- a/QuantumInfo/Operators/Unitary.lean +++ b/QuantumInfo/Operators/Unitary.lean @@ -7,10 +7,11 @@ module public import QuantumInfo.States.Mixed.MState -/-! # Unitary operators on quantum state +/-! # Unitary evolution of quantum states -This file is intended for lemmas about unitary matrices (`Matrix.unitaryGroup`) and how they -apply to `Bra`s, `Ket`s, and `MState` mixed states. +This file is about the action of a unitary on a state, by conjugation. The basis-free version +`DensityOp.uConj` takes a unitary operator; `MState.uConj`, notated `U ◃ ρ`, is the matrix +analogue, taking a unitary matrix (`Matrix.unitaryGroup`). This is imported by `CPTPMap` to define things like unitary channels, Kraus operators, and complementary channels, so this file itself does not discuss channels yet. -/ @@ -22,6 +23,31 @@ noncomputable section open RealInnerProductSpace open InnerProductSpace +namespace DensityOp + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] + +/-- Conjugate a state by a unitary operator (applying the unitary as an evolution). -/ +def uConj (ρ : DensityOp E) (U : unitary (E →L[ℂ] E)) : DensityOp E where + op := ρ.op.conj U.val + op_nonneg := HermitianOp.conj_nonneg ρ.op_nonneg U.val + op_trace := by rw [HermitianOp.trace_conj_unitary, ρ.op_trace] + +@[simp] +theorem uConj_op (ρ : DensityOp E) (U : unitary (E →L[ℂ] E)) : + (ρ.uConj U).op = ρ.op.conj U.val := + rfl + +/-- **Matrix analogue of `DensityOp.uConj`.** -/ +@[simp] +theorem uConj_M {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] (ρ : DensityOp E) + (U : unitary (E →L[ℂ] E)) : + ((ρ.uConj U).M : HermitianMat ι ℂ) = (ρ.M : HermitianMat ι ℂ).conj + (StdBasis.toMatUnitary (ι := ι) U).val := + HermitianOp.toMat_conj_unitary ρ.op U + +end DensityOp + namespace MState variable {d d₁ d₂ d₃ : Type*} @@ -30,16 +56,23 @@ variable [DecidableEq d] variable {ψ φ f : Ket d} /-- Conjugate a state by a unitary matrix (applying the unitary as an evolution). -/ -def uConj (ρ : MState d) (U : 𝐔[d]) : MState d where - M := ρ.M.conj U.val - nonneg := HermitianMat.conj_nonneg U.val ρ.nonneg - tr := by simp +def uConj (ρ : MState d) (U : 𝐔[d]) : MState d := + DensityOp.uConj ρ (StdBasis.unitaryOfMat U) /-- `MState.uConj`, the action of a unitary on a mixed state by conjugation. The ◃ notation comes from the theory of racks and quandles, where this is a conjugation-like operation. -/ scoped[MState] notation:80 U:80 " ◃ " ρ:81 => MState.uConj ρ U +@[simp] +theorem uConj_M (ρ : MState d) (U : 𝐔[d]) : (U ◃ ρ).M = ρ.M.conj U.val := by + rw [uConj, DensityOp.uConj_M, StdBasis.toMatUnitary_unitaryOfMat] + +@[simp] +theorem uConj_m (ρ : MState d) (U : 𝐔[d]) : + (U ◃ ρ).m = U.val * ρ.m * U.val.conjTranspose := by + rw [← DensityOp.mat_M, uConj_M, HermitianMat.conj_apply_mat, DensityOp.mat_M] + set_option backward.isDefEq.respectTransparency false in /-- You might think this should only be true up to permutation, so that it would read like `∃ σ : Equiv.Perm d, (ρ.uConj U).spectrum = ρ.spectrum.relabel σ`. But since eigenvalues @@ -48,11 +81,11 @@ of a matrix are always canonically sorted, this is actually an equality. @[simp] theorem uConj_spectrum_eq (ρ : MState d) (U : 𝐔[d]) : (ρ.uConj U).spectrum = ρ.spectrum := by - simp [spectrum, uConj] + simp [spectrum] @[simp] theorem inner_uConj (ρ σ : MState d) (U : 𝐔[d]) : ⟪U ◃ ρ, U ◃ σ⟫_Prob = ⟪ρ, σ⟫_Prob := by - simp [uConj, inner_def] + simp [inner_def] /-- The **No-cloning theorem**, saying that if states `ψ` and `φ` can both be perfectly cloned using a unitary `U` and a fiducial state `f`, and they aren't identical (their inner product is @@ -63,23 +96,13 @@ theorem no_cloning {U : 𝐔[d × d]} (hφ : U ◃ pure (φ ⊗ᵠ f) = pure (φ ⊗ᵠ φ)) (H : ⟪pure ψ, pure φ⟫_Prob < (1 : ℝ)) : ⟪pure ψ, pure φ⟫_Prob = (0 : ℝ) := by - set ρψ := pure ψ - set ρφ := pure φ - have h1 : ⟪ρψ, ρφ⟫_Prob * ⟪ρψ, ρφ⟫_Prob = ⟪pure (ψ ⊗ᵠ ψ), pure (φ ⊗ᵠ φ)⟫_Prob := by - grind only [pure_prod_pure, prod_inner_prod] - have h2 : (⟪pure (ψ ⊗ᵠ ψ), pure (φ ⊗ᵠ φ)⟫_Prob : ℝ) = - ⟪U ◃ pure (ψ ⊗ᵠ f), U ◃ pure (φ ⊗ᵠ f)⟫_Prob := by - grind only [pure_prod_pure] - replace h2 : ((pure (ψ ⊗ᵠ ψ)).m * (pure (φ ⊗ᵠ φ)).m).trace.re = (ρψ.m * ρφ.m).trace.re := by - convert! ← h2 - simp +zetaDelta only [inner_uConj, pure_prod_pure, prod] - simp [inner, ← Matrix.mul_kronecker_mul, pure_mul_self, - Matrix.trace_kronecker] - have h3 : (ρψ.m * ρφ.m).trace.re * ((ρψ.m * ρφ.m).trace.re - 1) = 0 := by - rw [mul_sub, sub_eq_zero, mul_one] - exact congr(Subtype.val $h1).trans h2 - rw [mul_eq_zero] at h3 - apply h3.resolve_right - exact sub_ne_zero_of_ne H.ne + have hff : ⟪pure f, pure f⟫_Prob = 1 := (pure_iff_purity_one _).mp ⟨f, rfl⟩ + -- Cloning turns the overlap into its own square: `x * x = x`. + have key : ⟪pure ψ, pure φ⟫_Prob * ⟪pure ψ, pure φ⟫_Prob = ⟪pure ψ, pure φ⟫_Prob := by + rw [← prod_inner_prod, ← pure_prod_pure, ← pure_prod_pure, ← hψ, ← hφ, inner_uConj, + pure_prod_pure, pure_prod_pure, prod_inner_prod, hff, mul_one] + have hx : (⟪pure ψ, pure φ⟫_Prob : ℝ) * ⟪pure ψ, pure φ⟫_Prob = ⟪pure ψ, pure φ⟫_Prob := by + exact_mod_cast congrArg Subtype.val key + nlinarith [hx, H, (Prob.zero_le_coe : (0:ℝ) ≤ ⟪pure ψ, pure φ⟫_Prob)] end MState diff --git a/QuantumInfo/Regularized.lean b/QuantumInfo/Regularized.lean index 8ee89edfa..eb6bc246f 100644 --- a/QuantumInfo/Regularized.lean +++ b/QuantumInfo/Regularized.lean @@ -36,9 +36,10 @@ variable {fn : ℕ → T} {_lb _ub : T} {hl : ∀ n, _lb ≤ fn n} {hu : ∀ n, /-- The `InfRegularized` value is also lower bounded. -/ theorem lb : _lb ≤ InfRegularized fn hl hu := by - convert le_csSup ?_ ?_; - · exact ⟨ _ub, fun a ha => by rcases Filter.eventually_atTop.mp ha with ⟨ n, hn ⟩ ; exact le_trans ( hn _ le_rfl ) ( hu _ ) ⟩; - · aesop + rw [InfRegularized, Filter.liminf_eq] + refine le_csSup ⟨_ub, fun a ha => ?_⟩ (Filter.Eventually.of_forall hl) + obtain ⟨n, hn⟩ := Filter.eventually_atTop.mp ha + exact (hn n le_rfl).trans (hu n) /-- The `InfRegularized` value is also upper bounded. -/ theorem ub : InfRegularized fn hl hu ≤ _ub := by @@ -62,12 +63,10 @@ theorem anti_inf (h : Antitone fn) : any particular value. -/ theorem anti_ub (h : Antitone fn) : ∀ n, InfRegularized fn hl hu ≤ fn n := by intro n - have h_inf_le : InfRegularized fn hl hu ≤ fn n := by - convert csSup_le _ _; - · exact ⟨ _lb, Filter.eventually_atTop.2 ⟨ 0, fun n hn => hl n ⟩ ⟩; - · simp +zetaDelta at *; - exact fun b x hx => le_trans ( hx ( Max.max x n ) ( le_max_left _ _ ) ) ( h ( le_max_right _ _ ) ) - exact h_inf_le + rw [InfRegularized, Filter.liminf_eq] + refine csSup_le ⟨_lb, Filter.Eventually.of_forall hl⟩ fun b hb => ?_ + obtain ⟨m, hm⟩ := Filter.eventually_atTop.mp hb + exact (hm (max m n) (le_max_left _ _)).trans (h (le_max_right _ _)) end InfRegularized @@ -121,41 +120,24 @@ end SupRegularized section real -private def realNegOrderIso : ℝ ≃o ℝᵒᵈ where - toEquiv := Equiv.neg ℝ - map_rel_iff' := by - intro a b - change (-b : ℝ) ≤ -a ↔ a ≤ b - simpa using neg_le_neg_iff - -private theorem limsup_eq_neg_liminf_neg {fn : ℕ → ℝ} {_lb _ub : ℝ}Expand commentComment on line R131Resolved - (hl : ∀ n, _lb ≤ fn n) (hu : ∀ n, fn n ≤ _ub) : - Filter.atTop.limsup fn = -Filter.atTop.liminf (fun n => -fn n) := by - have hneg : -Filter.atTop.limsup fn = Filter.atTop.liminf (fun n => -fn n) := by - have hdual := OrderIso.limsup_apply (f := Filter.atTop) (u := fn) realNegOrderIso - (hu := Filter.isBoundedUnder_of_eventually_le (f := Filter.atTop) (u := fn) - (Filter.Eventually.of_forall hu)) - (hu_co := Filter.isCoboundedUnder_le_of_le Filter.atTop hl) - (hgu := Filter.isBoundedUnder_of_eventually_le (α := ℝᵒᵈ) (f := Filter.atTop) - (u := fun n => (-fn n : ℝᵒᵈ)) (Filter.Eventually.of_forall fun n => neg_le_neg (hu n))) - (hgu_co := Filter.isCoboundedUnder_le_of_le (α := ℝᵒᵈ) Filter.atTop - (f := fun n => (-fn n : ℝᵒᵈ)) (x := (-_lb : ℝᵒᵈ)) fun n => neg_le_neg (hl n)) - simpa [Filter.limsup, Filter.liminf, Filter.limsSup, Filter.limsInf, realNegOrderIso] using - congrArg OrderDual.ofDual hdual - linarith - variable {fn : ℕ → ℝ} {_lb _ub : ℝ} {hl : ∀ n, _lb ≤ fn n} {hu : ∀ n, fn n ≤ _ub} +/-- Negating a real sequence swaps `liminf` and `limsup`. -/ +private theorem liminf_neg (s : ℕ → ℝ) : + Filter.liminf (fun n ↦ -s n) Filter.atTop = -Filter.limsup s Filter.atTop := by + rw [Filter.liminf_eq, Filter.limsup_eq, Real.sInf_def, neg_neg] + congr 1 + ext a + simp [le_neg] + theorem InfRegularized.to_SupRegularized : InfRegularized fn hl hu = -SupRegularized (-fn ·) (lb := -_ub) (ub := -_lb) (neg_le_neg_iff.mpr <| hu ·) (neg_le_neg_iff.mpr <| hl ·) := by - have liminf_neg : Filter.liminf fn Filter.atTop = -(Filter.limsup (-fn) Filter.atTop) := by - simp [Filter.limsup_eq, Filter.liminf_eq, Real.sInf_def] - exact Real.ext_cauchy (congrArg Real.cauchy liminf_neg) + rw [InfRegularized, SupRegularized, ← liminf_neg] + simp theorem SupRegularized.to_InfRegularized : SupRegularized fn hl hu = -InfRegularized (-fn ·) (lb := -_ub) (ub := -_lb) (neg_le_neg_iff.mpr <| hu ·) (neg_le_neg_iff.mpr <| hl ·) := by - unfold InfRegularized SupRegularized - exact limsup_eq_neg_liminf_neg hl hu + rw [InfRegularized, SupRegularized, liminf_neg, neg_neg] /-- For `Antitone` functions, the value `Filter.Tendsto` the `InfRegularized` value. -/ theorem InfRegularized.anti_tendsto (h : Antitone fn) : @@ -174,9 +156,9 @@ theorem InfRegularized.of_Subadditive (hf : Subadditive (fun n ↦ fn n * n)) rintro x ⟨y,(rfl : _ / _ = _)⟩ rcases y with (_|n) · simp - · rw [inf_le_iff] - convert Or.inr (hl (n+1)) - field_simp + · refine inf_le_right.trans ?_ + rw [mul_div_assoc, div_self (by positivity), mul_one] + exact hl (n + 1) ) have h₂ : Filter.Tendsto fn .atTop (nhds hf.lim) := by refine h₁.congr' ?_ diff --git a/QuantumInfo/ResourceTheory/FreeState.lean b/QuantumInfo/ResourceTheory/FreeState.lean index 45c83fee0..c0ea25c8b 100644 --- a/QuantumInfo/ResourceTheory/FreeState.lean +++ b/QuantumInfo/ResourceTheory/FreeState.lean @@ -102,13 +102,13 @@ theorem prodRelabel_relabel_cast_prod (ρ₁.relabel (Equiv.cast (congrArg H hik))) ⊗ᵣ (ρ₂.relabel (Equiv.cast (congrArg H hlj))) := by subst hik subst hlj - rfl + simp only [Equiv.cast_refl, MState.relabel_refl] /-- The `prod` operation of `ResourcePretheory` gives the natural product operation on `CPTPMap`s. Accessible by the notation `M₁ ⊗ᶜᵖᵣ M₂`. -/ noncomputable def prodCPTPMap (M₁ : CPTPMap (H i) (H j)) (M₂ : CPTPMap (H k) (H l)) : CPTPMap (H (i * k)) (H (j * l)) := - (CPTPMap.ofEquiv (prodEquiv j l).symm).compose ((M₁ ⊗ᶜᵖ M₂).compose (CPTPMap.ofEquiv (prodEquiv i k))) + (CPTPOp.ofEquiv (prodEquiv j l).symm).compose ((M₁ ⊗ᶜᵖ M₂).compose (CPTPOp.ofEquiv (prodEquiv i k))) @[inherit_doc] scoped notation M₁ " ⊗ᶜᵖᵣ " M₂ => prodCPTPMap M₁ M₂ @@ -325,7 +325,7 @@ lemma sInf_spectrum_spacePow (σ : MState (H i)) (n : ℕ) : enter [1, 1, 2] equals 1 => ext1 - simp [default, MState.uniform, MState.ofClassical, MState.m, HermitianMat.diagonal] + simp [default, MState.uniform, MState.ofClassical, DensityOp.m, HermitianMat.diagonal] rw [spectrum.one_eq, csInf_singleton] · rename_i n ih rw [statePow_succ, sInf_spectrum_rprod, ih, pow_succ] @@ -357,7 +357,7 @@ class FreeStateTheory (ι : Type*) extends ResourcePretheory ι where /-- The set F(H) of free states is closed -/ free_closed : IsClosed (@IsFree i) /-- The set F(H) of free states is convex (more precisely, their matrices are) -/ - free_convex : Convex ℝ (MState.M '' (@IsFree i)) + free_convex : Convex ℝ (DensityOp.M '' (@IsFree i)) /-- The set of free states is closed under tensor product -/ free_prod {ρ₁ : MState (H i)} {ρ₂ : MState (H j)} (h₁ : IsFree ρ₁) (h₂ : IsFree ρ₂) : IsFree (ρ₁ ⊗ᵣ ρ₂) /-- The set F(H) of free states contains a full-rank state `ρfull`, equivalently `ρfull` is positive definite. -/ @@ -387,7 +387,10 @@ theorem IsFree.mix {ι : Type*} [FreeStateTheory ι] {i : ι} {σ₁ σ₂ : MSt obtain ⟨m, hm₁, hm₂⟩ := free_convex (i := i) ⟨σ₁, hσ₁, rfl⟩ ⟨σ₂, hσ₂, rfl⟩ p.zero_le (1 - p).zero_le (by simp) simp [Mixable.mix, Mixable.mix_ab, MState.instMixable] simp at hm₂ - convert! ← hm₁ + refine Set.mem_of_eq_of_mem ?_ hm₁ + refine DensityOp.ext (ι := H i) ?_ + rw [DensityOp.M_ofMat] + exact hm₂.symm end FreeStateTheory @@ -467,7 +470,11 @@ theorem RelativeEntResource.Subadditive (ρ : MState (H i)) : Subadditive fun n exact free_prod hσ₂f hσ₃f · apply le_of_eq rw [← qRelEntropy_prodRelabel] - exact qRelEntropy_heq_congr ht (statePow_add ρ m n) (by rw [MState.relabel_cast]; exact cast_heq _ _) + refine qRelEntropy_heq_congr ht ?_ ?_ + · apply statePow_add + · rw [← eq_cast_iff_heq] + apply MState.relabel_cast + rw [spacePow_add] noncomputable def RegularizedRelativeEntResource (ρ : MState (H i)) : ℝ≥0 := ⟨(RelativeEntResource.Subadditive ρ).lim, by diff --git a/QuantumInfo/ResourceTheory/HypothesisTesting.lean b/QuantumInfo/ResourceTheory/HypothesisTesting.lean index 1be06a322..4a31f0b47 100644 --- a/QuantumInfo/ResourceTheory/HypothesisTesting.lean +++ b/QuantumInfo/ResourceTheory/HypothesisTesting.lean @@ -87,7 +87,7 @@ theorem iInf_IsConvex (ρ : MState d) (ε : Prob) : Convex ℝ { m | ρ.exp_val rw [← eq_sub_iff_add_eq'] at hab subst b refine And.intro ?_ (And.intro ?_ ?_) - · simp only [MState.exp_val, inner_sub_right, HermitianMat.inner_one, MState.tr, + · simp only [MState.exp_val, inner_sub_right, HermitianMat.inner_one, DensityOp.tr, tsub_le_iff_right, inner_add_right, inner_smul_right] at hx₁ hy₁ ⊢ linear_combination a * hx₁ + (1 - a) * hy₁ · apply HermitianMat.convex_cone <;> assumption @@ -159,7 +159,7 @@ theorem exists_min' (ρ : MState d) (ε : Prob) (S : Set (MState d)): refine Continuous.comp (g := fun T ↦ ⨆ (i : S), i.val.exp_val T) ?_ continuous_subtype_val convert! h with T rw [← sSup_image' (s := S) (f := fun i ↦ i.exp_val T)] - rw [← sSup_image' (s := (MState.M '' S)) (f := fun i ↦ i.innerₗ T)] + rw [← sSup_image' (s := (DensityOp.M '' S)) (f := fun i ↦ i.innerₗ T)] simp [Set.image, MState.exp_val, HermitianMat.innerₗ] ) clear hT₁ @@ -268,7 +268,7 @@ theorem pos_of_lt_one {ρ : MState d} (S : Set (MState d)) set_option backward.isDefEq.respectTransparency false in --Lemma 3 from Hayashi theorem Lemma3 {ρ : MState d} (ε : Prob) {S : Set (MState d)} (hS₁ : IsCompact S) - (hS₂ : Convex ℝ (MState.M '' S)) : ⨆ σ ∈ S, β_ ε(ρ‖{σ}) = β_ ε(ρ‖S) := by + (hS₂ : Convex ℝ (DensityOp.M '' S)) : ⨆ σ ∈ S, β_ ε(ρ‖{σ}) = β_ ε(ρ‖S) := by --Work out the case where S is empty, so we can now assume it's nonempty rcases S.eq_empty_or_nonempty with rfl|hnS · simpa using _root_.bot_eq_zero @@ -280,7 +280,7 @@ theorem Lemma3 {ρ : MState d} (ε : Prob) {S : Set (MState d)} (hS₁ : IsCompa --This parts needs the minimax theorem. Set up the relevant sets and hypotheses. --The function `f` will be the `MState.exp_val` function, but bundled as a bilinear form. let f : LinearMap.BilinForm ℝ (HermitianMat d ℂ) := HermitianMat.innerₗ - let S' : Set (HermitianMat d ℂ) := MState.M '' S + let S' : Set (HermitianMat d ℂ) := DensityOp.M '' S let T' : Set (HermitianMat d ℂ) := { m | ρ.exp_val (1 - m) ≤ ε ∧ 0 ≤ m ∧ m ≤ 1 } have hS'₁ : IsCompact S' := hS₁.image MState.Continuous_HermitianMat @@ -336,14 +336,14 @@ theorem optimalHypothesisRate_antitone (ρ σ : MState d) (ℰ : CPTPMap d d₂) β_ ε(ρ‖{σ}) ≤ β_ ε(ℰ ρ‖{ℰ σ}) := by simp only [of_singleton] obtain ⟨ℰdualSubtype, h⟩ : - ∃ e : ({ m : HermitianMat d₂ ℂ // (ℰ ρ).exp_val (1 - m) ≤ ε ∧ 0 ≤ m ∧ m ≤ 1} → + ∃ e : ({ m : HermitianMat d₂ ℂ // MState.exp_val (ℰ ρ) (1 - m) ≤ ε ∧ 0 ≤ m ∧ m ≤ 1} → { m : HermitianMat d ℂ // ρ.exp_val (1 - m) ≤ ε ∧ 0 ≤ m ∧ m ≤ 1}), ∀ x, e x = ℰ.hermDual x := by constructor; swap · rintro ⟨m, hm₁, hm₂⟩ - refine ⟨ℰ.toPTPMap.hermDual m, ?_, PTPMap.hermDual.PTP_POVM ℰ.toPTPMap hm₂⟩ - have hℰd : (ℰ ρ).exp_val (1 - m) = ρ.exp_val (ℰ.hermDual (1 - m)) := + refine ⟨ℰ.toPTPOp.hermDual m, ?_, PTPOp.hermDual.PTP_POVM ℰ.toPTPOp hm₂⟩ + have hℰd : MState.exp_val (ℰ ρ) (1 - m) = ρ.exp_val (ℰ.hermDual (1 - m)) := ℰ.exp_val_hermDual ρ (1 - m) simpa [hℰd] using hm₁ · rintro ⟨m, hm₁, hm₂⟩ @@ -367,11 +367,12 @@ theorem Ref81Lem5 (ρ σ : MState d) (ε : Prob) (hε : ε < 1) (α : ℝ) (hα —log β_ ε(ρ‖{σ}) ≤ D̃_ α(ρ‖σ) + —log (1 - ε) * (.ofNNReal ⟨α, zero_le_one.trans hα.le⟩) / (.ofNNReal ⟨α - 1, sub_nonneg_of_le hα.le⟩) := by - generalize_proofs pf1 pf2 + generalize_proofs pfF pf1 pf2 --If ρ isn't in the support of σ, the right hand side is just ⊤. (The left hand side is not, necessarily!) by_cases h_supp : σ.M.ker ≤ ρ.M.ker swap - · simp [SandwichedRelRentropy, h_supp, zero_lt_one.trans hα] + · rw [sandwichedRelRentropy_eq_top (zero_lt_one.trans hα) h_supp, top_add] + exact le_top --Now we know that ρ.support ≤ σ.support. This is the main case we actually care about. --Proof from https://link.springer.com/article/10.1007/s00220-016-2645-4 reproduced below. @@ -456,11 +457,13 @@ theorem Ref81Lem5 (ρ σ : MState d) (ε : Prob) (hε : ε < 1) (α : ℝ) (hα --The Renyi entropy is finite - rw [SandwichedRelRentropy, dif_pos (zero_lt_one.trans hα), dif_pos ?_]; swap + rw [MState.sandwichedRelRentropy_eq_matrix, dif_pos (zero_lt_one.trans hα), dif_pos ?_]; swap · suffices q2.M.ker = ⊥ by simp only [this, bot_le] --q2 has eigenvalues β_ ε(ρ‖{σ}) and 1-β_ ε(ρ‖{σ}), so as long as β_ ε(ρ‖{σ}) isn't 0 or 1, --this is true. + rw [show q2.M = HermitianMat.diagonal ℂ (ProbDistribution.coin q ·) from + MState.coe_ofClassical _] exact ker_diagonal_prob_eq_bot hq hq₂ conv => enter [2, 1, 1, 1]; rw [if_neg hα.ne'] diff --git a/QuantumInfo/ResourceTheory/ResourceTheory.lean b/QuantumInfo/ResourceTheory/ResourceTheory.lean index aa2348216..7aa27f75d 100644 --- a/QuantumInfo/ResourceTheory/ResourceTheory.lean +++ b/QuantumInfo/ResourceTheory/ResourceTheory.lean @@ -19,10 +19,15 @@ class ResourceTheory (ι : Type*) extends FreeStateTheory ι where --We might need to require some more closure properties on `freeOps`, like closure under tensor product...? --For now we just require that they include the identity and composition, so that we have at least a category. /-- The identity operation is free -/ - free_id i : CPTPMap.id ∈ freeOps i i + free_id i : CPTPOp.id ∈ freeOps i i /-- Free operations are closed under composition -/ free_comp {i j k} (Y : freeOps j k) (X : freeOps i j) : Y.1.compose X.1 ∈ freeOps i k +/-- A `ResourceTheory` whose index type is also a `UnitalPretheory`, so that there is a unit +object `1` and the product `i * j` of objects. This is the setting in which tensoriality of the +free operations can be stated. -/ +class UnitalResourceTheory (ι : Type*) extends ResourceTheory ι, UnitalPretheory ι + namespace ResourceTheory open ResourcePretheory open FreeStateTheory @@ -32,11 +37,14 @@ variable {ι : Type*} /-- Given a `FreeStateTheory`, there is a maximal set of free operations compatible with the free states. That is the set of all operations that don't generate non-free states from free states. We call this the maximal resource theory. -/ +@[instance_reducible] def maximal [FreeStateTheory ι] : ResourceTheory ι where freeOps i j := { f | ∀ ρ, IsFree ρ → IsFree (f ρ)} nongenerating := id - free_id _ _ _ := by rwa [CPTPMap.id_MState] - free_comp f g ρ h := f.prop _ (g.prop ρ h) + free_id _ _ _ := by rwa [CPTPOp.id_MState] + free_comp Y X ρ h := by + rw [CPTPOp.compose_eq] + exact Y.prop _ (X.prop ρ h) /-- A resource theory `IsMaximal` if it includes all non-generating operations. -/ def IsMaximal (r : ResourceTheory ι) : Prop := @@ -44,10 +52,11 @@ def IsMaximal (r : ResourceTheory ι) : Prop := /-- A resource theory `IsTensorial` if it includes tensor products of operations, creating free states, and discarding. This implies that includes a unit object. -/ -structure IsTensorial [UnitalPretheory ι] : Prop where - prod : ∀ {i j k l : ι} {f g}, f ∈ freeOps i k → g ∈ freeOps j l → (f ⊗ᶜᵖᵣ g) ∈ freeOps (prod i j) (prod k l) - create : ∀ {i : ι} (ρ), IsFree ρ → CPTPMap.replacement ρ ∈ freeOps Unital.unit i - destroy : ∀ (i : ι), CPTPMap.destroy ∈ freeOps i Unital.unit +structure IsTensorial [UnitalResourceTheory ι] : Prop where + prod : ∀ {i j k l : ι} {f g}, f ∈ freeOps i k → g ∈ freeOps j l → + (f ⊗ᶜᵖᵣ g) ∈ freeOps (i * j) (k * l) + create : ∀ {i : ι} (ρ), IsFree ρ → CPTPOp.replacement ρ ∈ freeOps 1 i + destroy : ∀ (i : ι), CPTPOp.destroy ∈ freeOps i 1 /-- The theory `ResourceTheory.maximal` always `IsMaximal`. -/ theorem maximal_IsMaximal [FreeStateTheory ι] : IsMaximal (maximal (ι := ι)) := @@ -55,8 +64,8 @@ theorem maximal_IsMaximal [FreeStateTheory ι] : IsMaximal (maximal (ι := ι)) -- --Helper theorem for ResourceTheory.mk_of_ops -- private lemma convex_states_of_convex_ops [ResourcePretheory ι] (O : ∀ (i j : ι), Set (CPTPMap (H i) (H j))) --- (h_convex : ∀ {i j}, Convex ℝ (CPTPMap.choi '' O i j)) (i : ι) : --- Convex ℝ (MState.M '' fun ρ ↦ ∀ {j} σ, ∃ f, O j i f ∧ f σ = ρ) := by +-- (h_convex : ∀ {i j}, Convex ℝ (CPTPOp.choi '' O i j)) (i : ι) : +-- Convex ℝ (DensityOp.M '' fun ρ ↦ ∀ {j} σ, ∃ f, O j i f ∧ f σ = ρ) := by -- intro _ hx _ hy a b ha hb hab -- rw [Set.mem_image] at hx hy ⊢ -- obtain ⟨x,hx1,hx2⟩ := hx @@ -73,16 +82,16 @@ theorem maximal_IsMaximal [FreeStateTheory ι] : IsMaximal (maximal (ι := ι)) -- rw [Set.mem_image] at h_convex -- obtain ⟨w,hw1,hw2⟩ := h_convex -- have : w = Mixable.mix_ab ha hb hab fx fy := by --- apply CPTPMap.choi_ext +-- apply CPTPOp.choi_ext -- convert hw2 -- --Should be a theorem -- simp only [Mixable.mix_ab, Mixable.mkT] --- exact CPTPMap.choi_of_CPTP_of_choi (a • fx.choi + b • fy.choi) +-- exact CPTPOp.choi_of_CPTP_of_choi (a • fx.choi + b • fy.choi) -- exact this ▸ hw1 --- · --Should be a theorem about CPTPMap.instMixable, really. Also, this proof is terrible. +-- · --Should be a theorem about CPTPOp.instMixable, really. Also, this proof is terrible. -- subst x y --- simp only [Mixable.mix_ab, Mixable.mkT, MState.instMixable, CPTPMap.instMFunLike, --- CPTPMap.CPTP_of_choi_PSD_Tr, CPTPMap.mk, MatrixMap.of_choi_matrix, Mixable.to_U] +-- simp only [Mixable.mix_ab, Mixable.mkT, MState.instMixable, CPTPOp.instMFunLike, +-- CPTPOp.CPTP_of_choi_PSD_Tr, CPTPOp.mk, MatrixMap.of_choi_matrix, Mixable.to_U] -- ext -- change (Finset.sum _ _) = ((_ : ℂ) + _) -- simp only [Matrix.add_apply, Matrix.smul_apply, Complex.real_smul] @@ -97,10 +106,10 @@ theorem maximal_IsMaximal [FreeStateTheory ι] : IsMaximal (maximal (ι := ι)) -- and then the free states are taken to be the set of states that can be prepared from any initial state. -- -/ -- def mk_of_ops [ResourcePretheory ι] (O : ∀ (i j : ι), Set (CPTPMap (H i) (H j))) --- (h_id : ∀ i, CPTPMap.id ∈ O i i) --Operations include identity +-- (h_id : ∀ i, CPTPOp.id ∈ O i i) --Operations include identity -- (h_comp : ∀ {i j k} (Y : O j k) (X : O i j), Y.1.compose X.1 ∈ O i k) --Operations include compositions -- (h_closed : ∀ {i j}, IsClosed (O i j)) -- Operations are topologically closed --- (h_convex : ∀ {i j}, Convex ℝ (CPTPMap.choi '' O i j)) -- (The choi matrices of) operations are convex +-- (h_convex : ∀ {i j}, Convex ℝ (CPTPOp.choi '' O i j)) -- (The choi matrices of) operations are convex -- (h_prod : ∀ {i j k l f g} (hf : f ∈ O i k) (hg : g ∈ O j l), (f ⊗ᶜᵖᵣ g) ∈ O (prod i j) (prod k l)) --Closed under products -- (h_fullRank : ∀ {i : ι}, sorry) --Some statement about having full rank states as output -- (h_appendFree : ∀ {i j k : ι}, sorry) --Some statement that appending free states is free @@ -123,17 +132,17 @@ theorem maximal_IsMaximal [FreeStateTheory ι] : IsMaximal (maximal (ι := ι)) -- use f.compose g -- constructor -- · exact h_comp ⟨f,hf⟩ ⟨g,hg1⟩ --- · simp only [CPTPMap.compose_eq, hg2] +-- · simp only [CPTPOp.compose_eq, hg2] -- /-- A `ResourceTheory` provides a category structure -/ -- instance instQRTCategory (ι : Type*) [ResourceTheory ι] : CategoryTheory.Category ι where -- Hom x y := freeOps x y --- id := fun _ ↦ ⟨CPTPMap.id, free_id _⟩ --- comp f g := ⟨CPTPMap.compose g.1 f.1, free_comp g f⟩ +-- id := fun _ ↦ ⟨CPTPOp.id, free_id _⟩ +-- comp f g := ⟨CPTPOp.compose g.1 f.1, free_comp g f⟩ -- id_comp X := by simp -- comp_id := by simp --- assoc := fun f g h ↦ by simpa using CPTPMap.compose_assoc h.1 g.1 f.1 +-- assoc := fun f g h ↦ by simpa using CPTPOp.compose_assoc h.1 g.1 f.1 -- open ComplexOrder in -- /-- The 'fully free' quantum resource theory: the category is all finite Hilbert spaces, all maps are @@ -150,7 +159,7 @@ theorem maximal_IsMaximal [FreeStateTheory ι] : IsMaximal (maximal (ι := ι)) -- IsFree := Set.univ -- free_closed := isClosed_univ -- free_convex {i} := by --- -- convert MState.convex (H i) --For MState.m, not MState.M +-- -- convert MState.convex (H i) --For DensityOp.m, not DensityOp.M -- sorry -- free_prod _ _ := trivial -- free_fullRank := by diff --git a/QuantumInfo/ResourceTheory/SteinsLemma.lean b/QuantumInfo/ResourceTheory/SteinsLemma.lean index fea696ede..2502a4298 100644 --- a/QuantumInfo/ResourceTheory/SteinsLemma.lean +++ b/QuantumInfo/ResourceTheory/SteinsLemma.lean @@ -63,14 +63,15 @@ private theorem Lemma6 {m : ℕ} (hm : 0 < m) (ρ σf : MState (H i)) (σₘ : M rw [← sandwichedRelRentropy_statePow] rw [← sandwichedRelRentropy_prodRelabel] - refine sandwichedRelRentropy_heq_congr h_Hn_eq ?_ ?_ - · rw [MState.relabel_cast] - refine (cast_heq _ _).trans ?_ - congr 1 - · exact pow_mul i m (n / m) - · exact statePow_mul ρ m (n / m) - · rw [MState.relabel_cast] - exact cast_heq _ _ + refine sandwichedRelRentropy_congr h_Hn_eq ?_ ?_ + · rw [MState.eq_relabel_iff] + simp only [MState.relabel_relabel, ← Equiv.cast_symm, ← Equiv.cast_trans] + rw [prodRelabel_relabel_cast_prod _ _ _ ((pow_mul ..).symm) rfl] + congr + · rw [statePow_mul_relabel] + simp [← Equiv.cast_trans] + · simp only [Equiv.cast_refl, MState.relabel_refl] + · simp --This will probably need 1 < α actually have h_α : ∀ α, (1 < α) → Filter.atTop.limsup (fun n ↦ —log β_ ε(ρ ⊗ᵣ^[n]‖{σn n}) / n) ≤ @@ -84,7 +85,7 @@ private theorem Lemma6 {m : ℕ} (hm : 0 < m) (ρ σf : MState (H i)) (σₘ : M --Distribute the limsup over subtraction --The term on the right is a constant, divided by n, which converges to zero. --Dropping that leaves the identity - generalize_proofs pf1 pf2 at h_lem5 + generalize_proofs pfF pf1 pf2 at h_lem5 let x n := —log β_ ε(ρ ⊗ᵣ^[n]‖{σn n}) let y n := D̃_ α(ρ ⊗ᵣ^[n]‖σn n) set z := —log (1 - ε) * (ENNReal.ofNNReal ⟨α, pf1⟩) / (ENNReal.ofNNReal ⟨α - 1, pf2⟩) @@ -204,7 +205,7 @@ theorem LemmaS2liminf {ε3 : Prob} {ε4 : ℝ≥0} (hε4 : 0 < ε4) let T (n : ℕ) := {(ρ n).M ≥ₚ (Real.exp (n * (Rinf + ε4))) • (σ n).M} have hT : ∀ n ≥ n₀, (ρ n).exp_val (1 - (T n)) ≤ ε3 := fun n hn ↦ by -- Eq (S23) unfold MState.exp_val T - rw [inner_sub_right, HermitianMat.inner_one, MState.tr, + rw [inner_sub_right, HermitianMat.inner_one, DensityOp.tr, HermitianMat.inner_comm, tsub_le_iff_right, add_comm, ← tsub_le_iff_right] apply le_of_lt exact h n hn @@ -270,7 +271,7 @@ theorem LemmaS2limsup {ε3 : Prob} {ε4 : ℝ≥0} (hε4 : 0 < ε4) obtain ⟨n, hn, h⟩ := h n₀ use n, hn unfold MState.exp_val T - rw [inner_sub_right, HermitianMat.inner_one, MState.tr, + rw [inner_sub_right, HermitianMat.inner_one, DensityOp.tr, HermitianMat.inner_comm, tsub_le_iff_right, add_comm, ← tsub_le_iff_right] apply le_of_lt exact h @@ -843,12 +844,8 @@ private theorem σ'_posdef : (σ' ρ ε m σ n).m.PosDef := by private theorem hσ'n_eq_sum_third : (σ' ρ ε m σ n).M = (1 / 3 : ℝ) • («σ̃» m σ n) + (1 / 3 : ℝ) • («σ⋆» ρ ε n) + (1 / 3 : ℝ) • ((σ₁ i) ⊗ᵣ^[n]) := by unfold σ' - change _ • _ + _ = _ - conv => - enter [1, 1, 2] - change _ + _ - dsimp [Mixable.to_U] - norm_num only [one_div, Prob.coe_one_minus, smul_add, smul_smul] + simp only [MState.mix_M, smul_add, smul_smul] + norm_num private theorem hσ₁_le_σ' : (1 / 3 : ℝ) • ((σ₁ i) ⊗ᵣ^[n]).M ≤ (σ' ρ ε m σ n).M := by rw [hσ'n_eq_sum_third] @@ -896,28 +893,28 @@ variable (m : ℕ) (σ : (n : ℕ) → IsFree (i := i ^ n)) (n : ℕ) --We're now finally ready to define the main sequence with the properties we want, σ''. --This is the normalized version of σ''_unnormalized, which gives a state because that sequence is -- already PosDef -private def σ'' : (n : ℕ) → MState (H (i ^ n)) := fun n ↦ { +private def σ'' : (n : ℕ) → MState (H (i ^ n)) := fun n ↦ --TODO make this its own definition: Normalizing a matrix to give a tr-1 op. - M := (σ''_unnormalized ρ ε m σ n).trace⁻¹ • (σ''_unnormalized ρ ε m σ n) - nonneg := by - have h1 : 0 < (σ''_unnormalized ρ ε m σ n).trace := - zero_lt_one.trans_le (σ''_tr_bounds ρ ε m σ n).left - have h2 : 0 < σ''_unnormalized ρ ε m σ n := - (σ''_unnormalized_PosDef ρ ε m σ n).zero_lt - positivity - tr := by - rw [HermitianMat.trace_smul] - apply inv_mul_cancel₀ - exact (zero_lt_one.trans_le (σ''_tr_bounds ρ ε m σ n).left).ne' -} + DensityOp.ofMat ((σ''_unnormalized ρ ε m σ n).trace⁻¹ • (σ''_unnormalized ρ ε m σ n)) + (by + have h1 : 0 < (σ''_unnormalized ρ ε m σ n).trace := + zero_lt_one.trans_le (σ''_tr_bounds ρ ε m σ n).left + have h2 : 0 < σ''_unnormalized ρ ε m σ n := + (σ''_unnormalized_PosDef ρ ε m σ n).zero_lt + positivity) + (by + rw [HermitianMat.trace_smul] + apply inv_mul_cancel₀ + exact (zero_lt_one.trans_le (σ''_tr_bounds ρ ε m σ n).left).ne') private lemma σ''_posdef n : (σ'' ρ ε m σ n).M.mat.PosDef := by + simp only [σ'', DensityOp.M_ofMat] apply (σ''_unnormalized_PosDef ρ ε m σ n).smul have := (σ''_tr_bounds ρ ε m σ n).left positivity private lemma σ'_le_σ'' (n) : Real.exp (-σ₁_c i n) • (σ' ρ ε m σ n).M ≤ σ'' ρ ε m σ n := by - dsimp [σ''] + simp only [σ'', DensityOp.M_ofMat] set x := (σ''_unnormalized ρ ε m σ n).trace dsimp [σ''_unnormalized] rw [← HermitianMat.cfc_const_mul_id, ← HermitianMat.cfc_const_mul_id, @@ -937,7 +934,7 @@ private lemma σ'_le_σ'' (n) : Real.exp (-σ₁_c i n) • (σ' ρ ε m σ n).M · positivity private lemma σ''_le_σ' (n) : σ'' ρ ε m σ n ≤ Real.exp (σ₁_c i n) • (σ' ρ ε m σ n).M := by - dsimp [σ''] + simp only [σ'', DensityOp.M_ofMat] set x := (σ''_unnormalized ρ ε m σ n).trace dsimp [σ''_unnormalized] rw [← HermitianMat.cfc_const_mul_id, ← HermitianMat.cfc_const_mul_id, @@ -1051,7 +1048,7 @@ private theorem EquationS88 (ρ : MState (H i)) (σ : (n : ℕ) → ↑IsFree) { apply mul_nonneg · rw [sub_nonneg] apply HermitianMat.inner_mono - · apply MState.nonneg + · apply DensityOp.nonneg · unfold P1 P2 rw [← sub_nonneg] change 0 ≤ E2 ε2 n @@ -1059,7 +1056,7 @@ private theorem EquationS88 (ρ : MState (H i)) (σ : (n : ℕ) → ↑IsFree) { · positivity · apply mul_nonneg · apply HermitianMat.inner_ge_zero - · apply MState.nonneg + · apply DensityOp.nonneg · apply HermitianMat.projLE_nonneg · positivity repeat rw [ENNReal.toReal_add (by finiteness) (by finiteness)] @@ -1069,10 +1066,10 @@ private theorem EquationS88 (ρ : MState (H i)) (σ : (n : ℕ) → ↑IsFree) { repeat rw [ENNReal.toReal_ofReal] rotate_left · apply HermitianMat.inner_ge_zero --TODO: Positivity extension for HermitianMat.inner - · apply MState.nonneg --TODO: Positivity extension for MState + · apply DensityOp.nonneg --TODO: Positivity extension for MState · apply HermitianMat.projLE_nonneg --TODO: Positivity extension for projections · apply HermitianMat.inner_ge_zero - · apply MState.nonneg + · apply DensityOp.nonneg · apply HermitianMat.projLE_nonneg · exact hε2.le rw [ENNReal.toReal_sub_of_le ?_ (by finiteness)]; swap @@ -1087,11 +1084,18 @@ private theorem EquationS88 (ρ : MState (H i)) (σ : (n : ℕ) → ↑IsFree) { repeat rw [ENNReal.toReal_ofReal (by positivity)] ring -set_option maxHeartbeats 1000000 in +/-- Two shifts of `A` by real multiples of `B` commute, as long as `A` and `B` do. -/ +private theorem commute_sub_smul_sub {α : Type*} [Fintype α] [DecidableEq α] + {A B : Matrix α α ℂ} (h : Commute A B) (a b : ℝ) : + Commute (A - a • B) (A - b • B) := + Commute.sub_left + (Commute.sub_right (Commute.refl A) (h.smul_right b)) + (Commute.sub_right (h.symm.smul_left a) (((Commute.refl B).smul_left a).smul_right b)) + set_option backward.isDefEq.respectTransparency false in private theorem EquationS62 (ρ : MState (H i)) (σ : (n : ℕ) → IsFree (i := i ^ n)) - {ε ε' : Prob} (hε'₁ : 0 < ε') (hε'₂ : ε' < ε) (hε : ε < 1) + {ε ε' : Prob} (hε : ε < 1) (hR1R2 : R1 ρ ε < R2 ρ σ) (hR1 : R1 ρ ε ≠ ⊤) (hR2 : R2 ρ σ ≠ ⊤) (hε₀ : 0 < ε₀_func ρ ε σ ε') (hε₀' : (R1 ρ ε).toReal ≤ (R2 ρ σ).toReal + ε₀_func ρ ε σ ε') (m : ℕ) (hm : m ≥ 1 ∧ 𝐃(ρ ⊗ᵣ^[m]‖↑(σ m)) / ↑m < R2 ρ σ + (.ofNNReal ⟨ε₀_func ρ ε σ ε', hε₀.le⟩)) @@ -1166,15 +1170,8 @@ private theorem EquationS62 have hEComm ε2 n : Commute (((ℰ n) (ρ ⊗ᵣ^[n])).M - Real.exp ((n : ℝ) * ((R2 ρ σ).toReal + ε₀ + ε2)) • (σ'' ρ ε m σ n).M).mat (((ℰ n) (ρ ⊗ᵣ^[n])).M - Real.exp ((n : ℝ) * ((R1 ρ ε).toReal + ε2)) • (σ'' ρ ε m σ n).M).mat := by - simp only [HermitianMat.mat_sub, MState.mat_M, HermitianMat.mat_smul] - suffices h : Commute (ℰ n (ρ ⊗ᵣ^[n])).m (σ'' ρ ε m σ n).m by - apply Commute.sub_left - · commutes - · apply Commute.smul_left - apply Commute.sub_right - · exact Commute.symm ‹_› - · commutes - exact pinching_commutes (ρ ⊗ᵣ^[n]) (σ'' ρ ε m σ n) + simp only [HermitianMat.mat_sub, DensityOp.mat_M, HermitianMat.mat_smul] + exact commute_sub_smul_sub (pinching_commutes (ρ ⊗ᵣ^[n]) (σ'' ρ ε m σ n)) _ _ have hPcomm ε2 n : Commute (P1 ε2 n).mat (P2 ε2 n).mat := by simp only [HermitianMat.projLE, HermitianMat.mat_cfc, P1, P2] @@ -1240,7 +1237,7 @@ private theorem EquationS62 dsimp [σ₁_mineig, iInf] rw [← Matrix.IsHermitian.spectrum_real_eq_range_eigenvalues] rw [← Matrix.IsHermitian.spectrum_real_eq_range_eigenvalues] - rw [MState.mat_M, sInf_spectrum_spacePow (σ₁ i) n, MState.mat_M, smul_smul] + rw [DensityOp.mat_M, sInf_spectrum_spacePow (σ₁ i) n, DensityOp.mat_M, smul_smul] _ = Real.exp (- n * (σ₁_c i n + (σ₁_c i n) / n)) • 1 := by rw [σ₁_c_identity i hn] _ ≥ Real.exp (-n * c' ε2 n) • 1 := by @@ -1285,10 +1282,10 @@ private theorem EquationS62 rw [← HermitianMat.val_eq_coe, ← HermitianMat.val_eq_coe] rw [Subtype.coe_le_coe, HermitianMat.cfc_nonneg_iff (ℰ n (ρ ⊗ᵣ^[n])).M (-Real.log)] intro i - simp + simp only [Pi.neg_apply, neg_nonneg] apply Real.log_nonpos - · apply MState.eigenvalue_nonneg _ - · apply MState.eigenvalue_le_one + · apply DensityOp.eigenvalue_nonneg _ + · apply DensityOp.eigenvalue_le_one rw [← sub_nonneg, ← mul_sub_left_distrib] conv => rhs @@ -1351,10 +1348,10 @@ private theorem EquationS62 + ⟪(ℰ n (ρ ⊗ᵣ^[n])).M, c' ε2 n • E3 ε2 n⟫) := by -- (S86) to (S88) - unfold qRelativeEnt SandwichedRelRentropy + rw [qRelativeEnt, MState.sandwichedRelRentropy_eq_matrix] simp only [↓reduceIte] have σ''_pd := σ''_posdef ρ ε m σ - simp only [MState.mat_M] at σ''_pd + simp only [DensityOp.mat_M] at σ''_pd have hker : (σ'' ρ ε m σ n).M.ker ≤ (ℰ n (ρ ⊗ᵣ^[n])).M.ker := ker_le_ker_pinching_of_PosDef (ρ ⊗ᵣ^[n]) (σ'' ρ ε m σ n) (σ''_pd n) simp only [hker, ↓reduceDIte] @@ -1389,14 +1386,14 @@ private theorem EquationS62 · apply HermitianMat.cfc_commute apply Commute.sub_left · rfl - · simp only [HermitianMat.val_eq_coe, HermitianMat.mat_smul, MState.mat_M, ne_eq, + · simp only [HermitianMat.val_eq_coe, HermitianMat.mat_smul, DensityOp.mat_M, ne_eq, Real.exp_ne_zero, not_false_eq_true, Commute.smul_left_iff₀] apply Commute.symm exact pinching_commutes (ρ ⊗ᵣ^[n]) (σ'' ρ ε m σ n) · apply HermitianMat.cfc_commute apply Commute.sub_left · rfl - · simp only [HermitianMat.val_eq_coe, HermitianMat.mat_smul, MState.mat_M, ne_eq, + · simp only [HermitianMat.val_eq_coe, HermitianMat.mat_smul, DensityOp.mat_M, ne_eq, Real.exp_ne_zero, not_false_eq_true, Commute.smul_left_iff₀] apply Commute.symm exact pinching_commutes (ρ ⊗ᵣ^[n]) (σ'' ρ ε m σ n) @@ -1420,7 +1417,7 @@ private theorem EquationS62 refine rexp_mul_smul_proj_lt_mul_sub_le_mul_sub' (pinching_commutes (ρ ⊗ᵣ^[n]) (σ'' ρ ε m σ n)) ?_ (σ''_posdef ρ ε m σ n) rfl rw [← HermitianMat.zero_le_iff] - apply MState.nonneg + apply DensityOp.nonneg simp at hE2leq rw [← Complex.re_ofReal_mul (↑n)⁻¹, ← smul_eq_mul, ← Matrix.trace_smul] rw [← RCLike.re_to_complex] @@ -1488,7 +1485,7 @@ private theorem EquationS62 rw [← HermitianMat.inner_eq_re_trace (ℰ n (ρ ⊗ᵣ^[n])).M ((n : ℝ)⁻¹ • (HermitianMat.symmMul _ _))] rw [← HermitianMat.inner_smul_right] exact ((HermitianMat.inner_mono ((ℰ n (ρ ⊗ᵣ^[n]))).nonneg) hE3leq) - simp only [IsMaximalSelfAdjoint.RCLike_selfadjMap, MState.mat_M, HermitianMat.mat_sub, + simp only [IsMaximalSelfAdjoint.RCLike_selfadjMap, DensityOp.mat_M, HermitianMat.mat_sub, RCLike.re_to_complex, HermitianMat.inner_smul_right, ge_iff_le] conv => enter [1, 1, 1, 1] @@ -1522,7 +1519,7 @@ private theorem EquationS62 refine rexp_mul_smul_proj_lt_mul_sub_le_mul_sub (pinching_commutes (ρ ⊗ᵣ^[n]) (σ'' ρ ε m σ n)) (by positivity) ?_ (σ''_posdef ρ ε m σ n) rfl rw [← HermitianMat.zero_le_iff] - apply MState.nonneg + apply DensityOp.nonneg simp only [HermitianMat.inner_def] at hE1leq conv at hE1leq => @@ -1596,7 +1593,7 @@ private theorem EquationS62 intro _ _ apply HermitianMat.inner_ge_zero · apply HermitianMat.projLE_nonneg - · apply MState.nonneg + · apply DensityOp.nonneg · have hlimsupP2' ε2 (hε2 : 0 < ε2) : Filter.atTop.limsup (fun n ↦ ⟪P2 ε2 n, ℰ n (ρ ⊗ᵣ^[n])⟫) = 0 := by apply le_antisymm @@ -1654,7 +1651,7 @@ private theorem EquationS62 grw [← hx x le_rfl] apply HermitianMat.inner_ge_zero · apply HermitianMat.projLE_nonneg - · apply MState.nonneg + · apply DensityOp.nonneg rcases this with ⟨ε2, hg₁, hg₂, hg₃, hliminf_g₁, hliminf_g₂⟩ replace hDleq := Filter.liminf_le_liminf (Filter.eventually_atTop.mpr ⟨1, fun (n : ℕ) hnge1 ↦ hDleq (ε2 n) n (hg₁ n) hnge1⟩) @@ -1760,7 +1757,7 @@ theorem Lemma7 (ρ : MState (H i)) {ε : Prob} (hε : 0 < ε ∧ ε < 1) (σ : ( -- conditions, nonzeroness, etc.) get S62, prove S61, and the conclusion is just `rw [S61] at S62`. --First deal with the easy case of R1 = R2. - intro hR1R2 ε' ⟨hε'₁, hε'₂⟩ + intro hR1R2 ε' ⟨_, hε'₂⟩ rw [ge_iff_le, le_iff_lt_or_eq, or_comm] at hR1R2 rcases hR1R2 with hR1R2|hR1R2 · use σ @@ -1834,9 +1831,10 @@ theorem Lemma7 (ρ : MState (H i)) {ε : Prob} (hε : 0 < ε ∧ ε < 1) (σ : ( (fun x ↦ (σ''_unnormalized ρ ε m σ n).trace⁻¹ * Real.exp (f_map i n x)) ∘ (σ' ρ ε m σ n).M.H.eigenvalues ∘ e := by convert (σ' ρ ε m σ n).M.cfc_eigenvalues (fun x ↦ (σ''_unnormalized ρ ε m σ n).trace⁻¹ * Real.exp (f_map i n x)) rw [HermitianMat.cfc_const_mul, ← σ''_unnormalized, σ''] + exact DensityOp.M_ofMat _ _ _ rcases eq with ⟨eq, heq⟩ rw [heq] - simp only [Set.range_comp, MState.mat_M, EquivLike.range_eq_univ, Set.image_univ, ge_iff_le] + simp only [Set.range_comp, DensityOp.mat_M, EquivLike.range_eq_univ, Set.image_univ, ge_iff_le] let S : Set ℝ := (fun x => Real.exp (f_map i n x)) '' Set.Icc ((σ₁_mineig i ^ n) / 3) 1 have h_card_subs : Set.ncard S ≤ n + 1 ∧ S.Finite := by exact f_image_bound (σ₁_mineig i) n (mineig_pos i) hn (log_le_f i) (f_le_log i) @@ -1853,7 +1851,7 @@ theorem Lemma7 (ρ : MState (H i)) {ε : Prob} (hε : 0 < ε ∧ ε < 1) (σ : ( Real.exp (f_map i n x)) '' Set.Icc (((σ₁_mineig i) ^ n) / 3) 1) ?_ h_s₂_finite).trans h_card_subs₂ apply Set.image_mono rintro _ ⟨k, rfl⟩ - refine ⟨?_, MState.eigenvalue_le_one _ _⟩ + refine ⟨?_, DensityOp.eigenvalue_le_one _ _⟩ refine le_trans ?_ (ciInf_le (Finite.bddBelow_range _) k) refine le_trans ?_ ((HermitianMat.H _).iInf_eigenvalues_le (hσ₁_le_σ' ρ ε m σ n) _) dsimp [σ₁_mineig, iInf] @@ -1862,7 +1860,7 @@ theorem Lemma7 (ρ : MState (H i)) {ε : Prob} (hε : 0 < ε ∧ ε < 1) (σ : ( rw [HermitianMat.val_eq_coe, HermitianMat.mat_smul] rw [spectrum.smul_eq_smul _ _ (ContinuousFunctionalCalculus.spectrum_nonempty _ ((σ₁ i) ⊗ᵣ^[n]).M.H)] rw [Real.sInf_smul_of_nonneg (by norm_num)] - simp [MState.mat_M, div_eq_inv_mul, sInf_spectrum_spacePow] + simp [DensityOp.mat_M, div_eq_inv_mul, sInf_spectrum_spacePow] have hdpos n : 0 < Fintype.card (spectrum ℝ (σ'' ρ ε m σ n).m) := by rw [Fintype.card_pos_iff, Set.nonempty_coe_sort] @@ -1944,7 +1942,7 @@ theorem Lemma7 (ρ : MState (H i)) {ε : Prob} (hε : 0 < ε ∧ ε < 1) (σ : ( use fun n ↦ ⟨σ' ρ ε m σ n, σ'_free ρ ε m σ n⟩ rw [R2, hliminf] - exact EquationS62 ρ σ hε'₁ hε'₂ hε.2 hR1R2 hR1 hR2 hε₀ hε₀' m hm + exact EquationS62 ρ σ hε.2 hR1R2 hR1 hR2 hε₀ hε₀' m hm /-- Lemma 7 gives us a way to repeatedly "improve" a sequence σ to one with a smaller gap between R2 and R1. The paper paints this as pretty much immediate from Lemma7, but we need to handle the case where R2 is below diff --git a/QuantumInfo/States/Ensemble.lean b/QuantumInfo/States/Ensemble.lean index 8a16b6c3e..bcf3e6843 100644 --- a/QuantumInfo/States/Ensemble.lean +++ b/QuantumInfo/States/Ensemble.lean @@ -14,6 +14,8 @@ open MState open BigOperators open scoped RealInnerProductSpace InnerProductSpace +set_option backward.isDefEq.respectTransparency false + noncomputable section /-- A mixed-state ensemble is a random variable valued in `MState d`. That is, @@ -64,9 +66,14 @@ respective probability weights. Note that, generically, a single mixed state has ensembles that mixes into it. -/ def mix (e : MEnsemble d α) : MState d := ProbDistribution.expect_val e +@[simp] +theorem mix_M (e : MEnsemble d α) : (mix e).M = ∑ i, (e.distr i : ℝ) • (e.states i).M := + ProbDistribution.to_U_expect_val e + @[simp] theorem mix_of (e : MEnsemble d α) : (mix e).m = ∑ i, (e.distr i : ℝ) • (e.states i).m := by - apply AddSubgroup.val_finsetSum -- *laughs in defeq* + rw [← DensityOp.mat_M, mix_M] + simp /-- Two mixed-state ensembles indexed by `\alpha` and `\beta` are equivalent if `α ≃ β`. -/ def congrMEnsemble (σ : α ≃ β) : MEnsemble d α ≃ MEnsemble d β := ProbDistribution.congrRandVar σ @@ -133,15 +140,19 @@ theorem mix_pEnsemble_pure_iff_pure {e : PEnsemble d α} : · apply MState.eq_of_sum_eq_pure ?_ ?_ ?_ e.distr.normalized i (Finset.mem_univ i) · exact_mod_cast lt_of_le_of_ne (e.distr i).zero_le hi.symm · exact (MState.pure_iff_purity_one _).mp ⟨ψ, rfl⟩ - · exact congr_arg MState.M h.symm + · rw [← h] + exact Ensemble.mix_M _ · grind - · have h_sum : mix (toMEnsemble e) = ∑ i, (e.distr i).val • (MState.pure ψ).M := by + · have h_sum : (mix (toMEnsemble e)).M = ∑ i, (e.distr i).val • (MState.pure ψ).M := by + rw [Ensemble.mix_M] refine Finset.sum_congr rfl fun i _ => ?_ by_cases hi : e.distr i = 0 · simp [hi] · rw [← h i hi] rfl - simp [MState.ext_iff, h_sum, ← Finset.sum_smul] + apply DensityOp.ext + rw [h_sum, ← Finset.sum_smul] + simp /- The theorem below is also false for the same reason as the original `mix_pEnsemble_pure_iff_pure`: knowing `MState.pure (e.states i) = MState.pure ψ` does not imply `e.states i = ψ` as Kets, @@ -203,13 +214,12 @@ theorem MState.exp_val_pure_eq_one_iff {d : Type*} [Fintype d] [DecidableEq d] have hsq : ⟪ρ.M - (MState.pure ψ).M, ρ.M - (MState.pure ψ).M⟫ = ⟪ρ.M, ρ.M⟫ - 2 * ⟪ρ.M, (MState.pure ψ).M⟫ + ⟪(MState.pure ψ).M, (MState.pure ψ).M⟫ := by simp only [HermitianMat.inner_def, IsMaximalSelfAdjoint.RCLike_selfadjMap, HermitianMat.mat_sub, - MState.mat_M, RCLike.re_to_complex] + DensityOp.mat_M, RCLike.re_to_complex] simp [Matrix.mul_sub, Matrix.sub_mul, Matrix.trace_sub, Matrix.trace_mul_comm (ρ.m)]; ring - exact MState.ext (eq_of_sub_eq_zero (inner_self_eq_zero.mp (le_antisymm + exact DensityOp.ext (eq_of_sub_eq_zero (inner_self_eq_zero.mp (le_antisymm (by rw [hsq]; linarith) (ρ.M - (MState.pure ψ).M).inner_self_nonneg))) · rintro rfl; simpa [MState.exp_val] using hpure_inner -set_option backward.isDefEq.respectTransparency false in theorem mix_mEnsemble_pure_iff_pure {e : MEnsemble d α} : mix e = pure ψ ↔ ∀ i : α, e.distr i ≠ 0 → e.states i = MState.pure ψ := by have h : (mix e).exp_val ↑(MState.pure ψ) = ∑ i, ↑(e.distr i) * (e.states i).exp_val ↑(MState.pure ψ) := by @@ -219,9 +229,8 @@ theorem mix_mEnsemble_pure_iff_pure {e : MEnsemble d α} : · exact fun i => ( e.distr i ).2.1; · simp · intro i - apply (e.states i).exp_val_le_one (MState.le_one _) + apply (e.states i).exp_val_le_one (DensityOp.le_one _) -set_option backward.isDefEq.respectTransparency false in /-- The average of `f : MState d → T` on an ensemble that mixes to a pure state `ψ` is `f (pure ψ)` -/ theorem mix_mEnsemble_pure_average {e : MEnsemble d α} {T : Type _} {U : Type*} [AddCommGroup U] [Module ℝ U] [inst : Mixable U T] (f : MState d → T) (hmix : mix e = pure ψ) : average f e = f (pure ψ) := by @@ -257,12 +266,11 @@ def trivial_mEnsemble (ρ : MState d) (i : α) : MEnsemble d α := ⟨fun _ ↦ /-- The trivial mixed-state ensemble of `ρ` mixes to `ρ` -/ theorem trivial_mEnsemble_mix (ρ : MState d) : ∀ i : α, mix (trivial_mEnsemble ρ i) = ρ := fun i ↦by - apply MState.ext_m + apply DensityOp.ext_m classical simp only [trivial_mEnsemble, ProbDistribution.constant, mix_of, DFunLike.coe, apply_ite, Prob.coe_one, Prob.coe_zero, ite_smul, one_smul, zero_smul, Finset.sum_ite_eq, Finset.mem_univ, ↓reduceIte] -set_option backward.isDefEq.respectTransparency false in /-- The average of `f : MState d → T` on a trivial ensemble of `ρ` is `f ρ`-/ theorem trivial_mEnsemble_average {T : Type _} {U : Type*} [AddCommGroup U] [Module ℝ U] [inst : Mixable U T] (f : MState d → T) (ρ : MState d): ∀ i : α, average f (trivial_mEnsemble ρ i) = f ρ := fun i ↦ by @@ -279,15 +287,13 @@ def trivial_pEnsemble (ψ : Ket d) (i : α) : PEnsemble d α := ⟨fun _ ↦ ψ, variable (ψ : Ket d) -set_option backward.isDefEq.respectTransparency false in /-- The trivial pure-state ensemble of `ψ` mixes to `ψ` -/ theorem trivial_pEnsemble_mix : ∀ i : α, mix (toMEnsemble (trivial_pEnsemble ψ i)) = MState.pure ψ := fun i ↦ by - apply MState.ext_m + apply DensityOp.ext_m classical simp only [trivial_pEnsemble, ProbDistribution.constant, toMEnsemble_mk, mix_of, DFunLike.coe, apply_ite, Prob.coe_one, Prob.coe_zero, MEnsemble.states, Function.comp_apply, ite_smul, one_smul, zero_smul, Finset.sum_ite_eq, Finset.mem_univ, ↓reduceIte] -set_option backward.isDefEq.respectTransparency false in omit [DecidableEq d] in /-- The average of `f : Ket d → T` on a trivial ensemble of `ψ` is `f ψ`-/ theorem trivial_pEnsemble_average {T : Type _} {U : Type*} [AddCommGroup U] [Module ℝ U] [inst : Mixable U T] (f : Ket d → T) : @@ -328,11 +334,13 @@ theorem spectral_decomposition_sum {d 𝕜 : Type*} [Fintype d] [DecidableEq d] /-- The spectral pure-state ensemble of `ρ` mixes to `ρ` -/ theorem spectral_ensemble_mix {ρ : MState d} : mix (↑(spectral_ensemble ρ) : MEnsemble d d) = ρ := by - ext i j - convert rfl; - convert rfl; - apply MState.ext_m; - convert Ensemble.mix_of _ - convert! (spectral_decomposition_sum ρ.Hermitian) using 1 + apply DensityOp.ext_m + rw [Ensemble.mix_of] + conv_rhs => rw [spectral_decomposition_sum ρ.Hermitian] + refine Finset.sum_congr rfl fun i _ ↦ ?_ + congr 1 + ext j k + show (MState.pure ((spectral_ensemble ρ).states i)).m j k = _ + simp [Matrix.vecMulVec_apply, Ket.apply, spectral_ensemble] end Ensemble diff --git a/QuantumInfo/States/Entanglement.lean b/QuantumInfo/States/Entanglement.lean index b670441d5..93348a64f 100644 --- a/QuantumInfo/States/Entanglement.lean +++ b/QuantumInfo/States/Entanglement.lean @@ -233,67 +233,40 @@ def EoF : MState (d₁ × d₂) → ℝ≥0 := convex_roof (KetUpToPhase.lift (fun ψ ↦ ⟨Sᵥₙ (pure ψ).traceRight, Sᵥₙ_nonneg (pure ψ).traceRight⟩) (fun ψ φ h ↦ by - congr 1 - congr 1 - exact congrArg MState.traceRight ((MState.PhaseEquiv_iff_pure_eq ψ φ).mp h))) + have hpure : (pure ψ : MState (d₁ × d₂)) = pure φ := + (MState.PhaseEquiv_iff_pure_eq ψ φ).mp h + simp only [hpure] + rfl)) /- The partial trace of the maximally entangled state is the maximally mixed state. -/ theorem traceRight_pure_MES (d : Type*) [Fintype d] [DecidableEq d] [Nonempty d] : (MState.pure (Ket.MES d)).traceRight = MState.uniform := by - -- By definition of partial trace, we sum over the second system. - have h_partial_trace : ∀ (i j : d), ∑ k : d, (Ket.MES d).vec (i, k) * (star (Ket.MES d).vec (j, k)) = (1 / Fintype.card d : ℝ) * (if i = j then 1 else 0) := by - unfold Ket.MES - intro i j - simp only [one_div, Pi.star_apply, RCLike.star_def, ite_mul, zero_mul, Finset.sum_ite_eq, - Finset.mem_univ, ↓reduceIte, Complex.ofReal_inv] - split - · subst i - simp only [map_inv₀, Complex.conj_ofReal] - ring_nf; norm_cast; norm_num; - · grind - unfold MState.pure MState.traceRight MState.uniform ext i j - convert! h_partial_trace i j - simp_all only [Pi.star_apply, RCLike.star_def, one_div, Complex.ofReal_inv, - Complex.ofReal_natCast, mul_ite, mul_one, mul_zero, HermitianMat.mat_apply, - coe_ofClassical, ProbDistribution.uniform_def, Finset.card_univ] - unfold HermitianMat.diagonal - simp_all only [map_inv₀, map_natCast] - rfl - -/- -The von Neumann entropy of a state is equal to the trace of `ρ log ρ` (technically `cfc ρ negMulLog`). --/ -theorem Sᵥₙ_eq_trace_cfc {d : Type*} [Fintype d] [DecidableEq d] (ρ : MState d) : - Sᵥₙ ρ = (HermitianMat.cfc ρ.M Real.negMulLog).trace := by - -- By definition of von Neumann entropy, we have Sᵥₙ ρ = Finset.sum Finset.univ (fun x ↦ Real.negMulLog (ρ.M.H.eigenvalues x)). - have h_def : Sᵥₙ ρ = Finset.sum Finset.univ (fun x ↦ Real.negMulLog (ρ.M.H.eigenvalues x)) := by - rfl - -- By definition of trace, the trace of `cfc ρ.M Real.negMulLog` is the sum of its eigenvalues. - have h_trace : (ρ.M.cfc Real.negMulLog).trace = - ∑ x, (ρ.M.cfc Real.negMulLog).H.eigenvalues x := by - exact (HermitianMat.sum_eigenvalues_eq_trace _).symm - obtain ⟨e, he⟩ : ∃ e : d ≃ d, (ρ.M.cfc Real.negMulLog).H.eigenvalues = - Real.negMulLog ∘ ρ.M.H.eigenvalues ∘ e := by - exact Matrix.IsHermitian.cfc_eigenvalues _ _ - rw [h_def, h_trace, he] - simp only [Function.comp_apply] - conv_lhs => rw [ ← Equiv.sum_comp e ] + rw [MState.traceRight_M, MState.uniform, coe_ofClassical] + simp only [HermitianMat.mat_apply, HermitianMat.traceRight_apply, MState.pure_M_apply, + HermitianMat.diagonal_apply, ProbDistribution.uniform_def, Ket.apply, Ket.MES, ite_mul, + zero_mul, Finset.sum_ite_eq, Finset.mem_univ, if_true, Finset.card_univ, one_div] + rcases eq_or_ne i j with rfl | h + · rw [if_pos rfl, if_pos rfl, map_inv₀, Complex.conj_ofReal, ← mul_inv, ← Complex.ofReal_mul, + Real.mul_self_sqrt (by positivity)] + push_cast + ring + · rw [if_neg h, if_neg h.symm] + simp /- The von Neumann entropy of a classical state (diagonal in the basis) is equal to the Shannon entropy of the corresponding distribution. -/ theorem Sᵥₙ_ofClassical {d : Type*} [Fintype d] [DecidableEq d] (dist : ProbDistribution d) : Sᵥₙ (MState.ofClassical dist) = Hₛ dist := by - -- Let's unfold the definition of `Sᵥₙ` using `Sᵥₙ_eq_trace_cfc`. - have h_def : Sᵥₙ (MState.ofClassical dist) = (HermitianMat.cfc (MState.ofClassical dist).M Real.negMulLog).trace := by - exact Sᵥₙ_eq_trace_cfc (ofClassical dist); + have h_def : Sᵥₙ (MState.ofClassical dist) = (HermitianMat.cfc (MState.ofClassical dist).M Real.negMulLog).trace := + Sᵥₙ_eq_trace_cfc_negMulLog (ι := d) (ofClassical dist) convert h_def using 1; -- By definition of $MState.ofClassical$, we know that $(MState.ofClassical dist).M$ is a diagonal matrix with entries $dist i$. - have h_diag : (MState.ofClassical dist).M = HermitianMat.diagonal ℂ (fun x => dist x) := by - exact rfl; + have h_diag : (MState.ofClassical dist).M = HermitianMat.diagonal ℂ (fun x => dist x) := + coe_ofClassical dist rw [ h_diag, HermitianMat.cfc_diagonal, HermitianMat.trace_diagonal ] ; aesop set_option backward.isDefEq.respectTransparency false in diff --git a/QuantumInfo/States/Mixed/Fidelity.lean b/QuantumInfo/States/Mixed/Fidelity.lean index 1c7371428..d41c1c74d 100644 --- a/QuantumInfo/States/Mixed/Fidelity.lean +++ b/QuantumInfo/States/Mixed/Fidelity.lean @@ -10,7 +10,13 @@ public import QuantumInfo.Channels.CPTP public import QuantumInfo.Channels.Dual public import QuantumInfo.Channels.MatrixMap public import QuantumInfo.Channels.Unbundled -public import Physlib.Meta.Sorry + +/-! # Fidelity between quantum states + +The fidelity `F(ρ,σ) = Tr[√(√ρ σ √ρ)]` of two states. The definition here is basis-free, on +`DensityOp`; `DensityOp.fidelity_eq_matrix` and `DensityOp.fidelity_eq_traceNorm` are the matrix +analogues. -/ + @[expose] public section noncomputable section @@ -18,38 +24,40 @@ noncomputable section open BigOperators open ComplexConjugate open Kronecker -open scoped Matrix ComplexOrder RealInnerProductSpace InnerProductSpace +open scoped Matrix ComplexOrder -variable {d d₂ : Type*} [Fintype d] [DecidableEq d] [Fintype d₂] (ρ σ : MState d) +variable {d d₂ : Type*} [Fintype d] [DecidableEq d] [Fintype d₂] +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] -namespace MState +namespace DensityOp /-- The fidelity of two quantum states. This is the quantum version of the Bhattacharyya - coefficient. -/ -def fidelity (ρ σ : MState d) : ℝ := - (σ.M.conj ρ.M.sqrt.mat).sqrt.trace +coefficient. + +This makes no reference to a basis; `fidelity_eq_matrix` is the matrix analogue. -/ +def fidelity (ρ σ : DensityOp E) : ℝ := + (σ.op.conj ρ.op.sqrt.op).sqrt.trace -theorem fidelity_ge_zero : 0 ≤ fidelity ρ σ := by - apply HermitianMat.trace_nonneg - apply HermitianMat.sqrt_nonneg +variable (ρ σ : DensityOp E) + +/-- **Matrix analogue of `DensityOp.fidelity`.** -/ +theorem fidelity_eq_matrix {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] : + fidelity ρ σ = ((σ.M : HermitianMat ι ℂ).conj (ρ.M : HermitianMat ι ℂ).sqrt.mat).sqrt.trace := by + have h : StdBasis.toMat ℂ E ι ρ.op.sqrt.op = ((ρ.M : HermitianMat ι ℂ).sqrt).mat := by + rw [← HermitianOp.toMat_mat (ι := ι), HermitianOp.toMat_sqrt] + rfl + rw [fidelity, ← HermitianOp.trace_toMat (ι := ι), HermitianOp.toMat_sqrt, + HermitianOp.toMat_conj, h] + rfl + +theorem fidelity_ge_zero : 0 ≤ fidelity ρ σ := + HermitianOp.trace_nonneg (HermitianOp.sqrt_nonneg _) theorem fidelity_le_one : fidelity ρ σ ≤ 1 := by - unfold fidelity - rw [HermitianMat.sqrt_eq_cfc_rpow_half, ← HermitianMat.rpow_eq_cfc] - calc ((σ.M.conj ρ.M.sqrt.mat) ^ (1/2 : ℝ)).trace - ≤ ((σ.M ^ (2/2 : ℝ)).trace ^ (1/2 : ℝ) * - (ρ.M.sqrt ^ (2 : ℝ)).trace ^ (1/2 : ℝ)) ^ (2 * (1/2 : ℝ)) := - HermitianMat.trace_rpow_conj_le σ.nonneg (HermitianMat.sqrt_nonneg ρ.M) - (by norm_num) (by norm_num) (by norm_num) (by norm_num) - _ = 1 := by - have h1 : (σ.M ^ (2/2 : ℝ)).trace = 1 := by - rw [show (2:ℝ)/2 = 1 from by norm_num, HermitianMat.rpow_one]; exact σ.tr - have h2 : (ρ.M.sqrt ^ (2 : ℝ)).trace = 1 := by - rw [show ρ.M.sqrt = ρ.M ^ (1/2 : ℝ) from by - rw [HermitianMat.sqrt_eq_cfc_rpow_half, ← HermitianMat.rpow_eq_cfc], - ← HermitianMat.rpow_mul ρ.nonneg, - show (1:ℝ)/2 * 2 = 1 from by norm_num, HermitianMat.rpow_one]; exact ρ.tr - simp [h2] + let _ : StdBasis ℂ E (Fin (Module.finrank ℂ E)) := StdBasis.some ℂ E + rw [fidelity_eq_matrix (ι := Fin (Module.finrank ℂ E))] + refine (HermitianMat.trace_sqrt_conj_sqrt_le σ.nonneg ρ.nonneg).trans_eq ?_ + rw [σ.tr, ρ.tr, Real.sqrt_one, one_mul] /-- The fidelity, as a `Prob` probability with value between 0 and 1. -/ def fidelity_prob : Prob := @@ -57,85 +65,119 @@ def fidelity_prob : Prob := /-- A state has perfect fidelity with itself. -/ theorem fidelity_self_eq_one : fidelity ρ ρ = 1 := by - simp only [fidelity, HermitianMat.sqrt_eq_cfc_rpow_half] + let _ : StdBasis ℂ E (Fin (Module.finrank ℂ E)) := StdBasis.some ℂ E + rw [fidelity_eq_matrix (ι := Fin (Module.finrank ℂ E))] + simp only [HermitianMat.sqrt_eq_cfc_rpow_half] conv => enter [1, 1, 1, 2] - rw [← HermitianMat.cfc_id ρ.M] + rw [← HermitianMat.cfc_id (ρ.M : HermitianMat _ ℂ)] rw [HermitianMat.cfc_conj, ← HermitianMat.cfc_comp_apply] convert ρ.tr using 2 - convert ρ.M.cfc_id using 1 + convert (ρ.M : HermitianMat _ ℂ).cfc_id using 1 apply HermitianMat.cfc_congr_of_nonneg ρ.nonneg intro x hx simp only [one_div, Pi.mul_apply, id_eq, Pi.pow_apply] rw [← Real.rpow_two, Real.rpow_inv_rpow hx (by norm_num), ← sq, ← Real.rpow_two] exact Real.rpow_rpow_inv hx (by norm_num) -/-- Fidelity can be rewritten as the trace norm of the product of square roots. -/ -theorem fidelity_eq_traceNorm_sqrt_mul_sqrt (ρ σ : MState d) : - fidelity ρ σ = (σ.M.sqrt.mat * ρ.M.sqrt.mat).traceNorm := by - open MatrixOrder in - rw [fidelity, HermitianMat.sqrt_eq_cfc_rpow_half, HermitianMat.trace_eq_re_trace, - Matrix.traceNorm, CFC.sqrt_eq_rpow] - change RCLike.re (((σ.M.conj ρ.M.sqrt.mat) ^ (1 / 2 : ℝ)).mat.trace) = _ - rw [show ((σ.M.conj ρ.M.sqrt.mat) ^ (1 / 2 : ℝ)).mat = - ((σ.M.conj ρ.M.sqrt.mat).mat) ^ (1 / 2 : ℝ) by - rw [HermitianMat.rpow_eq_cfc, HermitianMat.mat_cfc, CFC.rpow_eq_cfc_real (ha := by positivity)]] - simp [HermitianMat.conj_apply_mat, Matrix.mul_assoc, (HermitianMat.sqrt_sq σ.nonneg).symm] - /-- The fidelity is 1 if and only if the two states are the same. -/ theorem fidelity_eq_one_iff_self : fidelity ρ σ = 1 ↔ ρ = σ := by - refine ⟨fun h => ?_, fun h => h ▸ fidelity_self_eq_one ρ⟩ - set A : Matrix d d ℂ := ρ.M.sqrt.mat - set B : Matrix d d ℂ := σ.M.sqrt.mat - have hAh : Aᴴ = A := by simp [A] - have hBh : Bᴴ = B := by simp [B] - have hAeq : ρ.m = Aᴴ * A := by simpa [hAh] using (HermitianMat.sqrt_sq ρ.nonneg).symm - have hBeq : σ.m = Bᴴ * B := by simpa [hBh] using (HermitianMat.sqrt_sq σ.nonneg).symm - obtain ⟨U, hU⟩ := (Matrix.traceNorm_eq_max_re_tr_U (B * A)).left - have hUB : (U.1 * B)ᴴ * (U.1 * B) = Bᴴ * B := by - rw [Matrix.conjTranspose_mul, Matrix.mul_assoc] - simp [show (U.1)ᴴ = star U.1 from rfl, ← Matrix.mul_assoc, U.2.1] - set z : ℂ := (U.1 * (B * A)).trace - have hzre : z.re = 1 := hU.trans ((fidelity_eq_traceNorm_sqrt_mul_sqrt ρ σ).symm.trans h) - have hzconj : z + conj z = 2 := by rw [Complex.add_conj, hzre]; push_cast; ring - have hz1 : (Aᴴ * (U.1 * B)).trace = z := by - show _ = (U.1 * (B * A)).trace - rw [hAh, ← Matrix.mul_assoc, Matrix.trace_mul_cycle, Matrix.trace_mul_comm] - have hz2 : ((U.1 * B)ᴴ * A).trace = conj z := by - rw [show ((U.1 * B)ᴴ * A) = (Aᴴ * (U.1 * B))ᴴ from by - simp [Matrix.conjTranspose_mul, hAh, hBh]] - simpa [hz1] using Matrix.trace_conjTranspose (Aᴴ * (U.1 * B)) - have hAU : A = U.1 * B := by - refine sub_eq_zero.mp <| Matrix.trace_conjTranspose_mul_self_eq_zero_iff.mp ?_ - have h_expand : ((A - U.1 * B)ᴴ * (A - U.1 * B)).trace = - (Aᴴ * A).trace - (Aᴴ * (U.1 * B)).trace - ((U.1 * B)ᴴ * A).trace - + ((U.1 * B)ᴴ * (U.1 * B)).trace := by - simp [sub_eq_add_neg, Matrix.conjTranspose_mul, Matrix.mul_add, Matrix.add_mul, - Matrix.trace_add, Matrix.trace_neg, add_assoc, add_left_comm, add_comm] - rw [h_expand, hz1, hz2, ← hAeq, ρ.tr', hUB, ← hBeq, σ.tr'] - linear_combination -hzconj - exact MState.ext_m <| by rw [hAeq, hAU, hUB, ← hBeq] + refine ⟨fun h ↦ ?_, fun h ↦ h ▸ fidelity_self_eq_one ρ⟩ + let _ : StdBasis ℂ E (Fin (Module.finrank ℂ E)) := StdBasis.some ℂ E + rw [fidelity_eq_matrix (ι := Fin (Module.finrank ℂ E))] at h + exact (DensityOp.ext (ι := Fin (Module.finrank ℂ E)) + (HermitianMat.eq_of_trace_sqrt_conj_sqrt_eq_one σ.nonneg ρ.nonneg σ.tr ρ.tr h)).symm /-- The fidelity is a symmetric quantity. -/ theorem fidelity_symm : fidelity ρ σ = fidelity σ ρ := by - simp only [fidelity] - have expand : ∀ (a b : MState d), (a.M.conj b.M.sqrt.mat).mat = - (b.M.sqrt.mat * a.M.sqrt.mat) * (a.M.sqrt.mat * b.M.sqrt.mat) := fun a b => by - simp [HermitianMat.conj_apply_mat, b.M.sqrt.conjTranspose_mat, - (HermitianMat.sqrt_sq a.nonneg).symm, Matrix.mul_assoc] - have h_eig := ((σ.M.conj ρ.M.sqrt.mat).H.eigenvalues_eq_eigenvalues_iff - (ρ.M.conj σ.M.sqrt.mat).H).mpr (by rw [expand σ ρ, expand ρ σ, Matrix.charpoly_mul_comm]) - show ((σ.M.conj ρ.M.sqrt.mat).cfc Real.sqrt).trace = ((ρ.M.conj σ.M.sqrt.mat).cfc Real.sqrt).trace - rw [HermitianMat.trace_cfc_eq, HermitianMat.trace_cfc_eq, h_eig] - -/-- The fidelity cannot decrease under the application of a channel. -/ -@[sorryful] -theorem fidelity_channel_nondecreasing [DecidableEq d₂] (Λ : CPTPMap d d₂) : fidelity (Λ ρ) (Λ σ) ≥ fidelity ρ σ := - sorry + let _ : StdBasis ℂ E (Fin (Module.finrank ℂ E)) := StdBasis.some ℂ E + rw [fidelity_eq_matrix (ι := Fin (Module.finrank ℂ E)), + fidelity_eq_matrix (ι := Fin (Module.finrank ℂ E))] + exact HermitianMat.trace_sqrt_conj_sqrt_comm σ.nonneg ρ.nonneg + +/-- The fidelity of a pure state with any other state is the square root of the expectation value +of the pure state's projector, `F(∣ψ⟩⟨ψ∣, σ) = √(⟨ψ∣σ∣ψ⟩)`. -/ +theorem fidelity_pure (ψ : Ket d) (σ : MState d) : + fidelity (MState.pure ψ) σ = Real.sqrt (σ.exp_val (MState.pure ψ).M) := by + have hP : (MState.pure ψ).M.sqrt = (MState.pure ψ).M := + HermitianMat.sqrt_eq_self (MState.pure ψ).nonneg (by + rw [DensityOp.mat_M, MState.pure_mul_self] + rfl) + rw [fidelity_eq_matrix (ι := d), hP, MState.conj_pure, + HermitianMat.sqrt_smul (MState.pure ψ).nonneg + (MState.exp_val_nonneg σ (MState.pure ψ).nonneg), + HermitianMat.trace_smul, hP, (MState.pure ψ).tr, mul_one] + +/-- **Matrix analogue of `DensityOp.fidelity`** as a trace norm. -/ +theorem fidelity_eq_traceNorm (ρ σ : MState d) : + fidelity ρ σ = ((σ.M : HermitianMat d ℂ).sqrt.mat + * (ρ.M : HermitianMat d ℂ).sqrt.mat).traceNorm := by + rw [fidelity_eq_matrix (ι := d), HermitianMat.trace_sqrt_conj_sqrt_eq_traceNorm σ.nonneg] + +open scoped MatrixOrder in +omit [DecidableEq d] in +/-- The Kraus form of the Cauchy-Schwarz bound `Matrix.re_trace_conjTranspose_mul_le_traceNorm'`, +applied to the stacked matrices `(X K₁ᴴ, X K₂ᴴ, …)` and `(Y K₁ᴴ, Y K₂ᴴ, …)`. -/ +private theorem re_trace_kraus_le [DecidableEq d₂] {κ : Type*} [Fintype κ] (K : κ → Matrix d₂ d ℂ) + (hcard : Fintype.card d₂ ≤ Fintype.card (κ × d)) (X Y : Matrix d d ℂ) : + RCLike.re (∑ i, K i * (Xᴴ * Y) * (K i)ᴴ).trace ≤ + (CFC.sqrt (∑ i, K i * (Yᴴ * Y) * (K i)ᴴ) * + CFC.sqrt (∑ i, K i * (Xᴴ * X) * (K i)ᴴ)).traceNorm := by + have hstack : ∀ Z Z' : Matrix d d ℂ, + (Matrix.stack fun i ↦ Z * (K i)ᴴ)ᴴ * (Matrix.stack fun i ↦ Z' * (K i)ᴴ) + = ∑ i, K i * (Zᴴ * Z') * (K i)ᴴ := fun Z Z' ↦ by + rw [Matrix.conjTranspose_stack_mul_stack] + exact Finset.sum_congr rfl fun i _ ↦ by + simp [Matrix.conjTranspose_mul, Matrix.mul_assoc] + have key := Matrix.re_trace_conjTranspose_mul_le_traceNorm' + (Matrix.stack fun i ↦ X * (K i)ᴴ) (Matrix.stack fun i ↦ Y * (K i)ᴴ) hcard + rwa [hstack, hstack, hstack] at key + +/-- The fidelity cannot decrease under the application of a channel. + +Writing `Λ` in Kraus form `Λ(M) = ∑ᵢ Kᵢ M Kᵢᴴ` and letting `W` be a unitary attaining +`F(ρ,σ) = Re Tr[W √σ √ρ]`, the two stacked matrices `P = (√ρ Kᵢᴴ)ᵢ` and `Q = (W √σ Kᵢᴴ)ᵢ` satisfy +`PᴴP = Λ(ρ)`, `QᴴQ = Λ(σ)` and `Re Tr[PᴴQ] = F(ρ,σ)`, so the Cauchy-Schwarz bound +`Matrix.re_trace_conjTranspose_mul_le_traceNorm'` gives `F(ρ,σ) ≤ F(Λ ρ, Λ σ)`. -/ +theorem fidelity_channel_nondecreasing [DecidableEq d₂] (ρ σ : MState d) (Λ : CPTPMap d d₂) : + fidelity (Λ ρ) (Λ σ) ≥ fidelity ρ σ := by + obtain ⟨K, hK⟩ := Λ.map_cp.exists_kraus _ + have hmap (M : Matrix d d ℂ) : ∑ i, K i * M * (K i)ᴴ = Λ.map M := by + rw [hK, MatrixMap.of_kraus_apply] + obtain ⟨W, hWinv, hW⟩ := + Matrix.exists_unitary_re_trace_eq_traceNorm + ((σ.M : HermitianMat d ℂ).sqrt.mat * (ρ.M : HermitianMat d ℂ).sqrt.mat) + have hWW : Wᴴ * W = 1 := by simpa using hWinv 1 + have hd : 0 < Fintype.card d := Fintype.card_pos_iff.mpr (MState.nonempty ρ) + have hcard : Fintype.card d₂ ≤ Fintype.card ((d₂ × d) × d) := by + simp only [Fintype.card_prod] + calc Fintype.card d₂ = Fintype.card d₂ * 1 * 1 := by ring + _ ≤ Fintype.card d₂ * Fintype.card d * Fintype.card d := by gcongr <;> omega + have key := re_trace_kraus_le K hcard (ρ.M : HermitianMat d ℂ).sqrt.mat + (W * (σ.M : HermitianMat d ℂ).sqrt.mat) + have hXX : ((ρ.M : HermitianMat d ℂ).sqrt.mat)ᴴ * (ρ.M : HermitianMat d ℂ).sqrt.mat = ρ.m := by + rw [HermitianMat.conjTranspose_mat, HermitianMat.sqrt_sq ρ.nonneg, mat_M] + have hYY : (W * (σ.M : HermitianMat d ℂ).sqrt.mat)ᴴ * (W * (σ.M : HermitianMat d ℂ).sqrt.mat) + = σ.m := by + rw [Matrix.conjTranspose_mul, HermitianMat.conjTranspose_mat, Matrix.mul_assoc, + ← Matrix.mul_assoc Wᴴ, hWW, Matrix.one_mul, HermitianMat.sqrt_sq σ.nonneg, mat_M] + have hXY : ((ρ.M : HermitianMat d ℂ).sqrt.mat)ᴴ * (W * (σ.M : HermitianMat d ℂ).sqrt.mat) + = (ρ.M : HermitianMat d ℂ).sqrt.mat * (W * (σ.M : HermitianMat d ℂ).sqrt.mat) := by + rw [HermitianMat.conjTranspose_mat] + rw [hXX, hYY, hXY, hmap, hmap, hmap, ← CPTPOp.mat_coe_eq_apply_mat, + ← CPTPOp.mat_coe_eq_apply_mat] at key + rw [ge_iff_le, fidelity_eq_traceNorm, fidelity_eq_traceNorm] + refine le_trans (le_of_eq ?_) (key.trans (le_of_eq ?_)) + · rw [← hW, Λ.map_TP _] + congr 1 + rw [← Matrix.mul_assoc, ← Matrix.mul_assoc] + exact Matrix.trace_mul_cycle _ _ _ + · rw [← mat_M, ← mat_M, ← HermitianMat.mat_sqrt (Λ σ).nonneg, + ← HermitianMat.mat_sqrt (Λ ρ).nonneg] --TODO: Real.arccos ∘ fidelity forms a metric (triangle inequality), the Fubini–Study metric. --Matches with classical (squared) Bhattacharyya coefficient --Invariance under unitaries --Uhlmann's theorem -end MState +end DensityOp diff --git a/QuantumInfo/States/Mixed/MState.lean b/QuantumInfo/States/Mixed/MState.lean index 2b32fe4b8..13a45bed6 100644 --- a/QuantumInfo/States/Mixed/MState.lean +++ b/QuantumInfo/States/Mixed/MState.lean @@ -10,12 +10,15 @@ public import QuantumInfo.ForMathlib.ComplexLaplaceTransform public import QuantumInfo.ForMathlib.ContinuousSup public import QuantumInfo.ForMathlib.Filter public import QuantumInfo.ForMathlib.HermitianMat +public import QuantumInfo.ForMathlib.HermitianOp public import QuantumInfo.ForMathlib.Isometry public import QuantumInfo.ForMathlib.LinearEquiv public import QuantumInfo.ForMathlib.MatrixNorm.TraceNorm public import QuantumInfo.ForMathlib.Matrix public import QuantumInfo.ForMathlib.Minimax public import QuantumInfo.ForMathlib.Misc +public import QuantumInfo.ForMathlib.PartialTrace +public import QuantumInfo.ForMathlib.StdBasis public import QuantumInfo.ForMathlib.Unitary public import QuantumInfo.ClassicalInfo.Distribution public import QuantumInfo.States.Pure.Braket @@ -25,16 +28,12 @@ public import Mathlib.Logic.Equiv.Basic /-! Finite dimensional quantum mixed states, ρ. -The same comments apply as in `Braket`: - -These could be done with a Hilbert space of Fintype, which would look like -```lean4 -(H : Type*) [NormedAddCommGroup H] [InnerProductSpace ℂ H] [CompleteSpace H] [FiniteDimensional ℂ H] -``` -or by choosing a particular `Basis` and asserting it is `Fintype`. But frankly it seems easier to -mostly focus on the basis-dependent notion of `Matrix`, which has the added benefit of an obvious -"classical" interpretation (as the basis elements, or diagonal elements of a mixed state). In that -sense, this quantum theory comes with the a particular classical theory always preferred. +A state is stored basis-freely, as a positive unit-trace operator on a finite dimensional Hilbert +space: this is `DensityOp E`. A preferred orthonormal basis -- a `StdBasis ℂ E ι` instance -- turns +that operator into a density *matrix* `ρ.M : HermitianMat ι ℂ`, and every matrix-level fact below +is derived from an operator-level one through `HermitianOp.toMat`. The abbreviation `MState d` is +`DensityOp (EuclideanSpace ℂ d)`, whose preferred basis is the computational one; it carries the +"classical" interpretation of the diagonal entries as a probability distribution over `d`. Important definitions: * `instMixable`: the `Mixable` instance allowing convex combinations of `MState`s @@ -48,6 +47,8 @@ Important definitions: @[expose] public section +set_option backward.isDefEq.respectTransparency false + noncomputable section open BigOperators @@ -55,15 +56,28 @@ open ComplexConjugate open HermitianMat open scoped Matrix ComplexOrder -/-- A **mixed quantum state** is a PSD matrix with trace 1. - -We don't `extend (M : HermitianMat d ℂ)` because that gives an annoying thing where -`M` is actually a `Subtype`, which means `ρ.M.foo` notation doesn't work. -/ -@[ext] -structure MState (d : Type*) [Fintype d] [DecidableEq d] where - M : HermitianMat d ℂ - nonneg : 0 ≤ M - tr : M.trace = 1 +/-- A **mixed quantum state** on a finite-dimensional Hilbert space: a positive operator of unit +trace. + +The state is stored as an *operator* so that it does not depend on a choice of basis. Given a +preferred orthonormal basis -- that is, a `StdBasis ℂ E ι` instance -- `DensityOp.M` is the density +*matrix*, and the matrix-level facts below are all derived from the operator-level ones through +`HermitianOp.toMat`. -/ +structure DensityOp (E : Type*) [NormedAddCommGroup E] [InnerProductSpace ℂ E] + [FiniteDimensional ℂ E] where + /-- The density operator. -/ + op : HermitianOp E + /-- A density operator is positive semidefinite. -/ + op_nonneg : 0 ≤ op + /-- A density operator has unit trace. -/ + op_trace : op.trace = 1 + +/-- A **mixed quantum state** on a system whose preferred basis is indexed by `d`. + +This is `DensityOp` on `EuclideanSpace ℂ d`, so `ρ.M : HermitianMat d ℂ` is the density matrix in +the computational basis. -/ +abbrev MState (d : Type*) [Fintype d] [DecidableEq d] : Type _ := + DensityOp (EuclideanSpace ℂ d) variable {d d₁ d₂ d₃ : Type*} variable [Fintype d] [Fintype d₁] [Fintype d₂] [Fintype d₃] @@ -72,112 +86,322 @@ variable [DecidableEq d] [DecidableEq d₁] [DecidableEq d₂] [DecidableEq d₃ variable (ψ φ : Ket d) variable (ρ σ : MState d) -namespace MState +namespace DensityOp -attribute [coe] MState.M -instance instCoe : Coe (MState d) (HermitianMat d ℂ) := ⟨MState.M⟩ +section Operator -attribute [simp] MState.tr +variable {E ι : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] -/-- The underlying `Matrix` in an MState. Prefer `MState.M` for the `HermitianMat`. -/ -def m (ρ : MState d) : Matrix d d ℂ := ρ.M.mat +/-- The **density matrix** of a state, in the preferred basis. -/ +@[coe] def M (ρ : DensityOp E) : HermitianMat ι ℂ := + ρ.op.toMat @[simp] -theorem mat_M : ρ.M.mat = ρ.m := by +theorem toMat_op (ρ : DensityOp E) : ρ.op.toMat = (M ρ : HermitianMat ι ℂ) := rfl -theorem pos (ρ : MState d) : 0 < ρ.M := by - apply ρ.nonneg.lt_of_ne' - intro h - have := ρ.tr - simp [h] at this +theorem nonneg (ρ : DensityOp E) : 0 ≤ (M ρ : HermitianMat ι ℂ) := by + rw [M, ← HermitianOp.toMat_zero (E := E) (ι := ι), HermitianOp.toMat_le_toMat] + exact ρ.op_nonneg -open Lean Meta Mathlib.Meta.Positivity in -/-- Positivity extension for `MState.M`: it is always positive (`0 < ρ.M`). -Note: we must not call `whnfR` on `e` because `MState.M` is a structure -projection (reducible), so `whnfR` would reduce it and destroy the pattern. -/ -@[positivity MState.M _] -meta def evalMStateM : PositivityExt where eval {_u _α} _zα _pα? e := - match _pα? with | none => pure .none | some _ => do - let ρ := e.appArg! - pure (.positive (← mkAppM ``MState.pos #[ρ])) +@[simp] +theorem tr (ρ : DensityOp E) : (M ρ : HermitianMat ι ℂ).trace = 1 := by + rw [M, HermitianOp.trace_toMat] + exact ρ.op_trace + +/-- Build a state from its density matrix in the preferred basis. -/ +def ofMat (A : HermitianMat ι ℂ) (h₁ : 0 ≤ A) (h₂ : A.trace = 1) : DensityOp E where + op := HermitianOp.ofMat A + op_nonneg := by + rw [← HermitianOp.toMat_le_toMat (ι := ι)] + simpa using h₁ + op_trace := by + rw [← HermitianOp.trace_toMat (ι := ι)] + simpa using h₂ ---TODO: There should be a bunch of places where we can use `positivity` to prove things, --- that are currently proved manually. -example (ρ : MState d) : 0 < ρ.M := by positivity +@[simp] +theorem M_ofMat (A : HermitianMat ι ℂ) (h₁ : 0 ≤ A) (h₂ : A.trace = 1) : + (M (ofMat (E := E) A h₁ h₂) : HermitianMat ι ℂ) = A := by + simp [M, ofMat] ---XXX These are methods that directly reference the matrix, "m" or ".val". --- We'd like to remove these (where possible) so that mostly go through HermitianMat --- where possible. -theorem psd : ρ.m.PosSemidef := - HermitianMat.zero_le_iff.mp ρ.nonneg +/-- Two states with the same density operator are equal. -/ +theorem ext_op {ρ σ : DensityOp E} (h : ρ.op = σ.op) : ρ = σ := by + cases ρ; cases σ; simp_all + +/-- Two states with the same density matrix are equal. -/ +@[ext] theorem ext {ρ σ : DensityOp E} (h : (M ρ : HermitianMat ι ℂ) = M σ) : ρ = σ := + ext_op (HermitianOp.toMat_injective (ι := ι) h) + +@[simp] +theorem ofMat_M (ρ : DensityOp E) : + ofMat (M ρ : HermitianMat ι ℂ) ρ.nonneg ρ.tr = ρ := + DensityOp.ext (by simp) +/-- The underlying `Matrix` of a state. Prefer `DensityOp.M` for the `HermitianMat`. -/ +def m (ρ : DensityOp E) : Matrix ι ι ℂ := (M ρ : HermitianMat ι ℂ).mat + +@[simp] +theorem mat_M (ρ : DensityOp E) : (M ρ : HermitianMat ι ℂ).mat = m ρ := by + rfl + +@[simp] +theorem m_ofMat (A : HermitianMat ι ℂ) (h₁ : 0 ≤ A) (h₂ : A.trace = 1) : + (m (ofMat (E := E) A h₁ h₂) : Matrix ι ι ℂ) = A.mat := by + rw [← mat_M, M_ofMat] + +theorem pos (ρ : DensityOp E) : 0 < (M ρ : HermitianMat ι ℂ) := by + refine (nonneg ρ).lt_of_ne' fun h ↦ ?_ + have h₁ : (M ρ : HermitianMat ι ℂ).trace = 1 := tr ρ + rw [h] at h₁ + simp at h₁ + +theorem psd (ρ : DensityOp E) : (m ρ : Matrix ι ι ℂ).PosSemidef := + HermitianMat.zero_le_iff.mp ρ.nonneg /-- Every mixed state is Hermitian. -/ -theorem Hermitian : ρ.m.IsHermitian := - ρ.M.H +theorem Hermitian (ρ : DensityOp E) : (m ρ : Matrix ι ι ℂ).IsHermitian := + (M ρ : HermitianMat ι ℂ).H @[simp] -theorem tr' : ρ.m.trace = 1 := by - rw [MState.m.eq_def, ← HermitianMat.trace_eq_trace_rc, ρ.tr] +theorem tr' (ρ : DensityOp E) : (m ρ : Matrix ι ι ℂ).trace = 1 := by + rw [← mat_M, ← HermitianMat.trace_eq_trace_rc, ρ.tr] simp -theorem ext_m {ρ₁ ρ₂ : MState d} (h : ρ₁.m = ρ₂.m) : ρ₁ = ρ₂ := by - rw [MState.mk.injEq] - ext1 - exact h +theorem ext_m {ρ₁ ρ₂ : DensityOp E} (h : (m ρ₁ : Matrix ι ι ℂ) = m ρ₂) : ρ₁ = ρ₂ := + DensityOp.ext (HermitianMat.ext h) /-- The map from mixed states to their matrices is injective -/ -theorem m_inj : (MState.m (d := d)).Injective := - fun _ _ h ↦ by ext1; ext1; exact h +theorem m_inj : Function.Injective (m (E := E) (ι := ι)) := + fun _ _ h ↦ ext_m h + +theorem M_Injective : Function.Injective (M (E := E) (ι := ι)) := + fun _ _ h ↦ DensityOp.ext h + +-- Could have used properties of ρ.spectrum +theorem eigenvalue_nonneg (ρ : DensityOp E) : ∀ i, 0 ≤ (ρ.Hermitian (ι := ι)).eigenvalues i := by + rw [← Matrix.PosSemidef.nonneg_iff_eigenvalue_nonneg (ρ.Hermitian (ι := ι))] + exact ρ.nonneg + +-- Could have used properties of ρ.spectrum +theorem eigenvalue_le_one (ρ : DensityOp E) : ∀ i, (ρ.Hermitian (ι := ι)).eigenvalues i ≤ 1 := by + intro i + have h := Finset.single_le_sum (f := (M ρ : HermitianMat ι ℂ).H.eigenvalues) + (fun y _ ↦ (ρ.psd (ι := ι)).eigenvalues_nonneg y) (Finset.mem_univ i) + rwa [(M ρ : HermitianMat ι ℂ).sum_eigenvalues_eq_trace, ρ.tr] at h + +theorem le_one (ρ : DensityOp E) : (M ρ : HermitianMat ι ℂ) ≤ 1 := by + open MatrixOrder in + suffices h : (m ρ : Matrix ι ι ℂ) ≤ (1 : ℝ) • 1 by + rw [one_smul] at h + exact h + rw [← Matrix.PosSemidef.le_smul_one_of_eigenvalues_iff (ρ.Hermitian (ι := ι))] + exact eigenvalue_le_one ρ + +end Operator + +section PartialTrace + +/-! ### Partial traces + +The reduced states of a state on a tensor product. These are the basis-free versions; the +`MState`-level partial traces on a product index type are `MState.traceLeft` and +`MState.traceRight`, and the two agree through `DensityOp.traceLeft_M`. -/ + +open scoped TensorProduct + +variable {E F ι κ : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] + +section Left + +/-- The **partial trace** of a state over the left factor: the reduced state on `F`. -/ +def traceLeft (ρ : DensityOp (E ⊗[ℂ] F)) : DensityOp F where + op := ρ.op.traceLeft + op_nonneg := HermitianOp.traceLeft_nonneg _ ρ.op_nonneg + op_trace := by rw [HermitianOp.trace_traceLeft, ρ.op_trace] + +@[simp] +theorem op_traceLeft (ρ : DensityOp (E ⊗[ℂ] F)) : ρ.traceLeft.op = ρ.op.traceLeft := + rfl + +/-- **Matrix analogue of `DensityOp.traceLeft`.** -/ +@[simp] +theorem traceLeft_M [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] (ρ : DensityOp (E ⊗[ℂ] F)) : + (M ρ.traceLeft : HermitianMat κ ℂ) = (M ρ : HermitianMat (ι × κ) ℂ).traceLeft := by + rw [M, M, op_traceLeft, HermitianOp.toMat_traceLeft (ι := ι)] + +end Left + +section Right + +/-- The **partial trace** of a state over the right factor: the reduced state on `E`. -/ +def traceRight (ρ : DensityOp (E ⊗[ℂ] F)) : DensityOp E where + op := ρ.op.traceRight + op_nonneg := HermitianOp.traceRight_nonneg _ ρ.op_nonneg + op_trace := by rw [HermitianOp.trace_traceRight, ρ.op_trace] + +@[simp] +theorem op_traceRight (ρ : DensityOp (E ⊗[ℂ] F)) : ρ.traceRight.op = ρ.op.traceRight := + rfl -theorem M_Injective : Function.Injective (MState.M (d := d)) := by - intro _ _ - exact MState.ext +/-- **Matrix analogue of `DensityOp.traceRight`.** -/ +@[simp] +theorem traceRight_M [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + [Fintype κ] [DecidableEq κ] [StdBasis ℂ F κ] (ρ : DensityOp (E ⊗[ℂ] F)) : + (M ρ.traceRight : HermitianMat ι ℂ) = (M ρ : HermitianMat (ι × κ) ℂ).traceRight := by + rw [M, M, op_traceRight, HermitianOp.toMat_traceRight (κ := κ)] + +end Right + +end PartialTrace + +section Congr + +/-! ### Transport along an isometry + +A linear isometry equivalence `E ≃ₗᵢ[ℂ] F` carries states on `E` to states on `F`. The case of +interest is `StdBasis.equiv`, which identifies two spaces whose preferred bases share an index +type; along it the density matrix is literally unchanged, which is what lets a state on +`EuclideanSpace ℂ (d₁ × d₂)` be read as a state on a tensor product. -/ + +variable {E F ι : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] + +/-- Transport a state along a linear isometry equivalence. -/ +def congr (ρ : DensityOp E) (e : E ≃ₗᵢ[ℂ] F) : DensityOp F where + op := ρ.op.congr e + op_nonneg := HermitianOp.congr_nonneg ρ.op_nonneg e + op_trace := by rw [HermitianOp.trace_congr, ρ.op_trace] + +@[simp] +theorem op_congr (ρ : DensityOp E) (e : E ≃ₗᵢ[ℂ] F) : (ρ.congr e).op = ρ.op.congr e := + rfl + +@[simp] +theorem congr_congr_symm (ρ : DensityOp E) (e : E ≃ₗᵢ[ℂ] F) : (ρ.congr e).congr e.symm = ρ := by + refine ext_op (HermitianOp.ext ?_) + rw [op_congr, op_congr, HermitianOp.op_congr, HermitianOp.op_congr, + ← LinearIsometryEquiv.symm_conjStarAlgEquiv, StarAlgEquiv.symm_apply_apply] + +@[simp] +theorem congr_symm_congr (ρ : DensityOp F) (e : E ≃ₗᵢ[ℂ] F) : (ρ.congr e.symm).congr e = ρ := by + simpa using congr_congr_symm ρ e.symm + +/-- **Matrix analogue of `DensityOp.congr`** along `StdBasis.equiv`: the density matrix is +unchanged. -/ +@[simp] +theorem M_congr [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [StdBasis ℂ F ι] (ρ : DensityOp E) : + (M (ρ.congr (StdBasis.equiv ℂ E F ι)) : HermitianMat ι ℂ) = (M ρ : HermitianMat ι ℂ) := by + rw [M, M, op_congr, HermitianOp.toMat_congr_equiv] + +/-- **Matrix analogue of `DensityOp.congr`** along an isometry that carries the preferred basis of +`E` to that of `F` up to a relabelling `σ` of the index: the density matrix is relabelled along +`σ`. -/ +theorem M_congr_of_stdBasis {κ : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [Fintype κ] + [DecidableEq κ] [StdBasis ℂ F κ] (ρ : DensityOp E) (e : E ≃ₗᵢ[ℂ] F) (σ : ι ≃ κ) + (he : ∀ i, e (stdBasis (𝕜 := ℂ) (E := E) i) = stdBasis (𝕜 := ℂ) (E := F) (σ i)) : + (M (ρ.congr e) : HermitianMat κ ℂ) = (M ρ : HermitianMat ι ℂ).reindex σ := by + rw [M, M, op_congr, HermitianOp.toMat_congr_of_stdBasis _ e σ he] + +variable (F) in +/-- Read a state on `E` as a state on any other space whose preferred basis has the same index +type, by matching up the two preferred bases. The density matrix is unchanged: `M_transport`. -/ +noncomputable def transport [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [StdBasis ℂ F ι] + (ρ : DensityOp E) : DensityOp F := + ρ.congr (StdBasis.equiv ℂ E F ι) + +/-- **Matrix analogue of `DensityOp.transport`**: the density matrix is unchanged. -/ +@[simp] +theorem M_transport [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [StdBasis ℂ F ι] + (ρ : DensityOp E) : (M (ρ.transport F) : HermitianMat ι ℂ) = (M ρ : HermitianMat ι ℂ) := + M_congr ρ + +@[simp] +theorem transport_self [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] (ρ : DensityOp E) : + ρ.transport E = ρ := + DensityOp.ext (M_transport ρ) + +@[simp] +theorem transport_transport [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] [StdBasis ℂ F ι] + (ρ : DensityOp E) : (ρ.transport F).transport E = ρ := + DensityOp.ext (by rw [M_transport, M_transport]) + +/-- Every state is the transport of a state on the Euclidean space with the same index type. Used +to reduce a basis-free statement to its index-level counterpart: `obtain ⟨μ, rfl⟩ := +ρ.exists_transport_eq` replaces `ρ` by `μ.transport _` throughout. -/ +theorem exists_transport_eq [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] (ρ : DensityOp E) : + ∃ μ : MState ι, μ.transport E = ρ := + ⟨ρ.transport _, transport_transport ρ⟩ + +end Congr + +end DensityOp + +namespace MState + +open DensityOp + +instance instCoe : Coe (MState d) (HermitianMat d ℂ) := ⟨DensityOp.M⟩ + +open Lean Meta Mathlib.Meta.Positivity in +/-- Positivity extension for `DensityOp.M`: it is always positive (`0 < ρ.M`). -/ +@[positivity DensityOp.M _] +meta def evalMStateM : PositivityExt where eval {_u _α} _zα pα? e := + match pα? with + | none => pure .none + | some _ => do + let .const _ lvls := e.getAppFn | throwError "not an application of DensityOp.M" + pure (.positive (mkAppN (.const ``DensityOp.pos lvls) e.getAppArgs)) + +--TODO: There should be a bunch of places where we can use `positivity` to prove things, +-- that are currently proved manually. +example (ρ : MState d) : 0 < ρ.M := by positivity variable (d) in /-- The matrices corresponding to MStates are `Convex ℝ` -/ -theorem convex : Convex ℝ (Set.range (MState.M (d := d))) := by +theorem convex : + Convex ℝ (Set.range (DensityOp.M (E := EuclideanSpace ℂ d) (ι := d))) := by simp only [Convex, Set.mem_range, StarConvex, forall_exists_index, forall_apply_eq_imp_iff] intro x y a b ha hb hab replace hab : a + b = (1 : ℂ) := by norm_cast have := HermitianMat.convex_cone x.nonneg y.nonneg ha hb - exact ⟨⟨_, this, by simpa using mod_cast hab⟩, rfl⟩ + exact ⟨DensityOp.ofMat _ this (by simpa using mod_cast hab), by simp⟩ instance instMixable : Mixable (HermitianMat d ℂ) (MState d) where - to_U := MState.M - to_U_inj := MState.ext + to_U := DensityOp.M + to_U_inj := DensityOp.ext mkT {u} := fun h ↦ - ⟨⟨u, h.casesOn fun t ht ↦ ht ▸ t.nonneg, - h.casesOn fun t ht ↦ ht ▸ t.tr⟩, rfl⟩ + ⟨DensityOp.ofMat u (h.casesOn fun t ht ↦ ht ▸ t.nonneg) + (h.casesOn fun t ht ↦ ht ▸ t.tr), by simp⟩ convex := convex d +/-- Mixing states mixes their density matrices. -/ +@[simp] +theorem mix_M (p : Prob) (ρ σ : MState d) : + (p [ρ ↔ σ]).M = (p : ℝ) • ρ.M + (1 - (p : ℝ)) • σ.M := by + have h : Mixable.to_U (p [ρ ↔ σ] : MState d) + = (p : ℝ) • Mixable.to_U ρ + ((1 - p : Prob) : ℝ) • Mixable.to_U σ := + (Mixable.mkT _).2 + have hU (τ : MState d) : Mixable.to_U τ = τ.M := rfl + simpa [hU] using h + +@[simp] +theorem mix_m (p : Prob) (ρ σ : MState d) : + (p [ρ ↔ σ]).m = (p : ℝ) • ρ.m + (1 - (p : ℝ)) • σ.m := by + rw [← mat_M, mix_M] + simp + +--An MState is a witness that d is nonempty. include ρ in -/-- An MState is a witness that d is nonempty. -/ -lemma nonempty : Nonempty d := by +theorem nonempty : Nonempty d := by by_contra h simpa [HermitianMat.trace_eq_re_trace, not_nonempty_iff.mp h] using ρ.tr --- Could have used properties of ρ.spectrum -theorem eigenvalue_nonneg : ∀ i, 0 ≤ ρ.Hermitian.eigenvalues i := by - rw [← Matrix.PosSemidef.nonneg_iff_eigenvalue_nonneg ρ.Hermitian] - exact ρ.nonneg - -set_option backward.isDefEq.respectTransparency false in --- Could have used properties of ρ.spectrum -theorem eigenvalue_le_one : ∀ i, ρ.Hermitian.eigenvalues i ≤ 1 := by - intro i - convert! Finset.single_le_sum (fun y _ ↦ ρ.psd.eigenvalues_nonneg y) (Finset.mem_univ i) - rw [ρ.M.sum_eigenvalues_eq_trace, ρ.tr] - -theorem le_one : ρ.M ≤ 1 := by - open MatrixOrder in - suffices h : ρ.m ≤ (1 : ℝ) • 1 by - rw [one_smul] at h - exact h - rw [← Matrix.PosSemidef.le_smul_one_of_eigenvalues_iff ρ.Hermitian] - exact eigenvalue_le_one ρ open scoped RealInnerProductSpace InnerProductSpace @@ -264,23 +488,28 @@ end exp_val section pure -set_option backward.isDefEq.respectTransparency false in /-- A mixed state can be constructed as a pure state arising from a ket. -/ -def pure (ψ : Ket d) : MState d where - M := { - val := Matrix.vecMulVec ψ (ψ : Bra d) - property := (Matrix.PosSemidef.outer_self_conj ψ).1 - } - nonneg := HermitianMat.zero_le_iff.mpr (.outer_self_conj ψ) - tr := by - have h₁ (x) : ψ x * conj (ψ x) = Complex.normSq (ψ x) := by - rw [mul_comm, Complex.normSq_eq_conj_mul_self] - simp [HermitianMat.trace_eq_re_trace, Matrix.trace, Matrix.vecMulVec_apply, Bra.eq_conj, h₁] - exact ψ.normalized - -set_option backward.isDefEq.respectTransparency false in +def pure (ψ : Ket d) : MState d := + DensityOp.ofMat ⟨Matrix.vecMulVec ψ (ψ : Bra d), (Matrix.PosSemidef.outer_self_conj ψ).1⟩ + (HermitianMat.zero_le_iff.mpr (.outer_self_conj ψ)) + (by + have h₁ (x : d) : (Matrix.vecMulVec (ψ : d → ℂ) ((ψ : Bra d) : d → ℂ)).diag x + = (Complex.normSq (ψ x) : ℂ) := by + rw [Matrix.diag_apply, Matrix.vecMulVec_apply, Bra.eq_conj, mul_comm, + Complex.normSq_eq_conj_mul_self] + rw [HermitianMat.trace_eq_re_trace, HermitianMat.mat_mk, Matrix.trace] + simp_rw [h₁] + rw [← Complex.ofReal_sum] + simpa using ψ.normalized) + +@[simp] +theorem pure_M (ψ : Ket d) : + (pure ψ).M = ⟨Matrix.vecMulVec ψ (ψ : Bra d), (Matrix.PosSemidef.outer_self_conj ψ).1⟩ := by + rw [pure, M_ofMat] + +/-- The overlap of two pure states is the squared magnitude of their bracket. -/ theorem pure_inner : ⟪pure ψ, pure φ⟫_Prob = ‖Braket.dot ψ φ‖^2 := by - simp [MState.inner_def, HermitianMat.inner_def, pure, Matrix.vecMulVec_mul_vecMulVec, + simp [MState.inner_def, HermitianMat.inner_def, pure_M, Matrix.vecMulVec_mul_vecMulVec, Braket.dot_eq_dotProduct, Matrix.trace_smul] rw [show ((ψ : d → ℂ) ⬝ᵥ ((φ : Bra d) : d → ℂ)) = conj (((ψ : Bra d) : d → ℂ) ⬝ᵥ (φ : d → ℂ)) from by @@ -294,13 +523,46 @@ theorem pure_inner : ⟪pure ψ, pure φ⟫_Prob = ‖Braket.dot ψ φ‖^2 := b @[simp] theorem pure_apply {i j : d} : (pure ψ).m i j = (ψ i) * conj (ψ j) := by - rfl + rw [← mat_M, pure_M, mat_mk] + simp [Matrix.vecMulVec_apply, Bra.eq_conj] + +@[simp] +theorem pure_M_apply {i j : d} : (pure ψ).M i j = (ψ i) * conj (ψ j) := by + rw [← mat_apply, mat_M, pure_apply] -set_option backward.isDefEq.respectTransparency false in theorem pure_mul_self : (pure ψ).m * (pure ψ).m = (pure ψ : Matrix d d ℂ) := by - dsimp [pure, MState.m] + rw [show ((pure ψ : Matrix d d ℂ)) = (pure ψ).m from rfl, ← mat_M, pure_M] simp [Matrix.vecMulVec_mul_vecMulVec, ← Braket.dot_eq_dotProduct] +/-- Sandwiching a state between two copies of the projector `∣ψ⟩⟨ψ∣` gives that projector back, +scaled by the expectation value `⟨ψ∣σ∣ψ⟩`. -/ +theorem conj_pure : σ.M.conj (pure ψ).M.mat = (σ.exp_val (pure ψ).M) • (pure ψ).M := by + set u : d → ℂ := fun i ↦ ψ i with hu + set v : d → ℂ := fun i ↦ conj (ψ i) with hv + have hmat : (pure ψ).m = Matrix.vecMulVec u v := by + rw [← mat_M, pure_M, HermitianMat.mat_mk] + ext i j + simp [Matrix.vecMulVec_apply, Bra.eq_conj, hu, hv] + have hsc : ((σ.exp_val (pure ψ).M : ℝ) : ℂ) = (v ᵥ* σ.m) ⬝ᵥ u := by + rw [← RCLike.ofReal_eq_complex_ofReal, exp_val, HermitianMat.inner_eq_trace_rc, + mat_M, mat_M, hmat, Matrix.mul_vecMulVec, ← Matrix.dotProduct_mulVec, dotProduct_comm] + rfl + ext1 + rw [HermitianMat.conj_apply_mat, HermitianMat.conjTranspose_mat, HermitianMat.mat_smul] + simp only [mat_M, hmat] + rw [Matrix.vecMulVec_mul, Matrix.vecMulVec_mul_vecMulVec, Matrix.vecMulVec_smul, ← hsc] + ext i j + simp [Complex.real_smul] + +/-- The projectors onto the standard basis states sum to the identity. -/ +theorem sum_pure_basis : ∑ i : d, (pure (Ket.basis i)).M = 1 := by + ext1 + rw [show (∑ i : d, (pure (Ket.basis i)).M).mat = ∑ i : d, ((pure (Ket.basis i)).M).mat from + map_sum (HermitianMat.matₗ (R := ℝ)) _ _] + ext i j + simp [Matrix.sum_apply, Matrix.vecMulVec_apply, Bra.eq_conj, Ket.basis, Ket.apply, + Matrix.one_apply, apply_ite, eq_comm] + /-- The purity of a state is Tr[ρ^2]. This is a `Prob`, because it is always between zero and one. -/ def purity (ρ : MState d) : Prob := ⟪ρ, ρ⟫_Prob @@ -323,7 +585,7 @@ theorem spectrum_pure_eq_constant : -- Prove ψ is an eigenvector of ρ = pure ψ have hv : ρ.M *ᵥ ψ = ψ := by ext - simp_rw [ρ, pure, Matrix.mulVec, mat, Matrix.vecMulVec_apply, dotProduct, + simp_rw [ρ, pure_M, Matrix.mulVec, mat_mk, Matrix.vecMulVec_apply, dotProduct, Bra.apply', Ket.apply, mul_assoc, ← Finset.mul_sum, ← Complex.normSq_eq_conj_mul_self, ← Complex.ofReal_sum, ← Ket.apply, ψ.normalized, Complex.ofReal_one, mul_one] let U : Matrix.unitaryGroup d ℂ := star ρ.M.H.eigenvectorUnitary -- Diagonalizing unitary of ρ @@ -367,17 +629,16 @@ theorem spectrum_pure_eq_constant : have hDiagj := congr_fun hDiag j rw [Matrix.mulVec_diagonal, mul_eq_right₀ hwNonZero'] at hDiagj use j - simp_all + simpa [ρ] using hDiagj obtain ⟨i, hEig'⟩ := hEig use i ext - exact hEig' + simpa using hEig' --If 1 is in a distribution, the distribution is a constant. obtain ⟨i, hi⟩ := this use i exact ProbDistribution.constant_of_exists_one hi -set_option backward.isDefEq.respectTransparency false in /-- If the spectrum of a mixed state is (1,0,0...) i.e. a constant distribution, it is a pure state. -/ theorem pure_of_constant_spectrum (h : ∃ i, ρ.spectrum = ProbDistribution.constant i) : @@ -431,7 +692,6 @@ theorem pure_iff_constant_spectrum : (∃ ψ, ρ = pure ψ) ↔ ⟨fun h ↦ h.rec fun ψ h₂ ↦ h₂ ▸ spectrum_pure_eq_constant ψ, pure_of_constant_spectrum ρ⟩ -set_option backward.isDefEq.respectTransparency false in theorem pure_iff_purity_one : (∃ ψ, ρ = pure ψ) ↔ ρ.purity = 1 := by --purity = exp(-Collision entropy) --purity eq 1 iff collision entropy is zero @@ -439,42 +699,71 @@ theorem pure_iff_purity_one : (∃ ψ, ρ = pure ψ) ↔ ρ.purity = 1 := by --distribution is constant iff pure constructor <;> intro h; · obtain ⟨w, rfl⟩ := h - dsimp [purity, inner] - have := pure_mul_self w - aesop; - · -- A state is pure iff its spectrum is constant. - apply (pure_iff_constant_spectrum ρ).mpr + have h₁ : ((⟪(pure w).M, (pure w).M⟫ : ℝ) : ℂ) = 1 := by + rw [← RCLike.ofReal_eq_complex_ofReal, HermitianMat.inner_eq_trace_rc, mat_M, pure_mul_self] + exact (pure w).tr' + ext + show (⟪(pure w).M, (pure w).M⟫ : ℝ) = 1 + exact_mod_cast h₁ + · --TODO Cleanup + -- Apply the theorem that states a mixed state is pure if and only if its spectrum is constant. + apply (pure_iff_constant_spectrum ρ).mpr; have h_eigenvalues : ∑ i, (ρ.spectrum i).val ^ 2 = 1 := by -- By definition of purity, we know that the sum of the squares of the eigenvalues is equal to the trace of ρ squared. - have h_trace_sq : ∑ i, (ρ.spectrum i).val ^ 2 = ρ.purity := by - have h_eigenvalues : ∑ i, (ρ.M.H.eigenvalues i) ^ 2 = (ρ.M.mat * ρ.M.mat).trace := by - have := Matrix.IsHermitian.spectral_theorem ρ.M.H; - conv_rhs => rw [ this ]; - simp [ Matrix.trace_mul_comm, Matrix.mul_assoc ]; - exact Finset.sum_congr rfl fun _ _ => by ring; - convert! congr_arg Complex.re h_eigenvalues using 1; + have h_trace_sq : ∑ i, (ρ.spectrum i).val ^ 2 = (ρ.purity : ℝ) := by + have h_eig : ((∑ i, ρ.M.H.eigenvalues i ^ 2 : ℝ) : ℂ) = (ρ.M.mat * ρ.M.mat).trace := by + have := Matrix.IsHermitian.spectral_theorem ρ.M.H + conv_rhs => rw [this] + simp [Matrix.trace_mul_comm, Matrix.mul_assoc] + exact Finset.sum_congr rfl fun _ _ => by ring + have h_pur : ((ρ.purity : ℝ) : ℂ) = (ρ.M.mat * ρ.M.mat).trace := by + rw [show (ρ.purity : ℝ) = ⟪ρ.M, ρ.M⟫ from rfl, + ← RCLike.ofReal_eq_complex_ofReal, HermitianMat.inner_eq_trace_rc] + have h_sp (i : d) : (ρ.spectrum i).val = ρ.M.H.eigenvalues i := by + simp [spectrum, ProbDistribution.mk'] + simp only [h_sp] + exact_mod_cast h_eig.trans h_pur.symm simp_all only [Set.Icc.coe_one] - -- Each eigenvalue satisfies `λᵢ (1 - λᵢ) ≥ 0`, and these terms sum to - -- `∑ λᵢ - ∑ λᵢ² = 1 - 1 = 0`, so every `λᵢ` is `0` or `1`. - have hbin : ∀ i, (ρ.spectrum i).val = 0 ∨ (ρ.spectrum i).val = 1 := by - have hnn : ∀ i ∈ Finset.univ, 0 ≤ (ρ.spectrum i).val * (1 - (ρ.spectrum i).val) := - fun i _ => mul_nonneg (ρ.spectrum i).2.1 (by linarith [(ρ.spectrum i).2.2]) - have hz : ∑ i, (ρ.spectrum i).val * (1 - (ρ.spectrum i).val) = 0 := by - have e : ∑ i, (ρ.spectrum i).val * (1 - (ρ.spectrum i).val) - = (∑ i, (ρ.spectrum i : ℝ)) - ∑ i, (ρ.spectrum i).val ^ 2 := by - rw [← Finset.sum_sub_distrib] - exact Finset.sum_congr rfl fun i _ => by ring - rw [e, h_eigenvalues, ρ.spectrum.normalized]; norm_num - exact fun i => (mul_eq_zero.mp ((Finset.sum_eq_zero_iff_of_nonneg hnn).mp hz i - (Finset.mem_univ i))).imp id fun h => by linarith - -- Since `∑ λᵢ = 1`, not all eigenvalues vanish, so one of them equals `1`. + have h_eigenvalues : ∑ i, (ρ.spectrum i).val * ((ρ.spectrum i).val - 1) = 0 := by + simp_all [ sq, mul_sub ]; + -- Since each term in the sum is non-positive and their sum is zero, each term must be zero. + have h_each_zero : ∀ i, (ρ.spectrum i).val * ((ρ.spectrum i).val - 1) = 0 := by + have h_each_zero : ∀ i, (ρ.spectrum i).val * ((ρ.spectrum i).val - 1) ≤ 0 := by + exact fun i => by nlinarith only [ show ( ρ.spectrum i : ℝ ) ≥ 0 by exact_mod_cast ( ρ.spectrum i ) |>.2.1, show ( ρ.spectrum i : ℝ ) ≤ 1 by exact_mod_cast ( ρ.spectrum i ) |>.2.2 ] ; + exact fun i => le_antisymm ( h_each_zero i ) ( by simpa [ h_eigenvalues ] using Finset.single_le_sum ( fun i _ => neg_nonneg.mpr ( h_each_zero i ) ) ( Finset.mem_univ i ) ); + -- Since each term in the sum is non-positive and their sum is zero, each term must be zero. Therefore, for each i, either (ρ.spectrum i).val = 0 or (ρ.spectrum i).val = 1. + have h_each_zero : ∀ i, (ρ.spectrum i).val = 0 ∨ (ρ.spectrum i).val = 1 := by + exact fun i => mul_eq_zero.mp ( h_each_zero i ) |> Or.imp id fun h => by linarith; + have h_sum_one : ∑ i, (ρ.spectrum i).val = 1 := by + grind; obtain ⟨i, hi⟩ : ∃ i, (ρ.spectrum i).val = 1 := by - by_contra hcon - simp only [not_exists] at hcon - simpa [fun i => (hbin i).resolve_right (hcon i)] using ρ.spectrum.normalized - exact ⟨i, ProbDistribution.constant_of_exists_one (Subtype.ext hi)⟩ + contrapose! h_sum_one; aesop; + -- Since the sum of the eigenvalues is 1 and one of them is 1, the remaining eigenvalues must sum to 0. Given that each eigenvalue is either 0 or 1, the only way their sum can be 0 is if all of them are 0. + have h_sum_zero : ∑ j ∈ Finset.univ.erase i, (ρ.spectrum j).val = 0 := by + rw [ ← Finset.sum_erase_add _ _ ( Finset.mem_univ i ), hi ] at h_sum_one ; linarith; + rw [ Finset.sum_eq_zero_iff_of_nonneg ] at h_sum_zero + · simp_all only [Finset.sum_const_zero, mul_eq_zero, Set.Icc.coe_eq_zero, Set.Icc.coe_eq_one, + ProbDistribution.normalized, Finset.mem_erase, ne_eq, Finset.mem_univ, and_true] + apply Exists.intro + · ext x : 2 + simp_all only [ProbDistribution.constant_eq] + split + next h_1 => + subst h_1 + simp_all only [Set.Icc.coe_one, Set.Icc.coe_eq_one] + exact hi + next h_1 => + simp_all only [Set.Icc.coe_zero, Set.Icc.coe_eq_zero] + apply h_sum_zero + apply Aesop.BuiltinRules.not_intro + intro a + subst a + simp_all only [not_true_eq_false] + · intro i_1 a + simp_all only [Finset.sum_const_zero, mul_eq_zero, Set.Icc.coe_eq_zero, Set.Icc.coe_eq_one, + ProbDistribution.normalized, Finset.mem_univ, Finset.sum_erase_eq_sub, Set.Icc.coe_one, sub_self, Finset.mem_erase, + ne_eq, and_true, Prob.zero_le_coe] -set_option backward.isDefEq.respectTransparency false in --TODO: Would be better if there was an `MState.eigenstate` or similar (maybe extending -- a similar thing for `HermitianMat`) and then this could be an equality with that, as -- an explicit formula, instead of this `Exists`. @@ -485,25 +774,34 @@ theorem spectralDecomposition (ρ : MState d) : nth_rw 1 [ρ.M.H.spectral_theorem] --TODO Cleanup simp only [Complex.coe_algebraMap, spectrum, ProbDistribution.mk', - ProbDistribution.funlike_apply, pure, Matrix.IsHermitian.eigenvectorUnitary_apply] + ProbDistribution.funlike_apply, Matrix.IsHermitian.eigenvectorUnitary_apply] rw [HermitianMat.mat_finset_sum] simp only [Unitary.conjStarAlgAut_apply] rw [Finset.sum_apply, Finset.sum_apply, Matrix.mul_apply] congr! simp only [Matrix.mul_diagonal, Matrix.IsHermitian.eigenvectorUnitary_apply, mul_comm, Matrix.star_apply, RCLike.star_def] - simp only [Function.comp_apply, mat_M, mat_apply, HermitianMat.smul_apply, Complex.real_smul] - rw [mul_assoc] - rfl + simp only [Function.comp_apply, mat_M, mat_apply, HermitianMat.smul_apply, Complex.real_smul, + pure_M_apply, Ket.apply] + ring end pure section prod -def prod (ρ₁ : MState d₁) (ρ₂ : MState d₂) : MState (d₁ × d₂) where - M := ρ₁.M ⊗ₖ ρ₂.M - nonneg := HermitianMat.zero_le_iff.mpr (ρ₁.psd.PosSemidef_kronecker ρ₂.psd) - tr := by simp +def prod (ρ₁ : MState d₁) (ρ₂ : MState d₂) : MState (d₁ × d₂) := + DensityOp.ofMat (ρ₁.M ⊗ₖ ρ₂.M) + (HermitianMat.zero_le_iff.mpr (ρ₁.psd.PosSemidef_kronecker ρ₂.psd)) + (by simp) + +@[simp] +theorem prod_M (ρ₁ : MState d₁) (ρ₂ : MState d₂) : (prod ρ₁ ρ₂).M = ρ₁.M ⊗ₖ ρ₂.M := by + rw [prod, M_ofMat] + +@[simp] +theorem prod_m (ρ₁ : MState d₁) (ρ₂ : MState d₂) : + (prod ρ₁ ρ₂).m = Matrix.kroneckerMap (· * ·) ρ₁.m ρ₂.m := by + rw [← mat_M, prod_M, kronecker_mat, mat_M, mat_M] infixl:100 " ⊗ᴹ " => MState.prod @@ -512,7 +810,7 @@ theorem prod_inner_prod (ξ1 ψ1 : MState d₁) (ξ2 ψ2 : MState d₂) : ext1 simp only [inner_def, Prob.coe_mul, ← Complex.ofReal_inj] --Lots of this should actually be facts about HermitianMat first - simp only [prod, Complex.ofReal_mul] + simp only [prod_M, Complex.ofReal_mul] simp only [← RCLike.ofReal_eq_complex_ofReal, inner_eq_trace_rc] simp only [kronecker, ← Matrix.trace_kronecker] simp only [mat_M, mat_mk, Matrix.mul_kronecker_mul] @@ -520,22 +818,21 @@ theorem prod_inner_prod (ξ1 ψ1 : MState d₁) (ξ2 ψ2 : MState d₂) : /-- The product of pure states is a pure product state , `Ket.prod`. -/ theorem pure_prod_pure (ψ₁ : Ket d₁) (ψ₂ : Ket d₂) : pure (ψ₁ ⊗ᵠ ψ₂) = (pure ψ₁) ⊗ᴹ (pure ψ₂) := by ext : 3 - simp [Ket.prod, Ket.apply, prod, -mat_apply] - ac_rfl + simp [Ket.prod, Ket.apply, Matrix.vecMulVec_apply, Bra.eq_conj, kronecker_apply] + ring end prod -set_option backward.isDefEq.respectTransparency false in /-- A representation of a classical distribution as a quantum state, diagonal in the given basis. -/ -def ofClassical (dist : ProbDistribution d) : MState d where - M := diagonal ℂ (fun x ↦ dist x) - nonneg := by simp [zero_le_iff, diagonal, Matrix.posSemidef_diagonal_iff] - tr := by simp [trace_diagonal] +def ofClassical (dist : ProbDistribution d) : MState d := + DensityOp.ofMat (diagonal ℂ (fun x ↦ dist x)) + (by simp [zero_le_iff, diagonal, Matrix.posSemidef_diagonal_iff]) + (by simp [trace_diagonal]) @[simp] theorem coe_ofClassical (dist : ProbDistribution d) : (ofClassical dist).M = diagonal ℂ (dist ·) := by - rfl + rw [ofClassical, M_ofMat] theorem ofClassical_pow (dist : ProbDistribution d) (p : ℝ) : (ofClassical dist).M ^ p = diagonal ℂ (fun i ↦ (dist i) ^ p) := by @@ -556,7 +853,7 @@ instance instUnique [Unique d] : Unique (MState d) where ext have h₁ := ρ.tr have h₂ := (@uniform _ _ _ _ : MState d).tr - simp [Matrix.trace, Unique.eq_default, -MState.tr, HermitianMat.trace_eq_re_trace] at h₁ h₂ ⊢ + simp [Matrix.trace, Unique.eq_default, -DensityOp.tr, HermitianMat.trace_eq_re_trace] at h₁ h₂ ⊢ apply Complex.ext · exact h₁.trans h₂.symm · rw [complex_im_eq_zero, complex_im_eq_zero] @@ -570,24 +867,89 @@ lemma default_eq [Nonempty d] : (default : MState d) = uniform := rfl @[simp] theorem M_default [Unique d] : (default : MState d).M = 1 := by - simp [default_eq, uniform] - rfl + show (uniform : MState d).M = 1 + rw [uniform, coe_ofClassical] + ext1 + ext i j + simp [Subsingleton.elim i j] section ptrace /-- Partial tracing out the left half of a system. -/ -@[simps] -def traceLeft (ρ : MState (d₁ × d₂)) : MState d₂ where - M := ρ.M.traceLeft - nonneg := zero_le_iff.mpr ρ.psd.traceLeft - tr := by simp [trace] +def traceLeft (ρ : MState (d₁ × d₂)) : MState d₂ := + DensityOp.ofMat ρ.M.traceLeft (zero_le_iff.mpr ρ.psd.traceLeft) (by simp [trace]) + +@[simp] +theorem traceLeft_M (ρ : MState (d₁ × d₂)) : (traceLeft ρ).M = ρ.M.traceLeft := by + rw [traceLeft, M_ofMat] + +@[simp] +theorem traceLeft_m (ρ : MState (d₁ × d₂)) : (traceLeft ρ).m = ρ.m.traceLeft := by + rw [← mat_M, traceLeft_M, traceLeft_mat, mat_M] /-- Partial tracing out the right half of a system. -/ -@[simps] -def traceRight (ρ : MState (d₁ × d₂)) : MState d₁ where - M := ρ.M.traceRight - nonneg := zero_le_iff.mpr ρ.psd.traceRight - tr := by simp [trace] +def traceRight (ρ : MState (d₁ × d₂)) : MState d₁ := + DensityOp.ofMat ρ.M.traceRight (zero_le_iff.mpr ρ.psd.traceRight) (by simp [trace]) + +@[simp] +theorem traceRight_M (ρ : MState (d₁ × d₂)) : (traceRight ρ).M = ρ.M.traceRight := by + rw [traceRight, M_ofMat] + +@[simp] +theorem traceRight_m (ρ : MState (d₁ × d₂)) : (traceRight ρ).m = ρ.m.traceRight := by + rw [← mat_M, traceRight_M, traceRight_mat, mat_M] + +section Tensor + +open scoped TensorProduct + +/-- A bipartite state, read as a state on an honest tensor product. + +`MState (d₁ × d₂)` is a state on `EuclideanSpace ℂ (d₁ × d₂)`, whose preferred basis happens to be +indexed by a product; this transports it along `StdBasis.equiv` to the tensor product of the two +factors, where the basis-free partial traces `DensityOp.traceLeft` and `DensityOp.traceRight` +live. -/ +noncomputable def toTensor (ρ : MState (d₁ × d₂)) : + DensityOp (EuclideanSpace ℂ d₁ ⊗[ℂ] EuclideanSpace ℂ d₂) := + ρ.transport _ + +@[simp] +theorem M_toTensor (ρ : MState (d₁ × d₂)) : (M ρ.toTensor : HermitianMat (d₁ × d₂) ℂ) = ρ.M := + DensityOp.M_transport ρ + +section Factors + +variable {E F : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [StdBasis ℂ E d₁] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [StdBasis ℂ F d₂] + +/-- The index-level partial trace is the operator-level one, read through `DensityOp.transport`. -/ +@[simp] +theorem traceLeft_transport (ρ : MState (d₁ × d₂)) : + (ρ.transport (E ⊗[ℂ] F)).traceLeft = ρ.traceLeft.transport F := by + ext1 + rw [DensityOp.traceLeft_M (ι := d₁), DensityOp.M_transport, DensityOp.M_transport, traceLeft_M] + +/-- The index-level partial trace is the operator-level one, read through `DensityOp.transport`. -/ +@[simp] +theorem traceRight_transport (ρ : MState (d₁ × d₂)) : + (ρ.transport (E ⊗[ℂ] F)).traceRight = ρ.traceRight.transport E := by + ext1 + rw [DensityOp.traceRight_M (κ := d₂), DensityOp.M_transport, DensityOp.M_transport, traceRight_M] + +end Factors + +/-- The index-level partial trace is the operator-level one, read through `MState.toTensor`. -/ +@[simp] +theorem traceLeft_toTensor (ρ : MState (d₁ × d₂)) : ρ.toTensor.traceLeft = ρ.traceLeft := by + rw [toTensor, traceLeft_transport, DensityOp.transport_self] + +/-- The index-level partial trace is the operator-level one, read through `MState.toTensor`. -/ +@[simp] +theorem traceRight_toTensor (ρ : MState (d₁ × d₂)) : ρ.toTensor.traceRight = ρ.traceRight := by + rw [toTensor, traceRight_transport, DensityOp.transport_self] + +end Tensor /-- Taking the direct product on the left and tracing it back out gives the same state. -/ @[simp] @@ -652,14 +1014,14 @@ theorem sInf_spectrum_prod (ρ : MState d) (σ : MState d₂) : sInf (_root_.spectrum ℝ (ρ ⊗ᴹ σ).m) = sInf (_root_.spectrum ℝ ρ.m) * sInf (_root_.spectrum ℝ σ.m) := by rcases isEmpty_or_nonempty d with _ | _; · simp rcases isEmpty_or_nonempty d₂ with _ | _; · simp - rw [MState.m, MState.prod, HermitianMat.spectrum_prod, ← MState.m, ← MState.m] + rw [DensityOp.m, prod_M, HermitianMat.spectrum_prod, ← DensityOp.m, ← DensityOp.m] apply csInf_mul_nonneg · exact ContinuousFunctionalCalculus.spectrum_nonempty _ ρ.M.H - · rw [MState.m, ρ.M.H.spectrum_real_eq_range_eigenvalues] + · rw [DensityOp.m, ρ.M.H.spectrum_real_eq_range_eigenvalues] rintro _ ⟨i, rfl⟩ apply ρ.eigenvalue_nonneg · exact ContinuousFunctionalCalculus.spectrum_nonempty _ σ.M.H - · rw [MState.m, σ.M.H.spectrum_real_eq_range_eigenvalues] + · rw [DensityOp.m, σ.M.H.spectrum_real_eq_range_eigenvalues] rintro _ ⟨i, rfl⟩ apply σ.eigenvalue_nonneg @@ -677,7 +1039,6 @@ theorem IsSeparable_prod (ρ₁ : MState d₁) (ρ₂ : MState d₂) : IsSeparab use { only }, ProbDistribution.constant ⟨only, Finset.mem_singleton_self only⟩ simp [prod, Unique.eq_default, only] -set_option backward.isDefEq.respectTransparency false in theorem eq_of_sum_eq_pure {d : Type*} [Fintype d] [DecidableEq d] {ι : Type*} {s : Finset ι} {p : ι → ℝ} {ρs : ι → MState d} {ρ : MState d} (h_pure : ρ.purity = 1) (h_sum : ρ.M = ∑ i ∈ s, p i • (ρs i).M) @@ -723,13 +1084,13 @@ theorem eq_of_sum_eq_pure {d : Type*} [Fintype d] [DecidableEq d] have h_eq : ⟪A, A⟫ ≤ A.trace * A.trace := by apply HermitianMat.inner_le_mul_trace hA_nonneg hA_nonneg; aesop; - exact ⟨ by assumption, h_eq _ ( ρs i |>.2 ) ( ρs i |>.3 ) ⟩; + exact ⟨ by assumption, h_eq _ (ρs i).nonneg (ρs i).tr ⟩; linarith [ h_trace i hi hpi, (ρ.M - (ρs i).M).inner_self_nonneg ]; -- Since the inner product of a matrix with itself is zero if and only if the matrix is zero, we have ρ.M - (ρs i).M = 0. have h_zero : ρ.M - (ρs i).M = 0 := by apply inner_self_eq_zero.mp h_eq; exact eq_of_sub_eq_zero h_zero; - exact MState.ext h_eq.symm + exact DensityOp.ext h_eq.symm theorem purity_prod {d₁ d₂ : Type*} [Fintype d₁] [Fintype d₂] [DecidableEq d₁] [DecidableEq d₂] (ρ₁ : MState d₁) (ρ₂ : MState d₂) : (ρ₁ ⊗ᴹ ρ₂).purity = ρ₁.purity * ρ₂.purity := by @@ -766,7 +1127,7 @@ theorem pure_eq_pure_iff {d : Type*} [Fintype d] [DecidableEq d] (ψ φ : Ket d) simp [ *, Complex.ext_iff ]; intro i j; rw [ Complex.norm_def ] at left; simp_all [ Complex.normSq ]; grind +ring; - exact MState.ext_m ( by ext i j; exact h_simp i j ) + exact DensityOp.ext_m ( by ext i j; simpa [ Matrix.vecMulVec, Ket.apply ] using h_simp i j ) /-- Two kets are phase-equivalent if and only if their pure states are equal. -/ theorem PhaseEquiv_iff_pure_eq {d : Type*} [Fintype d] [DecidableEq d] (ψ φ : Ket d) : @@ -788,7 +1149,6 @@ theorem pureQ_injective {d : Type*} [Fintype d] [DecidableEq d] : Function.Injec simp [pureQ] at h exact Quotient.sound ((PhaseEquiv_iff_pure_eq _ _).mpr h) -set_option backward.isDefEq.respectTransparency false in theorem pure_separable_imp_IsProd {d₁ d₂ : Type*} [Fintype d₁] [Fintype d₂] [DecidableEq d₁] [DecidableEq d₂] (ψ : Ket (d₁ × d₂)) (h : IsSeparable (pure ψ)) : ψ.IsProd := by obtain ⟨ ρLRs, ps, hps ⟩ := h; @@ -805,33 +1165,26 @@ theorem pure_separable_imp_IsProd {d₁ d₂ : Type*} [Fintype d₁] [Fintype d use fun x => ( ps x : ℝ ); use fun x => MState.prod x.val.1 x.val.2; all_goals norm_cast; + · simpa using hps · exact fun i a => unitInterval.nonneg (ps i); · exact ps.2; · exact Finset.mem_univ k; - · obtain ⟨val, property⟩ := k - obtain ⟨fst, snd⟩ := val - have fwd : 0 ≤ (ps ⟨(fst, snd), property⟩).1 := le_of_lt hk - apply Iff.intro + · constructor · intro a - exact MState.ext_iff.mpr a.symm + exact DensityOp.ext (by simpa using a.symm) · intro a rw [← a] - rfl + simp -- Since `pure ψ` is pure (`purity = 1`), by `MState.pure_iff_purity_one`, `ρL_k = pure ξ` and `ρR_k = pure φ` for some `ξ, φ`. - obtain ⟨ξ, hξ⟩ : ∃ ξ : MState d₁, k.val.1 = ξ ∧ ξ.purity = 1 := by - have h_purity : (pure ψ).purity = (k.val.1).purity * (k.val.2).purity := by - convert MState.purity_prod _ _; - exact MState.ext hk.2; - have h_purity_one : (pure ψ).purity = 1 := by - exact ( pure_iff_purity_one _ ).mp ⟨ ψ, rfl ⟩; - rw [ h_purity, Prob.mul_eq_one_iff ] at h_purity_one ; aesop - obtain ⟨φ, hφ⟩ : ∃ φ : MState d₂, k.val.2 = φ ∧ φ.purity = 1 := by - have h_purity_prod : (pure ψ).purity = (k.val.1).purity * (k.val.2).purity := by - convert MState.purity_prod _ _; - exact MState.ext hk.2; - have h_purity_one : (MState.pure ψ).purity = 1 := by - exact pure_iff_purity_one _ |>.1 ⟨ ψ, rfl ⟩; - aesop; + have hprod : pure ψ = k.val.1 ⊗ᴹ k.val.2 := DensityOp.ext (by simpa using hk.2) + have h_purity : (pure ψ).purity = (k.val.1).purity * (k.val.2).purity := by + rw [hprod, MState.purity_prod] + have h_purity_one : (pure ψ).purity = 1 := (pure_iff_purity_one _).mp ⟨ψ, rfl⟩ + rw [h_purity, Prob.mul_eq_one_iff] at h_purity_one + obtain ⟨ξ, hξ⟩ : ∃ ξ : MState d₁, k.val.1 = ξ ∧ ξ.purity = 1 := + ⟨k.val.1, rfl, h_purity_one.1⟩ + obtain ⟨φ, hφ⟩ : ∃ φ : MState d₂, k.val.2 = φ ∧ φ.purity = 1 := + ⟨k.val.2, rfl, h_purity_one.2⟩ -- Since `ξ` and `φ` are pure states, we have `ξ = pure ξ'` and `φ = pure φ'` for some `ξ', φ'`. obtain ⟨ξ', hξ'⟩ : ∃ ξ' : Ket d₁, ξ = MState.pure ξ' := by have := MState.pure_iff_purity_one ξ; @@ -841,18 +1194,19 @@ theorem pure_separable_imp_IsProd {d₁ d₂ : Type*} [Fintype d₁] [Fintype d -- Since `pure ψ = pure ξ ⊗ᵠ pure φ`, we have `ψ = ξ ⊗ᵠ φ` up to a global phase `z`. have h_eq : (pure ψ).M = (pure (ξ' ⊗ᵠ φ')).M := by rw [ hk.2, hξ.1, hξ', hφ.1, hφ', MState.pure_prod_pure ]; - exact rfl; + simp -- Since `pure ψ = pure (ξ' ⊗ᵠ φ')`, we have `ψ = ξ' ⊗ᵠ φ'` up to a global phase `z`. have h_eq_ket : ∃ z : ℂ, ‖z‖ = 1 ∧ ψ.vec = z • (ξ' ⊗ᵠ φ').vec := by have := MState.pure_eq_pure_iff ψ ( ξ' ⊗ᵠ φ' ); - exact this.mp ( MState.ext h_eq ); + exact this.mp ( DensityOp.ext h_eq ); obtain ⟨ z, hz₁, hz₂ ⟩ := h_eq_ket; use ⟨ fun i => z * ξ' i, ?_ ⟩, φ'; - ext ⟨ i, j ⟩ ; simp [ Ket.prod ]; - convert! congr_fun hz₂ ( i, j ) using 1; - exact mul_assoc _ _ _; + ext ⟨ i, j ⟩ + have hz := congr_fun hz₂ ( i, j ) + simp only [ Ket.prod, Ket.apply, Pi.smul_apply, smul_eq_mul ] at hz ⊢ + linear_combination hz simp [ hz₁] - exact ξ'.normalized' + simpa [ Complex.normSq_eq_norm_sq, Ket.apply ] using ξ'.normalized' /-- A pure state is separable iff the ket is a product state. -/ theorem pure_separable_iff_IsProd (ψ : Ket (d₁ × d₂)) : @@ -870,6 +1224,11 @@ theorem pure_iff_rank_eq_one {d : Type*} [Fintype d] [DecidableEq d] (ρ : MStat (∃ ψ, ρ = pure ψ) ↔ ρ.m.rank = 1 := by constructor <;> intro h; · obtain ⟨w, rfl⟩ := h + have hm : (pure w).m = Matrix.vecMulVec (w : d → ℂ) (conj (w : d → ℂ)) := by + ext i j + rw [pure_apply] + rfl + rw [hm] -- The rank of the outer product of a vector with itself is 1. have h_rank : ∀ (v : d → ℂ), v ≠ 0 → Matrix.rank (Matrix.vecMulVec v (conj v)) = 1 := by intro v hv_ne_zero @@ -905,9 +1264,8 @@ theorem pure_iff_rank_eq_one {d : Type*} [Fintype d] [DecidableEq d] (ρ : MStat rw [ Fintype.card_subtype ] at h_diag ; exact h_diag; obtain ⟨i, hi⟩ : ∃ i : d, h_herm.eigenvalues i ≠ 0 := by exact not_forall.mp fun h => by simp [ h ] at h_diag; - rw [ Finset.sum_eq_add_sum_sdiff_singleton i _ (by simp) ] at h_diag; - exact ⟨i, hi, fun j hj => Classical.not_not.1 fun hj' => - absurd h_diag ( by rw [ if_neg hi ] ; exact ne_of_gt ( lt_add_of_pos_right _ ( lt_of_lt_of_le ( by simp [ hj' ] ) ( Finset.single_le_sum ( fun x _ => by positivity ) ( Finset.mem_sdiff.2 ⟨ Finset.mem_univ j, by simp [ hj ] ⟩ ) ) ) ) ) ⟩; + rw [ ← Finset.add_sum_erase _ _ ( Finset.mem_univ i ) ] at h_diag; + exact ⟨ i, hi, fun j hj => Classical.not_not.1 fun hj' => absurd h_diag ( by rw [ if_neg hi ] ; exact ne_of_gt ( lt_add_of_pos_right _ ( lt_of_lt_of_le ( by simp [ hj' ] ) ( Finset.single_le_sum ( fun x _ => by positivity ) ( Finset.mem_erase.2 ⟨ hj, Finset.mem_univ j ⟩ ) ) ) ) ) ⟩; -- Since the diagonal matrix in the spectral theorem has exactly one non-zero entry, we can write ρ.m as |ψ⟩⟨ψ| for some ket ψ. use fun j => (h_herm.eigenvectorUnitary : Matrix d d ℂ) j i * Real.sqrt (h_herm.eigenvalues i); convert this using 1 @@ -925,7 +1283,7 @@ theorem pure_iff_rank_eq_one {d : Type*} [Fintype d] [DecidableEq d] (ρ : MStat use ⟨ψ, by simpa [ Complex.normSq_eq_norm_sq ] using h_norm⟩ generalize_proofs at *; - refine' MState.ext_m _ ; aesop + refine' DensityOp.ext_m _ ; aesop /-- A ket on a product space is a product state if and only if its coefficient matrix has rank 1. @@ -952,9 +1310,13 @@ theorem Ket.IsProd_iff_rank_eq_one {d₁ d₂ : Type*} [Fintype d₁] [Fintype d exact ⟨ ∑ j, ψ' j * y j, by ext i; simp [ Matrix.mulVec, dotProduct, mul_comm, mul_left_comm, Finset.mul_sum _ _ _ ] ⟩; exact le_trans ( Submodule.finrank_mono h_range ) ( finrank_span_le_card _ ) |> le_trans <| by norm_num; cases h_rank.eq_or_lt <;> simp_all [ Matrix.rank, Submodule.eq_bot_iff ]; - · convert! ‹Module.finrank ℂ ( LinearMap.range ( Matrix.mulVecLin ( Matrix.of fun i j => ξ i * ψ' j ) ) ) = 1› using 3 ; aesop; + · convert ‹Module.finrank ℂ ( LinearMap.range ( Matrix.mulVecLin ( Matrix.of fun i j => ξ i * ψ' j ) ) ) = 1› using 3 ; aesop; · aesop; · ext; simp [hξψ']; + · have hof : (Matrix.of fun i j => ψ (i, j)) = (Matrix.of fun i j => ξ i * ψ' j) := by + ext i j + simp [hξψ'] + rw [hof] · have := ψ.exists_ne_zero simp_all only [ne_eq, mul_eq_zero, not_or, Prod.exists, exists_and_left, exists_and_right] obtain ⟨left, right⟩ := this @@ -988,10 +1350,10 @@ theorem pure_separable_iff_traceLeft_pure (ψ : Ket (d₁ × d₂)) : IsSeparabl have h2 := Ket.IsProd_iff_rank_eq_one ψ; have h3 := MState.pure_iff_rank_eq_one ( ( MState.pure ψ ).traceLeft ) simp_all - have h4 : Matrix.rank ((MState.pure ψ).traceLeft.m) = Matrix.rank (Matrix.of (fun i j => ψ (i, j))) := by - have h4 : (MState.pure ψ).traceLeft.m = Matrix.transpose (Matrix.conjTranspose (Matrix.of (fun i j => ψ (i, j))) * Matrix.of (fun i j => ψ (i, j))) := by + have h4 : Matrix.rank ((MState.pure ψ).m.traceLeft) = Matrix.rank (Matrix.of (fun i j => ψ (i, j))) := by + have h4 : (MState.pure ψ).m.traceLeft = Matrix.transpose (Matrix.conjTranspose (Matrix.of (fun i j => ψ (i, j))) * Matrix.of (fun i j => ψ (i, j))) := by ext i j - simp [ MState.traceLeft, Matrix.mul_apply ] ; + simp [ Matrix.traceLeft, Matrix.mul_apply ] ; exact Finset.sum_congr rfl fun _ _ => mul_comm _ _; rw [ h4, Matrix.rank_transpose, Matrix.rank_conjTranspose_mul_self ]; grind @@ -1023,29 +1385,30 @@ def purify (ρ : MState d) : Ket (d × d) where original mixed state. -/ @[simp] theorem purify_spec (ρ : MState d) : (pure ρ.purify).traceRight = ρ := by + -- The spectral theorem, written out entrywise. + have h_spectral : ∀ i j, ∑ x, ρ.Hermitian.eigenvectorUnitary i x * + (ρ.Hermitian.eigenvalues x : ℂ) * + starRingEnd ℂ (ρ.Hermitian.eigenvectorUnitary j x) = (ρ.M : Matrix d d ℂ) i j := by + intro i j + have h : (ρ.M : Matrix d d ℂ) = Matrix.of (fun i j => ∑ x, + ρ.Hermitian.eigenvectorUnitary i x * (ρ.Hermitian.eigenvalues x : ℂ) * + starRingEnd ℂ (ρ.Hermitian.eigenvectorUnitary j x)) := by + conv_lhs => rw [mat_M, ρ.Hermitian.spectral_theorem] + ext i j + simp [Unitary.conjStarAlgAut_apply, Matrix.mul_apply, Matrix.diagonal_apply] + exact (congr_fun (congr_fun h i) j).symm ext i j simp_rw [purify, traceRight, HermitianMat.traceRight, Matrix.traceRight] - simp only [Matrix.IsHermitian.eigenvectorUnitary_apply, mat_M, pure_apply, - mat_mk, Matrix.of_apply] - simp only [Ket.apply] - simp only [map_mul] - simp_rw [mul_assoc, mul_comm, ← mul_assoc (Complex.ofReal _), Complex.mul_conj] - -- By definition of eigenvectorUnitary and the properties of the unitary matrix and the eigenvalues, we can show that the matrix constructed from the purification is equal to ρ. - have h_eigenvectorUnitary : ∀ i j, ∑ x, ρ.Hermitian.eigenvectorUnitary i x * ((ρ.Hermitian.eigenvalues x).sqrt ^ 2) * starRingEnd ℂ (ρ.Hermitian.eigenvectorUnitary j x) = ρ.M i j := by - intro i j - have h_eigenvectorUnitary : ρ.M = Matrix.of (fun i j => ∑ x, ρ.Hermitian.eigenvectorUnitary i x * ρ.Hermitian.eigenvalues x * starRingEnd ℂ (ρ.Hermitian.eigenvectorUnitary j x)) := by - have := ρ.Hermitian.spectral_theorem; - convert! this using 1; - ext i j; simp [ Matrix.mul_apply, Matrix.diagonal ] ; - replace h_eigenvectorUnitary := congr_fun ( congr_fun h_eigenvectorUnitary i ) j - simp_all only [mat_apply, Matrix.IsHermitian.eigenvectorUnitary_apply, Matrix.of_apply] - congr! 2; - norm_num [ Complex.ext_iff, sq ]; - exact Or.inl (Real.mul_self_sqrt (ρ.psd.eigenvalues_nonneg _)) - simp_all [ Complex.normSq, sq ]; - have h1 := h_eigenvectorUnitary i j - convert! h1 using 1; - simp [mul_assoc] + simp only [Matrix.IsHermitian.eigenvectorUnitary_apply, m_ofMat, mat_M, pure_apply, + mat_mk, Matrix.of_apply, Ket.apply, map_mul, Complex.conj_ofReal] + show _ = (ρ.M : Matrix d d ℂ) i j + rw [← h_spectral i j] + refine Finset.sum_congr rfl fun x _ ↦ ?_ + have hs : ((ρ.Hermitian.eigenvalues x : ℝ) : ℂ) = + (√(ρ.Hermitian.eigenvalues x) : ℂ) * (√(ρ.Hermitian.eigenvalues x) : ℂ) := by + rw [← Complex.ofReal_mul, Real.mul_self_sqrt (ρ.eigenvalue_nonneg x)] + simp only [Matrix.IsHermitian.eigenvectorUnitary_apply, hs] + ring /-- `MState.purify` bundled with its defining property `MState.traceRight_of_purify`. -/ def purifyX (ρ : MState d) : { ψ : Ket (d × d) // (pure ψ).traceRight = ρ } := @@ -1053,15 +1416,17 @@ def purifyX (ρ : MState d) : { ψ : Ket (d × d) // (pure ψ).traceRight = ρ } end purification -@[simps] -def relabel (ρ : MState d₁) (e : d₂ ≃ d₁) : MState d₂ where - M := ρ.M.reindex e.symm - nonneg := by simp [zero_le_iff, ρ.psd] - tr := by simp [trace] +def relabel (ρ : MState d₁) (e : d₂ ≃ d₁) : MState d₂ := + DensityOp.ofMat (ρ.M.reindex e.symm) (by simp [zero_le_iff, ρ.psd]) (by simp [trace]) + +@[simp] +theorem relabel_M (ρ : MState d₁) (e : d₂ ≃ d₁) : (ρ.relabel e).M = ρ.M.reindex e.symm := by + rw [relabel, M_ofMat] @[simp] theorem relabel_m (ρ : MState d₁) (e : d₂ ≃ d₁) : (ρ.relabel e).m = ρ.m.submatrix e e := by + rw [← mat_M, relabel_M] rfl @[simp] @@ -1073,27 +1438,34 @@ theorem relabel_refl {d : Type*} [Fintype d] [DecidableEq d] (ρ : MState d) : /-- Relabeling a pure state by a bijection yields another pure state. -/ theorem relabel_pure_exists (ψ : Ket d₁) (e : d₂ ≃ d₁) : ∃ ψ' : Ket d₂, (pure ψ).relabel e = pure ψ' := by - refine ⟨⟨fun i => ψ (e i), ?_⟩, rfl⟩ - rw [← ψ.normalized', Fintype.sum_equiv e] - congr! + have hnorm : ∑ i, ‖ψ (e i)‖ ^ 2 = 1 := by + rw [← ψ.normalized', Fintype.sum_equiv e] + congr! + refine ⟨⟨fun i => ψ (e i), hnorm⟩, ?_⟩ + ext i j + simp [reindex_apply, Ket.apply, Matrix.vecMulVec_apply, Bra.eq_conj] @[simp] theorem relabel_relabel {d d₂ d₃ : Type*} [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] [Fintype d₃] [DecidableEq d₃] (ρ : MState d) (e : d₂ ≃ d) (e₂ : d₃ ≃ d₂) : (ρ.relabel e).relabel e₂ = ρ.relabel (e₂.trans e) := by - rfl + ext + simp [reindex_apply] theorem eq_relabel_iff {d₁ d₂ : Type u} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] (ρ : MState d₁) (σ : MState d₂) (h : d₁ ≃ d₂) : ρ = σ.relabel h ↔ ρ.relabel h.symm = σ := by - simp only [MState.ext_iff, HermitianMat.ext_iff, mat_M, relabel_m] - exact ⟨(by simp[·]), (by simp[← ·])⟩ + constructor + · rintro rfl + simp + · rintro rfl + simp theorem relabel_comp {d₁ d₂ d₃ : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] [Fintype d₃] [DecidableEq d₃] (ρ : MState d₁) (e : d₂ ≃ d₁) (f : d₃ ≃ d₂) : (ρ.relabel e).relabel f = ρ.relabel (f.trans e) := by ext - simp + simp [reindex_apply] theorem relabel_cast {d₁ d₂ : Type u} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] @@ -1106,7 +1478,6 @@ theorem relabel_cast {d₁ d₂ : Type u} [Fintype d₁] [DecidableEq d₁] symm apply cast_heq -set_option backward.isDefEq.respectTransparency false in @[simp] theorem spectrum_relabel {ρ : MState d} (e : d₂ ≃ d) : _root_.spectrum ℝ (ρ.relabel e).m = _root_.spectrum ℝ ρ.m := by @@ -1132,6 +1503,15 @@ theorem purity_relabel (ρ : MState d₁) (e : d₂ ≃ d₁) : (ρ.relabel e).p def SWAP (ρ : MState (d₁ × d₂)) : MState (d₂ × d₁) := ρ.relabel (Equiv.prodComm d₁ d₂).symm +@[simp] +theorem SWAP_M (ρ : MState (d₁ × d₂)) : ρ.SWAP.M = ρ.M.reindex (Equiv.prodComm d₁ d₂) := by + rw [SWAP, relabel_M, Equiv.symm_symm] + +@[simp] +theorem SWAP_m (ρ : MState (d₁ × d₂)) : ρ.SWAP.m = + ρ.m.submatrix (Equiv.prodComm d₁ d₂).symm (Equiv.prodComm d₁ d₂).symm := by + rw [SWAP, relabel_m] + /-- The multiset of values in the spectrum of a relabeled state is the same as the multiset of values in the spectrum of the original state. -/ @@ -1139,7 +1519,7 @@ lemma multiset_spectrum_relabel_eq {d₁ d₂ : Type*} [Fintype d₁] [Decidable (ρ : MState d₁) (e : d₂ ≃ d₁) : Multiset.map (ρ.relabel e).spectrum Finset.univ.val = Multiset.map ρ.spectrum Finset.univ.val := by have h_charpoly : Matrix.charpoly (ρ.relabel e).m = Matrix.charpoly ρ.m := by - exact Matrix.charpoly_reindex e.symm ρ.m + simpa [relabel_m] using Matrix.charpoly_reindex e.symm ρ.m have h_eigenvalues : Multiset.map (ρ.relabel e).M.H.eigenvalues Finset.univ.val = Multiset.map ρ.M.H.eigenvalues Finset.univ.val := by have h_eigenvalues : Polynomial.roots (Matrix.charpoly (ρ.relabel e).m) = Polynomial.roots (Matrix.charpoly ρ.m) := by rw [h_charpoly]; @@ -1163,7 +1543,7 @@ lemma multiset_spectrum_relabel_eq {d₁ d₂ : Type*} [Fintype d₁] [Decidable congr! 2; exact beq_eq_beq.mp rfl -lemma spectrum_SWAP (ρ : MState (d₁ × d₂)) : ∃ e, ρ.SWAP.spectrum.relabel e = ρ.spectrum := by +theorem spectrum_SWAP (ρ : MState (d₁ × d₂)) : ∃ e, ρ.SWAP.spectrum.relabel e = ρ.spectrum := by -- Apply the lemma exists_equiv_of_multiset_map_eq with the appropriate parameters. obtain ⟨w, h⟩ := exists_equiv_of_multiset_map_eq (fun p => ρ.spectrum p) (fun p => ρ.SWAP.spectrum p) (ρ.multiset_spectrum_relabel_eq (Equiv.prodComm _ _).symm ▸ rfl) @@ -1173,16 +1553,19 @@ lemma spectrum_SWAP (ρ : MState (d₁ × d₂)) : ∃ e, ρ.SWAP.spectrum.relab rfl @[simp] -theorem SWAP_SWAP (ρ : MState (d₁ × d₂)) : ρ.SWAP.SWAP = ρ := - rfl +theorem SWAP_SWAP (ρ : MState (d₁ × d₂)) : ρ.SWAP.SWAP = ρ := by + ext + simp [SWAP, reindex_apply] @[simp] -theorem traceLeft_SWAP (ρ : MState (d₁ × d₂)) : ρ.SWAP.traceLeft = ρ.traceRight := - rfl +theorem traceLeft_SWAP (ρ : MState (d₁ × d₂)) : ρ.SWAP.traceLeft = ρ.traceRight := by + ext + simp [SWAP, reindex_apply, traceLeft_apply, traceRight_apply] @[simp] -theorem traceRight_SWAP (ρ : MState (d₁ × d₂)) : ρ.SWAP.traceRight = ρ.traceLeft := - rfl +theorem traceRight_SWAP (ρ : MState (d₁ × d₂)) : ρ.SWAP.traceRight = ρ.traceLeft := by + ext + simp [SWAP, reindex_apply, traceLeft_apply, traceRight_apply] /-- The associator that re-clusters the parts of a quantum system. -/ def assoc (ρ : MState ((d₁ × d₂) × d₃)) : MState (d₁ × d₂ × d₃) := @@ -1192,19 +1575,50 @@ def assoc (ρ : MState ((d₁ × d₂) × d₃)) : MState (d₁ × d₂ × d₃) def assoc' (ρ : MState (d₁ × d₂ × d₃)) : MState ((d₁ × d₂) × d₃) := ρ.SWAP.assoc.SWAP.assoc.SWAP +/-- `MState.assoc'` is the relabelling along `Equiv.prodAssoc`; the chain of swaps and associators +in its definition composes to that single permutation. -/ +theorem assoc'_eq_relabel (ρ : MState (d₁ × d₂ × d₃)) : + ρ.assoc' = ρ.relabel (Equiv.prodAssoc d₁ d₂ d₃) := by + apply DensityOp.ext_m + ext ⟨⟨i, j⟩, k⟩ ⟨⟨i', j'⟩, k'⟩ + simp [assoc', assoc, SWAP] + +@[simp] +theorem assoc_M (ρ : MState ((d₁ × d₂) × d₃)) : + ρ.assoc.M = ρ.M.reindex (Equiv.prodAssoc d₁ d₂ d₃) := by + rw [assoc, relabel_M, Equiv.symm_symm] + +@[simp] +theorem assoc_m (ρ : MState ((d₁ × d₂) × d₃)) : ρ.assoc.m = + ρ.m.submatrix (Equiv.prodAssoc d₁ d₂ d₃).symm (Equiv.prodAssoc d₁ d₂ d₃).symm := by + rw [assoc, relabel_m] + +@[simp] +theorem assoc'_M (ρ : MState (d₁ × d₂ × d₃)) : + ρ.assoc'.M = ρ.M.reindex (Equiv.prodAssoc d₁ d₂ d₃).symm := by + rw [assoc'_eq_relabel, relabel_M] + +@[simp] +theorem assoc'_m (ρ : MState (d₁ × d₂ × d₃)) : + ρ.assoc'.m = ρ.m.submatrix (Equiv.prodAssoc d₁ d₂ d₃) (Equiv.prodAssoc d₁ d₂ d₃) := by + rw [assoc'_eq_relabel, relabel_m] + @[simp] theorem assoc_assoc' (ρ : MState (d₁ × d₂ × d₃)) : ρ.assoc'.assoc = ρ := by - rfl + ext + simp [assoc, assoc', SWAP, reindex_apply] @[simp] theorem assoc'_assoc (ρ : MState ((d₁ × d₂) × d₃)) : ρ.assoc.assoc' = ρ := by - rfl + ext + simp [assoc, assoc', SWAP, reindex_apply] @[simp] theorem traceLeft_right_assoc (ρ : MState ((d₁ × d₂) × d₃)) : ρ.assoc.traceLeft.traceRight = ρ.traceRight.traceLeft := by ext - exact Finset.sum_comm + simpa [assoc, reindex_apply, traceLeft_apply, traceRight_apply] + using Finset.sum_comm @[simp] theorem traceRight_left_assoc' (ρ : MState (d₁ × d₂ × d₃)) : @@ -1214,14 +1628,14 @@ theorem traceRight_left_assoc' (ρ : MState (d₁ × d₂ × d₃)) : @[simp] theorem traceRight_assoc (ρ : MState ((d₁ × d₂) × d₃)) : ρ.assoc.traceRight = ρ.traceRight.traceRight := by - ext : 3 - apply Finset.sum_product + ext + simp [assoc, reindex_apply, traceRight_apply, Fintype.sum_prod_type] @[simp] theorem traceLeft_assoc' (ρ : MState (d₁ × d₂ × d₃)) : ρ.assoc'.traceLeft = ρ.traceLeft.traceLeft := by - convert! ρ.SWAP.assoc.SWAP.traceRight_assoc - simp + ext + simp [assoc', traceLeft_apply] @[simp] theorem traceLeft_left_assoc (ρ : MState ((d₁ × d₂) × d₃)) : @@ -1233,6 +1647,8 @@ theorem traceRight_right_assoc' (ρ : MState (d₁ × d₂ × d₃)) : ρ.assoc'.traceRight.traceRight = ρ.traceRight := by simp [assoc'] +/-- **Matrix analogue of unit trace**: a density matrix, being PSD with trace one, has trace +norm one. -/ @[simp] theorem traceNorm_eq_one (ρ : MState d) : ρ.m.traceNorm = 1 := have := calc (ρ.m.traceNorm : ℂ) @@ -1240,15 +1656,57 @@ theorem traceNorm_eq_one (ρ : MState d) : ρ.m.traceNorm = 1 := _ = 1 := ρ.tr' Complex.ofReal_eq_one.mp this +section TensorRearrange + +/-! ### Rearranging tensor factors, operator-side + +`MState.SWAP` and `MState.assoc` relabel the index type of a composite system; read through +`DensityOp.transport`, they are the tensor-product isometries of Mathlib. -/ + +open scoped TensorProduct + +variable {E F G : Type*} +variable [NormedAddCommGroup E] [InnerProductSpace ℂ E] [StdBasis ℂ E d₁] +variable [NormedAddCommGroup F] [InnerProductSpace ℂ F] [StdBasis ℂ F d₂] +variable [NormedAddCommGroup G] [InnerProductSpace ℂ G] [StdBasis ℂ G d₃] + +/-- Swapping the two halves of a bipartite state is `TensorProduct.commIsometry`. -/ +theorem SWAP_transport (ρ : MState (d₁ × d₂)) : + ρ.SWAP.transport (F ⊗[ℂ] E) = (ρ.transport (E ⊗[ℂ] F)).congr (TensorProduct.commIsometry ℂ E F) + := by + ext1 + rw [DensityOp.M_congr_of_stdBasis _ _ (Equiv.prodComm d₁ d₂) StdBasis.commIsometry_stdBasis, + DensityOp.M_transport, DensityOp.M_transport, SWAP_M] + +/-- Reassociating a tripartite state is `TensorProduct.assocIsometry`. -/ +theorem assoc_transport (ρ : MState ((d₁ × d₂) × d₃)) : + ρ.assoc.transport (E ⊗[ℂ] (F ⊗[ℂ] G)) = + (ρ.transport ((E ⊗[ℂ] F) ⊗[ℂ] G)).congr (TensorProduct.assocIsometry ℂ E F G) := by + ext1 + rw [DensityOp.M_congr_of_stdBasis _ _ (Equiv.prodAssoc d₁ d₂ d₃) + StdBasis.assocIsometry_stdBasis, DensityOp.M_transport, DensityOp.M_transport, assoc_M] + +/-- Reassociating a tripartite state the other way is `TensorProduct.assocIsometry.symm`. -/ +theorem assoc'_transport (ρ : MState (d₁ × d₂ × d₃)) : + ρ.assoc'.transport ((E ⊗[ℂ] F) ⊗[ℂ] G) = + (ρ.transport (E ⊗[ℂ] (F ⊗[ℂ] G))).congr (TensorProduct.assocIsometry ℂ E F G).symm := by + have h := assoc_transport (E := E) (F := F) (G := G) ρ.assoc' + rw [assoc_assoc'] at h + rw [h, DensityOp.congr_congr_symm] + +end TensorRearrange + --TODO: This naming is very inconsistent. Should be better about "prod" vs "kron" theorem relabel_kron (ρ : MState d₁) (σ : MState d₂) (e : d₃ ≃ d₁) : ((ρ.relabel e) ⊗ᴹ σ) = (ρ ⊗ᴹ σ).relabel (e.prodCongr (Equiv.refl d₂)) := by - rfl --is this defeq abuse? I don't know + ext i j + simp [reindex_apply, kronecker_apply] theorem kron_relabel (ρ : MState d₁) (σ : MState d₂) (e : d₃ ≃ d₂) : (ρ ⊗ᴹ σ.relabel e) = (ρ ⊗ᴹ σ).relabel ((Equiv.refl d₁).prodCongr e) := by - rfl + ext i j + simp [reindex_apply, kronecker_apply] theorem prod_assoc (ρ : MState d₁) (σ : MState d₂) (τ : MState d₃) : (ρ ⊗ᴹ (σ ⊗ᴹ τ)) = (ρ ⊗ᴹ σ ⊗ᴹ τ).relabel (Equiv.prodAssoc d₁ d₂ d₃).symm := by @@ -1260,52 +1718,52 @@ section topology /-- Mixed states inherit the subspace topology from matrices -/ instance : TopologicalSpace (MState d) := - TopologicalSpace.induced MState.M inferInstance + TopologicalSpace.induced DensityOp.M inferInstance /-- The projection from mixed states to their Hermitian matrices is an embedding -/ -theorem toMat_IsEmbedding : Topology.IsEmbedding (MState.M (d := d)) where +theorem toMat_IsEmbedding : + Topology.IsEmbedding (DensityOp.M : MState d → HermitianMat d ℂ) where eq_induced := rfl - injective := @MState.ext _ _ _ + injective := DensityOp.M_Injective instance : T3Space (MState d) := Topology.IsEmbedding.t3Space toMat_IsEmbedding instance : CompactSpace (MState d) := by constructor - rw [(Topology.IsInducing.induced MState.M).isCompact_iff] + rw [(Topology.IsInducing.induced DensityOp.M).isCompact_iff] suffices IsCompact (Set.Icc 0 1 ∩ { m | m.trace = 1} : Set (HermitianMat d ℂ)) by convert this ext1 m constructor · rintro ⟨ρ, _, rfl⟩ simp [ρ.nonneg, ρ.le_one] - · simpa using fun m_pos _ m_tr ↦ ⟨⟨m, m_pos, m_tr⟩, rfl⟩ + · simpa using fun m_pos _ m_tr ↦ ⟨DensityOp.ofMat m m_pos m_tr, by simp⟩ apply isCompact_Icc.inter_right refine isClosed_eq ?_ continuous_const rw [funext trace_eq_re_trace] fun_prop noncomputable instance : MetricSpace (MState d) := - MetricSpace.induced MState.M MState.M_Injective inferInstance + MetricSpace.induced DensityOp.M DensityOp.M_Injective inferInstance theorem dist_eq (x y : MState d) : dist x y = dist x.M y.M := by rfl -set_option backward.isDefEq.respectTransparency false in instance : BoundedSpace (MState d) where bounded_univ := CompactSpace.isCompact_univ.isBounded @[fun_prop] -theorem Continuous_HermitianMat : Continuous (MState.M (d := d)) := +theorem Continuous_HermitianMat : Continuous (DensityOp.M : MState d → HermitianMat d ℂ) := continuous_iff_le_induced.mpr fun _ => id @[fun_prop] -theorem Continuous_Matrix : Continuous (MState.m (d := d)) := by - show Continuous (fun ρ : MState d => ρ.M.mat) +theorem Continuous_Matrix : Continuous (DensityOp.m : MState d → Matrix d d ℂ) := by + unfold DensityOp.m fun_prop -theorem image_M_isBounded (S : Set (MState d)) : Bornology.IsBounded (MState.M '' S) := by +theorem image_M_isBounded (S : Set (MState d)) : Bornology.IsBounded (DensityOp.M '' S) := by rw [← Bornology.isBounded_induced] exact Bornology.IsBounded.all S @@ -1316,16 +1774,19 @@ section finprod variable {ι : Type u} [DecidableEq ι] [fι : Fintype ι] variable {dI : ι → Type v} [∀(i :ι), Fintype (dI i)] [∀(i :ι), DecidableEq (dI i)] -set_option backward.isDefEq.respectTransparency false in -def piProd (ρi : (i:ι) → MState (dI i)) : MState ((i:ι) → dI i) where - M := { - val := Matrix.piProd (fun i ↦ (ρi i).m) - property := Matrix.IsHermitian.piProd (fun i ↦ (ρi i).Hermitian) - } - nonneg := by - rw [zero_le_iff] - exact Matrix.PosSemidef.piProd (fun i => psd (ρi i)) - tr := by simp [trace, Matrix.trace_piProd] +def piProd (ρi : (i:ι) → MState (dI i)) : MState ((i:ι) → dI i) := + DensityOp.ofMat + ⟨Matrix.piProd (fun i ↦ (ρi i).m), Matrix.IsHermitian.piProd (fun i ↦ (ρi i).Hermitian)⟩ + (by + rw [zero_le_iff] + exact Matrix.PosSemidef.piProd (fun i => psd (ρi i))) + (by simp [trace, Matrix.trace_piProd]) + +@[simp] +theorem piProd_M (ρi : (i:ι) → MState (dI i)) : + (piProd ρi).M = + ⟨Matrix.piProd (fun i ↦ (ρi i).m), Matrix.IsHermitian.piProd (fun i ↦ (ρi i).Hermitian)⟩ := by + rw [piProd, M_ofMat] /-- The n-copy "power" of a mixed state, with the standard basis indexed by pi types. -/ def npow (ρ : MState d) (n : ℕ) : MState (Fin n → d) := @@ -1339,38 +1800,35 @@ end finprod section posdef theorem PosDef.kron {d₁ d₂ : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] - {σ₁ : MState d₁} {σ₂ : MState d₂} (hσ₁ : σ₁.m.PosDef) (hσ₂ : σ₂.m.PosDef) : (σ₁ ⊗ᴹ σ₂).m.PosDef := - hσ₁.kron hσ₂ + {σ₁ : MState d₁} {σ₂ : MState d₂} (hσ₁ : σ₁.m.PosDef) (hσ₂ : σ₂.m.PosDef) : (σ₁ ⊗ᴹ σ₂).m.PosDef := by + rw [prod_m] + exact hσ₁.kron hσ₂ theorem PosDef.relabel {d₁ d₂ : Type*} [Fintype d₁] [DecidableEq d₁] [Fintype d₂] [DecidableEq d₂] - {ρ : MState d₁} (hρ : ρ.m.PosDef) (e : d₂ ≃ d₁) : (ρ.relabel e).m.PosDef := - Matrix.PosDef.reindex hρ e.symm + {ρ : MState d₁} (hρ : ρ.m.PosDef) (e : d₂ ≃ d₁) : (ρ.relabel e).m.PosDef := by + simpa [relabel_m] using Matrix.PosDef.reindex hρ e.symm /-- If both states positive definite, so is their mixture. -/ theorem PosDef_mix {d : Type*} [Fintype d] [DecidableEq d] {σ₁ σ₂ : MState d} - (hσ₁ : σ₁.m.PosDef) (hσ₂ : σ₂.m.PosDef) (p : Prob) : (p [σ₁ ↔ σ₂]).m.PosDef := - Matrix.PosDef.Convex hσ₁ hσ₂ p.zero_le (1 - p).zero_le (by simp) + (hσ₁ : σ₁.m.PosDef) (hσ₂ : σ₂.m.PosDef) (p : Prob) : (p [σ₁ ↔ σ₂]).m.PosDef := by + rw [mix_m] + exact Matrix.PosDef.Convex hσ₁ hσ₂ p.zero_le_coe (sub_nonneg.mpr Prob.coe_le_one) (by ring) /-- If one state is positive definite and the mixture is nondegenerate, their mixture is also positive definite. -/ theorem PosDef_mix_of_ne_zero {d : Type*} [Fintype d] [DecidableEq d] {σ₁ σ₂ : MState d} (hσ₁ : σ₁.m.PosDef) (p : Prob) (hp : p ≠ 0) : (p [σ₁ ↔ σ₂]).m.PosDef := by - rw [← zero_lt_iff] at hp - exact (hσ₁.smul hp).add_posSemidef (σ₂.psd.rsmul (1 - p).zero_le) + rw [mix_m] + have hp' : (0 : ℝ) < (p : ℝ) := + Prob.zero_le_coe.lt_of_ne fun h ↦ hp (Prob.ext (by simpa using h.symm)) + exact (hσ₁.smul hp').add_posSemidef (σ₂.psd.rsmul (sub_nonneg.mpr Prob.coe_le_one)) /-- If the second state is positive definite and the mixture is nondegenerate, their mixture is also positive definite. -/ theorem PosDef_mix_of_ne_one {d : Type*} [Fintype d] [DecidableEq d] {σ₁ σ₂ : MState d} (hσ₂ : σ₂.m.PosDef) (p : Prob) (hp : p ≠ 1) : (p [σ₁ ↔ σ₂]).m.PosDef := by - have : 0 < 1 - p := by - --TODO this is ridiculous, move to Prob - contrapose! hp - have : (1 : ℝ) - (p : ℝ) = (0 : ℝ) := by - have := le_antisymm hp (1 - p).zero_le - rw [Subtype.ext_iff] at this - simpa using this - ext - change (p : ℝ) = 1 - linarith - exact (hσ₂.smul this).posSemidef_add (σ₁.psd.rsmul p.zero_le) + rw [mix_m] + have hp' : (0 : ℝ) < 1 - (p : ℝ) := + sub_pos.mpr <| Prob.coe_le_one.lt_of_ne fun h ↦ hp (Prob.ext (by simpa using h)) + exact (hσ₂.smul hp').posSemidef_add (σ₁.psd.rsmul Prob.zero_le_coe) theorem uniform_posDef {d : Type*} [Nonempty d] [Fintype d] [DecidableEq d] : (uniform (d := d)).m.PosDef := by diff --git a/QuantumInfo/States/Mixed/TraceDistance.lean b/QuantumInfo/States/Mixed/TraceDistance.lean index 93a5439b2..19e4506db 100644 --- a/QuantumInfo/States/Mixed/TraceDistance.lean +++ b/QuantumInfo/States/Mixed/TraceDistance.lean @@ -6,6 +6,11 @@ Authors: Alex Meiburg module public import QuantumInfo.States.Mixed.MState +public import QuantumInfo.Channels.Bundled +public import QuantumInfo.Channels.CPTP +public import QuantumInfo.Channels.Dual +public import QuantumInfo.Channels.MatrixMap +public import QuantumInfo.Channels.Unbundled public import QuantumInfo.ForMathlib.ContinuousLinearMap public import QuantumInfo.ForMathlib.ComplexLaplaceTransform @@ -31,23 +36,37 @@ open Kronecker open scoped Matrix ComplexOrder variable {d : Type*} [Fintype d] [DecidableEq d] +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] +variable [FiniteDimensional ℂ E] -/--The trace distance between two quantum states: half the trace norm of the difference (ρ - σ). -/ -def TrDistance (ρ σ : MState d) : ℝ := - (1/2:ℝ) * (ρ.m - σ.m).traceNorm +/-- The trace distance between two quantum states: half the trace norm of the difference (ρ - σ). + +This makes no reference to a basis; `TrDistance_eq_matrix_traceNorm` is the matrix analogue. -/ +def TrDistance (ρ σ : DensityOp E) : ℝ := + (1/2 : ℝ) * (ρ.op - σ.op).traceNorm namespace TrDistance -variable {d d₂ : Type*} [Fintype d] [Fintype d₂] (ρ σ : MState d) +variable (ρ σ : DensityOp E) -theorem ge_zero : 0 ≤ TrDistance ρ σ := by - rw [TrDistance] - simp [Matrix.traceNorm_nonneg] +/-- **Matrix analogue of `TrDistance`**: half the trace norm of the difference of the density +matrices in the preferred basis. -/ +theorem eq_matrix_traceNorm {ι : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] : + TrDistance ρ σ = (1/2 : ℝ) * Matrix.traceNorm (ρ.m - σ.m : Matrix ι ι ℂ) := by + rw [TrDistance, HermitianOp.traceNorm_toMat (ι := ι), HermitianOp.toMat_sub] + rfl + +theorem ge_zero : 0 ≤ TrDistance ρ σ := + mul_nonneg (by norm_num) (HermitianOp.traceNorm_nonneg _) + +/-- A density operator has unit trace norm. -/ +theorem traceNorm_op (ρ : DensityOp E) : ρ.op.traceNorm = 1 := by + rw [HermitianOp.traceNorm_of_nonneg ρ.op_nonneg, ρ.op_trace] theorem le_one : TrDistance ρ σ ≤ 1 := by - have htri := Matrix.traceNorm_add_le ρ.m (-σ.m) - simp [TrDistance, sub_eq_add_neg, Matrix.traceNorm_neg, - ρ.traceNorm_eq_one, σ.traceNorm_eq_one] at htri ⊢ + have h := HermitianOp.traceNorm_sub_le ρ.op σ.op + rw [traceNorm_op, traceNorm_op] at h + rw [TrDistance] linarith /-- The trace distance, as a `Prob` probability with value between 0 and 1. -/ @@ -56,13 +75,41 @@ def prob : Prob := /-- The trace distance is a symmetric quantity. -/ theorem symm : TrDistance ρ σ = TrDistance σ ρ := by - dsimp [TrDistance] - rw [← Matrix.traceNorm_neg, neg_sub] + rw [TrDistance, TrDistance, ← HermitianOp.traceNorm_neg (ρ.op - σ.op), neg_sub] -/-- The trace distance is equal to half the 1-norm of the eigenvalues of their difference . -/ -theorem eq_abs_eigenvalues : TrDistance ρ σ = (1/2:ℝ) * +/-- The trace distance is equal to half the 1-norm of the eigenvalues of their difference. -/ +theorem eq_abs_eigenvalues (ρ σ : MState d) : TrDistance ρ σ = (1/2 : ℝ) * ∑ i, abs ((ρ.Hermitian.sub σ.Hermitian).eigenvalues i) := by - rw [TrDistance, Matrix.traceNorm_Hermitian_eq_sum_abs_eigenvalues] + rw [eq_matrix_traceNorm (ι := d), + Matrix.traceNorm_Hermitian_eq_sum_abs_eigenvalues (ρ.Hermitian.sub σ.Hermitian)] + congr! + +/-- The data processing inequality for the trace distance, once preferred bases have been chosen on +both sides. `TrDistance.DPI_PTP` is the statement itself, which needs no basis. -/ +private theorem DPI_PTP_of_stdBasis {F ι κ : Type*} [Fintype ι] [DecidableEq ι] [StdBasis ℂ E ι] + [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] [Fintype κ] + [DecidableEq κ] [StdBasis ℂ F κ] (ρ σ : DensityOp E) (Λ : PTPOp E F) : + TrDistance (Λ ρ) (Λ σ) ≤ TrDistance ρ σ := by + have hmat : ∀ τ : DensityOp E, ((Λ τ : DensityOp F).m : Matrix κ κ ℂ) = Λ.map τ.m := fun τ ↦ + congrArg HermitianMat.mat (PTPOp.M_apply_MState Λ τ) + have hin : (ρ.m - σ.m : Matrix ι ι ℂ) = ((ρ.M : HermitianMat ι ℂ) - σ.M).mat := by + rw [HermitianMat.mat_sub, DensityOp.mat_M, DensityOp.mat_M] + rw [eq_matrix_traceNorm (ι := κ), eq_matrix_traceNorm (ι := ι), hmat, hmat, ← map_sub, hin] + exact mul_le_mul_of_nonneg_left (Λ.map_pos.traceNorm_le Λ.map_TP _) (by norm_num) + +/-- **Data processing inequality for the trace distance**: a positive trace-preserving map never +increases the trace distance between two states. Complete positivity is not needed. -/ +theorem DPI_PTP {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] + (ρ σ : DensityOp E) (Λ : PTPOp E F) : TrDistance (Λ ρ) (Λ σ) ≤ TrDistance ρ σ := + let _ := StdBasis.some ℂ E + let _ := StdBasis.some ℂ F + DPI_PTP_of_stdBasis ρ σ Λ + +/-- **Data processing inequality for the trace distance**: a quantum channel never increases the +trace distance between two states. -/ +theorem DPI {F : Type*} [NormedAddCommGroup F] [InnerProductSpace ℂ F] [FiniteDimensional ℂ F] + (ρ σ : DensityOp E) (Φ : CPTPOp E F) : TrDistance (Φ ρ) (Φ σ) ≤ TrDistance ρ σ := + DPI_PTP ρ σ Φ.toPTPOp -- Fuchs–van de Graaf inequalities -- Relation to classical TV distance diff --git a/QuantumInfo/States/Pure/BlochSphere.lean b/QuantumInfo/States/Pure/BlochSphere.lean index f07ef1331..e054bca3b 100644 --- a/QuantumInfo/States/Pure/BlochSphere.lean +++ b/QuantumInfo/States/Pure/BlochSphere.lean @@ -53,7 +53,7 @@ private lemma blochVecRaw_norm (α θ : ℝ) : (blochVecRaw α θ 2) ^ 2) = 1 := by have : (blochVecRaw α θ 0) ^ 2 + (blochVecRaw α θ 1) ^ 2 + (blochVecRaw α θ 2) ^ 2 = 1 := by - simp [blochVecRaw, Fin.sum_univ_three] + simp [blochVecRaw] have h1 := Real.sin_sq_add_cos_sq α have h2 := Real.sin_sq_add_cos_sq θ nlinarith [sq_nonneg (Real.sin α * Real.cos θ), @@ -66,8 +66,7 @@ private lemma blochVecRaw_norm (α θ : ℝ) : def blochPoint (α θ : ℝ) : BlochSphere := ⟨(WithLp.equiv 2 _).symm (blochVecRaw α θ), by rw [Metric.mem_sphere, dist_comm, EuclideanSpace.dist_eq] - simp [EuclideanSpace.norm_eq, Fin.sum_univ_three, sub_zero, blochVecRaw_norm α θ, - Real.sqrt_one]⟩ + simp [Fin.sum_univ_three, blochVecRaw_norm α θ]⟩ /-- The underlying vector of a `blochPoint`. -/ lemma blochPoint_val (α θ : ℝ) : diff --git a/QuantumInfo/StdBasisState.lean b/QuantumInfo/StdBasisState.lean new file mode 100644 index 000000000..76d53bd66 --- /dev/null +++ b/QuantumInfo/StdBasisState.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2025 Alex Meiburg. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Alex Meiburg +-/ +module + +public import QuantumInfo.ForMathlib.StdBasis +public import QuantumInfo.Entropy.VonNeumann +public import QuantumInfo.Operators.Unitary + +/-! +# Mixed states of an abstract Hilbert space + +`MState d` is a matrix-level notion: it is a positive semidefinite `Matrix d d ℂ` of trace one. +Physically, a mixed state is an operator on a Hilbert space, and the matrix only appears after a +choice of orthonormal basis. This file bridges the two, using `StdBasis`. + +`MState.ofOp` turns a positive trace-one operator `A : E →L[ℂ] E` into an `MState d`, given an +orthonormal basis of `E` indexed by `d`. The main results say that the quantities computed from the +resulting matrix do not depend on which orthonormal basis was used: changing the basis conjugates +the matrix by a unitary (`MState.ofOp_eq_uConj`), and the von Neumann entropy is therefore +basis-independent (`MState.Sᵥₙ_ofOp_congr`). + +This is the pattern intended for migrating the rest of the library: a matrix-level definition is +first shown to be insensitive to the choice of `StdBasis`, after which it can be restated for the +operator directly. +-/ + +@[expose] public section + +set_option backward.isDefEq.respectTransparency false + +noncomputable section + +open scoped ComplexOrder MState + +variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℂ E] [FiniteDimensional ℂ E] +variable {d d₂ : Type*} [Fintype d] [DecidableEq d] [Fintype d₂] [DecidableEq d₂] + +namespace MState + +/-- The mixed state whose matrix, in the orthonormal basis `b`, is the matrix of the positive +trace-one operator `A`. -/ +def ofOp (b : OrthonormalBasis d ℂ E) (A : E →L[ℂ] E) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ E (A : E →ₗ[ℂ] E) = 1) : MState d := + DensityOp.ofMat + ⟨StdBasis.toMatOf b A, ((StdBasis.posSemidef_toMatOf_iff_nonneg b A).mpr hA).isHermitian⟩ + (HermitianMat.zero_le_iff.mpr ((StdBasis.posSemidef_toMatOf_iff_nonneg b A).mpr hA)) + (by + rw [HermitianMat.trace_eq_one_iff] + exact (StdBasis.trace_toMatOf b A).trans htr) + +@[simp] +theorem ofOp_m (b : OrthonormalBasis d ℂ E) (A : E →L[ℂ] E) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ E (A : E →ₗ[ℂ] E) = 1) : + (ofOp b A hA htr).m = StdBasis.toMatOf b A := by + rw [ofOp, DensityOp.m_ofMat, HermitianMat.mat_mk] + +/-- On `EuclideanSpace ℂ d` with its standard basis, `MState.ofOp` is just the existing matrix +description of a state: the current matrix-level development is the special case +`E := EuclideanSpace ℂ d` of the abstract one. -/ +@[simp] +theorem ofOp_basisFun (A : EuclideanSpace ℂ d →L[ℂ] EuclideanSpace ℂ d) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ (EuclideanSpace ℂ d) (A : _ →ₗ[ℂ] _) = 1) : + (ofOp (EuclideanSpace.basisFun d ℂ) A hA htr).m = + (Matrix.toEuclideanCLM (𝕜 := ℂ) (n := d)).symm A := by + rw [ofOp_m] + rfl + +/-- Von Neumann entropy is unchanged by conjugating a state by a unitary. -/ +@[simp] +theorem Sᵥₙ_uConj (ρ : MState d) (U : Matrix.unitaryGroup d ℂ) : Sᵥₙ (U ◃ ρ) = Sᵥₙ ρ := by + rw [Sᵥₙ_eq_Hₛ_spectrum, Sᵥₙ_eq_Hₛ_spectrum, uConj_spectrum_eq] + +/-- Changing the orthonormal basis used to represent an operator conjugates the resulting state by +the (unitary) change-of-basis matrix. -/ +theorem ofOp_eq_uConj (b b' : OrthonormalBasis d ℂ E) (A : E →L[ℂ] E) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ E (A : E →ₗ[ℂ] E) = 1) : + ofOp b' A hA htr = star (StdBasis.changeOfBasis b b') ◃ ofOp b A hA htr := by + apply DensityOp.ext_m + rw [ofOp_m, uConj_m, ofOp_m, StdBasis.toMatOf_conj b b'] + simp [Matrix.star_eq_conjTranspose] + +/-- **The von Neumann entropy of an operator does not depend on the choice of orthonormal basis.** + +This is the insensitivity lemma that licenses defining the entropy of an abstract density +operator: the matrix-level `Sᵥₙ` descends through `MState.ofOp`. -/ +theorem Sᵥₙ_ofOp_congr (b b' : OrthonormalBasis d ℂ E) (A : E →L[ℂ] E) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ E (A : E →ₗ[ℂ] E) = 1) : + Sᵥₙ (ofOp b' A hA htr) = Sᵥₙ (ofOp b A hA htr) := by + rw [ofOp_eq_uConj b b', Sᵥₙ_uConj] + +/-- The same insensitivity, phrased for two `StdBasis` instances on the same space. -/ +theorem Sᵥₙ_ofOp_congr_instances (inst inst' : StdBasis ℂ E d) (A : E →L[ℂ] E) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ E (A : E →ₗ[ℂ] E) = 1) : + Sᵥₙ (ofOp inst'.stdBasis A hA htr) = Sᵥₙ (ofOp inst.stdBasis A hA htr) := + Sᵥₙ_ofOp_congr _ _ A hA htr + +/-- Relabelling the index type of the orthonormal basis relabels the state. -/ +theorem ofOp_reindex (b : OrthonormalBasis d ℂ E) (e : d ≃ d₂) (A : E →L[ℂ] E) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ E (A : E →ₗ[ℂ] E) = 1) : + ofOp (b.reindex e) A hA htr = (ofOp b A hA htr).relabel e.symm := by + apply DensityOp.ext_m + rw [ofOp_m, relabel_m, ofOp_m, StdBasis.toMatOf_reindex] + rfl + +/-- Von Neumann entropy is also insensitive to the index type of the orthonormal basis. -/ +theorem Sᵥₙ_ofOp_reindex (b : OrthonormalBasis d ℂ E) (e : d ≃ d₂) (A : E →L[ℂ] E) (hA : 0 ≤ A) + (htr : LinearMap.trace ℂ E (A : E →ₗ[ℂ] E) = 1) : + Sᵥₙ (ofOp (b.reindex e) A hA htr) = Sᵥₙ (ofOp b A hA htr) := by + rw [ofOp_reindex, Sᵥₙ_relabel] + +end MState