Skip to content

Commit d91262b

Browse files
Allow for non-square MPOHamiltonians in make_time_mpo(..., ::TaylorCluster) (#253)
* remove all square-forcing code * add tests for `make_time_mpo` with non-square `FiniteMPOHamiltonians` * Revert "remove all square-forcing code" This reverts commit bc37174. * attempt to fix * fix size issue in MPO * performance improvement * update tests * Nth order has (N+1)th order error --------- Co-authored-by: Lukas Devos <[email protected]>
1 parent 750ec89 commit d91262b

File tree

10 files changed

+459
-326
lines changed

10 files changed

+459
-326
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Accessors = "0.1"
2626
Aqua = "0.8.9"
2727
BlockTensorKit = "0.1.4"
2828
Compat = "3.47, 4.10"
29+
Combinatorics = "1"
2930
DocStringExtensions = "0.9.3"
3031
HalfIntegers = "1.6.0"
3132
KrylovKit = "0.8.3, 0.9.2"
@@ -48,11 +49,12 @@ julia = "1.10"
4849

4950
[extras]
5051
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
52+
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
5153
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
5254
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
5355
TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2"
5456
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
5557
TestExtras = "5ed8adda-3752-4e41-b88a-e8b09835ee3a"
5658

5759
[targets]
58-
test = ["Aqua", "Pkg", "Test", "TestExtras", "Plots"]
60+
test = ["Aqua", "Pkg", "Test", "TestExtras", "Plots", "Combinatorics"]

src/MPSKit.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ include("algorithms/changebonds/svdcut.jl")
143143
include("algorithms/changebonds/randexpand.jl")
144144

145145
include("algorithms/timestep/tdvp.jl")
146-
include("algorithms/timestep/timeevmpo.jl")
146+
include("algorithms/timestep/taylorcluster.jl")
147+
include("algorithms/timestep/wii.jl")
147148
include("algorithms/timestep/integrators.jl")
148149
include("algorithms/timestep/time_evolve.jl")
149150

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
"""
2+
$(TYPEDEF)
3+
4+
Algorithm for constructing the `N`th order time evolution MPO using the Taylor cluster expansion.
5+
6+
## Fields
7+
8+
$(TYPEDFIELDS)
9+
10+
## References
11+
12+
* [Van Damme et al. SciPost Phys. 17 (2024)](@cite vandamme2024)
13+
"""
14+
@kwdef struct TaylorCluster <: Algorithm
15+
"order of the Taylor expansion"
16+
N::Int = 2
17+
"include higher-order corrections"
18+
extension::Bool = true
19+
"approximate compression of corrections, accurate up to order `N`"
20+
compression::Bool = false
21+
end
22+
23+
"""
24+
const WI = TaylorCluster(; N=1, extension=false, compression=false)
25+
26+
First order Taylor expansion for a time-evolution MPO.
27+
"""
28+
const WI = TaylorCluster(; N=1, extension=false, compression=false)
29+
30+
function make_time_mpo(H::MPOHamiltonian, dt::Number, alg::TaylorCluster;
31+
tol=eps(real(scalartype(H))))
32+
N = alg.N
33+
τ = -1im * dt
34+
35+
# start with H^N
36+
H_n = H^N
37+
virtual_sz = map(0:length(H)) do i
38+
return i == 0 ? size(H[1], 1) : size(H[i], 4)
39+
end
40+
linds = map(virtual_sz) do sz
41+
return LinearIndices(ntuple(Returns(sz), N))
42+
end
43+
44+
# extension step: Algorithm 3
45+
# incorporate higher order terms
46+
# TODO: don't need to fully construct H_next...
47+
if alg.extension
48+
H_next = H_n * H
49+
for (i, slice) in enumerate(parent(H_n))
50+
linds_left = linds[i]
51+
linds_right = linds[i + 1]
52+
V_left = virtual_sz[i]
53+
V_right = virtual_sz[i + 1]
54+
linds_next_left = LinearIndices(ntuple(Returns(V_left), N + 1))
55+
linds_next_right = LinearIndices(ntuple(Returns(V_right), N + 1))
56+
57+
for a in CartesianIndices(linds_left), b in CartesianIndices(linds_right)
58+
all(>(1), b.I) || continue
59+
all(in((1, V_left)), a.I) && any(==(V_left), a.I) && continue
60+
61+
n1 = count(==(1), a.I) + 1
62+
n3 = count(==(V_right), b.I) + 1
63+
factor = τ * factorial(N) / (factorial(N + 1) * n1 * n3)
64+
65+
for c in 1:(N + 1), d in 1:(N + 1)
66+
aₑ = insert!([a.I...], c, 1)
67+
bₑ = insert!([b.I...], d, V_right)
68+
69+
# TODO: use VectorInterface for memory efficiency
70+
slice[linds_left[a], 1, 1, linds_right[b]] += factor *
71+
H_next[i][linds_next_left[aₑ...],
72+
1, 1,
73+
linds_next_right[bₑ...]]
74+
end
75+
end
76+
end
77+
end
78+
79+
# loopback step: Algorithm 1
80+
# constructing the Nth order time evolution MPO
81+
mpo = MPO(parent(H_n))
82+
for (i, slice) in enumerate(parent(mpo))
83+
V_right = virtual_sz[i + 1]
84+
linds_right = linds[i + 1]
85+
cinds_right = CartesianIndices(linds_right)
86+
for b in cinds_right[2:end]
87+
all(in((1, V_right)), b.I) || continue
88+
89+
b_lin = linds_right[b]
90+
a = count(==(V_right), b.I)
91+
factor = τ^a * factorial(N - a) / factorial(N)
92+
slice[:, 1, 1, 1] = slice[:, 1, 1, 1] + factor * slice[:, 1, 1, b_lin]
93+
for I in nonzero_keys(slice)
94+
(I[1] == b_lin || I[4] == b_lin) && delete!(slice, I)
95+
end
96+
end
97+
end
98+
99+
# Remove equivalent rows and columns: Algorithm 2
100+
for (i, slice) in enumerate(parent(mpo))
101+
V_left = virtual_sz[i]
102+
linds_left = linds[i]
103+
for c in CartesianIndices(linds_left)
104+
c_lin = linds_left[c]
105+
s_c = CartesianIndex(sort(collect(c.I); by=(!=(1)))...)
106+
107+
n1 = count(==(1), c.I)
108+
n3 = count(==(V_left), c.I)
109+
110+
if n3 <= n1 && s_c != c
111+
for k in 1:size(slice, 4)
112+
I = CartesianIndex(c_lin, 1, 1, k)
113+
if I in nonzero_keys(slice)
114+
slice[linds_left[s_c], 1, 1, k] += slice[I]
115+
delete!(slice, I)
116+
end
117+
end
118+
end
119+
end
120+
121+
V_right = virtual_sz[i + 1]
122+
linds_right = linds[i + 1]
123+
for c in CartesianIndices(linds_right)
124+
c_lin = linds_right[c]
125+
s_r = CartesianIndex(sort(collect(c.I); by=(!=(V_right)))...)
126+
127+
n1 = count(==(1), c.I)
128+
n3 = count(==(V_right), c.I)
129+
130+
if n3 > n1 && s_r != c
131+
for k in 1:size(slice, 1)
132+
I = CartesianIndex(k, 1, 1, c_lin)
133+
if I in nonzero_keys(slice)
134+
slice[k, 1, 1, linds_right[s_r]] += slice[I]
135+
delete!(slice, I)
136+
end
137+
end
138+
end
139+
end
140+
end
141+
142+
# Approximate compression step: Algorithm 4
143+
if alg.compression
144+
for (i, slice) in enumerate(parent(mpo))
145+
V_right = virtual_sz[i + 1]
146+
linds_right = linds[i + 1]
147+
for a in CartesianIndices(linds_right)
148+
all(>(1), a.I) || continue
149+
a_lin = linds_right[a]
150+
n1 = count(==(V_right), a.I)
151+
n1 == 0 && continue
152+
b = CartesianIndex(replace(a.I, V_right => 1))
153+
b_lin = linds_right[b]
154+
factor = τ^n1 * factorial(N - n1) / factorial(N)
155+
for k in 1:size(slice, 1)
156+
I = CartesianIndex(k, 1, 1, a_lin)
157+
if I in nonzero_keys(slice)
158+
slice[k, 1, 1, b_lin] += factor * slice[I]
159+
delete!(slice, I)
160+
end
161+
end
162+
end
163+
V_left = virtual_sz[i]
164+
linds_left = linds[i]
165+
for a in CartesianIndices(linds_left)
166+
all(>(1), a.I) || continue
167+
a_lin = linds_left[a]
168+
n1 = count(==(V_left), a.I)
169+
n1 == 0 && continue
170+
for k in 1:size(slice, 4)
171+
I = CartesianIndex(a_lin, 1, 1, k)
172+
delete!(slice, I)
173+
end
174+
end
175+
end
176+
end
177+
178+
return remove_orphans!(mpo; tol)
179+
end
180+
181+
# Hack to treat FiniteMPOhamiltonians as Infinite
182+
function make_time_mpo(H::FiniteMPOHamiltonian, dt::Number, alg::TaylorCluster;
183+
tol=eps(real(scalartype(H))))
184+
H′ = copy(parent(H))
185+
186+
V_left = left_virtualspace(H[1])
187+
V_left′ = BlockTensorKit.oplus(V_left, oneunit(V_left), oneunit(V_left))
188+
H′[1] = similar(H[1], V_left′ space(H[1], 2) domain(H[1]))
189+
for (I, v) in nonzero_pairs(H[1])
190+
H′[1][I] = v
191+
end
192+
193+
V_right = right_virtualspace(H[end])
194+
V_right′ = BlockTensorKit.oplus(oneunit(V_right), oneunit(V_right), V_right)
195+
H′[end] = similar(H[end], codomain(H[end]) space(H[end], 3)' V_right′)
196+
for (I, v) in nonzero_pairs(H[end])
197+
H′[end][I[1], 1, 1, end] = v
198+
end
199+
200+
H′[1][end, 1, 1, end] = H′[1][1, 1, 1, 1]
201+
H′[end][1, 1, 1, 1] = H′[end][end, 1, 1, end]
202+
203+
mpo = make_time_mpo(InfiniteMPOHamiltonian(H′), dt, alg; tol)
204+
205+
# Impose boundary conditions
206+
mpo_fin = open_boundary_conditions(mpo, length(H))
207+
return remove_orphans!(mpo_fin; tol)
208+
end

src/algorithms/timestep/time_evolve.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,9 @@ solving the Schroedinger equation: ``i ∂ψ/∂t = H ψ``.
5252
- `[envs]`: MPS environment manager
5353
"""
5454
function timestep end, function timestep! end
55+
56+
@doc """
57+
make_time_mpo(H::MPOHamiltonian, dt::Number, alg) -> O::MPO
58+
59+
Construct an `MPO` that approximates ``\\exp(-iHdt)``.
60+
""" make_time_mpo

0 commit comments

Comments
 (0)