Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
c101c79
Change ALS tensor axis order
Yue-Zhengyuan Apr 6, 2026
2fdd026
bondenv_ctm for PEPO
Yue-Zhengyuan Apr 6, 2026
1a13c39
Rotation of LocalCircuit
Yue-Zhengyuan Apr 6, 2026
bfb826a
Use vector of sites in `nearest_neighbours` etc
Yue-Zhengyuan Apr 7, 2026
0c74c01
Merge branch 'rotate-circuit' into bondenv-ntu
Yue-Zhengyuan Apr 8, 2026
f6554f3
Neighbourhood tensor update
Yue-Zhengyuan Apr 8, 2026
2540e48
Fix benv_tensor for dual physical space
Yue-Zhengyuan Apr 8, 2026
24145b1
Add NTU tests
Yue-Zhengyuan Apr 8, 2026
f9d029a
Remove unneeded bipartite restrictions
Yue-Zhengyuan Apr 8, 2026
71e6e31
Merge remote-tracking branch 'upstream/master' into bondenv-ntu
Yue-Zhengyuan Apr 10, 2026
c51b54b
Test `timestep` for NTU
Yue-Zhengyuan Apr 12, 2026
e8f07eb
Convergence check for NTU
Yue-Zhengyuan Apr 12, 2026
a4de5ff
Streamline convergence checks
Yue-Zhengyuan Apr 15, 2026
c14fa16
Merge #360
Yue-Zhengyuan Apr 17, 2026
3a915c4
FixedSpaceTruncation for NTU
Yue-Zhengyuan Apr 17, 2026
6bfb34f
Fix time_evolve interface in tests
Yue-Zhengyuan Apr 17, 2026
26344d2
Fix realtime Ising test
Yue-Zhengyuan Apr 18, 2026
8ed7d13
Fix FixedSpaceTruncation for simple update
Yue-Zhengyuan Apr 24, 2026
3498fea
Improve efficiency of bond truncation
Yue-Zhengyuan Apr 24, 2026
ea0a968
Make j1j2 finiteT test work for all allowed symmetries
Yue-Zhengyuan Apr 24, 2026
3cc7769
Reorganize code to get bond tensors
Yue-Zhengyuan Apr 24, 2026
5a90194
Improve NTU struct type stability
Yue-Zhengyuan Apr 24, 2026
3d1c3f9
Fix FixedSpaceTruncation again
Yue-Zhengyuan Apr 25, 2026
99e3e48
Require bond tensor to be an MPSTensor
Yue-Zhengyuan Apr 25, 2026
b6aeff3
Replace `_qr_bond` with `bond_tensor` functions
Yue-Zhengyuan Apr 25, 2026
bbc3bc4
Fix `undo_bond_tensor_last`
Yue-Zhengyuan Apr 25, 2026
de5a5d5
Change bond_tensor return order
Yue-Zhengyuan Apr 25, 2026
2d90e04
Do not move phys leg to bond tensor for middle cluster sites
Yue-Zhengyuan Apr 26, 2026
931a575
Fix bondenv tests
Yue-Zhengyuan Apr 26, 2026
14c2fde
Get rid of ncon in benv_tensor
Yue-Zhengyuan May 3, 2026
f8aaff0
More optimizations for benv_tensor
Yue-Zhengyuan May 3, 2026
9079316
Improve simple update performance
Yue-Zhengyuan May 4, 2026
511a654
Fix formatting
Yue-Zhengyuan May 4, 2026
eb06fc0
Merge remote-tracking branch 'upstream/master' into bondenv-ntu
Yue-Zhengyuan May 4, 2026
c60ab31
Add Printf to test environment
Yue-Zhengyuan May 4, 2026
dbb4a6b
Remove unnecessary type specification
Yue-Zhengyuan May 5, 2026
d0bcf0e
Merge remote-tracking branch 'upstream/master' into bondenv-ntu
Yue-Zhengyuan May 5, 2026
5234126
Revert a comment
Yue-Zhengyuan May 5, 2026
6034a67
Change default opt_alg for NTU
Yue-Zhengyuan May 7, 2026
8e4d44a
Add verbosity setting for NTU
Yue-Zhengyuan May 7, 2026
fa109bc
Test coverage on NTU for ground state
Yue-Zhengyuan May 7, 2026
e16c935
Unexport infinite_temperature_density_matrix
Yue-Zhengyuan May 7, 2026
fba41a2
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan May 8, 2026
1fdc81c
Use periodic indexing
Yue-Zhengyuan May 8, 2026
1655e48
Slightly reduce output
Yue-Zhengyuan May 8, 2026
03d2c8f
Update tests
Yue-Zhengyuan May 15, 2026
e1e0a0a
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan Jun 1, 2026
676f8d7
Refactor _cluster_truncate!
Yue-Zhengyuan Jun 2, 2026
e89a35b
Reverse cluster truncation order at even NTU iterations
Yue-Zhengyuan Jun 2, 2026
6b54e1c
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan Jun 14, 2026
a0f819a
Fix condition when reconverging env
Yue-Zhengyuan Jun 15, 2026
9976630
Comment to reverse_trunc
Yue-Zhengyuan Jun 15, 2026
03a160f
Refactor bond direction helpers to use lattice direction constants
Yue-Zhengyuan Jun 15, 2026
897ed8d
Merge branch 'main' into bondenv-ntu
Yue-Zhengyuan Jun 17, 2026
8d21293
Allow complex time in `timestep`
Yue-Zhengyuan Jul 2, 2026
01ae874
Simplify type annotation for bondenv_ntu
Yue-Zhengyuan Jul 2, 2026
18d2041
Remove unnecessary const Dict
Yue-Zhengyuan Jul 2, 2026
81f5f18
Type stability fix for hair_axs
Yue-Zhengyuan Jul 2, 2026
44ac6d8
Explicitly keep trivially charged leading singular value in _svd_cut!
Yue-Zhengyuan Jul 2, 2026
e80948b
Test NNpEnv with Heisenberg ground state
Yue-Zhengyuan Jul 2, 2026
b3db831
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan Jul 16, 2026
06b8c02
One-pass `@tensoropt` contraction for NTU envs
Yue-Zhengyuan Jul 16, 2026
6dadf4f
Add some BP related TODOs
Yue-Zhengyuan Jul 16, 2026
e3f92d1
More detailed docstring for `_svd_cut!`
Yue-Zhengyuan Jul 16, 2026
ab1f4be
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan Aug 6, 2026
afb00ac
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan Aug 29, 2026
495cc8a
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan Sep 5, 2026
886533d
Adapt to changed behavior of `twistdual` when nothing needs twisting
Yue-Zhengyuan Sep 5, 2026
28bf083
Merge upstream/main into bondenv-ntu
Yue-Zhengyuan Sep 23, 2026
f65ce10
Move existing NTU bond environment tests into TestSuite
Yue-Zhengyuan Sep 23, 2026
d5d7c60
Formatting
Yue-Zhengyuan Sep 23, 2026
c79dce6
Exclude NNNEnv from this PR
Yue-Zhengyuan Sep 25, 2026
7a0302e
Merge remote-tracking branch 'upstream/main' into bondenv-ntu
Yue-Zhengyuan Sep 25, 2026
392aeec
Optimize NTU environment contractions using actual leg dimensions
Yue-Zhengyuan Sep 26, 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
8 changes: 6 additions & 2 deletions src/PEPSKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module PEPSKit
using LinearAlgebra, Statistics, Base.Threads, Base.Iterators, Printf
using Random
using Compat
using Accessors: @set, @reset
using Accessors: @set, @reset, @insert
using VectorInterface
import VectorInterface as VI

Expand Down Expand Up @@ -115,6 +115,7 @@ include("algorithms/contractions/local_patch/densitymatrix/bp.jl")
include("algorithms/contractions/bondenv/benv_tools.jl")
include("algorithms/contractions/bondenv/gaugefix.jl")
include("algorithms/contractions/bondenv/als_solve.jl")
include("algorithms/contractions/bondenv/benv_ntu.jl")
include("algorithms/contractions/bondenv/benv_ctm.jl")
include("algorithms/contractions/correlator/peps.jl")
include("algorithms/contractions/correlator/pepo_purified.jl")
Expand Down Expand Up @@ -147,6 +148,8 @@ include("algorithms/time_evolution/trotter_gate.jl")
include("algorithms/time_evolution/time_evolve.jl")
include("algorithms/time_evolution/simpleupdate.jl")
include("algorithms/time_evolution/simpleupdate3site.jl")
include("algorithms/time_evolution/ntupdate.jl")
include("algorithms/time_evolution/ntupdate3site.jl")
include("algorithms/time_evolution/gaugefix_su.jl")

include("algorithms/bp/beliefpropagation.jl")
Expand Down Expand Up @@ -191,7 +194,8 @@ export compress

export absorb_weight
export ALSTruncation, FullEnvTruncation
export SimpleUpdate
export NNEnv, NNpEnv
export SimpleUpdate, NeighbourUpdate
export TimeEvolver, timestep, time_evolve

export InfiniteSquareNetwork
Expand Down
203 changes: 203 additions & 0 deletions src/algorithms/contractions/bondenv/benv_ntu.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#=
The construction of bond environment for Neighborhood Tensor Update (NTU)
is adapted from YASTN (https://github.com/yastn/yastn).
Copyright 2024 The YASTN Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0
=#

"""
Algorithms to construct bond environment for Neighborhood Tensor Update (NTU).
"""
abstract type NeighbourEnv end

"""
For a rank-(2,2) tensor `T_{ik;jl}`, approximately decompose it to the
product of two rank-2 tensors `A_{ik;} * B_{;jl}` using truncated SVD
that keeps only the largest singular value in the charge-neutral sector.

The kept singular value is the largest in the entire SVD spectrum if:
- `permute(T, ((1, 3), (2, 4)))` is positive semi-definite, and
- `space(T, 1) == space(T, 2)'` and `space(T, 3) == space(T, 4)`,
"""
function _svd_cut!(t::AbstractTensorMap{<:Any, <:Any, 2, 2})
A, B = left_orth!(t; trunc = truncspace(oneunit(spacetype(t))))
return removeunit(A, numind(A)), removeunit(B, 1)
end

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.

Out of curiosity: do you need the weight to be distributed equally? Otherwise this might be replaced with left_orth!(t; trunc = truncrank(1)) or right_orth!(t; trunc = truncrank(1)), which I think removes some intermediate allocations.

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.

On a separate note, do we need this to be trivially charged for everything to work? It might reduce quite a bit of the number of legs if we can do something like:

A, B = left_orth!(t; trunc = truncspace(oneunit(spacetype(t))))
return removeunit(A, numind(A)), removeunit(B, 1)

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.

do you need the weight to be distributed equally?

Nice observation! Actually no, since this D = 1 leg is in the end contracted, so how we distribute s is irrelevant.

do we need this to be trivially charged for everything to work?

It seems that _svd_cut is always applied to a positive map, which then (if my feeling is right) ensures that the leading singular value has trivial charge. I'll double-check this.

In YASTN they don't need to worry about it, since they only support Abelian symmetries and allow a tensor to have nonzero total charge.

@Yue-Zhengyuan Yue-Zhengyuan Jul 16, 2026 •

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.

For _svd_cut! I have updated its docstring, describing a sufficient condition under which the largest singular value in the charge-neutral sector is also the largest in the entire SVD spectrum. The condition is satisfied by tensors constructed from contracting a bra-ket pair of tensors in a twist-free way, so indeed keeping the largest charge-neutral singular value works just as fine as truncrank(1).


"""
Algorithm struct for "NTU-NN" bond environment.
"""
struct NNEnv <: NeighbourEnv end
"""
Calculate the bond environment within "NTU-NN" approximation.
```
-1 ●=======●
║ ║
0 ●===X== ==Y===●
║ ║
1 ●=======●
-1 0 1 2
```
"""
function bondenv_ntu(
row::Int, col::Int, X, Y, state::InfiniteState, alg::NNEnv
)
neighbors = [(-1, 0), (0, -1), (1, 0), (1, 1), (0, 2), (-1, 1)]
m = collect_neighbors(state, row, col, neighbors)
X, Y = _prepare_site_tensor(X), _prepare_site_tensor(Y)
return _contract_ntu_NNEnv(
X, Y, hair_w(m[0, -1]), hair_e(m[0, 2]),
cor_nw(m[-1, 0]), cor_ne(m[-1, 1]), cor_sw(m[1, 0]), cor_se(m[1, 1])
)
end

"""
Algorithm struct for "NTU-NN+" bond environment.
"""
struct NNpEnv <: NeighbourEnv end
"""
Calculate the bond environment within "NTU-NN+" approximation.
```
-2 ●.......●
║ ║
-1 ○===●=======●===○
║ ║ ║ ║
0 ●===●===X== ==Y===●===●
║ ║ ║ ║
1 ○===●=======●===○
║ ║
2 ●.......●
-2 -1 0 1 2 3
```
Dotted lines and ○ are splitted using SVD with `truncrank(1)`.
"""
function bondenv_ntu(
row::Int, col::Int, X, Y, state::InfiniteState, alg::NNpEnv
)
neighbors = [
(-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), (1, 2), (0, 2), (-1, 2),
(-1, 1), (-1, 0), (0, -2), (2, 0), (2, 1), (0, 3), (-2, 1), (-2, 0),
]
ms = collect_neighbors(state, row, col, neighbors)
X, Y = _prepare_site_tensor(X), _prepare_site_tensor(Y)

# ---- hairs (size D^2) with a 1D auxiliary leg ----

@tensor top[-1 -2; -3 -4] := cor_nw(ms[-2, 0])[1 2 -1 -2] * cor_ne(ms[-2, 1])[-3 -4 1 2]
tl, tr = _svd_cut!(top)

@tensor bot[-1 -2; -3 -4] := cor_sw(ms[2, 0])[-1 -2 1 2] * cor_se(ms[2, 1])[-3 -4 1 2]
bl, br = _svd_cut!(bot)

nw = permute(cor_nw(ms[-1, -1]), ((3, 4), (1, 2)))
nw1, nw2 = _svd_cut!(nw)

ne = permute(cor_ne(ms[-1, 2]), ((3, 4), (1, 2)))
ne1, ne2 = _svd_cut!(ne)

sw = permute(cor_sw(ms[1, -1]), ((1, 2), (3, 4)))
sw1, sw2 = _svd_cut!(sw)

se = permute(cor_se(ms[1, 2]), ((3, 4), (1, 2)))
se1, se2 = _svd_cut!(se)

@tensoropt hW[DXw1 DXw0] :=
hair_w(ms[0, -2])[Dw21 Dw20] *
nw1[Dnw11 Dnw10] * sw1[Dsw11 Dsw10] *
twistdual(ms[0, -1], 1)[phW Dnw10 DXw0 Dsw10 Dw20] *
conj(ms[0, -1][phW Dnw11 DXw1 Dsw11 Dw21])
@tensoropt hE[DYe1 DYe0] :=
hair_e(ms[0, 3])[De21 De20] *
ne2[Dne21 Dne20] * se2[Dse21 Dse20] *
twistdual(ms[0, 2], 1)[phE Dne20 De20 Dse20 DYe0] *
conj(ms[0, 2][phE Dne21 De21 Dse21 DYe1])
@tensoropt NW[Dn1 Dn0 DXn1 DXn0] :=
tl[Dtl1 Dtl0] * nw2[Dnw21 Dnw20] *
twistdual(ms[-1, 0], 1)[pNW Dtl0 Dn0 DXn0 Dnw20] *
conj(ms[-1, 0][pNW Dtl1 Dn1 DXn1 Dnw21])
@tensoropt NE[DYn1 DYn0 Dn1 Dn0] :=
tr[Dtr1 Dtr0] * ne1[Dne11 Dne10] *
twistdual(ms[-1, 1], 1)[pNE Dtr0 Dne10 DYn0 Dn0] *
conj(ms[-1, 1][pNE Dtr1 Dne11 DYn1 Dn1])
@tensoropt SW[DXs1 DXs0 Ds1 Ds0] :=
bl[Dbl1 Dbl0] * sw2[Dsw21 Dsw20] *
twistdual(ms[1, 0], 1)[pSW DXs0 Ds0 Dbl0 Dsw20] *
conj(ms[1, 0][pSW DXs1 Ds1 Dbl1 Dsw21])
@tensoropt SE[DYs1 DYs0 Ds1 Ds0] :=
br[Dbr1 Dbr0] * se1[Dse11 Dse10] *
twistdual(ms[1, 1], 1)[pSE DYs0 Dse10 Dbr0 Ds0] *
conj(ms[1, 1][pSE DYs1 Dse11 Dbr1 Ds1])
return _contract_ntu_NNEnv(X, Y, hW, hE, NW, NE, SW, SE)
end

# Common NN/NN+ network; each virtual bond has a bra and a ket index.
# NW -- NE
# | |
# hW --- X Y --- hE
# | |
# SW -- SE
# X/Y identify the central site; n/e/s/w identify its virtual leg.
# N/S join the two upper/lower corners; pX/pY are physical indices.
# Suffix 0 denotes ket and 1 denotes bra.
# The four open QR indices are Xq1, Yq1, Xq0, Yq0.
const _NTU_ENV_NETWORK = [
[:Xw1, :Xw0],
[:Ye1, :Ye0],
[:N1, :N0, :Xn1, :Xn0],
[:Yn1, :Yn0, :N1, :N0],
[:Xs1, :Xs0, :S1, :S0],
[:Ys1, :Ys0, :S1, :S0],
[:pX, :Xn1, :Xq1, :Xs1, :Xw1],
[:pX, :Xn0, :Xq0, :Xs0, :Xw0],
[:pY, :Yn1, :Ye1, :Ys1, :Yq1],
[:pY, :Yn0, :Ye0, :Ys0, :Yq0],
]

"""
Choose the common environment's contraction order using actual leg dimensions.
The fixed ten-tensor network is independent of the MPO path and its length.
Floating-point costs avoid integer overflow; dense dimensions approximate symmetry-block costs.
"""
function _ntu_contraction_order(tensors::NamedTuple)

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.

🤖 has helped introduce this contraction order selector. It thinks that @tensoropt is not sufficient because it assumes that all legs involved have more or less the same dimension. But for bond environment after applying a gate MPO, such as an L-shapes NNN gate, the dimension of bond connecting, say, Y and NE will be enlarged by a factor of D_MPO, while the others remain D.

Therefore, in determine the contraction order, we need to know where the next bond that's enlarged by the MPO gate is.

I would like some help in reviewing if 🤖's approach is recommended.

costs = Dict(
label => Float64(dim(space(t, axis)))
for (t, labels) in zip(tensors, _NTU_ENV_NETWORK)
for (axis, label) in enumerate(labels)
)
tree, _ = TensorOperations.optimaltree(_NTU_ENV_NETWORK, costs)
return first(TensorOperations.tree2indexorder(tree, _NTU_ENV_NETWORK))
end

"""
Compile the selected order to ordinary `@tensor` contractions with statically known ranks.
Specializations depend on the contraction order and tensor types, not on leg dimensions.
"""
@generated function _contract_ntu_kernel(::Val{Order}, tensors::NamedTuple) where {Order}
order = Expr(:tuple, Order...)
return macroexpand(
@__MODULE__, :(
@tensor order = $order benv[Xq1 Yq1; Xq0 Yq0] :=
tensors.hW[Xw1 Xw0] * tensors.hE[Ye1 Ye0] *
tensors.NW[N1 N0 Xn1 Xn0] * tensors.NE[Yn1 Yn0 N1 N0] *
tensors.SW[Xs1 Xs0 S1 S0] * tensors.SE[Ys1 Ys0 S1 S0] *
conj(tensors.Xbra[pX Xn1 Xq1 Xs1 Xw1]) *
tensors.Xket[pX Xn0 Xq0 Xs0 Xw0] *
conj(tensors.Ybra[pY Yn1 Ye1 Ys1 Yq1]) *
tensors.Yket[pY Yn0 Ye0 Ys0 Yq0]
)
)
end

"Contract an NN or NN+ boundary while keeping the central bra and ket layers separate."
function _contract_ntu_NNEnv(X::PEPSTensor, Y::PEPSTensor, hW, hE, NW, NE, SW, SE)
# Bra factors are conjugated in the kernel; ket factors carry the physical-leg twist.
tensors = (;
hW, hE, NW, NE, SW, SE, Xbra = X, Xket = twistdual(X, 1),
Ybra = Y, Yket = twistdual(Y, 1),
)
order = _ntu_contraction_order(tensors)
# The runtime-selected kernel always returns the same concrete rank-(2,2) type.
T = tensormaptype(spacetype(X), 2, 2, TensorKit.promote_storagetype(tensors...))
benv = _contract_ntu_kernel(Val(Tuple(order)), tensors)::T
return normalize!(benv, Inf)
end
Loading
Loading