Skip to content

Refactor expectation_value implementations - #423

Open
leburgel wants to merge 20 commits into
mainfrom
lb/expval
Open

Refactor expectation_value implementations#423
leburgel wants to merge 20 commits into
mainfrom
lb/expval

Conversation

@leburgel

@leburgel leburgel commented Aug 31, 2026

Copy link
Copy Markdown
Member

Some refactoring and reorganization related to the evaluation of expectation values. The main motivation for this refactor is making it easier to add support for computing expectation values of different kinds of operators, and evaluate these using different kinds of environments. Some examples of this are given in #425 and #426, which are built on top of this refactor.

Short summary:

  • Moved around some existing code to different/new files, hopefully improving the organization a bit.
  • Remove as many type annotations as possible from the methods used along the evaluation of expectation values, allowing to reuse the main entry points for different types of operators and environments.
  • Insert a dedicated local_expectation_value method to catch local expectation values for different kinds of operators. This goes through the reduced_densitymatrix approach for AbstractTensorMap local terms, but can follow a different path for different kinds of local terms (see for example [WIP] Add support for local expectation values of tensor product and MPO terms #425).
  • Implement BPEnv expectation values via reduced density matrix contraction along the generic AbstractTensorMap path, by specializing the appropriate methods.
  • Write the main local patch contraction as generic methods in terms of a fixed set of contraction expression generators . These generators then dispatch on the appropriate type to generate an appropriate expression. The idea would be that to add support support for a different type of state, operator or environment, we now only need to overload one of the main expression generators boundary_contraction_expr, bulk_contraction_expr or operator_contraction_expr.

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.18774% with 23 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/algorithms/contractions/localoperator.jl 86.00% 14 Missing ⚠️
src/utility/util.jl 75.00% 4 Missing ⚠️
src/algorithms/contractions/bp_contractions.jl 78.57% 3 Missing ⚠️
src/algorithms/expectation_value.jl 98.80% 1 Missing ⚠️
src/algorithms/optimization/peps_optimization.jl 83.33% 1 Missing ⚠️
Files with missing lines Coverage Δ
...hms/contractions/ctmrg/characteristic_equations.jl 87.59% <ø> (+0.46%) ⬆️
src/algorithms/contractions/ctmrg/network_value.jl 100.00% <100.00%> (ø)
src/algorithms/toolbox.jl 95.08% <ø> (-1.85%) ⬇️
src/algorithms/expectation_value.jl 98.80% <98.80%> (ø)
src/algorithms/optimization/peps_optimization.jl 96.03% <83.33%> (-0.81%) ⬇️
src/algorithms/contractions/bp_contractions.jl 87.27% <78.57%> (-5.11%) ⬇️
src/utility/util.jl 76.14% <75.00%> (-0.20%) ⬇️
src/algorithms/contractions/localoperator.jl 92.18% <86.00%> (-4.27%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@leburgel
leburgel marked this pull request as draft August 31, 2026 14:18
@leburgel leburgel changed the title Slight reorganization of expectation_value implementation Refactor expectation_value implementations Sep 2, 2026
@leburgel
leburgel marked this pull request as ready for review September 3, 2026 06:55
@Yue-Zhengyuan

Copy link
Copy Markdown
Member

I'll try coming up with a source tree of expectation-value-related functions to better discuss where each function should be placed.

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

First-round comments focusing on docstring inconsistencies.

Comment thread src/utility/util.jl
Comment thread src/utility/util.jl Outdated
Comment thread src/algorithms/contractions/localoperator.jl Outdated
Comment thread src/algorithms/expectation_value.jl Outdated
Comment thread src/algorithms/expectation_value.jl Outdated
Comment thread src/algorithms/expectation_value.jl Outdated
)

"""
contract_local_operator(inds, O, ket::InfinitePEPS, bra::InfinitePEPS = ket, env)

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.

There are actually no overloads yet that omits bra. Also, do we want to adapt a new breaking (bra, ket) interface?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, at this level I think packing the arguments as (bra, ket) makes sense, the argument juggling on these methods is quite annoying and is never actually used. My only concern is whether it's really worth breaking reduced_densitymatrix over this.

)

"""
contract_local_norm(inds, ket::InfinitePEPS, bra::InfinitePEPS = ket, env)

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.

Same issue as contract_local_operator: lack of overload without bra, and whether we want to adapt a new (bra, ket) interface.

Comment thread src/algorithms/expectation_value.jl Outdated
Comment thread src/algorithms/expectation_value.jl Outdated
Contract a local norm corresponding to the overlap `ket` and `bra` states, computing a patch
of `⟨bra|ket⟩`, where `ket` and `bra` correspond to either a PEPS or a PEPO representing a
PEPS with ancillary legs. Alternatively, contract a local norm patch of a density matrix
PEPO `state`, computing a patch of `tr(state)`. The norm patch is contracted around the open

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'm not sure if tr is the correct unambiguous terminology. It gives the feeling that the trace is taken without fermion twists.

What if we just do not mention the two different internal implementations (direct contraction vs density matrix)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I would be fine with not mentioning the different internal implementations only if we merge the calling signatures in terms of a single state variable. Now it's kind of impossible to explain the available calling signatures without making a distinction between the internal implementations.

Comment thread src/algorithms/contractions/localoperator.jl
@Yue-Zhengyuan

Copy link
Copy Markdown
Member

Some files put too many things together in my opinion. I would like to suggest the following source tree:

src/
└── algorithms/
    ├── expectation_value.jl
    │   ├── local_expectation_value(inds, bra, operator::AbstractTensorMap, ket, env)
    │   │   local_expectation_value(inds, state, operator::AbstractTensorMap, env)
    │   │   Select the evaluation strategy by operator type; evaluate the current dense-operator implementation through a reduced density matrix.
    │   │
    │   └── MPSKit.expectation_value(bra::S, O::LocalOperator, ket::S, env) where {S<:InfiniteState}
    │       MPSKit.expectation_value(peps::InfinitePEPS, O::LocalOperator, env)
    │       MPSKit.expectation_value(state::InfinitePEPO, O::LocalOperator, env)
    │       MPSKit.expectation_value(pf::InfinitePartitionFunction, op::Pair{CartesianIndex{2},<:AbstractTensorMap{T,S,2,2}}, env) where {T,S}
    │       MPSKit.expectation_value(pf::InfinitePartitionFunction, op::Pair{Tuple{Int,Int}}, env)
    │       Delegate each LocalOperator term to local_expectation_value and sum the results, or evaluate a local tensor insertion in a partition function.
    │
    ├── reduced_densitymatrix.jl
    │   └── reduced_densitymatrix(inds::Vector{CartesianIndex{2}}, ket::InfinitePEPS, bra::InfinitePEPS, env)
    │       reduced_densitymatrix(inds, ket::InfinitePEPS, env)
    │       reduced_densitymatrix(inds::Vector{CartesianIndex{2}}, state::InfinitePEPO, env)
    │       reduced_densitymatrix(inds::Vector{CartesianIndex{2}}, ket::InfinitePEPO, bra::InfinitePEPO, env)
    │       reduced_densitymatrix(inds::NTuple{N,CartesianIndex{2}}, args...) where {N} [deprecated]
    │       reduced_densitymatrix(inds::NTuple{N,Tuple{Int,Int}}, args...) where {N} [deprecated]
    │       Select an optimized density-matrix contraction when available, otherwise construct a normalized density matrix with _contract_densitymatrix.
    │
    ├── contractions/
    │   ├── tensor_traces.jl
    │   │   ├── _str(::Bosonic, t::AbstractTensorMap)
    │   │   │   _str(::Fermionic, t::AbstractTensorMap{<:Any,<:Any,N,N}) where {N}
    │   │   │   Dispatch the trace calculation on bosonic or fermionic braiding style.
    │   │   │
    │   │   ├── str(t::AbstractTensorMap)
    │   │   │   Compute the ordinary trace for bosonic tensors and the supertrace for fermionic tensors.
    │   │   │
    │   │   └── trmul(H::AbstractTensorMap{<:Any,S,N,N}, ρ::AbstractTensorMap{<:Any,S,N,N}) where {S,N}
    │   │       Contract an operator with a density matrix without materializing their product.
    │   │
    │   ├── absorb.jl
    │   │   ├── absorb_left(A::AbstractTensorMap{<:Any,S}, C::AbstractTensorMap{<:Any,S,1,1}) where {S}
    │   │   │   absorb_left(P::AbstractTensorMap{<:Any,S,1,N}, C::AbstractTensorMap{<:Any,S,1,1}) where {S,N}
    │   │   │   Contract a matrix into the leftmost leg of a tensor.
    │   │   │
    │   │   ├── absorb_right(A::AbstractTensorMap{<:Any,S}, C::AbstractTensorMap{<:Any,S,1,1}) where {S}
    │   │   │   absorb_right(E::AbstractTensorMap{<:Any,S,N,1}, C::AbstractTensorMap{<:Any,S,1,1}) where {S,N}
    │   │   │   Contract a matrix into the rightmost leg of a tensor.
    │   │   │
    │   │   └── absorb_left_right(T::AbstractTensorMap{<:Any,S}, CL::AbstractTensorMap{<:Any,S,1,1}, CR::AbstractTensorMap{<:Any,S,1,1}) where {S}
    │   │       Contract matrices into both outer legs of a tensor.
    │   │
    │   ├── bp_messages.jl
    │   │   ├── absorb_north_message(A::PEPSTensor, M::PEPSMessage)
    │   │   │   Absorb a north BP message into a PEPS tensor.
    │   │   │
    │   │   ├── absorb_east_message(A::PEPSTensor, M::PEPSMessage)
    │   │   │   Absorb an east BP message into a PEPS tensor.
    │   │   │
    │   │   ├── absorb_south_message(A::PEPSTensor, M::PEPSMessage)
    │   │   │   Absorb a south BP message into a PEPS tensor.
    │   │   │
    │   │   ├── absorb_west_message(A::PEPSTensor, M::PEPSMessage)
    │   │   │   Absorb a west BP message into a PEPS tensor.
    │   │   │
    │   │   ├── contract_north_message(A::PEPSSandwich, M_west::PEPSMessage, M_north::PEPSMessage, M_east::PEPSMessage)
    │   │   │   Update the BP message leaving a site toward the north.
    │   │   │
    │   │   ├── contract_east_message(A::PEPSSandwich, M_north::PEPSMessage, M_east::PEPSMessage, M_south::PEPSMessage)
    │   │   │   Update the BP message leaving a site toward the east.
    │   │   │
    │   │   ├── contract_south_message(A::PEPSSandwich, M_east::PEPSMessage, M_south::PEPSMessage, M_west::PEPSMessage)
    │   │   │   Update the BP message leaving a site toward the south.
    │   │   │
    │   │   └── contract_west_message(A::PEPSSandwich, M_south::PEPSMessage, M_west::PEPSMessage, M_north::PEPSMessage)
    │   │       Update the BP message leaving a site toward the west.
    │   │
    │   ├── local_patch/
    │   │   ├── expr_utils.jl
    │   │   │   ├── tensorlabel(args...)
    │   │   │   │   Join label components into a tensor-contraction symbol.
    │   │   │   │
    │   │   │   ├── envlabel(args...)
    │   │   │   │   Construct an environment-leg label.
    │   │   │   │
    │   │   │   ├── virtuallabel(args...)
    │   │   │   │   Construct a state virtual-leg label.
    │   │   │   │
    │   │   │   ├── physicallabel(args...)
    │   │   │   │   Construct a physical-leg label.
    │   │   │   │
    │   │   │   ├── _open_slot(open, r, c)
    │   │   │   │   Return the operator slot assigned to a patch site.
    │   │   │   │
    │   │   │   ├── _bulk_virtuallabels(i, j, layer, gridsize)
    │   │   │   │   Return the north, east, south, and west labels of one bulk tensor.
    │   │   │   │
    │   │   │   ├── _patch_inds(inds::Type)
    │   │   │   │   _patch_inds(inds::Tuple{Vararg{Val}})
    │   │   │   │   Recover and validate patch coordinates encoded in Val parameters.
    │   │   │   │
    │   │   │   ├── _patch_ranges(sites)
    │   │   │   │   Return the row and column ranges spanned by patch sites.
    │   │   │   │
    │   │   │   ├── _patch_gridsize(sites)
    │   │   │   │   Return the rectangular dimensions spanned by patch sites.
    │   │   │   │
    │   │   │   └── _patch_shape_string(sites)
    │   │   │       Format the rectangular patch dimensions for diagnostics.
    │   │   │
    │   │   ├── network_expr.jl
    │   │   │   ├── contractcheck(state::Type)
    │   │   │   │   contractcheck(::Type{<:InfinitePEPO})
    │   │   │   │   contractcheck(::Type{<:Tuple{Vararg{InfinitePEPO}}})
    │   │   │   │   Select whether tensor-space compatibility is checked for a state representation.
    │   │   │   │
    │   │   │   ├── _tensor_expr(prod, lhs, check::Bool)
    │   │   │   │   Assemble indexed tensor expressions into an optimized `@tensor` contraction.
    │   │   │   │
    │   │   │   ├── boundary_contraction_expr(env::Type, rowrange, colrange)
    │   │   │   │   boundary_contraction_expr(::Type{<:CTMRGEnv{C,T}}, rowrange, colrange) where {C,T}
    │   │   │   │   Generate indexed CTMRG boundary tensor expressions for an `@tensor` contraction, or report that an environment implementation is unavailable.
    │   │   │   │
    │   │   │   ├── bulk_contraction_expr(state::Type, rowrange, colrange, open)
    │   │   │   │   bulk_contraction_expr(::Type{<:Tuple{InfinitePEPS,InfinitePEPS}}, rowrange, colrange, open)
    │   │   │   │   bulk_contraction_expr(::Type{<:InfinitePEPO}, rowrange, colrange, open)
    │   │   │   │   bulk_contraction_expr(::Type{<:Tuple{InfinitePEPO,InfinitePEPO}}, rowrange, colrange, open)
    │   │   │   │   bulk_contraction_expr(state::Type{<:Tuple{Vararg{InfinitePEPO}}}, rowrange, colrange, open)
    │   │   │   │   Generate indexed PEPS or PEPO tensor expressions for the patch interior, or report that a state implementation is unavailable.
    │   │   │   │
    │   │   │   └── operator_contraction_expr(operator::Type, nsites)
    │   │   │       operator_contraction_expr(::Type{<:AbstractTensorMap}, nsites)
    │   │   │       Generate an indexed dense-operator expression for an `@tensor` contraction, or report that an operator implementation is unavailable.
    │   │   │
    │   │   ├── patch_contractions.jl
    │   │   │   ├── _contract_local_operator(inds::NTuple{N,Val}, operator, state, env) where {N}
    │   │   │   │   Assemble and execute the unnormalized operator-inserted patch contraction for direct evaluation.
    │   │   │   │
    │   │   │   ├── contract_local_operator(inds::Vector{CartesianIndex{2}}, O, ket::InfinitePEPS, bra::InfinitePEPS, env)
    │   │   │   │   contract_local_operator(inds::Vector{CartesianIndex{2}}, O, state::InfinitePEPO, env)
    │   │   │   │   contract_local_operator(inds::Vector{CartesianIndex{2}}, O, ket::InfinitePEPO, bra::InfinitePEPO, env)
    │   │   │   │   contract_local_operator(inds::Vector{Tuple{Int,Int}}, O, args...)
    │   │   │   │   contract_local_operator(inds::NTuple, args...) [deprecated]
    │   │   │   │   Validate the inputs and invoke the generated direct contraction for an operator insertion.
    │   │   │   │
    │   │   │   ├── _contract_local_norm(inds::NTuple{N,Val}, state, env) where {N}
    │   │   │   │   Assemble and execute the corresponding patch-normalization contraction with closed physical legs.
    │   │   │   │
    │   │   │   └── contract_local_norm(inds::Vector{CartesianIndex{2}}, ket::InfinitePEPS, bra::InfinitePEPS, env)
    │   │   │       contract_local_norm(inds::Vector{CartesianIndex{2}}, state::InfinitePEPO, env)
    │   │   │       contract_local_norm(inds::Vector{CartesianIndex{2}}, ket::InfinitePEPO, bra::InfinitePEPO, env)
    │   │   │       contract_local_norm(inds::Vector{Tuple{Int,Int}}, args...)
    │   │   │       contract_local_norm(inds::NTuple, ket::InfinitePEPS, bra::InfinitePEPS, env) [deprecated]
    │   │   │       Validate the inputs and invoke the generated direct contraction used to normalize an operator insertion.
    │   │   │
    │   │   └── densitymatrix/
    │   │       ├── generic.jl
    │   │       │   └── _contract_densitymatrix(inds::NTuple{N,Val}, state, env) where {N}
    │   │       │       Assemble and execute a generated patch contraction with open physical legs.
    │   │       │
    │   │       ├── ctmrg.jl
    │   │       │   ├── reduced_densitymatrix1x1(ind::CartesianIndex{2}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv)
    │   │       │   │   Contract an optimized one-site PEPS density matrix using a CTMRG environment.
    │   │       │   │
    │   │       │   ├── reduced_densitymatrix2x1(ind::CartesianIndex, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv)
    │   │       │   │   Contract an optimized vertical two-site PEPS density matrix using a CTMRG environment.
    │   │       │   │
    │   │       │   └── reduced_densitymatrix1x2(ind::CartesianIndex, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv)
    │   │       │       Contract an optimized horizontal two-site PEPS density matrix using a CTMRG environment.
    │   │       │
    │   │       └── bp.jl
    │   │           ├── _contract_densitymatrix(inds::NTuple{N,Val}, state, env::BPEnv) where {N}
    │   │           │   Reject patch geometries that cannot be represented by local BP messages.
    │   │           │
    │   │           ├── reduced_densitymatrix1x1(ind::CartesianIndex{2}, ket::InfinitePEPS, bra::InfinitePEPS, env::BPEnv)
    │   │           │   Contract a one-site PEPS density matrix using BP messages.
    │   │           │
    │   │           ├── reduced_densitymatrix2x1(coord::CartesianIndex{2}, ket::InfinitePEPS, bra::InfinitePEPS, env::BPEnv)
    │   │           │   Contract a vertical two-site PEPS density matrix using BP messages.
    │   │           │
    │   │           └── reduced_densitymatrix1x2(coord::CartesianIndex{2}, ket::InfinitePEPS, bra::InfinitePEPS, env::BPEnv)
    │   │               Contract a horizontal two-site PEPS density matrix using BP messages.
    │   │
    │   └── ctmrg/
    │       └── network_value.jl
    │           ├── _contract_site(ind::Tuple{Int,Int}, network, env::CTMRGEnv)
    │           │   _contract_site(C_northwest, C_northeast, C_southeast, C_southwest, E_north::CTMRG_PEPS_EdgeTensor, E_east::CTMRG_PEPS_EdgeTensor, E_south::CTMRG_PEPS_EdgeTensor, E_west::CTMRG_PEPS_EdgeTensor, O::PEPSSandwich)
    │           │   _contract_site(C_northwest, C_northeast, C_southeast, C_southwest, E_north::CTMRG_PF_EdgeTensor, E_east::CTMRG_PF_EdgeTensor, E_south::CTMRG_PF_EdgeTensor, E_west::CTMRG_PF_EdgeTensor, O::PFTensor)
    │           │   _contract_site(C_northwest, C_northeast, C_southeast, C_southwest, E_north::TE, E_east::TE, E_south::TE, E_west::TE, O::PEPOSandwich{H}) where {TE<:CTMRGEdgeTensor,H}
    │           │   Contract one network site with its surrounding CTMRG corners and edges.
    │           │
    │           ├── _contract_corners(ind::Tuple{Int,Int}, env::CTMRGEnv)
    │           │   _contract_corners(C_northwest::CTMRGCornerTensor, C_northeast::CTMRGCornerTensor, C_southeast::CTMRGCornerTensor, C_southwest::CTMRGCornerTensor)
    │           │   Contract four CTMRG corners for network-value normalization.
    │           │
    │           ├── _contract_vertical_edges(ind::Tuple{Int,Int}, env::CTMRGEnv)
    │           │   _contract_vertical_edges(C_northwest::CTMRGCornerTensor, C_northeast::CTMRGCornerTensor, C_southeast::CTMRGCornerTensor, C_southwest::CTMRGCornerTensor, E_east::CTMRGEdgeTensor{T,S,N}, E_west::CTMRGEdgeTensor{T,S,N}) where {T,S,N}
    │           │   Contract two vertical edges and four corners for network-value normalization.
    │           │
    │           ├── _contract_horizontal_edges(ind::Tuple{Int,Int}, env::CTMRGEnv)
    │           │   _contract_horizontal_edges(C_northwest::CTMRGCornerTensor, C_northeast::CTMRGCornerTensor, C_southeast::CTMRGCornerTensor, C_southwest::CTMRGCornerTensor, E_north::CTMRGEdgeTensor{T,S,N}, E_south::CTMRGEdgeTensor{T,S,N}) where {T,S,N}
    │           │   Contract two horizontal edges and four corners for network-value normalization.
    │           │
    │           ├── network_value(network::InfiniteSquareNetwork, env::CTMRGEnv)
    │           │   network_value(state, env::CTMRGEnv)
    │           │   Evaluate the normalized value per unit cell of an infinite square network.
    │           │
    │           └── LinearAlgebra.norm(peps::InfinitePEPS, env::CTMRGEnv)
    │               Evaluate the CTMRG norm of an infinite PEPS.
    │
    └── optimization/
        └── peps_optimization.jl
            └── cost_function(peps::InfinitePEPS, env, O::LocalOperator)
                Return the real part of an expectation value as the PEPS optimization cost.

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.

2 participants