Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct adjoint for truncated SVD #15

Merged
merged 29 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c390bec
Add truncated SVD adjoint with wrapper for KrylovKit iterative SVD, a…
pbrehmer Mar 4, 2024
51507ce
Use KrylovKit.linsolve for truncation linear problem, make loss funct…
pbrehmer Mar 5, 2024
d3a31fb
Improve loss function, compare SVD gradient with TensorKit.tsvd gradient
pbrehmer Mar 5, 2024
8ac307e
Update SVD adjoint linear problem to use Tuple and remove reshapes
pbrehmer Mar 26, 2024
80db1c0
Fix ZeroTangent case for linear problem
pbrehmer Apr 11, 2024
ae96298
Add SVD wrapper structs and function, utilize tsvd machinery, convert…
pbrehmer Jun 20, 2024
78ab152
Copy ctmrg.jl from master, add svdalg field to CTMRG, use svdwrap in …
pbrehmer Jun 20, 2024
dc6ad2b
Merge branch 'master' into svd_adjoint
lkdvos Jun 28, 2024
fc6600e
Use KrylovKit implementation of eigsolve instead of eigsolve.jl, dele…
pbrehmer Jul 4, 2024
823f52e
Add IterSVD _tsvd! method and adjoint using KrylovKit.svdsolve adjoint
pbrehmer Jul 5, 2024
a615b4a
Add PEPSKit.tsvd wrapper, fix IterSVD adjoint
pbrehmer Jul 5, 2024
a815e41
Add TensorKit compat entry for softened tsvd type restrictions
pbrehmer Jul 5, 2024
447bfd8
Add ProjectorAlg and refactor all tests and examples
pbrehmer Jul 5, 2024
bbd132d
Update MPSKit compat
lkdvos Jul 5, 2024
0c03cd4
Replace tsvd with tsvd!, add views to IterSVD adjoint
pbrehmer Jul 8, 2024
14f0065
Improve IterSVD allocation, implement CTMRG convenience constructor, …
pbrehmer Jul 8, 2024
9b2c4b7
Fix tests
pbrehmer Jul 8, 2024
0c13d47
Add block-wise dense fallback option
pbrehmer Jul 8, 2024
538652d
Add SVDrrule wrapper, add separate adjoint structs and rrules, update…
pbrehmer Jul 8, 2024
7032ed0
Add IterSVD test for symmetric tensor with fallback
pbrehmer Jul 9, 2024
66a827e
Merge branch 'master' into svd_adjoint
pbrehmer Jul 9, 2024
d09561f
Formatting
pbrehmer Jul 9, 2024
b3a0726
Fix missing cnext in ctmrg, update README example
pbrehmer Jul 9, 2024
89ae0a4
Rename DenseSVDAdjoint, update svd_wrapper test
pbrehmer Jul 9, 2024
25d198c
Make CRCExt extension backwards compatible with v1.8
pbrehmer Jul 9, 2024
6b818e7
Replace SVDrrule with SVDAdjoint, clean up adjoint algorithms
pbrehmer Jul 9, 2024
bb96664
Small cleanup
lkdvos Jul 9, 2024
fa7a56a
Update minimal julia version 1.9
lkdvos Jul 10, 2024
6df8efc
Remove duplicate line in left_move
pbrehmer Jul 10, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
fail-fast: false
matrix:
version:
- '1.8' # LTS version
- '1.9'
- '1' # automatically expands to the latest stable 1.x release of Julia
os:
- ubuntu-latest
Expand Down
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
Accessors = "0.1"
ChainRulesCore = "1.0"
Compat = "3.46, 4.2"
KrylovKit = "0.6, 0.7, 0.8"
KrylovKit = "0.8"
LinearAlgebra = "1"
MPSKit = "0.11"
OptimKit = "0.3"
Printf = "1"
Random = "1"
Statistics = "1"
TensorKit = "0.12.4"
TensorKit = "0.12.5"
TensorOperations = "4"
VectorInterface = "0.4"
Zygote = "0.6"
julia = "1.8"
julia = "1.9"

[extras]
ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a"
Expand Down
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,12 @@ For example, in order to obtain the groundstate of the 2D Heisenberg model, we c
using TensorKit, PEPSKit, KrylovKit, OptimKit

# constructing the Hamiltonian:
Jx, Jy, Jz = (-1, 1, -1) # sublattice rotation to obtain single-site unit cell
physical_space = ComplexSpace(2)
T = ComplexF64
σx = TensorMap(T[0 1; 1 0], physical_space, physical_space)
σy = TensorMap(T[0 im; -im 0], physical_space, physical_space)
σz = TensorMap(T[1 0; 0 -1], physical_space, physical_space)
H = (Jx * σx ⊗ σx) + (Jy * σy ⊗ σy) + (Jz * σz ⊗ σz)
Heisenberg_hamiltonian = NLocalOperator{NearestNeighbor}(H / 4)
H = square_lattice_heisenberg(; Jx=-1, Jy=1, Jz=-1) # sublattice rotation to obtain single-site unit cell

# configuring the parameters
D = 2
chi = 20
ctm_alg = CTMRG(; trscheme = truncdim(chi), tol=1e-20, miniter=4, maxiter=100, verbosity=1)
ctm_alg = CTMRG(; tol=1e-10, miniter=4, maxiter=100, verbosity=1, trscheme=truncdim(chi))
opt_alg = PEPSOptimize(;
boundary_alg=ctm_alg,
optimizer=LBFGS(4; maxiter=100, gradtol=1e-4, verbosity=2),
Expand All @@ -60,7 +53,7 @@ opt_alg = PEPSOptimize(;
# ground state search
state = InfinitePEPS(2, D)
ctm = leading_boundary(CTMRGEnv(state; Venv=ComplexSpace(chi)), state, ctm_alg)
result = fixedpoint(state, Heisenberg_hamiltonian, opt_alg, ctm)
result = fixedpoint(state, H, opt_alg, ctm)

@show result.E # -0.6625...
```
Expand Down
13 changes: 3 additions & 10 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,12 @@ For example, in order to obtain the groundstate of the 2D Heisenberg model, we c
using TensorKit, PEPSKit, KrylovKit, OptimKit

# constructing the Hamiltonian:
Jx, Jy, Jz = (-1, 1, -1) # sublattice rotation to obtain single-site unit cell
physical_space = ComplexSpace(2)
T = ComplexF64
σx = TensorMap(T[0 1; 1 0], physical_space, physical_space)
σy = TensorMap(T[0 im; -im 0], physical_space, physical_space)
σz = TensorMap(T[1 0; 0 -1], physical_space, physical_space)
H = (Jx * σx ⊗ σx) + (Jy * σy ⊗ σy) + (Jz * σz ⊗ σz)
Heisenberg_hamiltonian = NLocalOperator{NearestNeighbor}(H / 4)
H = square_lattice_heisenberg(; Jx=-1, Jy=1, Jz=-1) # sublattice rotation to obtain single-site unit cell

# configuring the parameters
D = 2
chi = 20
ctm_alg = CTMRG(; trscheme = truncdim(chi), tol=1e-20, miniter=4, maxiter=100, verbosity=1)
ctm_alg = CTMRG(; tol=1e-10, miniter=4, maxiter=100, verbosity=1, trscheme=truncdim(chi))
opt_alg = PEPSOptimize(;
boundary_alg=ctm_alg,
optimizer=LBFGS(4; maxiter=100, gradtol=1e-4, verbosity=2),
Expand All @@ -45,7 +38,7 @@ opt_alg = PEPSOptimize(;
# ground state search
state = InfinitePEPS(2, D)
ctm = leading_boundary(CTMRGEnv(state; Venv=ComplexSpace(chi)), state, ctm_alg)
result = fixedpoint(state, Heisenberg_hamiltonian, opt_alg, ctm)
result = fixedpoint(state, H, opt_alg, ctm)

@show result.E # -0.6625...
```
8 changes: 2 additions & 6 deletions examples/boundary_mps.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ mps, envs, ϵ = leading_boundary(mps, T, VUMPS())
N = abs(prod(expectation_value(mps, T)))

# This can be compared to the result obtained using the CTMRG algorithm
ctm = leading_boundary(
peps, CTMRG(; verbosity=1, fixedspace=true), CTMRGEnv(peps; Venv=ComplexSpace(20))
)
ctm = leading_boundary(peps, CTMRG(; verbosity=1), CTMRGEnv(peps; Venv=ComplexSpace(20)))
N´ = abs(norm(peps, ctm))

@show abs(N - N´) / N
Expand All @@ -55,9 +53,7 @@ mps2 = PEPSKit.initializeMPS(T2, fill(ComplexSpace(20), 2, 2))
mps2, envs2, ϵ = leading_boundary(mps2, T2, VUMPS())
N2 = abs(prod(expectation_value(mps2, T2)))

ctm2 = leading_boundary(
peps2, CTMRG(; verbosity=1, fixedspace=true), CTMRGEnv(peps2; Venv=ComplexSpace(20))
)
ctm2 = leading_boundary(peps2, CTMRG(; verbosity=1), CTMRGEnv(peps2; Venv=ComplexSpace(20)))
N2´ = abs(norm(peps2, ctm2))

@show abs(N2 - N2´) / N2
Expand Down
10 changes: 5 additions & 5 deletions examples/heisenberg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ H = square_lattice_heisenberg(; Jx=-1, Jy=1, Jz=-1)
# Parameters
χbond = 2
χenv = 20
ctmalg = CTMRG(; trscheme=truncdim(χenv), tol=1e-10, miniter=4, maxiter=100, verbosity=1)
alg = PEPSOptimize(;
boundary_alg=ctmalg,
ctm_alg = CTMRG(; tol=1e-10, miniter=4, maxiter=100, verbosity=1, trscheme=truncdim(χenv))
opt_alg = PEPSOptimize(;
boundary_alg=ctm_alg,
optimizer=LBFGS(4; maxiter=100, gradtol=1e-4, verbosity=2),
gradient_alg=GMRES(; tol=1e-6, maxiter=100),
reuse_env=true,
Expand All @@ -27,6 +27,6 @@ alg = PEPSOptimize(;
# E/N = −0.6694421, which is a QMC estimate from https://arxiv.org/abs/1101.3281.
# Of course there is a noticable bias for small χbond and χenv.
ψ₀ = InfinitePEPS(2, χbond)
env₀ = leading_boundary(CTMRGEnv(ψ₀; Venv=ℂ^χenv), ψ₀, ctmalg)
result = fixedpoint(ψ₀, H, alg, env₀)
env₀ = leading_boundary(CTMRGEnv(ψ₀; Venv=ℂ^χenv), ψ₀, ctm_alg)
result = fixedpoint(ψ₀, H, opt_alg, env₀)
@show result.E
6 changes: 4 additions & 2 deletions src/PEPSKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ using TensorKit, KrylovKit, MPSKit, OptimKit, TensorOperations
using ChainRulesCore, Zygote

include("utility/util.jl")
include("utility/eigsolve.jl")
include("utility/svd.jl")
include("utility/rotations.jl")
include("utility/hook_pullback.jl")
include("utility/autoopt.jl")
Expand Down Expand Up @@ -59,12 +59,14 @@ module Defaults
const fpgrad_tol = 1e-6
end

export CTMRG, CTMRGEnv
export SVDAdjoint, IterSVD, NonTruncSVDAdjoint
export FixedSpaceTruncation, ProjectorAlg, CTMRG, CTMRGEnv
export LocalOperator
export expectation_value, costfun
export leading_boundary
export PEPSOptimize, GeomSum, ManualIter, LinSolve
export fixedpoint

export InfinitePEPS, InfiniteTransferPEPS
export InfinitePEPO, InfiniteTransferPEPO
export initializeMPS, initializePEPS
Expand Down
84 changes: 60 additions & 24 deletions src/algorithms/ctmrg.jl
Original file line number Diff line number Diff line change
@@ -1,24 +1,60 @@
"""
FixedSpaceTruncation <: TensorKit.TruncationScheme

CTMRG specific truncation scheme for `tsvd` which keeps the bond space on which the SVD
is performed fixed. Since different environment directions and unit cell entries might
have different spaces, this truncation style is different from `TruncationSpace`.
"""
struct FixedSpaceTruncation <: TensorKit.TruncationScheme end

# TODO: add option for different projector styles (half-infinite, full-infinite, etc.)
"""
struct ProjectorAlg{S}(; svd_alg = TensorKit.SVD(), trscheme = TensorKit.notrunc(),
fixedspace = false, verbosity = 0)

Algorithm struct collecting all projector related parameters. The truncation scheme has to be
a `TensorKit.TruncationScheme`, and some SVD algorithms might have further restrictions on what
kind of truncation scheme can be used. If `fixedspace` is true, the truncation scheme is set to
`truncspace(V)` where `V` is the environment bond space, adjusted to the corresponding
environment direction/unit cell entry.
"""
@kwdef struct ProjectorAlg{S<:SVDAdjoint,T}
svd_alg::S = SVDAdjoint()
trscheme::T = FixedSpaceTruncation()
verbosity::Int = 0
end

# TODO: add abstract Algorithm type?
"""
struct CTMRG(; trscheme = TensorKit.notrunc(), tol = Defaults.ctmrg_tol,
maxiter = Defaults.ctmrg_maxiter, miniter = Defaults.ctmrg_miniter,
verbosity = 0, fixedspace = false)
CTMRG(; tol=Defaults.ctmrg_tol, maxiter=Defaults.ctmrg_maxiter,
miniter=Defaults.ctmrg_miniter, verbosity=0,
svd_alg=TensorKit.SVD(), trscheme=FixedSpaceTruncation())

Algorithm struct that represents the CTMRG algorithm for contracting infinite PEPS.
The projector bond dimensions are set via `trscheme` which controls the truncation
properties inside of `TensorKit.tsvd`. Each CTMRG run is converged up to `tol`
where the singular value convergence of the corners as well as the norm is checked.
The maximal and minimal number of CTMRG iterations is set with `maxiter` and `miniter`.
Different levels of output information are printed depending on `verbosity` (0, 1 or 2).
Regardless of the truncation scheme, the space can be kept fixed with `fixedspace`.
Each CTMRG run is converged up to `tol` where the singular value convergence of the
corners as well as the norm is checked. The maximal and minimal number of CTMRG iterations
is set with `maxiter` and `miniter`. Different levels of output information are printed
depending on `verbosity` (0, 1 or 2). The projectors are computed from `svd_alg` SVDs
where the truncation scheme is set via `trscheme`.
"""
@kwdef struct CTMRG
trscheme::TruncationScheme = TensorKit.notrunc()
tol::Float64 = Defaults.ctmrg_tol
maxiter::Int = Defaults.ctmrg_maxiter
miniter::Int = Defaults.ctmrg_miniter
verbosity::Int = 0
fixedspace::Bool = false
struct CTMRG
tol::Float64
maxiter::Int
miniter::Int
verbosity::Int
projector_alg::ProjectorAlg
end
function CTMRG(;
tol=Defaults.ctmrg_tol,
maxiter=Defaults.ctmrg_maxiter,
miniter=Defaults.ctmrg_miniter,
verbosity=0,
svd_alg=SVDAdjoint(),
trscheme=FixedSpaceTruncation(),
)
return CTMRG(
tol, maxiter, miniter, verbosity, ProjectorAlg(; svd_alg, trscheme, verbosity)
)
end

"""
Expand All @@ -39,6 +75,7 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRG)

for i in 1:(alg.maxiter)
env, ϵ = ctmrg_iter(state, env, alg) # Grow and renormalize in all 4 directions

conv_condition, normold, CSold, TSold, ϵ = ignore_derivatives() do
# Compute convergence criteria and take max (TODO: How should we handle logging all of this?)
Δϵ = abs((ϵold - ϵ) / ϵold)
Expand Down Expand Up @@ -86,9 +123,7 @@ function MPSKit.leading_boundary(envinit, state, alg::CTMRG)
end

# Do one final iteration that does not change the spaces
alg_fixed = CTMRG(;
alg.trscheme, alg.tol, alg.maxiter, alg.miniter, alg.verbosity, fixedspace=true
)
alg_fixed = @set alg.projector_alg.trscheme = FixedSpaceTruncation()
env′, = ctmrg_iter(state, env, alg_fixed)
envfix = gauge_fix(env, env′)
check_elementwise_convergence(env, envfix; atol=alg.tol^(1 / 2)) ||
Expand Down Expand Up @@ -288,7 +323,7 @@ function ctmrg_iter(state, env::CTMRGEnv{C,T}, alg::CTMRG) where {C,T}
ϵ = 0.0

for _ in 1:4
env, _, _, ϵ₀ = left_move(state, env, alg)
env, _, _, ϵ₀ = left_move(state, env, alg.projector_alg)
state = rotate_north(state, EAST)
env = rotate_north(env, EAST)
ϵ = max(ϵ, ϵ₀)
Expand All @@ -303,14 +338,15 @@ end
Grow, project and renormalize the environment `env` in west direction.
Return the updated environment as well as the projectors and truncation error.
"""
function left_move(state, env::CTMRGEnv{C,T}, alg::CTMRG) where {C,T}
function left_move(state, env::CTMRGEnv{C,T}, alg::ProjectorAlg) where {C,T}
corners::typeof(env.corners) = copy(env.corners)
edges::typeof(env.edges) = copy(env.edges)
ϵ = 0.0
Pleft, Pright = Zygote.Buffer.(projector_type(T, size(state))) # Use Zygote.Buffer instead of @diffset to avoid ZeroTangent errors in _setindex

for col in 1:size(state, 2)
cprev = _prev(col, size(state, 2))
cnext = _next(col, size(state, 2))

# Compute projectors
for row in 1:size(state, 1)
Expand All @@ -332,15 +368,15 @@ function left_move(state, env::CTMRGEnv{C,T}, alg::CTMRG) where {C,T}
)

# SVD half-infinite environment
trscheme = if alg.fixedspace == true
truncspace(space(env.edges[WEST, row, col], 1))
trscheme = if alg.trscheme isa FixedSpaceTruncation
truncspace(space(env.edges[WEST, row, cnext], 1))
else
alg.trscheme
end
@autoopt @tensor QQ[χ_EB D_EBabove D_EBbelow; χ_ET D_ETabove D_ETbelow] :=
Q_sw[χ_EB D_EBabove D_EBbelow; χ D1 D2] *
Q_nw[χ D1 D2; χ_ET D_ETabove D_ETbelow]
U, S, V, ϵ_local = tsvd!(QQ; trunc=trscheme, alg=TensorKit.SVD())
U, S, V, ϵ_local = PEPSKit.tsvd!(QQ, alg.svd_alg; trunc=trscheme)
ϵ = max(ϵ, ϵ_local / norm(S))
# TODO: check if we can just normalize enlarged corners s.t. trunc behaves a bit better

Expand Down
Loading
Loading