Skip to content

Add support for local expectation values of tensor product and MPO terms - #425

Merged
Yue-Zhengyuan merged 11 commits into
mainfrom
lb/product_expval
Oct 26, 2026
Merged

Yue-Zhengyuan merged 11 commits into
mainfrom
lb/product_expval

Conversation

@leburgel

@leburgel leburgel commented Sep 2, 2026 •

Copy link
Copy Markdown
Member

Two alternative representations for a LocalOperator term, for cases the dense form handles badly, both contracted exactly and both AD-compatible:

  • TensorProductTerm — one rank-2 operator per site, for interactions that factor across sites.
  • MPOTerm — one tensor per site, linked by a bond of the term's Schmidt rank.

Instead of leaving 2N physical legs open for a reduced density matrix, the factors go straight into the patch contraction, divided by the local norm of the same patch.

Some bechmarks

All timings single-threaded with BLAS pinned to one thread, one measurement per process.

Large spatial support

X^{⊗n} on one patch, D = 2, χ = 8:

sites dense TensorProductTerm MPOTerm
2 (line) 0.21 ms / 0.5 MiB 0.31 ms / 0.6 MiB 0.42 ms / 0.6 MiB
4 (2×2) 0.88 ms / 2.0 MiB 0.55 ms / 1.2 MiB 0.84 ms / 1.4 MiB
6 (2×3) 12.4 ms / 30 MiB 1.15 ms / 3.0 MiB 1.62 ms / 3.6 MiB
9 (3×3) 2294 ms / 1000 MiB 4.38 ms / 13.7 MiB 5.56 ms / 15.8 MiB

Large physical dimension

Two-site term, D = 2, χ = 8:

d dense TensorProductTerm MPOTerm
2 0.208 ms / 0.54 MiB 0.310 ms / 0.59 MiB 0.424 ms / 0.61 MiB
3 0.309 ms / 0.99 MiB 0.333 ms / 0.72 MiB 0.459 ms / 0.73 MiB
4 0.465 ms / 1.61 MiB 0.342 ms / 0.85 MiB 0.485 ms / 0.86 MiB
6 0.826 ms / 3.37 MiB 0.421 ms / 1.10 MiB 0.538 ms / 1.12 MiB
8 1.486 ms / 5.85 MiB 0.554 ms / 1.36 MiB 0.671 ms / 1.37 MiB

The two axes compound. On a 2×3 patch:

d dense TensorProductTerm
2 12.4 ms / 30 MiB 1.15 ms / 3.0 MiB
3 444 ms / 303 MiB 1.63 ms / 3.7 MiB

Where the factored forms lose

Two-site terms at small d, where the dense path is the hand-written reduced_densitymatrix1x2/2x1 specialization rather than a generated contraction. At D = 3, χ = 20 the penalty is ~1.3× at d = 2 and gone by d = 3:

d dense TensorProductTerm MPOTerm
2 3.89 ms / 15.0 MiB 5.19 ms / 16.7 MiB 5.45 ms / 16.7 MiB
3 8.18 ms / 27.9 MiB 8.76 ms / 20.6 MiB 7.95 ms / 20.6 MiB
4 12.81 ms / 45.3 MiB 8.50 ms / 24.6 MiB 8.93 ms / 24.6 MiB
6 29.71 ms / 93.3 MiB 12.16 ms / 32.5 MiB 10.55 ms / 32.5 MiB
8 60.58 ms / 158.9 MiB 14.67 ms / 40.5 MiB 14.61 ms / 40.5 MiB

So dense is the better default only for nearest-neighbour terms at d = 2.

The penalty is small in context anyway. Differentiating through CTMRG at D = 3, χ = 20:

model form forward energy+gradient forward as % of fg
TFI dense 10.66 ms 695.9 ms 1.5%
TFI product 14.67 ms 677.3 ms 2.2%
TFI mpo 14.93 ms 655.1 ms 2.3%
XYZ spin 1/2 dense 9.62 ms 686.9 ms 1.4%
XYZ spin 1/2 mpo 16.51 ms 736.1 ms 2.2%
XYZ spin 3/2 dense 28.93 ms 835.7 ms 3.5%
XYZ spin 3/2 mpo 25.13 ms 764.3 ms 3.3%

Time to first evaluation

The first call pays for both the @autoopt contraction-order search and compiling the
expression it returns, and the search dominates. It grows steeply with the number of tensors
in the patch:

patch dense TensorProductTerm MPOTerm
2 sites 35 s 38 s 64 s
2×3 82 s 60 s 141 s
3×3 178 s 854 s 6208 s

So while all three forms do compile at nine sites, the MPO form just takes ~1.7 h to do it. This is a pre-existing property of the order search rather than something this PR introduces, and is worth a separate follow-up.

Notes for review

  • add_term! and + reject accumulating onto an existing MPO term rather than combining it, since a sum of MPOs of fixed bond dimension is not one of the same bond dimension. This esolves @Yue-Zhengyuan's comment, but by refusing rather than going through a dense intermediate — flagging in case that is not what you wanted.
  • MPO terms keep the caller's site order instead of being sorted like the other term types: the bonds are Schmidt cuts of one chain ordering, so the factors cannot be permuted after the fact. Factor i acts on site inds[i].
  • TensorKit.storagetype(::LocalOperator) needed fixing: it promotes over the term values, which are vectors of factors rather than single tensors for both new term types, so it threw a MethodError for any operator containing one. It now flattens the terms first, with regression tests in both term test files. Two pre-existing limitations are left alone, since they affect dense terms equally: mixed real/complex terms have no storage promotion rule in TensorKit, and an empty LocalOperator has no terms to promote.

@leburgel
leburgel marked this pull request as draft September 2, 2026 13:56
@Yue-Zhengyuan

Copy link
Copy Markdown
Member

I suppose this is exact contraction with CTMRG/BPEnv ?

@codecov

codecov Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.72131% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...lgorithms/contractions/local_patch/network_expr.jl 93.33% 1 Missing ⚠️
...algorithms/expectation_value/patch_contractions.jl 85.71% 1 Missing ⚠️
src/operators/localoperator.jl 93.75% 1 Missing ⚠️
src/operators/localoperator/dense.jl 95.65% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/PEPSKit.jl 100.00% <ø> (ø)
.../algorithms/expectation_value/expectation_value.jl 100.00% <100.00%> (ø)
src/operators/localoperator/mpo.jl 100.00% <100.00%> (ø)
src/operators/localoperator/tensorproduct.jl 100.00% <100.00%> (ø)
...lgorithms/contractions/local_patch/network_expr.jl 95.91% <93.33%> (-0.47%) ⬇️
...algorithms/expectation_value/patch_contractions.jl 97.14% <85.71%> (-2.86%) ⬇️
src/operators/localoperator.jl 83.80% <93.75%> (+9.70%) ⬆️
src/operators/localoperator/dense.jl 95.65% <95.65%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@leburgel

leburgel commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

I suppose this is exact contraction with CTMRG/BPEnv ?

Yes, precisely that. The idea was to add support for alternative operator forms for situations that are very badly suited to our current dense-tensor-operator approach, while still being compatible with AD to use them in variational optimizations. In particular, the motivating cases are multi-site interactions that factor into a plain tensor product across sites (e.g. plaquette terms in gauge theories), and short-range two-site interactions for systems with a large physical dimension. Both of these are extremely expensive for our current approach, while simply storing the tensor product factors separately or storing the operator as a 2-site MPO of low bond dimension greatly reduces the cost.

This is still very much a draft, but I already filed the PR to demonstrate how #423 allows to easily add support for exact evaluation of different operator types. In particular, the MPOTerm here is a straight duplicate of the MPOObservable introduced in #415. This PR can just stay in draft status until the design that is merged, and then I'll adopt the resulting design here.

Comment thread src/operators/localoperator.jl
Yue-Zhengyuan added a commit to Yue-Zhengyuan/PEPSKit.jl that referenced this pull request Oct 1, 2026
Adapt the draft MPO representation with ordered factors, physical-space and bond validation, scalar promotion, and safe scaling. Guard unsupported accumulation and real/imaginary operations, and add focused bookkeeping tests.
@Yue-Zhengyuan Yue-Zhengyuan self-assigned this Oct 3, 2026

@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.

Good to go for me. Let's merge this before #415 (which has already adopted MPOTerm, and is still blocked by MPSKit).

@leburgel
leburgel marked this pull request as ready for review October 4, 2026 08:13
@Yue-Zhengyuan
Yue-Zhengyuan requested a review from kshyatt October 15, 2026 10:00
@Yue-Zhengyuan

Copy link
Copy Markdown
Member

@kshyatt You may want to have a look on how should we handle the storage type for the tensor product terms and MPO terms in a LocalOperator.

Comment thread src/operators/localoperator.jl Outdated
Comment thread src/operators/localoperator.jl Outdated
Comment thread src/operators/localoperator.jl Outdated

@kshyatt kshyatt 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.

Storage type handling looks ok to me, just a few stray comments elsewhere

leburgel and others added 4 commits October 22, 2026 13:48
Validate physical and bond spaces, preserve input containers, and support scalar promotion without losing tensor-product dispatch. Reject unsupported term accumulation and real/imaginary operations, and consolidate regression tests.
Add the missing single-layer MPO overload and document both PEPO evaluation forms. Cover tensor-product and MPO terms in single-layer and purified PEPOs with compact tests for trivial and fermionic symmetry.
Convert BP environments to CTMRG environments for MPO and tensor-product terms, with a TODO for native BP boundary contractions. Add regression comparisons against dense BP expectation values.
@leburgel leburgel changed the title [WIP] Add support for local expectation values of tensor product and MPO terms Add support for local expectation values of tensor product and MPO terms Oct 22, 2026
@leburgel

Copy link
Copy Markdown
Member Author

I circled back to this after getting very distracted, and added some timings that I was working on when I first filed this but then lost track of.

One big caveat on the ability to scale these exact contractions is that the larger patches with MPO observables get completely stuck in the @autoopt contraction order optimizer. There's just too many tensors in these cases. This could be resolved if QuantumKitHub/TensorOperations.jl#185 is revived and merged at some point. From quickly hacking this in locally I could confirm that cheaper contraction order finders allow us to go to quite big patch sizes. But I guess that's for later.

I think this has everything I had in mind, I'll circle back to having a look at #415 next.

@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 made the following cleanups:

  • storagetype for mixed real/complex terms is addressed in #475.
  • When adding an MPOTerm/TensorProductTerm to LocalOperator, explicitly convert each tensor in the vector to the same scalar type. Otherwise scalartype(::LocalOperator) can fail.
  • Split src/operators/localoperator.jl (which has become long and messy) into several smaller focused files.

I also notice that add_physical_charge hasn’t been generalized to support MPOTerm/TensorProductTerm, and I already have it implemented. But let me do it in a follow-up PR to not add more features here.

I'll not merge my own changes. @leburgel Please do it if there are no problems.

@Yue-Zhengyuan

Copy link
Copy Markdown
Member

@leburgel Any more comments?

@leburgel

Copy link
Copy Markdown
Member Author

This looks good to me! I just added some final related comments on QuantumKitHub/TensorKit.jl#593, but given the choice to promote scalar types that shouldn't affect this that much anymore.

@Yue-Zhengyuan

Copy link
Copy Markdown
Member

I'll proceed to merge and push new changes in #415.

@Yue-Zhengyuan
Yue-Zhengyuan merged commit 04d9fde into main Oct 26, 2026
84 checks passed
@Yue-Zhengyuan
Yue-Zhengyuan deleted the lb/product_expval branch October 26, 2026 10:51
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.

3 participants