Skip to content

Commit a6ed6d9

Browse files
authored
Bring back InfiniteSum{ITensor} for 2-local Hamiltonians (#55)
1 parent 7d7b745 commit a6ed6d9

File tree

11 files changed

+870
-607
lines changed

11 files changed

+870
-607
lines changed

examples/vumps/vumps_hubbard_extended.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ maxdim = 30 # Maximum bond dimension
99
cutoff = 1e-6 # Singular value cutoff when increasing the bond dimension
1010
max_vumps_iters = 200 # Maximum number of iterations of the VUMPS algorithm at each bond dimension
1111
outer_iters = 5 # Number of times to increase the bond dimension
12+
localham_type = ITensor
1213

1314
model_params = (t=1.0, U=10.0, V=0.0)
1415

@@ -36,7 +37,7 @@ model = Model"hubbard"()
3637
@show model, model_params
3738

3839
# Form the Hamiltonian
39-
H = InfiniteSum{MPO}(model, s; model_params...)
40+
H = InfiniteSum{localham_type}(model, s; model_params...)
4041

4142
# Check translational invariance
4243
println("\nCheck translational invariance of initial infinite MPS")
@@ -58,7 +59,7 @@ for _ in 1:outer_iters
5859
println("\nIncrease bond dimension")
5960
global ψ = subspace_expansion(ψ, H; subspace_expansion_kwargs...)
6061
println("Run VUMPS with new bond dimension")
61-
global ψ = vumps(H, ψ; vumps_kwargs...)
62+
global ψ = @time vumps(H, ψ; vumps_kwargs...)
6263
end
6364

6465
# Check translational invariance

examples/vumps/vumps_ising.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ solver_tol = (x -> x / 100) # Tolerance for the local solver (eigsolve in VUMPS
1515
multisite_update_alg = "parallel" # Choose between ["sequential", "parallel"]. Only parallel works with TDVP.
1616
conserve_qns = true # Whether or not to conserve spin parity
1717
nsite = 2 # Number of sites in the unit cell
18+
localham_type = MPO # Can choose `ITensor` or `MPO`
1819

1920
# Parameters of the transverse field Ising model
2021
model_params = (J=1.0, h=0.9)
@@ -42,7 +43,7 @@ initstate(n) = "↑"
4243
ψ = InfMPS(s, initstate)
4344

4445
# Form the Hamiltonian
45-
H = InfiniteSum{MPO}(model, s; model_params...)
46+
H = InfiniteSum{localham_type}(model, s; model_params...)
4647

4748
# Check translational invariance
4849
@show norm(contract.AL[1:nsite]..., ψ.C[nsite]) - contract.C[0], ψ.AR[1:nsite]...))
@@ -81,11 +82,13 @@ setcutoff!(sweeps, cutoff)
8182
energy_finite_total, ψ_finite = @time dmrg(H_finite, ψ_finite, sweeps)
8283
@show energy_finite_total / nsite_finite
8384

84-
function energy_local(ψ1, ψ2, h)
85+
function energy_local(ψ1, ψ2, h::ITensor)
8586
ϕ = ψ1 * ψ2
86-
return (noprime* prod(h)) * dag(ϕ))[]
87+
return (noprime* h) * dag(ϕ))[]
8788
end
8889

90+
energy_local(ψ1, ψ2, h::MPO) = energy_local(ψ1, ψ2, prod(h))
91+
8992
function ITensors.expect(ψ, o)
9093
return (noprime* op(o, filterinds(ψ, "Site")...)) * dag(ψ))[]
9194
end

src/ITensorInfiniteMPS.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ include("orthogonalize.jl")
4444
include("infinitemps_approx.jl")
4545
include("nullspace.jl")
4646
include("subspace_expansion.jl")
47+
include("vumps_generic.jl")
4748
include("vumps_localham.jl")
49+
include("vumps_nonlocalham.jl")
4850
include("vumps_mpo.jl")
4951

5052
export Cell,

src/ITensors.jl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,6 @@ ITensors.noncommoninds(is::IndexSet) = is
133133

134134
using ITensors.NDTensors
135135

136-
function LinearAlgebra.ishermitian(T::ITensor, pairs=0 => 1; kwargs...)
137-
Tᴴ = swapprime(dag(T), pairs)
138-
return isapprox(Tᴴ, T; kwargs...)
139-
end
140-
141136
# Helpful for making sure the ITensor doesn't contract
142137
ITensors.sim(A::ITensor) = ITensors.setinds(A, sim(inds(A)))
143138

src/models/hubbard.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ function ITensors.MPO(::Model"hubbard", s; t, U, V)
3434
return MPO(opsum, s)
3535
end
3636

37+
function ITensors.ITensor(model::Model"hubbard", s; kwargs...)
38+
return prod(MPO(model, s; kwargs...))
39+
end
40+
3741
"""
3842
@article{PhysRevB.6.930,
3943
title = {Magnetic Susceptibility at Zero Temperature for the One-Dimensional Hubbard Model},

src/subspace_expansion.jl

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ function replaceind_indval(IV::Tuple, iĩ::Pair)
33
return ntuple(n -> first(IV[n]) == i ?=> last(IV[n]) : IV[n], length(IV))
44
end
55

6-
#TODO implement the nullspace generation for InfiniteSum{ITensor}?
7-
86
function generate_twobody_nullspace(
97
ψ::InfiniteCanonicalMPS, H::InfiniteSum{MPO}, b::Tuple{Int,Int}; atol=1e-2
108
)
@@ -93,6 +91,28 @@ function generate_twobody_nullspace(
9391
return ψH2
9492
end
9593

94+
function generate_twobody_nullspace(
95+
ψ::InfiniteCanonicalMPS, H::InfiniteSum{ITensor}, b::Tuple{Int,Int}; atol=1e-2
96+
)
97+
n1, n2 = b
98+
lⁿ¹ = commoninds.AL[n1], ψ.C[n1])
99+
rⁿ¹ = commoninds.AR[n2], ψ.C[n1])
100+
l = linkinds(only, ψ.AL)
101+
r = linkinds(only, ψ.AR)
102+
s = siteinds(only, ψ)
103+
δʳ(n) = δ(dag(r[n]), prime(r[n]))
104+
δˢ(n) = δ(dag(s[n]), prime(s[n]))
105+
δˡ(n) = δ(l[n], dag(prime(l[n])))
106+
107+
range_H = nrange(H, 1)
108+
@assert range_H == 2 "Subspace expansion for InfiniteSum{ITensor} currently only works for 2-local Hamiltonians"
109+
110+
if range_H == 2
111+
ψH2 = noprime.AL[n1] * H[n1] * ψ.C[n1] * ψ.AR[n2])
112+
end
113+
return ψH2
114+
end
115+
96116
function generate_twobody_nullspace(
97117
ψ::InfiniteCanonicalMPS, H::InfiniteMPOMatrix, b::Tuple{Int,Int}; atol=1e-2
98118
)

src/vumps_generic.jl

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
struct Hᶜ{T}
2+
∑h::InfiniteSum{T}
3+
Hᴸ::InfiniteMPS
4+
Hᴿ::InfiniteMPS
5+
ψ::InfiniteCanonicalMPS
6+
n::Int
7+
end
8+
9+
struct Hᴬᶜ{T}
10+
∑h::InfiniteSum{T}
11+
Hᴸ::InfiniteMPS
12+
Hᴿ::InfiniteMPS
13+
ψ::InfiniteCanonicalMPS
14+
n::Int
15+
end
16+
17+
function (H::Hᶜ)(v)
18+
Hᶜv = H * v
19+
## return Hᶜv * δˡ * δʳ
20+
return noprime(Hᶜv)
21+
end
22+
23+
function (H::Hᴬᶜ)(v)
24+
Hᴬᶜv = H * v
25+
## return Hᶜv * δˡ⁻¹ * δˢ * δʳ
26+
return noprime(Hᴬᶜv)
27+
end
28+
29+
# Struct for use in linear system solver
30+
struct Aᴸ
31+
ψ::InfiniteCanonicalMPS
32+
n::Int
33+
end
34+
35+
function (A::Aᴸ)(x)
36+
ψ = A.ψ
37+
ψᴴ = dag(ψ)
38+
ψ′ = ψᴴ'
39+
ψ̃ = prime(linkinds, ψᴴ)
40+
n = A.n
41+
42+
N = length(ψ)
43+
#@assert n == N
44+
45+
l = linkinds(only, ψ.AL)
46+
l′ = linkinds(only, ψ′.AL)
47+
r = linkinds(only, ψ.AR)
48+
r′ = linkinds(only, ψ′.AR)
49+
50+
xT = translatecell(x, -1)
51+
for k in (n - N + 1):n
52+
xT = xT * ψ.AL[k] * ψ̃.AL[k]
53+
end
54+
δˡ = δ(l[n], l′[n])
55+
δʳ = δ(r[n], r′[n])
56+
xR = x * ψ.C[n] * ψ′.C[n] * dag(δʳ) * denseblocks(δˡ)
57+
return xT - xR
58+
end
59+
60+
function left_environment(hᴸ, 𝕙ᴸ, ψ; tol=1e-15)
61+
ψ̃ = prime(linkinds, dag(ψ))
62+
N = nsites(ψ)
63+
64+
Aᴺ = Aᴸ(ψ, N)
65+
Hᴸᴺ¹, info = linsolve(Aᴺ, 𝕙ᴸ[N], 1, -1; tol=tol)
66+
# Get the rest of the environments in the unit cell
67+
Hᴸ = InfiniteMPS(Vector{ITensor}(undef, N))
68+
Hᴸ[N] = Hᴸᴺ¹
69+
for n in 1:(N - 1)
70+
Hᴸ[n] = Hᴸ[n - 1] * ψ.AL[n] * ψ̃.AL[n] + hᴸ[n]
71+
end
72+
return Hᴸ
73+
end
74+
75+
# Struct for use in linear system solver
76+
struct Aᴿ
77+
hᴿ::InfiniteMPS
78+
ψ::InfiniteCanonicalMPS
79+
n::Int
80+
end
81+
82+
function (A::Aᴿ)(x)
83+
hᴿ = A.hᴿ
84+
ψ = A.ψ
85+
ψᴴ = dag(ψ)
86+
ψ′ = ψᴴ'
87+
ψ̃ = prime(linkinds, ψᴴ)
88+
n = A.n
89+
90+
N = length(ψ)
91+
@assert n == N
92+
93+
l = linkinds(only, ψ.AL)
94+
l′ = linkinds(only, ψ′.AL)
95+
r = linkinds(only, ψ.AR)
96+
r′ = linkinds(only, ψ′.AR)
97+
98+
xT = x
99+
for k in reverse(1:N)
100+
xT = xT * ψ.AR[k] * ψ̃.AR[k]
101+
end
102+
xT = translatecell(xT, 1)
103+
δˡ = δ(l[n], l′[n])
104+
δʳ = δ(r[n], r′[n])
105+
xR = x * ψ.C[n] * ψ′.C[n] * δˡ * denseblocks(dag(δʳ))
106+
return xT - xR
107+
end
108+
109+
function right_environment(hᴿ, ψ; tol=1e-15)
110+
ψ̃ = prime(linkinds, dag(ψ))
111+
# XXX: replace with `nsites`
112+
#N = nsites(ψ)
113+
N = length(ψ)
114+
115+
A = Aᴿ(hᴿ, ψ, N)
116+
Hᴿᴺ¹, info = linsolve(A, hᴿ[N], 1, -1; tol=tol)
117+
# Get the rest of the environments in the unit cell
118+
Hᴿ = InfiniteMPS(Vector{ITensor}(undef, N))
119+
Hᴿ[N] = Hᴿᴺ¹
120+
for n in reverse(1:(N - 1))
121+
Hᴿ[n] = Hᴿ[n + 1] * ψ.AR[n + 1] * ψ̃.AR[n + 1] + hᴿ[n]
122+
end
123+
return Hᴿ
124+
end
125+
126+
# TODO Generate all environments, why? Only one is needed in the sequential version
127+
function right_environment(hᴿ, 𝕙ᴿ, ψ; tol=1e-15)
128+
ψ̃ = prime(linkinds, dag(ψ))
129+
N = nsites(ψ)
130+
131+
A = Aᴿ(hᴿ, ψ, N)
132+
Hᴿᴺ¹, info = linsolve(A, 𝕙ᴿ[N], 1, -1; tol=tol)
133+
# Get the rest of the environments in the unit cell
134+
Hᴿ = InfiniteMPS(Vector{ITensor}(undef, N))
135+
Hᴿ[N] = Hᴿᴺ¹
136+
for n in reverse(1:(N - 1))
137+
Hᴿ[n] = Hᴿ[n + 1] * ψ.AR[n + 1] * ψ̃.AR[n + 1] + hᴿ[n]
138+
end
139+
return Hᴿ
140+
end
141+
142+
function tdvp_iteration(args...; multisite_update_alg="sequential", kwargs...)
143+
if multisite_update_alg == "sequential"
144+
return tdvp_iteration_sequential(args...; kwargs...)
145+
elseif multisite_update_alg == "parallel"
146+
return tdvp_iteration_parallel(args...; kwargs...)
147+
else
148+
error(
149+
"Multisite update algorithm multisite_update_alg = $multisite_update_alg not supported, use \"parallel\" or \"sequential\"",
150+
)
151+
end
152+
end
153+
154+
function tdvp(
155+
solver::Function,
156+
∑h,
157+
ψ;
158+
maxiter=10,
159+
tol=1e-8,
160+
outputlevel=1,
161+
multisite_update_alg="sequential",
162+
time_step,
163+
solver_tol=(x -> x / 100),
164+
)
165+
N = nsites(ψ)
166+
(ϵᴸ!) = fill(tol, nsites(ψ))
167+
(ϵᴿ!) = fill(tol, nsites(ψ))
168+
if outputlevel > 0
169+
println("Running VUMPS with multisite_update_alg = $multisite_update_alg")
170+
flush(stdout)
171+
flush(stderr)
172+
end
173+
for iter in 1:maxiter
174+
ψ, (eᴸ, eᴿ) = tdvp_iteration(
175+
solver,
176+
∑h,
177+
ψ;
178+
(ϵᴸ!)=(ϵᴸ!),
179+
(ϵᴿ!)=(ϵᴿ!),
180+
multisite_update_alg=multisite_update_alg,
181+
solver_tol=solver_tol,
182+
time_step=time_step,
183+
)
184+
ϵᵖʳᵉˢ = max(maximum(ϵᴸ!), maximum(ϵᴿ!))
185+
maxdimψ = maxlinkdim(ψ[0:(N + 1)])
186+
if outputlevel > 0
187+
println(
188+
"VUMPS iteration $iter (out of maximum $maxiter). Bond dimension = $maxdimψ, energy = $((eᴸ, eᴿ)), ϵᵖʳᵉˢ = $ϵᵖʳᵉˢ, tol = $tol",
189+
)
190+
flush(stdout)
191+
flush(stderr)
192+
end
193+
if ϵᵖʳᵉˢ < tol
194+
println(
195+
"Precision error $ϵᵖʳᵉˢ reached tolerance $tol, stopping VUMPS after $iter iterations (of a maximum $maxiter).",
196+
)
197+
flush(stdout)
198+
flush(stderr)
199+
break
200+
end
201+
end
202+
return ψ
203+
end
204+
205+
function vumps_solver(M, time_step, v₀, solver_tol)
206+
λ⃗, v⃗, info = eigsolve(M, v₀, 1, :SR; ishermitian=true, tol=solver_tol)
207+
return λ⃗[1], v⃗[1], info
208+
end
209+
210+
return function tdvp_solver(M, time_step, v₀, solver_tol)
211+
v, info = exponentiate(M, time_step, v₀; ishermitian=true, tol=solver_tol)
212+
v = v / norm(v)
213+
return nothing, v, info
214+
end
215+
216+
function vumps(
217+
args...; time_step=-Inf, eigsolve_tol=(x -> x / 100), solver_tol=eigsolve_tol, kwargs...
218+
)
219+
@assert isinf(time_step) && time_step < 0
220+
println("Using VUMPS solver with time step $time_step")
221+
flush(stdout)
222+
flush(stderr)
223+
return tdvp(vumps_solver, args...; time_step=time_step, solver_tol=solver_tol, kwargs...)
224+
end
225+
226+
function tdvp(args...; time_step, solver_tol=(x -> x / 100), kwargs...)
227+
solver = if !isinf(time_step)
228+
println("Using TDVP solver with time step $time_step")
229+
flush(stdout)
230+
flush(stderr)
231+
tdvp_solver
232+
elseif time_step < 0
233+
# Call VUMPS instead
234+
println("Using VUMPS solver with time step $time_step")
235+
flush(stdout)
236+
flush(stderr)
237+
vumps_solver
238+
else
239+
error("Time step $time_step not supported.")
240+
end
241+
return tdvp(solver, args...; time_step=time_step, solver_tol=solver_tol, kwargs...)
242+
end

0 commit comments

Comments
 (0)