Skip to content

Commit 898de3a

Browse files
authored
Refactor of IDMRG with IterativeSolver (#348)
* refactor IDMRG by following the IterativeSolver interface and by sharing code between IDMRG and IDMRG2 * fix format * Fix energy definition for IDMRG2 * fix energy of IDMRG2 * make energy in IDMRGState type stable and implement suggestion by Lukas * format * apply Lukas comments
1 parent c680a1c commit 898de3a

File tree

1 file changed

+187
-174
lines changed

1 file changed

+187
-174
lines changed

src/algorithms/groundstate/idmrg.jl

Lines changed: 187 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -24,81 +24,6 @@ $(TYPEDFIELDS)
2424
alg_eigsolve::A = Defaults.alg_eigsolve()
2525
end
2626

27-
function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG, envs = environments(ost, H))
28-
ϵ::Float64 = calc_galerkin(ost, H, ost, envs)
29-
ψ = copy(ost)
30-
log = IterLog("IDMRG")
31-
local iter, E_current
32-
33-
LoggingExtras.withlevel(; alg.verbosity) do
34-
@infov 2 begin
35-
E_current = expectation_value(ψ, H, envs)
36-
loginit!(log, ϵ, E_current)
37-
end
38-
for outer iter in 1:(alg.maxiter)
39-
alg_eigsolve = updatetol(alg.alg_eigsolve, iter, ϵ)
40-
C_current = ψ.C[0]
41-
42-
# left to right sweep
43-
for pos in 1:length(ψ)
44-
h = AC_hamiltonian(pos, ψ, H, ψ, envs)
45-
_, ψ.AC[pos] = fixedpoint(h, ψ.AC[pos], :SR, alg_eigsolve)
46-
if pos == length(ψ)
47-
# AC needed in next sweep
48-
ψ.AL[pos], ψ.C[pos] = left_orth.AC[pos])
49-
else
50-
ψ.AL[pos], ψ.C[pos] = left_orth!.AC[pos])
51-
end
52-
transfer_leftenv!(envs, ψ, H, ψ, pos + 1)
53-
end
54-
55-
# right to left sweep
56-
for pos in length(ψ):-1:1
57-
h = AC_hamiltonian(pos, ψ, H, ψ, envs)
58-
_, ψ.AC[pos] = fixedpoint(h, ψ.AC[pos], :SR, alg_eigsolve)
59-
60-
ψ.C[pos - 1], temp = right_orth!(_transpose_tail.AC[pos]; copy = (pos == 1)))
61-
ψ.AR[pos] = _transpose_front(temp)
62-
63-
transfer_rightenv!(envs, ψ, H, ψ, pos - 1)
64-
end
65-
66-
ϵ = norm(C_current - ψ.C[0])
67-
68-
if ϵ < alg.tol
69-
@infov 2 begin
70-
E_next = expectation_value(ψ, H, envs)
71-
ΔE = E_next - E_current
72-
E_current = E_next
73-
logfinish!(log, iter, ϵ, ΔE)
74-
end
75-
break
76-
end
77-
if iter == alg.maxiter
78-
@warnv 1 begin
79-
E_next = expectation_value(ψ, H, envs)
80-
ΔE = E_next - E_current
81-
E_current = E_next
82-
logcancel!(log, iter, ϵ, ΔE)
83-
end
84-
else
85-
@infov 3 begin
86-
E_next = expectation_value(ψ, H, envs)
87-
ΔE = E_next - E_current
88-
E_current = E_next
89-
logiter!(log, iter, ϵ, ΔE)
90-
end
91-
end
92-
end
93-
end
94-
95-
alg_gauge = updatetol(alg.alg_gauge, iter, ϵ)
96-
ψ′ = InfiniteMPS.AR[1:end]; alg_gauge.tol, alg_gauge.maxiter)
97-
recalculate!(envs, ψ′, H, ψ′)
98-
99-
return ψ′, envs, ϵ
100-
end
101-
10227
"""
10328
$(TYPEDEF)
10429
@@ -131,121 +56,209 @@ $(TYPEDFIELDS)
13156
trscheme::TruncationStrategy
13257
end
13358

134-
function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG2, envs = environments(ost, H))
135-
length(ost) < 2 && throw(ArgumentError("unit cell should be >= 2"))
136-
ϵ::Float64 = calc_galerkin(ost, H, ost, envs)
13759

138-
ψ = copy(ost)
139-
log = IterLog("IDMRG2")
140-
local iter
60+
# Internal state of the IDMRG algorithm
61+
struct IDMRGState{S, O, E, T}
62+
mps::S
63+
operator::O
64+
envs::E
65+
iter::Int
66+
ϵ::Float64 # TODO: Could be any <:Real
67+
energy::T
68+
end
69+
function IDMRGState{T}(mps::S, operator::O, envs::E, iter::Int, ϵ::Float64, energy) where {S, O, E, T}
70+
return IDMRGState{S, O, E, T}(mps, operator, envs, iter, ϵ, T(energy))
71+
end
72+
73+
function find_groundstate(mps, operator, alg::alg_type, envs = environments(mps, operator)) where {alg_type <: Union{<:IDMRG, <:IDMRG2}}
74+
(length(mps) 1 && alg isa IDMRG2) && throw(ArgumentError("unit cell should be >= 2"))
75+
log = alg isa IDMRG ? IterLog("IDMRG") : IterLog("IDMRG2")
76+
mps = copy(mps)
77+
iter = 0
78+
ϵ = calc_galerkin(mps, operator, mps, envs)
79+
E = zero(TensorOperations.promote_contract(scalartype(mps), scalartype(operator)))
14180

14281
LoggingExtras.withlevel(; alg.verbosity) do
143-
@infov 2 loginit!(log, ϵ)
144-
for outer iter in 1:(alg.maxiter)
145-
alg_eigsolve = updatetol(alg.alg_eigsolve, iter, ϵ)
146-
C_current = ψ.C[0]
147-
148-
# sweep from left to right
149-
for pos in 1:(length(ψ) - 1)
150-
ac2 = AC2(ψ, pos; kind = :ACAR)
151-
h_ac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs)
152-
_, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
153-
154-
al, c, ar = svd_trunc!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd)
155-
normalize!(c)
156-
157-
ψ.AL[pos] = al
158-
ψ.C[pos] = complex(c)
159-
ψ.AR[pos + 1] = _transpose_front(ar)
160-
ψ.AC[pos + 1] = _transpose_front(c * ar)
161-
162-
transfer_leftenv!(envs, ψ, H, ψ, pos + 1)
163-
transfer_rightenv!(envs, ψ, H, ψ, pos)
82+
@infov 2 begin
83+
E = expectation_value(mps, operator, envs)
84+
loginit!(log, ϵ, E)
85+
end
86+
end
87+
88+
state = IDMRGState(mps, operator, envs, iter, ϵ, E)
89+
it = IterativeSolver(alg, state)
90+
91+
return LoggingExtras.withlevel(; alg.verbosity) do
92+
for (mps, envs, ϵ, ΔE) in it
93+
if ϵ alg.tol
94+
@infov 2 logfinish!(log, it.iter, ϵ, ΔE)
95+
break
96+
end
97+
if it.iter alg.maxiter
98+
@warnv 1 logcancel!(log, it.iter, ϵ, ΔE)
99+
break
164100
end
101+
@infov 3 logiter!(log, it.iter, ϵ, ΔE)
102+
end
165103

166-
# update the edge
167-
ψ.AL[end] = ψ.AC[end] / ψ.C[end]
168-
ψ.AC[1] = _mul_tail.AL[1], ψ.C[1])
169-
ac2 = AC2(ψ, 0; kind = :ALAC)
170-
h_ac2 = AC2_hamiltonian(0, ψ, H, ψ, envs)
171-
_, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
104+
alg_gauge = updatetol(alg.alg_gauge, it.state.iter, it.state.ϵ)
105+
ψ′ = InfiniteMPS(it.state.mps.AR[1:end]; alg_gauge.tol, alg_gauge.maxiter)
106+
envs = recalculate!(it.state.envs, ψ′, it.state.operator, ψ′)
107+
return ψ′, envs, it.state.ϵ
108+
end
109+
end
172110

173-
al, c, ar = svd_trunc!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd)
174-
normalize!(c)
111+
function Base.iterate(
112+
it::IterativeSolver{alg_type}, state::IDMRGState{<:Any, <:Any, <:Any, T} = it.state
113+
) where {alg_type <: Union{<:IDMRG, <:IDMRG2}, T}
114+
mps, envs, C_old, E_new = localupdate_step!(it, state)
115+
116+
# error criterion
117+
C = mps.C[0]
118+
space_C_old = _firstspace(C_old)
119+
space_C = _firstspace(C)
120+
if space_C != space_C_old
121+
smallest = infimum(space_C_old, space_C)
122+
e1 = isometry(space_C_old, smallest)
123+
e2 = isometry(space_C, smallest)
124+
ϵ = norm(e2' * C * e2 - e1' * C_old * e1)
125+
else
126+
ϵ = norm(C - C_old)
127+
end
175128

176-
ψ.AL[end] = al
177-
ψ.C[end] = complex(c)
178-
ψ.AR[1] = _transpose_front(ar)
129+
# New energy
130+
ΔE = (E_new - state.energy) / 2
131+
(alg_type <: IDMRG2 && length(mps) == 2) && (ΔE /= 2) # This extra factor gives the correct energy per unit cell. I have no clue why right now.
179132

180-
ψ.AC[end] = _mul_tail(al, c)
181-
ψ.AC[1] = _transpose_front(c * ar)
182-
ψ.AL[1] = ψ.AC[1] / ψ.C[1]
133+
# update state
134+
it.state = IDMRGState{T}(mps, state.operator, envs, state.iter + 1, ϵ, E_new)
183135

184-
C_current = complex(c)
136+
return (mps, envs, ϵ, ΔE), it.state
137+
end
185138

186-
# update environments
187-
transfer_leftenv!(envs, ψ, H, ψ, 1)
188-
transfer_rightenv!(envs, ψ, H, ψ, 0)
139+
function localupdate_step!(
140+
it::IterativeSolver{<:IDMRG}, state
141+
)
142+
alg_eigsolve = updatetol(it.alg_eigsolve, state.iter, state.ϵ)
143+
return _localupdate_sweep_idmrg!(state.mps, state.operator, state.envs, alg_eigsolve)
144+
end
189145

190-
# sweep from right to left
191-
for pos in (length(ψ) - 1):-1:1
192-
ac2 = AC2(ψ, pos; kind = :ALAC)
193-
h_ac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs)
194-
_, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
146+
function localupdate_step!(
147+
it::IterativeSolver{<:IDMRG2}, state
148+
)
149+
alg_eigsolve = updatetol(it.alg_eigsolve, state.iter, state.ϵ)
150+
return _localupdate_sweep_idmrg2!(state.mps, state.operator, state.envs, alg_eigsolve, it.trscheme, it.alg_svd)
151+
end
195152

196-
al, c, ar = svd_trunc!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd)
197-
normalize!(c)
153+
function _localupdate_sweep_idmrg!(ψ, H, envs, alg_eigsolve)
154+
local E
155+
C_old = ψ.C[0]
156+
# left to right sweep
157+
for pos in 1:length(ψ)
158+
h = AC_hamiltonian(pos, ψ, H, ψ, envs)
159+
_, ψ.AC[pos] = fixedpoint(h, ψ.AC[pos], :SR, alg_eigsolve)
160+
if pos == length(ψ)
161+
# AC needed in next sweep
162+
ψ.AL[pos], ψ.C[pos] = left_orth.AC[pos])
163+
else
164+
ψ.AL[pos], ψ.C[pos] = left_orth!.AC[pos])
165+
end
166+
transfer_leftenv!(envs, ψ, H, ψ, pos + 1)
167+
end
198168

199-
ψ.AL[pos] = al
200-
ψ.AC[pos] = _mul_tail(al, c)
201-
ψ.C[pos] = complex(c)
202-
ψ.AR[pos + 1] = _transpose_front(ar)
203-
ψ.AC[pos + 1] = _transpose_front(c * ar)
169+
# right to left sweep
170+
for pos in length(ψ):-1:1
171+
h = AC_hamiltonian(pos, ψ, H, ψ, envs)
172+
E, ψ.AC[pos] = fixedpoint(h, ψ.AC[pos], :SR, alg_eigsolve)
204173

205-
transfer_leftenv!(envs, ψ, H, ψ, pos + 1)
206-
transfer_rightenv!(envs, ψ, H, ψ, pos)
207-
end
174+
ψ.C[pos - 1], temp = right_orth!(_transpose_tail.AC[pos]; copy = (pos == 1)))
175+
ψ.AR[pos] = _transpose_front(temp)
208176

209-
# update the edge
210-
ψ.AC[end] = _mul_front.C[end - 1], ψ.AR[end])
211-
ψ.AR[1] = _transpose_front.C[end] \ _transpose_tail.AC[1]))
212-
ac2 = AC2(ψ, 0; kind = :ACAR)
213-
h_ac2 = AC2_hamiltonian(0, ψ, H, ψ, envs)
214-
_, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
215-
al, c, ar = svd_trunc!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd)
216-
normalize!(c)
217-
218-
ψ.AL[end] = al
219-
ψ.C[end] = complex(c)
220-
ψ.AR[1] = _transpose_front(ar)
221-
222-
ψ.AR[end] = _transpose_front.C[end - 1] \ _transpose_tail(al * c))
223-
ψ.AC[1] = _transpose_front(c * ar)
224-
225-
transfer_leftenv!(envs, ψ, H, ψ, 1)
226-
transfer_rightenv!(envs, ψ, H, ψ, 0)
227-
228-
# update error
229-
smallest = infimum(_firstspace(C_current), _firstspace(c))
230-
e1 = isometry(_firstspace(C_current), smallest)
231-
e2 = isometry(_firstspace(c), smallest)
232-
ϵ = norm(e2' * c * e2 - e1' * C_current * e1)
233-
234-
if ϵ < alg.tol
235-
@infov 2 logfinish!(log, iter, ϵ)
236-
break
237-
end
238-
if iter == alg.maxiter
239-
@warnv 1 logcancel!(log, iter, ϵ)
240-
else
241-
@infov 3 logiter!(log, iter, ϵ)
242-
end
243-
end
177+
transfer_rightenv!(envs, ψ, H, ψ, pos - 1)
244178
end
179+
return ψ, envs, C_old, E
180+
end
181+
245182

246-
alg_gauge = updatetol(alg.alg_gauge, iter, ϵ)
247-
ψ′ = InfiniteMPS.AR[1:end]; alg_gauge.tol, alg_gauge.maxiter)
248-
recalculate!(envs, ψ′, H, ψ′)
183+
function _localupdate_sweep_idmrg2!(ψ, H, envs, alg_eigsolve, alg_trscheme, alg_svd)
184+
# sweep from left to right
185+
for pos in 1:(length(ψ) - 1)
186+
ac2 = AC2(ψ, pos; kind = :ACAR)
187+
h_ac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs)
188+
_, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
189+
190+
al, c, ar = svd_trunc!(ac2′; trunc = alg_trscheme, alg = alg_svd)
191+
normalize!(c)
192+
193+
ψ.AL[pos] = al
194+
ψ.C[pos] = complex(c)
195+
ψ.AR[pos + 1] = _transpose_front(ar)
196+
ψ.AC[pos + 1] = _transpose_front(c * ar)
197+
198+
transfer_leftenv!(envs, ψ, H, ψ, pos + 1)
199+
transfer_rightenv!(envs, ψ, H, ψ, pos)
200+
end
201+
202+
# update the edge
203+
ψ.AL[end] = ψ.AC[end] / ψ.C[end]
204+
ψ.AC[1] = _mul_tail.AL[1], ψ.C[1])
205+
ac2 = AC2(ψ, 0; kind = :ALAC)
206+
h_ac2 = AC2_hamiltonian(0, ψ, H, ψ, envs)
207+
_, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
208+
209+
al, c, ar = svd_trunc!(ac2′; trunc = alg_trscheme, alg = alg_svd)
210+
normalize!(c)
211+
212+
ψ.AL[end] = al
213+
ψ.C[end] = complex(c)
214+
ψ.AR[1] = _transpose_front(ar)
215+
216+
ψ.AC[end] = _mul_tail(al, c)
217+
ψ.AC[1] = _transpose_front(c * ar)
218+
ψ.AL[1] = ψ.AC[1] / ψ.C[1]
219+
220+
C_old = complex(c)
221+
222+
# update environments
223+
transfer_leftenv!(envs, ψ, H, ψ, 1)
224+
transfer_rightenv!(envs, ψ, H, ψ, 0)
225+
226+
# sweep from right to left
227+
for pos in (length(ψ) - 1):-1:1
228+
ac2 = AC2(ψ, pos; kind = :ALAC)
229+
h_ac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs)
230+
_, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
231+
232+
al, c, ar = svd_trunc!(ac2′; trunc = alg_trscheme, alg = alg_svd)
233+
normalize!(c)
234+
235+
ψ.AL[pos] = al
236+
ψ.AC[pos] = _mul_tail(al, c)
237+
ψ.C[pos] = complex(c)
238+
ψ.AR[pos + 1] = _transpose_front(ar)
239+
ψ.AC[pos + 1] = _transpose_front(c * ar)
240+
241+
transfer_leftenv!(envs, ψ, H, ψ, pos + 1)
242+
transfer_rightenv!(envs, ψ, H, ψ, pos)
243+
end
249244

250-
return ψ′, envs, ϵ
245+
# update the edge
246+
ψ.AC[end] = _mul_front.C[end - 1], ψ.AR[end])
247+
ψ.AR[1] = _transpose_front.C[end] \ _transpose_tail.AC[1]))
248+
ac2 = AC2(ψ, 0; kind = :ACAR)
249+
h_ac2 = AC2_hamiltonian(0, ψ, H, ψ, envs)
250+
E, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve)
251+
al, c, ar = svd_trunc!(ac2′; trunc = alg_trscheme, alg = alg_svd)
252+
normalize!(c)
253+
254+
ψ.AL[end] = al
255+
ψ.C[end] = complex(c)
256+
ψ.AR[1] = _transpose_front(ar)
257+
258+
ψ.AR[end] = _transpose_front.C[end - 1] \ _transpose_tail(al * c))
259+
ψ.AC[1] = _transpose_front(c * ar)
260+
261+
transfer_leftenv!(envs, ψ, H, ψ, 1)
262+
transfer_rightenv!(envs, ψ, H, ψ, 0)
263+
return ψ, envs, C_old, E
251264
end

0 commit comments

Comments
 (0)