Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
200ce7c
Approximate contraction of an iPEPO window
Yue-Zhengyuan Aug 12, 2026
7aab80f
Standardize virtual arrows before contraction
Yue-Zhengyuan Aug 17, 2026
4fa7745
Fix fusers
Yue-Zhengyuan Aug 18, 2026
84f56e0
Fix fermions with proper planar contractions
Yue-Zhengyuan Aug 18, 2026
f158212
Promote WindowRowCache to a struct
Yue-Zhengyuan Aug 18, 2026
93476a3
Make south boundary construction planar
Yue-Zhengyuan Aug 18, 2026
2991f9b
Cover fermions in tests
Yue-Zhengyuan Aug 18, 2026
86c1367
Use repartition where appropriate
Yue-Zhengyuan Aug 18, 2026
631df2c
Reorganize code
Yue-Zhengyuan Aug 18, 2026
72b0733
Merge remote-tracking branch 'upstream/main' into longrange-expval
Yue-Zhengyuan Aug 18, 2026
6f5f4d3
Standardize envspace arrow on east CTM boundary
Yue-Zhengyuan Aug 19, 2026
57820f8
Rename fields of `WindowRowCache`
Yue-Zhengyuan Sep 15, 2026
2a67cf7
Separate `mpo_path` functions from their generated contraction part
Yue-Zhengyuan Sep 15, 2026
3d62510
Merge remote-tracking branch 'upstream/main' into longrange-expval
Yue-Zhengyuan Sep 15, 2026
af6a64c
Rename `_approximate`
Yue-Zhengyuan Sep 15, 2026
13c069c
Make `correlator_approx` interface similar to `correlator` and simpli…
Yue-Zhengyuan Sep 15, 2026
95f1555
Adaptive window depth (row number) for `correlator_approx`
Yue-Zhengyuan Sep 15, 2026
f56fed8
No longer adjointing south boundary
Yue-Zhengyuan Sep 17, 2026
ad059ee
Merge remote-tracking branch 'upstream/main' into longrange-expval
Yue-Zhengyuan Oct 1, 2026
00bd534
Adapt to new test format
Yue-Zhengyuan Oct 1, 2026
8b446cc
Port MPOTerm support to LocalOperator from PR #425
Yue-Zhengyuan Oct 1, 2026
a886e24
Accept LocalOperator in expectation_value_approx
Yue-Zhengyuan Oct 1, 2026
ff85542
Rename `window` to `patch`
Yue-Zhengyuan Oct 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions src/PEPSKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ using LoggingExtras
import TupleTools

using MPSKit
using MPSKit: MPSTensor, MPOTensor, GenericMPSTensor, MPSBondTensor, ProductTransferMatrix
using MPSKit:
MPSTensor, MPOTensor, GenericMPSTensor, MPSBondTensor,
ProductTransferMatrix, TransferMatrix
using MPSKit: InfiniteEnvironments
using MPSKit: DynamicTol, updatetol
import MPSKit.DynamicTols: _updatetol
import MPSKit: tensorexpr, leading_boundary, loginit!, logiter!, logfinish!, logcancel!, physicalspace
import MPSKit: infinite_temperature_density_matrix
import MPSKit: fuser

using TensorKitTensors: fuse_charge
import TensorKitTensors.SpinOperators as SO
Expand Down Expand Up @@ -79,6 +82,7 @@ include("operators/infinitepepo.jl")
include("operators/transfermatrix.jl")
include("operators/localoperator.jl")
include("operators/localcircuit.jl")

include("operators/lattices/squarelattice.jl")
include("operators/models.jl")

Expand Down Expand Up @@ -120,6 +124,12 @@ include("algorithms/contractions/correlator/peps.jl")
include("algorithms/contractions/correlator/pepo_purified.jl")
include("algorithms/contractions/correlator/pepo_1layer.jl")

include("algorithms/contractions/mpo_path/routing.jl")
include("algorithms/contractions/mpo_path/pepo_1layer.jl")
include("algorithms/contractions/patch/tools.jl")
include("algorithms/contractions/patch/pepo_1layer.jl")
include("algorithms/contractions/patch/twosite/pepo_1layer.jl")

include("algorithms/ctmrg/sparse_environments.jl")
include("algorithms/ctmrg/ctmrg.jl")
include("algorithms/ctmrg/projectors/projectors.jl")
Expand Down Expand Up @@ -160,6 +170,8 @@ include("algorithms/expectation_value/network_value.jl")
include("algorithms/expectation_value/correlator_adapters.jl")
include("algorithms/expectation_value/correlators.jl")
include("algorithms/toolbox.jl")
include("algorithms/expval_approx.jl")
include("algorithms/correlator_approx.jl")

include("algorithms/optimization/implicit_differentiation.jl")
include("algorithms/optimization/preconditioning.jl")
Expand All @@ -178,9 +190,10 @@ export FixedSpaceTruncation, SiteDependentTruncation
export HalfInfiniteProjector, FullInfiniteProjector
export C4vCTMRG, C4vEighProjector, C4vQRProjector
export initialize_random_c4v_env, initialize_singlet_c4v_env
export LocalOperator, physicalspace
export LocalOperator, MPOTerm, physicalspace
export product_peps
export reduced_densitymatrix, expectation_value, network_value, cost_function
export reduced_densitymatrix, expectation_value_approx, correlator_approx
export expectation_value, network_value, cost_function
export correlator, correlation_length
export leading_boundary
export PEPSOptimize, FixedPointGradient, GeomSum, ManualIter, ImplicitGradient
Expand Down
247 changes: 247 additions & 0 deletions src/algorithms/contractions/mpo_path/pepo_1layer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
"""
Check that the physical legs of a first OBC-MPO tensor match the PEPO site's physical space.
"""
function _check_pepo_first_physicalspace(A, op)
physicalspace(A) == space(op, 1) == space(op, 2)' ||
throw(SpaceMismatch("first MPO tensor physical space does not match PEPO site"))
return nothing
end

"""
Check that the physical legs of a last OBC-MPO tensor match the PEPO site's physical space.
"""
function _check_pepo_last_physicalspace(A, op)
physicalspace(A) == space(op, 2) == space(op, 3)' ||
throw(SpaceMismatch("last MPO tensor physical space does not match PEPO site"))
return nothing
end

"""
Check that the physical legs of a middle MPO tensor match the PEPO site's physical space.
"""
function _check_pepo_middle_physicalspace(A, op)
physicalspace(A) == space(op, 2) == space(op, 3)' ||
throw(SpaceMismatch("middle MPO tensor physical space does not match PEPO site"))
return nothing
end

"""
Convert a symbolic cardinal path direction to the corresponding PEPO virtual-leg index.
"""
function _mpo_path_direction(direction::Symbol)
direction === :north && return NORTH
direction === :east && return EAST
direction === :south && return SOUTH
direction === :west && return WEST
throw(ArgumentError("invalid MPO path direction: $direction"))
end

"""
Return the tensor-expression label for the PEPO virtual leg in a cardinal direction.
"""
function _mpo_path_virtual_label(direction::Symbol)
direction === :north && return :N
direction === :east && return :E
direction === :south && return :S
direction === :west && return :W
throw(ArgumentError("invalid MPO path direction: $direction"))
end

"""
Canonicalize an incoming MPO fuser so its fused PEPO leg has standard dualness.
"""
function _mpo_path_incoming_fuser(F, direction::Int)
direction in (NORTH, EAST) && return F
direction in (SOUTH, WEST) && return twist(flip(F, 1), 1)
throw(ArgumentError("invalid MPO path direction index: $direction"))
end

"""
Canonicalize an outgoing MPO fuser so its fused PEPO leg has standard dualness and braiding.
"""
function _mpo_path_outgoing_fuser(F, direction::Int)
direction in (NORTH, EAST) && return twist(flip(F, 1), 1)
direction in (SOUTH, WEST) && return twist(F, 3)
throw(ArgumentError("invalid MPO path direction index: $direction"))
end

"""
Build the `@tensor` labels from `(direction, suffix)` pairs
used to fuse MPO virtual strings.

- Each direction is `:north`, `:east`, `:south`, or `:west`.
- Suffix `:l` marks an incoming MPO bond and `:r` an outgoing one.

Examples:

- `((:east, :r),)` produces `[W S; N Er]`.
- `((:west, :l), (:north, :r))` produces `[Wl S; Nr E]`.
"""
function _mpo_path_result_expr(directions)
labels = [:N, :E, :S, :W]
for (direction, suffix) in directions
index = _mpo_path_direction(direction)
labels[index] = Symbol(labels[index], suffix)
end
return tensorexpr(:t, (labels[WEST], labels[SOUTH]), (labels[NORTH], labels[EAST]))
end

"""
Act the first tensor `op` of an OBC-MPO on PEPO tensor `A` and fuse the outgoing MPO string with the virtual space of `A` along `direction`.
"""
function mpo_path_first(A::PEPOTensor, op, direction::Val{D}) where {D}
_check_pepo_first_physicalspace(A, op)
direction_index = _mpo_path_direction(D)
A′ = twistdual(A, 2)
F = _mpo_path_outgoing_fuser(
fuser(storagetype(A), domain(A, direction_index)', space(op, 3)),
direction_index,
)
return _mpo_path_first(A′, op, F, direction)
end

"""
Contract a prepared PEPO tensor with the first MPO tensor and its outgoing fuser.
"""
@generated function _mpo_path_first(A, op, F, ::Val{direction}) where {direction}
virtual_label = _mpo_path_virtual_label(direction)
fused_label = Symbol(virtual_label, :r)

result_e = _mpo_path_result_expr(((direction, :r),))
op_e = tensorexpr(:op, :dout, (:din, :r))
A_e = tensorexpr(:A, (:din, :dout), (:N, :E, :S, :W))
F_e = tensorexpr(:F, fused_label, (virtual_label, :r))
rhs = Expr(:call, :*, op_e, A_e, F_e)
return macroexpand(
@__MODULE__, :(return @tensoropt $result_e := $rhs)
)
end

"""
Act the last tensor `op` of an OBC-MPO on PEPO tensor `A` and fuse the incoming MPO string with the virtual space of `A` along `direction`.
"""
function mpo_path_last(A::PEPOTensor, op, direction::Val{D}) where {D}
_check_pepo_last_physicalspace(A, op)
direction_index = _mpo_path_direction(D)
A′ = twistdual(A, 2)
F = _mpo_path_incoming_fuser(
fuser(storagetype(A), domain(A, direction_index), space(op, 1)'),
direction_index,
)
return _mpo_path_last(A′, op, F, direction)
end

"""
Contract a prepared PEPO tensor with the last MPO tensor and its incoming fuser.
"""
@generated function _mpo_path_last(A, op, F, ::Val{direction}) where {direction}
virtual_label = _mpo_path_virtual_label(direction)
fused_label = Symbol(virtual_label, :l)

result_e = _mpo_path_result_expr(((direction, :l),))
F_e = Expr(:call, :conj, tensorexpr(:F, fused_label, (virtual_label, :l)))
op_e = tensorexpr(:op, (:l, :dout), :din)
A_e = tensorexpr(:A, (:din, :dout), (:N, :E, :S, :W))
rhs = Expr(:call, :*, F_e, op_e, A_e)
return macroexpand(
@__MODULE__, :(return @tensoropt $result_e := $rhs)
)
end

"""
Act the middle tensor `op` of an MPO on PEPO tensor `A` and fuse the incoming and outgoing MPO strings with the virtual space of `A` along `directions = (incoming, outgoing)`.
"""
function mpo_path_middle(A::PEPOTensor, op, directions::Val{D}) where {D}
incoming, outgoing = D
incoming == outgoing &&
throw(ArgumentError("MPO path should enter and exit in different directions"))
_check_pepo_middle_physicalspace(A, op)
incoming_index = _mpo_path_direction(incoming)
outgoing_index = _mpo_path_direction(outgoing)
A′ = twistdual(A, 2)
Fin = _mpo_path_incoming_fuser(
fuser(storagetype(A), domain(A, incoming_index), space(op, 1)'),
incoming_index,
)
Fout = _mpo_path_outgoing_fuser(
fuser(storagetype(A), domain(A, outgoing_index)', space(op, 4)),
outgoing_index,
)
return _mpo_path_middle(A′, op, Fin, Fout, directions)
end

"""
Contract a prepared PEPO tensor with a middle MPO tensor and its incoming and outgoing fusers.
"""
@generated function _mpo_path_middle(
A, op, Fin, Fout, ::Val{directions}
) where {directions}
incoming, outgoing = directions
incoming_label = _mpo_path_virtual_label(incoming)
outgoing_label = _mpo_path_virtual_label(outgoing)
fused_incoming_label = Symbol(incoming_label, :l)
fused_outgoing_label = Symbol(outgoing_label, :r)

result_e = _mpo_path_result_expr(((incoming, :l), (outgoing, :r)))
Fin_e = Expr(
:call, :conj,
tensorexpr(:Fin, fused_incoming_label, (incoming_label, :l)),
)
op_e = tensorexpr(:op, (:l, :dout), (:din, :r))
A_e = tensorexpr(:A, (:din, :dout), (:N, :E, :S, :W))
Fout_e = tensorexpr(
:Fout, fused_outgoing_label, (outgoing_label, :r)
)
rhs = Expr(:call, :*, Fin_e, op_e, A_e, Fout_e)
return macroexpand(
@__MODULE__, :(return @tensoropt $result_e := $rhs)
)
end

"""
Route an MPO virtual string with `stringspace` through a PEPO tensor `A`
along `directions = (incoming, outgoing)`.
"""
@generated function mpo_path_string(
A::PEPOTensor, stringspace::ElementarySpace, ::Val{directions}
) where {directions}
incoming, outgoing = directions
incoming == outgoing &&
throw(ArgumentError("MPO path should enter and exit in different directions"))

incoming_index = _mpo_path_direction(incoming)
outgoing_index = _mpo_path_direction(outgoing)
incoming_label = _mpo_path_virtual_label(incoming)
outgoing_label = _mpo_path_virtual_label(outgoing)
fused_incoming_label = Symbol(incoming_label, :l)
fused_outgoing_label = Symbol(outgoing_label, :r)

result_e = _mpo_path_result_expr(((incoming, :l), (outgoing, :r)))
Fin_e = Expr(
:call, :conj,
tensorexpr(:Fin, fused_incoming_label, (incoming_label, :l)),
)
O_e = tensorexpr(:O, (:W, :S), (:N, :E))
I_e = tensorexpr(:I, :l, :r)
Fout_e = tensorexpr(
:Fout, fused_outgoing_label, (outgoing_label, :r)
)
rhs = Expr(:call, :*, Fin_e, O_e, I_e, Fout_e)
contraction = macroexpand(
@__MODULE__, :(return @tensoropt $result_e := $rhs)
)

return quote
O = trace_physicalspaces(A)
I = id(storagetype(A), stringspace)
Fin = _mpo_path_incoming_fuser(
fuser(storagetype(A), domain(A, $incoming_index), stringspace'),
$incoming_index,
)
Fout = _mpo_path_outgoing_fuser(
fuser(storagetype(A), domain(A, $outgoing_index)', stringspace'),
$outgoing_index,
)
$contraction
end
end
Loading
Loading