Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 14 additions & 4 deletions src/algorithms/changebonds/optimalexpand.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ function changebonds(
# Use the nullspaces and SVD decomposition to determine the optimal expansion space
VL = left_null(ψ.AL[i])
VR = right_null!(_transpose_tail(ψ.AR[i + 1]; copy = true))
intermediate = normalize!(adjoint(VL) * AC2 * adjoint(VR))
intermediate = adjoint(VL) * AC2 * adjoint(VR)
nrm = norm(intermediate)
# skip empty-content bonds; normalizing zero would NaN the SVD
nrm > eps(real(scalartype(intermediate)))^(3 / 4) && scale!(intermediate, inv(nrm))
U, _, Vᴴ = svd_trunc!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd)

AL′[i] = VL * U
Expand All @@ -72,7 +75,9 @@ function changebonds(ψ::MultilineMPS, H, alg::OptimalExpand, envs = environment
# Use the nullspaces and SVD decomposition to determine the optimal expansion space
VL = left_null(ψ.AL[i, j])
VR = right_null!(_transpose_tail(ψ.AR[i, j + 1]; copy = true))
intermediate = normalize!(adjoint(VL) * AC2 * adjoint(VR))
intermediate = adjoint(VL) * AC2 * adjoint(VR)
nrm = norm(intermediate)
nrm > eps(real(scalartype(intermediate)))^(3 / 4) && scale!(intermediate, inv(nrm))
U, _, Vᴴ = svd_trunc!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd)

AL′[i, j] = VL * U
Expand All @@ -99,7 +104,10 @@ function changebond!(site::Int, ::Val{:right}, ψ::AbstractFiniteMPS, H, alg::Op

# select the dominant directions in the complement of the current state
g2 = adjoint(NL) * AC2 * adjoint(NR)
_, _, Vᴴ = svd_trunc!(normalize!(g2); trunc = alg.trscheme, alg = alg.alg_svd)
nrm = norm(g2)
# nothing to expand here; normalizing a zero g2 would NaN the SVD
nrm ≤ eps(real(scalartype(g2)))^(3 / 4) && return ψ
_, _, Vᴴ = svd_trunc!(scale!(g2, inv(nrm)); trunc = alg.trscheme, alg = alg.alg_svd)

# optimal vectors at site+1
ar_re = Vᴴ * NR
Expand All @@ -126,7 +134,9 @@ function changebond!(site::Int, ::Val{:left}, ψ::AbstractFiniteMPS, H, alg::Opt

# select the dominant directions in the complement of the current state
g2 = adjoint(NL) * AC2 * adjoint(NR)
U, _, _ = svd_trunc!(normalize!(g2); trunc = alg.trscheme, alg = alg.alg_svd)
nrm = norm(g2)
nrm ≤ eps(real(scalartype(g2)))^(3 / 4) && return ψ
U, _, _ = svd_trunc!(scale!(g2, inv(nrm)); trunc = alg.trscheme, alg = alg.alg_svd)

# optimal vectors at site-1
Q = NL * U
Expand Down
9 changes: 7 additions & 2 deletions src/algorithms/changebonds/randexpand.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ function changebond!(site::Int, ::Val{:right}, ψ::AbstractFiniteMPS, H, alg::Ra

# select the dominant directions in the complement of the current state
g2 = adjoint(NL) * ac2 * adjoint(NR)
_, _, Vᴴ = svd_trunc!(normalize!(g2); trunc = alg.trscheme, alg = alg.alg_svd)
nrm = norm(g2)
# nothing to expand here; normalizing a zero g2 would NaN the SVD
nrm ≤ eps(real(scalartype(g2)))^(3 / 4) && return ψ
_, _, Vᴴ = svd_trunc!(scale!(g2, inv(nrm)); trunc = alg.trscheme, alg = alg.alg_svd)

# optimal vectors at site+1, zero weight at site
ar_re = Vᴴ * NR
Expand All @@ -115,7 +118,9 @@ function changebond!(site::Int, ::Val{:left}, ψ::AbstractFiniteMPS, H, alg::Ran

# select the dominant directions in the complement of the current state
g2 = adjoint(NL) * ac2 * adjoint(NR)
U, _, _ = svd_trunc!(normalize!(g2); trunc = alg.trscheme, alg = alg.alg_svd)
nrm = norm(g2)
nrm ≤ eps(real(scalartype(g2)))^(3 / 4) && return ψ
U, _, _ = svd_trunc!(scale!(g2, inv(nrm)); trunc = alg.trscheme, alg = alg.alg_svd)

# optimal vectors at site-1, zero weight at site
Q = NL * U
Expand Down
26 changes: 26 additions & 0 deletions test/algorithms/changebonds.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,32 @@ end
@test dim(left_virtualspace(state_tr, 5)) < dim(left_virtualspace(state_oe, 5))
end

# Regression: CBE expanders must not crash on bonds with no expansion content. A charged
# single-particle fermionic (fℤ₂-graded) state carries only one parity sector on the bonds away
# from the particle, so the projected two-site update `adjoint(NL) * AC2 * adjoint(NR)` is
# *structurally exactly zero* there. `OptimalExpand`/`RandExpand` used to `normalize!` that zero
# tensor, producing NaNs that crashed the SVD (`ArgumentError: invalid argument #4 to LAPACK
# call`); they must instead skip such bonds, leaving the state unchanged.
@testset "CBE zero-content bonds (graded)" begin
L = 6
H = kitaev_model(ComplexF64, Trivial; t = 1.0, mu = 0, Delta = 0, L = L)
P = physicalspace(H)[1]
S = typeof(P)
Vtriv = oneunit(P)
Vodd = S(c => dim(P, c) for c in sectors(P) if !isone(c))
# single particle created on the last site: interior bonds carry only the even sector, so
# the expansion content on them is exactly zero
state = FiniteMPS(
[isometry(ComplexF64, (k <= L ? Vtriv : Vodd) ⊗ P, (k < L ? Vtriv : Vodd)) for k in 1:L]
)

state_oe, _ = changebonds(state, H, OptimalExpand(; trscheme = truncrank(2)))
@test abs(dot(state, state_oe)) ≈ 1 atol = 1.0e-8

state_re = changebonds(state, RandExpand(; trscheme = truncrank(2)))
@test abs(dot(state, state_re)) ≈ 1 atol = 1.0e-8
end

# density-matrix-style MPS: each site carries two physical legs (ket ⊗ bra). The operator-free
# bond-change algorithms (`RandExpand` expansion, `SvdCut` truncation) must handle the extra
# physical leg. Operator-based expanders (`OptimalExpand`/`SketchedExpand`) are not covered here
Expand Down
Loading