Skip to content

QuantumInfo: StdBasis refactor - #1578

Open
Timeroot wants to merge 11 commits into
masterfrom
stdbasis-refactor
Open

QuantumInfo: StdBasis refactor#1578
Timeroot wants to merge 11 commits into
masterfrom
stdbasis-refactor

Conversation

@Timeroot

Copy link
Copy Markdown
Collaborator

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:

  • Change density matrix (and bra, and ket) to stated in terms of hermitian operators, instead of matrices. Similarly for CPTPMap and others. (CPTPOp in general, for working on operators; CPTPMap for when it's on matrices.)
  • Have a StdBasis instance that gives a "canonical" basis for much work, and a .toMatrix / .ofMatrix equivalence with the matrix form wherever that instance is available. This is heavily connected to EuclideanSpace as the space given on that basis then.
  • Do definitions without StdBasis where possible (a basis-free proof), but one can also do them with a basis and then show they are basis-invariant. Both can be natural at times.

I think this will greatly increase the compatibility of the library with other efforts, down the line!

Timeroot and others added 11 commits August 22, 2026 17:16
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>
@github-actions github-actions Bot added the large label Aug 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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.

  1. Some automated checks will be run on your PR. You can see the results of these checks at the buttom of your PR page. If any of these checks fail, you will need to fix the issues before your PR can be merged. You can learn more about these here, including how to run them locally, which is sometimes quicker than relying on the GitHub Actions. If you have never had a PR merged before, you may have to wait for a reviewer to manually start these checks (this is for security).

  2. A reviewer will look at your PR and may ask you to make changes. This may happen a couple of days after you submit your PR, so you may need to be patient. But it should not be longer than that - if it is please bring it to the attention of the community on the Zulip. The level of review will depend on where your PR is submitted. If it is submitted to ./Physlib or ./QuantumInfo, the review will be more thorough than if it is submitted to ./PhyslibAlpha. You can find out more about what the review process is looking for in our review guidelines. If a reviewer adds an awaiting-author label to your PR, address the review comments, then please remove that label by adding a comment with -awaiting-author. This helps us keep track of reviews.

  3. The reviewer will either approve your PR, or request more changes (in which case we return to step 2). Once your PR is approved, it will be merged by a maintainer, this should happen shortly after approval, though you may get more comments at this stage.

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.

@goliath-klein

Copy link
Copy Markdown

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. :-)

@goliath-klein

Copy link
Copy Markdown

I’m not convinced that the StdBasis approach is net positive. Compare it to this alternative:

  1. Constructions (like the Pauli operators you mention) that rely on a distinguished basis are formalized as acting on EuclideanSpace (as in @PrParadoxy's branch linked to above)
  2. Likewise, if a physical system comes with a canonic basis, it typically makes sense to formalize it, too, directly in terms of an EuclideanSpace.
  3. There are exceptions. For example, a subspace of an already-formalized space, which come with a distinguished basis (let's say I have already defined a Hilbert space of functions, and now want to talk about a GKP code inside of it). But I’d say that’s not too common, and one can always use OrthonormalBasis.repr to pull back a EuclideanSpace-based construction.

As far as I can see, StdBasis would make things easier only in case 3.

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 ι 𝕜 :=

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ι 𝕜 :=

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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] :=

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :=

@goliath-klein goliath-klein Aug 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 _ _⟩

@goliath-klein goliath-klein Aug 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⟨_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).

@jstoobysmith jstoobysmith added the awaiting-author A reviewer has asked the author a question or requested changes label Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-author A reviewer has asked the author a question or requested changes large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants