QuantumInfo: StdBasis refactor - #1578
Conversation
Introduce a preferred-orthonormal-basis class `StdBasis ℂ E ι` and an operator-level self-adjoint type `HermitianOp E := selfAdjoint (E →L[ℂ] E)`, with a star-order bridge `HermitianOp.toMat` to `HermitianMat ι ℂ`. Mixed states become `DensityOp E`, a positive unit-trace `HermitianOp E`, with `MState d` an abbreviation for `DensityOp (EuclideanSpace ℂ d)`. The density matrix `ρ.M` is recovered through the preferred basis, and the matrix-level API of `MState` is re-derived from operator-level facts. Downstream files are repaired against the new API; they still work in matrices. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…archy `OpMap E F := (E →L[ℂ] E) →ₗ[ℂ] (F →L[ℂ] F)` is the operator-level counterpart of `MatrixMap`, with `OpMap.toMat`/`OpMap.ofMat` bridging the two through a `StdBasis`. The bundled hierarchy `HPOp/UnitalOp/TPOp/POp/CPOp/PTPOp/PUOp/ CPTPOp/CPUOp` is restated on operators, and `PTPMap`, `CPTPMap` and friends become abbreviations for the `EuclideanSpace ℂ d` case. Channels that are naturally given by a matrix presentation -- `of_kraus`, `ofUnitary`, `traceLeft`/`traceRight`, `assoc`, `piProd`, `replacement`, ... -- are built with `<Class>Op.ofMat`, each paired with a `@[simp]` matrix-analogue lemma `X_map : X.map = <matrixmap>` so that matrix-level reasoning is still one rewrite away. Also: * `MatrixMap.piProd_id`, and `CPTPOp.prepDefault` for the purification of a channel, replacing an inlined `let` that the kernel could not check. * `SteinsLemma.EquationS62` no longer needs `commutes` or an unrestricted `simp` on density-operator eigenvalues; the file builds in 190s instead of timing out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stage 3 of the StdBasis migration. The state-level quantities now take a
`DensityOp E` over an arbitrary finite-dimensional inner product space, and
each carries a "matrix analogue" theorem recovering the old matrix formula
through any `StdBasis ℂ E ι`:
Sᵥₙ Sᵥₙ_eq_trace_cfc_negMulLog
TrDistance TrDistance.eq_matrix_traceNorm
DensityOp.fidelity DensityOp.fidelity_eq_matrix
DensityOp.U_conj DensityOp.U_conj_M
SandwichedRelRentropy, qRelativeEnt sandwichedRelRentropy_eq_matrix
`MState.U_conj` (`U ◃ ρ`) is now defined from `DensityOp.U_conj` through the
new `StdBasis.unitaryOfMat`, and `MState.sandwichedRelRentropy_eq_matrix` is
the specialisation of the matrix analogue whose index type is determined by
the state, so `simp` can use it.
Supporting API: `HermitianOp.{conj_nonneg, trace_conj_unitary,
conj_unitary_le_conj_unitary, inner_conj_unitary, ker, support}` with the
coordinate bridge `lin_toMat_apply` / `mem_ker_toMat_iff` /
`ker_toMat_le_ker_toMat`, and the bijection `StdBasis.toMatUnitary` /
`StdBasis.unitaryOfMat` between `unitary (E →L[𝕜] E)` and
`Matrix.unitaryGroup ι 𝕜`.
The four `HermitianMat` unitary-conjugation simp lemmas move to a new leaf
file `ForMathlib/HermitianMat/UnitaryConj.lean`; putting them in
`HermitianMat/Unitary.lean` made them visible to `HermitianMat/Schatten.lean`,
where they blew past `maxHeartbeats` in six places.
`sandwichedRelRentropy_congr` and `qRelEntropy_heq_congr` lose their
`@[gcongr]` attributes: `gcongr` needs the varying arguments of the head
function to be free variables, and the index type now appears as
`EuclideanSpace ℂ d`. Their two call sites apply them by name.
Also adds `sandwichedRelRentropy_eq_top` / `qRelativeEnt_eq_top`, which the
support-condition branches of `Pinching` and `HypothesisTesting` now use.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…oundwork Stage 4 of the StdBasis refactor: ForMathlib/PartialTrace.lean builds the partial trace from scratch, with HermitianOp and DensityOp liftings and matrix analogues. ForMathlib/HermitianMat/LiebThirring.lean proves Araki-Lieb-Thirring via compound matrices. ForMathlib/LaplaceAnalytic.lean gives analyticity of Laplace-type integrals, which repairs StatMech.ThermoQuantities: differentiability of the partition function needs integrability on a neighbourhood of beta, not at a point, so the hypothesis is strengthened to LocallyZIntegrable. Also adds the trace-norm contraction lemmas for positive maps and the data processing inequality for the trace distance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Brings the branch up to Lean v4.33.0 and master's Physlib layout, and resolves the resulting conflicts against the basis-free `DensityOp` / `CPTPOp` API. What master contributes: * The Lean module system throughout (`module`, `public import`, `@[expose] public section`, `public meta import` for `native_decide`), and the `PhysLean` -> `Physlib` rename. * `QuantumInfo/Finite/` flattened into `States/`, `Channels/`, `Entropy/`, `Measurements/`, `Operators/` and `Capacity/`. * A complete data processing inequality for the sandwiched Renyi divergence (`Entropy/DPI.lean`), proved from Stinespring dilation and monotonicity under partial trace rather than Riesz-Thorin interpolation, together with joint convexity of the relative entropy. * `UnitalPretheory` (unit object `1`, product `i * j`) and the `HermitianMat.projLE` / `projLT` projector API. Conflict resolution and repairs on our side: * QECC: dropped the local `CPTPOp.piProd_id` and `Mixable.mix_one`, now upstream; `card_le_of_decodes` goes through `CPTPOp.compose_map`; deprecations (`push Not`, `Set.mem_ofPred_eq`, `have`/`let`) cleared. * `ResourceTheory/ResourceTheory.lean`: new `UnitalResourceTheory` class so `IsTensorial` can speak of `1` and `i * j`; `maximal` is `@[instance_reducible]` and its `free_comp` no longer times out. * `Regularized.lean` and `Entropy/Axiomatized/Defs.lean`: `convert`-based proofs that produced spurious instance-equality goals replaced with explicit `calc`/`rw`/`exact` chains. * `Entropy/Axiomatized/Renyi.lean` moved into the `Axiomatized` namespace, since it defines the same `qRelativeEnt` as `Entropy/Relative.lean`. * `QuantumInfo.lean` now imports every module under `QuantumInfo/`, so the default target covers the QECC, axiomatized-entropy, regularized and classical-capacity subtrees that previously built only by hand. `lake build` is green: 9368 jobs, zero errors, zero warnings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The quantum error correction development is maintained in the separate QuantumLib repository, which has a strictly larger, sorry-free version of this material (699 declarations vs 218 here, including an unconditional Eastin-Knill theorem). Keeping a second, weaker copy here only invites drift. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`FiniteDimensional.toCompleteSpaceComplex` registers what Mathlib leaves as a theorem: over `ℂ` the scalar field is pinned down, so the instance search terminates, exactly as for the existing `StdBasis.toCompleteSpace`. With it available, `[CompleteSpace E]` no longer has to sit next to `[FiniteDimensional ℂ E]` in section variables, and `DensityOp` needs only finite-dimensionality in its signature. `CompleteSpace` is a `Prop`, so the extra route to it creates no diamond. `[CompleteSpace ℋ]` stays where it is the genuinely weaker hypothesis, i.e. the general-Hilbert-space material in `HayataGroup/`, `HermitianOp`, `OpMap` and the scalar-generic parts of `StdBasis`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A sweep over `QuantumInfo` removed each `set_option backward.isDefEq.respectTransparency false` in turn and rebuilt the module; the 17 that were no longer load-bearing are gone. The 125 that remain still are, so they stay. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each removal was checked by rebuilding the module it lives in; the eight that are still load-bearing stay. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…to date The action of a map no longer needs a `StdBasis` on either side. `HPOp.opApply` sends a `HermitianOp E` to a `HermitianOp F`; `POp.opApply_nonneg` and `PTPOp.trace_opApply` say it preserves positivity and the trace; `PTPOp.applyState` assembles those into the `FunLike` coercion, and `CPTPOp`'s coercion factors through it. `CPTPOp.transport` reads a channel as a `CPTPMap` between the Euclidean spaces of the two preferred bases, with `map_transport` and `transport_apply`. With that in place, `TrDistance.DPI_PTP`/`DPI` are stated for `PTPOp E F`/`CPTPOp E F` rather than for indexed channels, and `Entropy/DPI.lean` gains a basis-free section with `DensityOp.sandwichedRenyiEntropy_DPI` and `DensityOp.qRelativeEnt_DPI`, resting on the new `sandwichedRelRentropy_transport` and `qRelativeEnt_transport`. Because the coercion no longer mentions the domain's index type, the matrix analogues of an application (`PTPOp.M_apply_MState`, `PTPOp.M_applyState`, `HPOp.toMat_opApply`) cannot be `simp` lemmas: `simp` cannot infer an index type that occurs only on the right-hand side. They are plain `rw` lemmas now, and their docstrings say why. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The plan it tracked has landed through Stage 5; what remains is recorded in the code itself. Nothing references it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thank you for this pull-request (PR). If this is your first PR, welcome to the community! Below is what will happen next. Please read carefully if you are not familiar with the process. You may open other PRs while this one is being reviewed, and can stack PRs on top of each other, so don't let these steps slow you down.
Tip: The easiest way to get have a fast review is to submit a PR that is small and self-contained, and has clear documentation explaining why things are the way they are in your chages. If you have any problems or questions, please reach out to the community on the Zulip. |
|
Hi Alex. My main impression: Going from concrete matrices to more abstract types is a great development! (BTW: While I was traveling, @PrParadoxy has ported large parts of our QCLib from matrices to CLMs. See this branch) Below are some high-level comments. I feel like I haven’t contributed enough to the project to criticize your work. So please read what follows as vague suggestions. So far I've only looked at StdBasis.lean and HermitianOp.lean, not at the, uh, 10k other lines in the diff. :-) |
|
I’m not convinced that the
As far as I can see, But it comes with a price! All Mathlib defs that treat bases as explicit parameters have to be re-implemented using an instance-implicit signature. That leads to code duplication, and a steeper learning curve. Also, definitions (like e.g. a Clifford group implementation) using this Physlib-specific API will be harder to port / reuse in other contexts. So my gut feeling is that this abstraction introduces more costs than benefits. |
| 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 ι 𝕜 := |
There was a problem hiding this comment.
One could also use existing constructions, like so:
noncomputable def toMatUnitary' (U : unitary (E →L[𝕜] E)) : Matrix.unitaryGroup ι 𝕜 := ⟨(Unitary.linearIsometryEquiv U).toMatrix stdBasis.toBasis stdBasis.toBasis, LinearIsometryEquiv.toMatrix_mem_unitaryGroup ..⟩
A related def from QCLib, which gives a star algebra equivalence:
noncomputable def unitaryGroupEquiv : unitaryGroup n 𝕜 ≃⋆* unitary ((EuclideanSpace 𝕜 n) →L[𝕜] (EuclideanSpace 𝕜 n)) := Unitary.mapEquiv (StarMulEquiv.ofClass (Matrix.toEuclideanCLM (𝕜 := 𝕜)))
| 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 ι 𝕜 := |
There was a problem hiding this comment.
One could use the namespace _root_.Subtype.toMatUnitary to enable dot notation.
|
|
||
| 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] := |
There was a problem hiding this comment.
I know this came up on Zulip, and you were a bit exasperated recalling the story of this type synonym. So sorry for brining it up again...
...but why is this a definition (semi-reducible, even)? Could one not just introduce a custom notation for the somewhat lengthy selfAdjoint (E →L[ℂ] E)? This would have the advantage of making much of the boilerplate that follows redundant (instances, like AddCommGroup; simp lemmas like op_zero, op_add, ...). It would also remove one discrepancy between the Physlib and the Mathlib way of saying things.
You mentioned performance issues on Zulip. Is that the only reason? If so, I'd be happy to offer help looking into their origin. Maybe a few shortcut instances can address it?
| 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 := |
There was a problem hiding this comment.
One could make this definition much more general. E.g. like this:
noncomputable def selfAdjoint.cfc {R : Type*} [TopologicalSpace R] [Ring R] [StarRing R]
[Algebra ℂ R] [ContinuousFunctionalCalculus ℂ R IsStarNormal]
(A : selfAdjoint R) (f : ℝ → ℝ) : selfAdjoint R :=
⟨_root_.cfc f A.val, cfc_predicate f A.val⟩
This def would cover both your intended use case selfAdjoint (E →L[ℂ] E), but also self-adjoint elements in a C^*-algebra.
|
|
||
| /-- 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 _ _⟩ |
There was a problem hiding this comment.
⟨_root_.cfc f A.op, cfc_predicate f A.op⟩: more information at essentially the same length.
(Side remark: I'm amazed the elaborator can figure out the predicate (IsSelfAdjoint) in the first argument of the constructor. It took me some time to understand what the definition does. So it's maybe not the best place to hide information).
This is a very large refactor, but it's also the kind of thing that made sense to do in one big sweep, and by nature it touches nearly everything in QuantumInfo.
The idea is to:
StdBasisinstance that gives a "canonical" basis for much work, and a.toMatrix/.ofMatrixequivalence with the matrix form wherever that instance is available. This is heavily connected toEuclideanSpaceas the space given on that basis then.I think this will greatly increase the compatibility of the library with other efforts, down the line!