Skip to content

Commit 76c32a2

Browse files
authored
Fix VUMPS for multi-site unit cells and nonlocal interactions (#31)
1 parent 4cf3198 commit 76c32a2

15 files changed

+940
-154
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using ITensors
2+
using ITensorInfiniteMPS
3+
4+
##############################################################################
5+
# VUMPS parameters
6+
#
7+
8+
maxdim = 64 # Maximum bond dimension
9+
cutoff = 1e-6 # Singular value cutoff when increasing the bond dimension
10+
max_vumps_iters = 100 # Maximum number of iterations of the VUMPS algorithm at each bond dimension
11+
vumps_tol = 1e-6
12+
outer_iters = 4 # Number of times to increase the bond dimension
13+
14+
##############################################################################
15+
# CODE BELOW HERE DOES NOT NEED TO BE MODIFIED
16+
#
17+
N = 3# Number of sites in the unit cell
18+
J = -1.0
19+
J₂ = -0.2
20+
h = 1.0;
21+
22+
function space_shifted(q̃sz)
23+
return [QN("SzParity", 1 - q̃sz, 2) => 1, QN("SzParity", 0 - q̃sz, 2) => 1]
24+
end
25+
26+
space_ = fill(space_shifted(1), N);
27+
s = infsiteinds("S=1/2", N; space=space_)
28+
initstate(n) = ""
29+
ψ = InfMPS(s, initstate);
30+
31+
model = Model("ising_extended");
32+
H = InfiniteITensorSum(model, s; J=J, J₂=J₂, h=h);
33+
34+
@show norm(contract.AL[1:N]..., ψ.C[N]) - contract.C[0], ψ.AR[1:N]...));
35+
36+
vumps_kwargs = (tol=vumps_tol, maxiter=max_vumps_iters)
37+
subspace_expansion_kwargs = (cutoff=cutoff, maxdim=maxdim)
38+
ψ_0 = vumps(H, ψ; vumps_kwargs...)
39+
40+
for j in 1:outer_iters
41+
println("\nIncrease bond dimension")
42+
ψ_1 = subspace_expansion(ψ_0, H; subspace_expansion_kwargs...)
43+
println("Run VUMPS with new bond dimension")
44+
ψ_0 = vumps(H, ψ_1; vumps_kwargs...)
45+
end
46+
47+
function ITensors.expect::InfiniteCanonicalMPS, o, n)
48+
return (noprime.AL[n] * ψ.C[n] * op(o, s[n])) * dag.AL[n] * ψ.C[n]))[]
49+
end
50+
51+
function ITensors.expect::InfiniteCanonicalMPS, h::ITensor)
52+
l = linkinds(only, ψ.AL)
53+
r = linkinds(only, ψ.AR)
54+
s = siteinds(only, ψ)
55+
δˢ(n) = δ(dag(s[n]), prime(s[n]))
56+
δˡ(n) = δ(l[n], prime(dag(l[n])))
57+
δʳ(n) = δ(dag(r[n]), prime(r[n]))
58+
ψ′ = prime(dag(ψ))
59+
60+
ns = sort(findsites(ψ, h))
61+
nrange = ns[end] - ns[1] + 1
62+
idx = 2
63+
temp_O = δˡ(ns[1] - 1) * ψ.AL[ns[1]] * h * ψ′.AL[ns[1]]
64+
for n in (ns[1] + 1):(ns[1] + nrange - 1)
65+
if n == ns[idx]
66+
temp_O = temp_O * ψ.AL[n] * ψ′.AL[n]
67+
idx += 1
68+
else
69+
temp_O = temp_O * ψ.AL[n] * δˢ(n) * ψ′.AL[n]
70+
end
71+
end
72+
temp_O = temp_O * ψ.C[ns[end]] * δʳ(ns[end]) * ψ′.C[ns[end]]
73+
return temp_O[]
74+
end
75+
76+
function ITensors.expect::InfiniteCanonicalMPS, h::InfiniteITensorSum)
77+
return [expect(ψ, h[(j, j + 1)]) for j in 1:nsites(ψ)]
78+
end
79+
80+
Sz = [expect(ψ_0, "Sz", n) for n in 1:N]
81+
energy_infinite = expect(ψ_0, H)
82+
83+
Nfinite = 100
84+
sfinite = siteinds("S=1/2", Nfinite; conserve_szparity=true)
85+
Hfinite = MPO(model, sfinite; J=J, J₂=J₂, h=h)
86+
ψfinite = randomMPS(sfinite, initstate; linkdims=10)
87+
@show flux(ψfinite)
88+
sweeps = Sweeps(15)
89+
setmaxdim!(sweeps, maxdim)
90+
setcutoff!(sweeps, cutoff)
91+
energy_finite_total, ψfinite = dmrg(Hfinite, ψfinite, sweeps)
92+
93+
nfinite = Nfinite ÷ 2
94+
Sz_finite = expect(ψfinite, "Sz")[nfinite:(nfinite + N - 1)]
95+
96+
@show (
97+
energy_infinite,
98+
energy_finite_total / Nfinite,
99+
reference(model, Observable("energy"); J=J, h=h, J₂=J₂),
100+
)
101+
102+
@show (Sz, Sz_finite)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using ITensors
2+
using ITensorInfiniteMPS
3+
4+
##############################################################################
5+
# VUMPS parameters
6+
#
7+
8+
maxdim = 64 # Maximum bond dimension
9+
cutoff = 1e-6 # Singular value cutoff when increasing the bond dimension
10+
max_vumps_iters = 100 # Maximum number of iterations of the VUMPS algorithm at each bond dimension
11+
vumps_tol = 1e-6
12+
outer_iters = 4 # Number of times to increase the bond dimension
13+
14+
##############################################################################
15+
# CODE BELOW HERE DOES NOT NEED TO BE MODIFIED
16+
#
17+
N = 2# Number of sites in the unit cell
18+
J = 1.0
19+
h = 1.0;
20+
21+
function space_shifted(q̃sz)
22+
return [QN("SzParity", 1 - q̃sz, 2) => 1, QN("SzParity", 0 - q̃sz, 2) => 1]
23+
end
24+
25+
space_ = fill(space_shifted(1), N);
26+
s = infsiteinds("S=1/2", N; space=space_)
27+
initstate(n) = ""
28+
ψ = InfMPS(s, initstate);
29+
30+
model = Model("ising");
31+
H = InfiniteITensorSum(model, s; J=J, h=h);
32+
#to test the case where the range is larger than the unit cell size
33+
for x in 1:N
34+
H.data[x] = H.data[x] * delta(prime(s[x + 3]), dag(s[x + 3]))
35+
end
36+
37+
@show norm(contract.AL[1:N]..., ψ.C[N]) - contract.C[0], ψ.AR[1:N]...));
38+
39+
vumps_kwargs = (tol=vumps_tol, maxiter=max_vumps_iters)
40+
subspace_expansion_kwargs = (cutoff=cutoff, maxdim=maxdim)
41+
ψ_0 = vumps(H, ψ; vumps_kwargs...)
42+
43+
for j in 1:outer_iters
44+
println("\nIncrease bond dimension")
45+
ψ_1 = subspace_expansion(ψ_0, H; subspace_expansion_kwargs...)
46+
println("Run VUMPS with new bond dimension")
47+
ψ_0 = vumps(H, ψ_1; vumps_kwargs...)
48+
end
49+
50+
function ITensors.expect::InfiniteCanonicalMPS, o, n)
51+
return (noprime.AL[n] * ψ.C[n] * op(o, s[n])) * dag.AL[n] * ψ.C[n]))[]
52+
end
53+
54+
function ITensors.expect::InfiniteCanonicalMPS, h::ITensor)
55+
l = linkinds(only, ψ.AL)
56+
r = linkinds(only, ψ.AR)
57+
s = siteinds(only, ψ)
58+
δˢ(n) = δ(dag(s[n]), prime(s[n]))
59+
δˡ(n) = δ(l[n], prime(dag(l[n])))
60+
δʳ(n) = δ(dag(r[n]), prime(r[n]))
61+
ψ′ = prime(dag(ψ))
62+
63+
ns = sort(findsites(ψ, h))
64+
nrange = ns[end] - ns[1] + 1
65+
idx = 2
66+
temp_O = δˡ(ns[1] - 1) * ψ.AL[ns[1]] * h * ψ′.AL[ns[1]]
67+
for n in (ns[1] + 1):(ns[1] + nrange - 1)
68+
if n == ns[idx]
69+
temp_O = temp_O * ψ.AL[n] * ψ′.AL[n]
70+
idx += 1
71+
else
72+
temp_O = temp_O * ψ.AL[n] * δˢ(n) * ψ′.AL[n]
73+
end
74+
end
75+
temp_O = temp_O * ψ.C[ns[end]] * δʳ(ns[end]) * ψ′.C[ns[end]]
76+
return temp_O[]
77+
end
78+
79+
function ITensors.expect::InfiniteCanonicalMPS, h::InfiniteITensorSum)
80+
return [expect(ψ, h[(j, j + 1)]) for j in 1:nsites(ψ)]
81+
end
82+
83+
Sz = [expect(ψ_0, "Sz", n) for n in 1:N]
84+
energy_infinite = expect(ψ_0, H)
85+
86+
Nfinite = 100
87+
sfinite = siteinds("S=1/2", Nfinite; conserve_szparity=true)
88+
Hfinite = MPO(model, sfinite; J=J, h=h)
89+
ψfinite = randomMPS(sfinite, initstate; linkdims=10)
90+
@show flux(ψfinite)
91+
sweeps = Sweeps(15)
92+
setmaxdim!(sweeps, maxdim)
93+
setcutoff!(sweeps, cutoff)
94+
energy_finite_total, ψfinite = dmrg(Hfinite, ψfinite, sweeps)
95+
96+
nfinite = Nfinite ÷ 2
97+
Sz_finite = expect(ψfinite, "Sz")[nfinite:(nfinite + N - 1)]
98+
99+
@show (
100+
energy_infinite,
101+
energy_finite_total / Nfinite,
102+
reference(model, Observable("energy"); J=J, h=h, J₂=J₂),
103+
)
104+
105+
@show (Sz, Sz_finite)

src/ITensorInfiniteMPS.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ include("infinitecanonicalmps.jl")
3535
include("transfermatrix.jl")
3636
include("models/models.jl")
3737
include("models/ising.jl")
38+
include("models/ising_extended.jl")
3839
include("models/heisenberg.jl")
3940
include("models/hubbard.jl")
4041
include("models/xx.jl")

src/celledvectors.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ celltagprefix() = "c="
1111
celltags(n::Integer) = TagSet(celltagprefix() * string(n))
1212
celltags(n1::Integer, n2::Integer) = TagSet(celltagprefix() * n1 * "|" * n2)
1313

14+
indextagprefix() = "n="
1415
#
1516
# translatecell
1617
#
@@ -27,6 +28,11 @@ function getcell(ts::TagSet)
2728
return parse(Int, celltag[(length(celltagprefix()) + 1):end])
2829
end
2930

31+
function getsite(ts::TagSet)
32+
celltag = ITensorInfiniteMPS.tag_starting_with(ts, indextagprefix())
33+
return parse(Int, celltag[(length(indextagprefix()) + 1):end])
34+
end
35+
3036
function translatecell(ts::TagSet, n::Integer)
3137
ncell = getcell(ts)
3238
return replacetags(ts, celltags(ncell) => celltags(ncell + n))

src/infinitecanonicalmps.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,23 @@ end
5555

5656
function insert_linkinds!(A; left_dir=ITensors.Out)
5757
# TODO: use `celllength` here
58-
N = length(A)
58+
N = nsites(A)
5959
l = CelledVector{indtype(A)}(undef, N)
6060
n = N
6161
s = siteind(A, 1)
6262
dim = if hasqns(s)
6363
kwargs = (; dir=left_dir)
6464
qn_ln = zero_qn(s)
65-
[qn_ln => 1]
65+
[qn_ln => 1] #Default to 0 on the right
6666
else
67-
kwargs = (;)
67+
kwargs = ()
6868
1
6969
end
7070
l[N] = Index(dim, default_link_tags("l", n, 1); kwargs...)
7171
for n in 1:(N - 1)
7272
# TODO: is this correct?
7373
dim = if hasqns(s)
74-
qn_ln = (flux(A[n]) + qn_ln) * left_dir
74+
qn_ln = flux(A[n]) * left_dir + qn_ln#Fixed a bug on flux conservation
7575
[qn_ln => 1]
7676
else
7777
1
@@ -81,6 +81,9 @@ function insert_linkinds!(A; left_dir=ITensors.Out)
8181
for n in 1:N
8282
A[n] = A[n] * onehot(l[n - 1] => 1) * onehot(dag(l[n]) => 1)
8383
end
84+
85+
@assert all(i -> flux(i) == zero_qn(s), A) "Flux not invariant under one unit cell translation, not implemented"
86+
8487
return A
8588
end
8689

src/infinitemps.jl

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ end
5757

5858
ITensors.siteinds(f::typeof(only), ψ::InfiniteCanonicalMPS) = siteinds(f, ψ.AL)
5959

60+
ITensorInfiniteMPS.getcell(i::Index) = ITensorInfiniteMPS.getcell(tags(i))
61+
getsite(i::Index) = getsite(tags(i))
62+
function ITensors.findfirstsiteind::InfiniteMPS, i::Index)
63+
c = ITensorInfiniteMPS.getcell(i)
64+
i1 = translatecell(i, -(c - 1))
65+
n1 = findfirst(hascommoninds(i1), ψ[Cell(1)])
66+
return (c - 1) * nsites(ψ) + n1
67+
end
68+
function ITensors.findsites::InfiniteMPS, T::ITensor)
69+
s = filterinds(T; plev=0)
70+
return sort([ITensors.findfirstsiteind(ψ, i) for i in s])
71+
end
72+
ITensors.findsites::InfiniteCanonicalMPS, T::ITensor) = findsites.AL, T)
73+
6074
# For now, only represents nearest neighbor interactions
6175
# on a linear chain
6276
struct InfiniteITensorSum
@@ -69,7 +83,29 @@ function Base.getindex(l::InfiniteITensorSum, n1n2::Tuple{Int,Int})
6983
@assert n2 == n1 + 1
7084
return l.data[n1]
7185
end
72-
nsites(h::InfiniteITensorSum) = length(l.data)
86+
nsites(h::InfiniteITensorSum) = length(h.data)
87+
#Gives the range of the Hamiltonian. Useful for better optimized contraction in VUMPS
88+
nsites_support(h::InfiniteITensorSum) = order.(h.data) ÷ 2
89+
nsites_support(h::InfiniteITensorSum, n::Int64) = order(h.data[n]) ÷ 2
90+
91+
nrange(h::InfiniteITensorSum) = nrange.(h.data, ncell=nsites(h))
92+
nrange(h::InfiniteITensorSum, n::Int64) = nrange(h.data[n]; ncell=nsites(h))
93+
function nrange(h::ITensor; ncell=0)
94+
ns = findsites(h; ncell=ncell)
95+
return ns[end] - ns[1] + 1
96+
end
97+
98+
function ITensors.findfirstsiteind(h::ITensor, i::Index, ncell::Int64)
99+
c = ITensorInfiniteMPS.getcell(i)
100+
n1 = getsite(i)
101+
return (c - 1) * ncell + n1
102+
end
103+
function ITensors.findsites(h::ITensor; ncell::Int64=1)
104+
s = filterinds(h; plev=0)
105+
return sort([ITensors.findfirstsiteind(h, i, ncell) for i in s])
106+
end
107+
ITensors.findsites(h::InfiniteITensorSum) = [findsites(h, n) for n in 1:nsites(h)]
108+
ITensors.findsites(h::InfiniteITensorSum, n::Int64) = findsites(h.data[n]; ncell=nsites(h))
73109

74110
## HDF5 support for the InfiniteCanonicalMPS type
75111

src/models/ising.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@ function ITensors.MPO(::Model"ising", s; J, h)
1111
return MPO(a, s)
1212
end
1313

14+
# H = -J Σⱼ XⱼXⱼ₊₁ - h Σⱼ Zⱼ
15+
function ITensors.OpSum(::Model"ising", n1, n2; J, h)
16+
opsum = OpSum()
17+
if J != 0
18+
opsum += -J, "X", n1, "X", n2
19+
end
20+
if h != 0
21+
opsum += -h / 2, "Z", n1
22+
opsum += -h / 2, "Z", n2
23+
end
24+
return opsum
25+
end
26+
1427
# H = -J X₁X₂ - h Z₁
1528
# XXX: use `op` instead of `ITensor`
1629
function ITensors.ITensor(::Model"ising", s1::Index, s2::Index; J, h)

src/models/ising_extended.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
nrange(::Model"ising_extended") = 3
2+
# H = -J Σⱼ XⱼXⱼ₊₁ - h Σⱼ Zⱼ - J₂ Σⱼ XⱼZⱼ₊₁Xⱼ₊₂
3+
function ITensors.MPO(::Model"ising_extended", s; J=1.0, h=1.0, J₂=0.0)
4+
N = length(s)
5+
a = OpSum()
6+
if J != 0
7+
for n in 1:(N - 1)
8+
a += -J, "X", n, "X", n + 1
9+
end
10+
end
11+
if J₂ != 0
12+
for n in 1:(N - 2)
13+
a += -J₂, "X", n, "Z", n + 1, "X", n + 2
14+
end
15+
end
16+
if h != 0
17+
for n in 1:N
18+
a += -h, "Z", n
19+
end
20+
end
21+
return MPO(a, s)
22+
end
23+
24+
# H = -J Σⱼ XⱼXⱼ₊₁ - h Σⱼ Zⱼ - J₂ Σⱼ XⱼZⱼ₊₁Xⱼ₊₂
25+
function ITensors.OpSum(::Model"ising_extended", n1, n2; J=1.0, h=1.0, J₂=0.0)
26+
opsum = OpSum()
27+
if J != 0
28+
opsum += -J / 2, "X", n1, "X", n2
29+
opsum += -J / 2, "X", n2, "X", n2 + 1
30+
end
31+
if J₂ != 0
32+
opsum += -J₂, "X", n1, "Z", n2, "X", n2 + 1
33+
end
34+
opsum += -h / 3, "Z", n1
35+
opsum += -h / 3, "Z", n2
36+
opsum += -h / 3, "Z", n2 + 1
37+
return opsum
38+
end
39+
40+
function ITensors.ITensor(::Model"ising_extended", s1, s2, s3; J=1.0, h=1.0, J₂=0.0)
41+
opsum = OpSum(Model"ising_extended"(), 1, 2; J=J, h=h, J₂=J₂)
42+
return prod(MPO(opsum, [s1, s2, s3]))
43+
end
44+
45+
function reference(::Model"ising_extended", ::Observable"energy"; J=1.0, h=1.0, J₂=0.0)
46+
f(k) = sqrt((J * cos(k) + J₂ * cos(2k) - h)^2 + (J * sin(k) + J₂ * sin(2k))^2)
47+
return -1 / 2π * ITensorInfiniteMPS.(k -> f(k), -π, π)
48+
end

0 commit comments

Comments
 (0)