Skip to content

Implement symmetric operators by basis transformation + projection#52

Merged
lkdvos merged 29 commits into
mainfrom
refactor/basis-transform-projection
Jul 7, 2026
Merged

Implement symmetric operators by basis transformation + projection#52
lkdvos merged 29 commits into
mainfrom
refactor/basis-transform-projection

Conversation

@lkdvos

@lkdvos lkdvos commented Jul 5, 2026

Copy link
Copy Markdown
Member

This is a somewhat larger refactor of this package, as brought up and discussed a couple of times with @Yue-Zhengyuan. The idea is to have a lot less of the manual code set up for the symmetric operators by simply defining the basis transformation once, and then reusing that for all of the defined operators.

This has a number of benefits: it is more maintainable, less error-prone, and a lot easier to add new operators, both in the package as well as for custom operators in user scripts since it is usually easier to just express the non-symmetric matrix. Maybe less of a huge benefit is that this also gives a more direct error for non symmetric operators - it now really shows how an operator was tried to project onto a symmetric subspace and failed.

I would however say that it also comes with a number of drawbacks: Things might be slightly more expensive, as we now have to do some multiplications and projections, and it is no longer an exact analytical mapping (especially for non-abelian), which in particular is bounded by the precision of the symmetric projection, which in turn depends on the CGC precision.

My feeling is that the drawbacks are probably warranted, since I don't think the goal here should be to have a system that generates operators in tight loops as fast as possible, as that case should probably use some form of caching the operators anyways.
Additionally, as the precision of TensorKit calculations is anyways bounded by the precision of the Fsymbol and Rsymbols, it probably does not actually make that much of a difference that the operator entries have small errors of the same magnitude.


An important follow-up is probably to actually make sure to make documentation pages for these conventions, as for now this is mostly restricted to the docstrings.
I can have a look at that, but I first would like some feedback and opinions.

lkdvos and others added 11 commits July 4, 2026 12:43
Symmetric operators are now generated from their Trivial counterparts by
applying a documented basis transformation (exposed as basis_transform)
and projecting onto the symmetric tensor structure via TensorKit's
TensorMap(::AbstractArray, ...) constructor. Incompatible symmetries are
detected automatically and throw an informative ArgumentError.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Z2Irrep uses the Hadamard transformation onto the spin-flip eigenbasis,
U1Irrep a permutation to TensorKit's charge order, and SU2Irrep the
identity (descending-m convention). All hand-written symmetric blocks
are replaced by projection of the Trivial operators.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The U1Irrep grading is a refinement of the fermion-parity grading with
identical basis order, so the transformation is the identity and all
U1 operators are generated by projection of the fZ2 versions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 9 (particle, spin) symmetry combinations are generated from the
(Trivial, Trivial) operators. The basis transformation is a permutation
derived from the sector order; for SU2Irrep particle symmetry the
eta-pairing doublet (|ud>, |0>) is combined with the documented
staggered gauge G = i^n per site.

This newly enables half_ud_num for all SU2 combinations, the spin
operators and S_plus_S_min/S_min_S_plus/S_exchange with SU2 particle
symmetry, and S_exchange for (SU2, SU2). It also fixes a bug where the
hand-written e_plus_e_min/e_min_e_plus with SU2 spin symmetry were
transposed relative to their (Trivial, Trivial) counterparts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- test/hubbardoperators.jl: per-operator availability predicates over all
  9 symmetry combinations, spectral comparisons for the newly enabled
  operators, regression pins for the staggered-gauge e_hopping values and
  the e_plus_e_min matrix-element direction, and a particle-hole
  symmetric Hamiltonian spectrum test covering the SU2-particle combos.
- new test/utils.jl covering the public symmetrize API.
- basis_transform unitarity testsets in all modules.
- docs: design section in index.md, symmetrize docstring with doctest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Exact transformations (permutations, identities, the staggered gauge)
  are returned as (Gaussian) integer matrices, which promote to any
  scalar type without loss of precision.
- basis_transform accepts an optional element type; this only affects
  the Z2 spin Hadamard, the single transformation with inexact entries,
  which is now computed at the requested precision.
- The symmetric operators are constructed directly at the requested
  scalar type, so real operators stay real end-to-end instead of
  round-tripping through complex.
- The default projection tolerance is floored at sqrt(eps(Float64)):
  the fusion-tensor data is Float64 for non-abelian sectors, so e.g.
  S_exchange(Complex{BigFloat}, SU2Irrep) now constructs (at Float64
  accuracy, documented) instead of spuriously failing the symmetry
  check. Abelian symmetries preserve full precision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The public interface now stays within the tensor language: each module's
basis_transform returns a TensorMap between the (desymmetrized) trivial
reference space and the desymmetrized symmetric space, and the new
exported desymmetrize maps graded spaces and tensors onto their dense
counterparts over ComplexSpace — the inverse of symmetrize up to the
basis transformation. Since a TensorMap cannot mix sector types, both
sides of a transformation live over ComplexSpace.

Exact transformations keep their integer scalar type (matching the
existing tj_projector convention), so promotion remains lossless.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Error messages from symmetrize and _restrict_scalartype are now generic
(the method name already appears in the stacktrace). The default
projection tolerance is floored at eps of TensorKit.sectorscalartype,
the element type of the fusion-tensor data: sectors with exact integer
topological data get no floor (full-precision projection), while e.g.
SU2Irrep is floored at its Float64 Clebsch-Gordan precision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sectorscalartype is only available since the TensorKitSectors split in
TensorKit v0.14.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The symmetrization pipeline is now scalar-type faithful by construction:

- Trivial constructors respect the requested scalar type: S_y_S_y takes
  the (exactly zero-imaginary) real part for real types, and the Hubbard
  S_y requires a complex type, matching SpinOperators.
- The staggered gauge for SU2Irrep particle symmetry is only inserted
  for operators that do not commute with it. For commuting operators,
  skipping the gauge is an exact identity, so they never touch complex
  intermediates. Non-commuting operators are genuinely complex and
  throw an informative error for real scalar types (after the symmetry
  check, so incompatible operators still report as such).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.40000% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/bosonoperators.jl 91.66% 3 Missing ⚠️
src/hubbardoperators.jl 97.00% 3 Missing ⚠️
src/fermionoperators.jl 91.66% 2 Missing ⚠️
src/spinoperators.jl 98.41% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/TensorKitTensors.jl 100.00% <ø> (ø)
src/tjoperators.jl 92.00% <ø> (ø)
src/utils.jl 100.00% <100.00%> (ø)
src/spinoperators.jl 92.07% <98.41%> (+3.19%) ⬆️
src/fermionoperators.jl 93.47% <91.66%> (+3.77%) ⬆️
src/bosonoperators.jl 94.11% <91.66%> (+2.71%) ⬆️
src/hubbardoperators.jl 98.31% <97.00%> (+0.59%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread src/utils.jl Outdated
Comment thread src/utils.jl Outdated
Comment thread src/utils.jl Outdated
Comment thread src/utils.jl Outdated
Comment thread src/utils.jl
Comment thread src/utils.jl
Comment thread test/bosonoperators.jl Outdated
Comment thread src/utils.jl Outdated
Comment thread test/spinoperators.jl Outdated
Comment thread test/bosonoperators.jl Outdated
Comment thread src/hubbardoperators.jl Outdated
Comment thread src/hubbardoperators.jl
Comment thread src/hubbardoperators.jl
Comment thread test/hubbardoperators.jl
Comment thread src/utils.jl Outdated
Comment thread src/spinoperators.jl Outdated
@Jutho

Jutho commented Jul 6, 2026

Copy link
Copy Markdown
Member

Up to minor suggestions, I think this looks good.

lkdvos and others added 4 commits July 6, 2026 08:20
Co-authored-by: Yue Zhengyuan <yuezy1997@icloud.com>
Co-authored-by: Jutho <Jutho@users.noreply.github.com>
@lkdvos

lkdvos commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

I think I've addressed the comments that were there, and tried to also include changes to the test suite and keep everything somewhat clear. Let me know if there are other comments, but I think this is ready for another round of review and/or merging.

I propose to update the minor version here, since this does seem like quite a substantial change.

@Yue-Zhengyuan Yue-Zhengyuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In SpinOperators, do we split spinmatrices into dedicated functions for only one of Sx, Sy, Sz and identity? Then for Sy we can explicitly reject real element type.

Comment thread src/spinoperators.jl Outdated
Comment thread src/hubbardoperators.jl Outdated
Comment thread test/testsetup.jl

@Yue-Zhengyuan Yue-Zhengyuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I notice that the eigenvalue test test_operator is now only used by test/tjoperators.jl. Can we update the t-J tests to more closely follow the Hubbard ones, and then remove test_operator and its dependence entirely?

@lkdvos

lkdvos commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

Can we update the t-J tests to more closely follow the Hubbard ones

The main reason I am not following these is that these operators are defined in function of the Hubbard ones, and not in function of the non-symmetric ones.
So there is no basis_transform defined, which means that the test_operator_dense function does not work.
While I'm happy to also change that, I really wanted to avoid having to significantly change these definitions in the same PR as this one, as this is already quite large.

can we reduce test pressure by no longer summing each operator on an L-site chain?

I don't think this is fair though: now you really only check that the eigenvalues are the same, while the whole idea of the L-site chain is to also catch errors in eigenvectors as these would interact non-trivially for non-commuting operators. I agree that there are definitely issues with that approach, but I would prefer to update the TJ operators in a separate PR, as this was kind of supposed to be just about the general approach of using basis transformations, not also rewriting the TJ module definitions?

In SpinOperators, do we split spinmatrices into dedicated functions for only one of Sx, Sy, Sz and identity?

Fine by me, although that really is a separate change, so can we keep this for a follow-up PR as well?

@Yue-Zhengyuan

Copy link
Copy Markdown
Member

there is no basis_transform defined

I overlooked this. Then I'm OK not to change the symmetric-vs-trivial checks for t-J now.

the whole idea of the L-site chain is to also catch errors in eigenvectors as these would interact non-trivially for non-commuting operators

I have restored the old behavior of test_operator to check spectrum on a chain.

@Yue-Zhengyuan Yue-Zhengyuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK to merge if all tests pass!

@lkdvos lkdvos enabled auto-merge (squash) July 7, 2026 12:01
@lkdvos lkdvos disabled auto-merge July 7, 2026 12:02
@lkdvos lkdvos merged commit 1524458 into main Jul 7, 2026
9 checks passed
@lkdvos lkdvos deleted the refactor/basis-transform-projection branch July 7, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

More robust way to test symmetric operators

3 participants