Skip to content

Commit 1abb75c

Browse files
authored
[Feature] FiniteMPOHamiltonian compression (#312)
* Start setting up ortho for mpohamiltonian * Add left_canonicalize! * Add right_canonicalize! * Add JordanMPOTensor constructor that promotes types * Add error messages * Add SvdCut * Add SvdCut * fix more edge cases * Add copying version * formatter * little bit of cleanup * Add some tests * Handle more edge cases * Bump BlockTensorKit compat * Formatter * update docstring and reference * Add comment explaining todo
1 parent 8cf041f commit 1abb75c

File tree

7 files changed

+273
-15
lines changed

7 files changed

+273
-15
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ VectorInterface = "409d34a3-91d5-4945-b6ec-7529ddf182d8"
2525
[compat]
2626
Accessors = "0.1"
2727
Aqua = "0.8.9"
28-
BlockTensorKit = "0.1.6"
28+
BlockTensorKit = "0.2"
2929
Combinatorics = "1"
3030
Compat = "3.47, 4.10"
3131
DocStringExtensions = "0.9.3"

docs/src/assets/mpskit.bib

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,22 @@ @article{paeckel2019
196196
keywords = {Density matrix renormalization group (DMRG),Matrix-product states (MPS),Strongly-correlated systems,Time-dependent variational principle (TDVP),Time-evolution methods,Time-evolving block decimation (TEBD)}
197197
}
198198

199+
@article{parker2020,
200+
title = {Local Matrix Product Operators: {{Canonical}} Form, Compression, and Control Theory},
201+
shorttitle = {Local Matrix Product Operators},
202+
author = {Parker, Daniel E. and Cao, Xiangyu and Zaletel, Michael P.},
203+
year = {2020},
204+
month = jul,
205+
journal = {Physical Review B},
206+
volume = {102},
207+
number = {3},
208+
pages = {035147},
209+
publisher = {American Physical Society},
210+
doi = {10.1103/PhysRevB.102.035147},
211+
url = {https://link.aps.org/doi/10.1103/PhysRevB.102.035147},
212+
abstract = {We present a new method for compressing matrix product operators (MPOs) which represent sums of local terms, such as Hamiltonians. Just as with area law states, such local operators may be fully specified with a small amount of information per site. Standard matrix product state (MPS) tools are ill-suited to this case, due to extensive Schmidt values that coexist with intensive ones, and Jordan blocks in the transfer matrix. We ameliorate these issues by introducing an ``almost Schmidt decomposition'' that respects locality. Our method is ``{$\varepsilon$}-close'' to the accuracy of MPS-based methods for finite MPOs, and extends seamlessly to the thermodynamic limit, where MPS techniques are inapplicable. In the framework of control theory, our method generalizes Kung's algorithm for model order reduction. Several examples are provided, including an all-MPO version of the operator recursion method (Lanczos algorithm) directly in the thermodynamic limit. All results are accompanied by practical algorithms, well-suited for the large MPOs that arise in DMRG for long-range or quasi-2D models.}
213+
}
214+
199215
@article{pollmann2009,
200216
title = {Theory of {{Finite-Entanglement Scaling}} at {{One-Dimensional Quantum Critical Points}}},
201217
author = {Pollmann, Frank and Mukerjee, Subroto and Turner, Ari M. and Moore, Joel E.},

src/MPSKit.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ include("operators/abstractmpo.jl")
115115
include("operators/mpo.jl")
116116
include("operators/jordanmpotensor.jl")
117117
include("operators/mpohamiltonian.jl") # the mpohamiltonian objects
118+
include("operators/ortho.jl")
118119
include("operators/multilinempo.jl")
119120
include("operators/projection.jl")
120121
include("operators/timedependence.jl")

src/algorithms/changebonds/svdcut.jl

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
"""
22
$(TYPEDEF)
33
4-
An algorithm that uses truncated SVD to change the bond dimension of a ψ.
4+
An algorithm that uses truncated SVD to change the bond dimension of a state or operator.
5+
This is achieved by a sweeping algorithm that locally performs (optimal) truncations in a gauged basis.
6+
7+
See also [`changebonds(!)`](@ref changebonds)
58
69
## Fields
710
811
$(TYPEDFIELDS)
12+
13+
## References
14+
15+
* [Parker et al. Phys. Rev. B 102 (2020)](@cite parker2020)
916
"""
1017
@kwdef struct SvdCut{S} <: Algorithm
1118
"algorithm used for the singular value decomposition"
@@ -105,3 +112,22 @@ end
105112
function changebonds(ψ, H, alg::SvdCut, envs = environments(ψ, H))
106113
return changebonds(ψ, alg), envs
107114
end
115+
116+
changebonds(mpo::FiniteMPOHamiltonian, alg::SvdCut) = changebonds!(copy(mpo), alg)
117+
function changebonds!(H::FiniteMPOHamiltonian, alg::SvdCut)
118+
# orthogonality center to the left
119+
for i in length(H):-1:2
120+
H = right_canonicalize!(H, i)
121+
end
122+
123+
# swipe right
124+
for i in 1:(length(H) - 1)
125+
H = left_canonicalize!(H, i; alg = alg.alg_svd, alg.trscheme)
126+
end
127+
# swipe left -- TODO: do we really need this double sweep?
128+
for i in length(H):-1:2
129+
H = right_canonicalize!(H, i; alg = alg.alg_svd, alg.trscheme)
130+
end
131+
132+
return H
133+
end

src/operators/jordanmpotensor.jl

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,37 @@ end
6767

6868
function JordanMPOTensor(
6969
V::TensorMapSumSpace{S, 2, 2},
70-
A::SparseBlockTensorMap{TA, E, S, 2, 2},
71-
B::SparseBlockTensorMap{TB, E, S, 2, 1},
72-
C::SparseBlockTensorMap{TC, E, S, 1, 2},
73-
D::SparseBlockTensorMap{TD, E, S, 1, 1}
70+
A::SparseBlockTensorMap{TA, E, S, 2, 2}, B::SparseBlockTensorMap{TB, E, S, 2, 1},
71+
C::SparseBlockTensorMap{TC, E, S, 1, 2}, D::SparseBlockTensorMap{TD, E, S, 1, 1}
7472
) where {E, S, TA, TB, TC, TD}
7573
allVs = eachspace(V)
7674
VA = space(allVs[2:(end - 1), 1, 1, 2:(end - 1)])
77-
VA == space(A) || throw(SpaceMismatch("A-block has incompatible spaces"))
75+
VA == space(A) || throw(SpaceMismatch(lazy"A-block has incompatible spaces:\n$VA\n$(space(A))"))
7876

7977
VB = removeunit(space(allVs[2:(end - 1), 1, 1, end]), 4)
80-
VB == space(B) || throw(SpaceMismatch("B-block has incompatible spaces"))
78+
VB == space(B) || throw(SpaceMismatch(lazy"B-block has incompatible spaces:\n$VB\n$(space(B))"))
8179

8280
VC = removeunit(space(allVs[1, 1, 1, 2:(end - 1)]), 1)
83-
VC == space(C) || throw(SpaceMismatch("C-block has incompatible spaces"))
81+
VC == space(C) || throw(SpaceMismatch(lazy"C-block has incompatible spaces:\n$VC\n$(space(C))"))
8482

8583
VD = removeunit(removeunit(space(allVs[1, 1, 1, end:end]), 4), 1)
86-
VD == space(D) || throw(SpaceMismatch("D-block has incompatible spaces"))
84+
VD == space(D) || throw(SpaceMismatch(lazy"D-block has incompatible spaces:\n$VD\n$(space(D))"))
8785

8886
return JordanMPOTensor{E, S, TA, TB, TC, TD}(V, A, B, C, D)
8987
end
88+
function JordanMPOTensor(
89+
V::TensorMapSumSpace{S, 2, 2},
90+
A::AbstractTensorMap{E, S, 2, 2}, B::AbstractTensorMap{E, S, 2, 1},
91+
C::AbstractTensorMap{E, S, 1, 2}, D::AbstractTensorMap{E, S, 1, 1}
92+
) where {E, S}
93+
return JordanMPOTensor(
94+
V,
95+
A isa SparseBlockTensorMap ? A : SparseBlockTensorMap(A),
96+
B isa SparseBlockTensorMap ? B : SparseBlockTensorMap(B),
97+
C isa SparseBlockTensorMap ? C : SparseBlockTensorMap(C),
98+
D isa SparseBlockTensorMap ? D : SparseBlockTensorMap(D)
99+
)
100+
end
90101

91102
function JordanMPOTensor(W::SparseBlockTensorMap{TT, E, S, 2, 2}) where {TT, E, S}
92103
@assert W[1, 1, 1, 1] isa BraidingTensor && W[end, 1, 1, end] isa BraidingTensor

src/operators/ortho.jl

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
function left_canonicalize!(
2+
H::FiniteMPOHamiltonian, i::Int;
3+
alg = QRpos(), trscheme::TruncationScheme = notrunc()
4+
)
5+
1 i < length(H) || throw(ArgumentError("Bounds error in canonicalize"))
6+
7+
W = H[i]
8+
S = spacetype(W)
9+
d = sqrt(dim(physicalspace(W)))
10+
11+
# orthogonalize second column against first
12+
WI = removeunit(W[1, 1, 1, 1], 1)
13+
@tensor t[l; r] := conj(WI[p; p' l]) * W.C[p; p' r]
14+
# TODO: the following is currently broken due to a TensorKit bug
15+
# @plansor C′[p; p' r] := W.C[p; p' r] - WI[p; p' l] * t[l; r]
16+
@plansor C′[p; p' r] := -WI[p; p' l] * t[l; r]
17+
add!(C′, W.C)
18+
19+
# QR of second column
20+
if size(W, 1) == 1
21+
tmp = transpose(C′, ((2, 1), (3,)))
22+
23+
if trscheme == notrunc()
24+
Q, R = leftorth!(tmp; alg)
25+
else
26+
@assert alg == SVD() || alg == SDD()
27+
Q, Σ, Vᴴ = tsvd!(tmp; alg, trunc = trscheme)
28+
R = Σ * Vᴴ
29+
end
30+
31+
if dim(R) == 0 # fully truncated
32+
V = BlockTensorKit.oplus(oneunit(S), oneunit(S))
33+
Q1 = typeof(W.A)(undef, SumSpace{S}() physicalspace(W) physicalspace(W) SumSpace{S}())
34+
Q2 = typeof(W.C)(undef, physicalspace(W) physicalspace(W) SumSpace{S}())
35+
else
36+
V = BlockTensorKit.oplus(oneunit(S), space(R, 1), oneunit(S))
37+
scale!(Q, d)
38+
scale!(R, inv(d))
39+
Q1 = typeof(W.A)(undef, SumSpace{S}() physicalspace(W) physicalspace(W) space(R, 1))
40+
Q2 = transpose(Q, ((2,), (1, 3)))
41+
end
42+
H[i] = JordanMPOTensor(codomain(W) physicalspace(W) V, Q1, W.B, Q2, W.D)
43+
else
44+
tmp = transpose(cat(insertleftunit(C′, 1), W.A; dims = 1), ((3, 1, 2), (4,)))
45+
if trscheme == notrunc()
46+
Q, R = leftorth!(tmp; alg)
47+
else
48+
@assert alg == SVD() || alg == SDD()
49+
Q, Σ, Vᴴ = tsvd!(tmp; alg, trunc = trscheme)
50+
R = Σ * Vᴴ
51+
end
52+
if dim(R) == 0 # fully truncated
53+
V = BlockTensorKit.oplus(oneunit(S), oneunit(S))
54+
Q1 = typeof(W.A)(undef, SumSpace{S}() physicalspace(W) physicalspace(W) SumSpace{S}())
55+
Q2 = typeof(W.C)(undef, physicalspace(W) physicalspace(W) SumSpace{S}())
56+
else
57+
scale!(Q, d)
58+
scale!(R, inv(d))
59+
Q′ = transpose(Q, ((2, 3), (1, 4)))
60+
Q1 = Q′[2:end, 1, 1, 1]
61+
Q2 = removeunit(SparseBlockTensorMap(Q′[1:1, 1, 1, 1]), 1)
62+
V = BlockTensorKit.oplus(oneunit(S), right_virtualspace(Q′), oneunit(S))
63+
end
64+
H[i] = JordanMPOTensor(codomain(W) physicalspace(W) V, Q1, W.B, Q2, W.D)
65+
end
66+
67+
# absorb into next site
68+
W′ = H[i + 1]
69+
70+
if size(W′, 4) > 1 && dim(R) != 0
71+
@plansor A′[l p; p' r] := R[l; r'] * W′.A[r' p; p' r]
72+
else
73+
A′ = similar(W′.A, right_virtualspace(H[i].A) physicalspace(W′) domain(W′.A))
74+
end
75+
76+
if size(W′, 4) > 1
77+
@plansor C′[l p; p' r] := t[l; r'] * W′.A[r' p; p' r]
78+
C′ = add!(removeunit(C′, 1), W′.C)
79+
else
80+
C′ = W′.C # empty
81+
end
82+
83+
if dim(R) != 0
84+
@plansor B′[l p; p'] := R[l; r] * W′.B[r p; p']
85+
else
86+
B′ = similar(W′.B, right_virtualspace(H[i].A) physicalspace(W′) domain(W′.B))
87+
end
88+
89+
@plansor D′[l p; p'] := t[l; r] * W′.B[r p; p']
90+
D′ = add!(removeunit(D′, 1), W′.D)
91+
92+
H[i + 1] = JordanMPOTensor(
93+
right_virtualspace(H[i]) physicalspace(W′) domain(W′),
94+
A′, B′, C′, D′
95+
)
96+
return H
97+
end
98+
99+
function right_canonicalize!(
100+
H::FiniteMPOHamiltonian, i::Int;
101+
alg = LQpos(), trscheme::TruncationScheme = notrunc()
102+
)
103+
1 < i length(H) || throw(ArgumentError("Bounds error in canonicalize"))
104+
105+
W = H[i]
106+
S = spacetype(W)
107+
d = sqrt(dim(physicalspace(W)))
108+
109+
# orthogonalize second row against last
110+
WI = removeunit(W[end, 1, 1, end], 4)
111+
@plansor t[l; r] := conj(WI[r p; p']) * W.B[l p; p']
112+
# TODO: the following is currently broken due to a TensorKit bug
113+
# @plansor B′[l p; p'] := W.B[l p; p'] - WI[r p; p'] * t[l; r]
114+
@plansor B′[l p; p'] := -WI[r p; p'] * t[l; r]
115+
add!(B′, W.B)
116+
117+
# LQ of second row
118+
if size(W, 4) == 1
119+
tmp = transpose(B′, ((1,), (3, 2)))
120+
if trscheme == notrunc()
121+
R, Q = rightorth!(tmp; alg)
122+
else
123+
@assert alg == SVD() || alg == SDD()
124+
U, Σ, Q = tsvd!(tmp; alg, trunc = trscheme)
125+
R = U * Σ
126+
end
127+
128+
if dim(R) == 0
129+
V = BlockTensorKit.oplus(oneunit(S), oneunit(S))
130+
Q1 = typeof(W.A)(undef, SumSpace{S}() physicalspace(W) physicalspace(W) SumSpace{S}())
131+
Q2 = typeof(W.B)(undef, SumSpace{S}() physicalspace(W) physicalspace(W))
132+
else
133+
V = BlockTensorKit.oplus(oneunit(S), space(Q, 1), oneunit(S))
134+
scale!(Q, d)
135+
scale!(R, inv(d))
136+
Q1 = typeof(W.A)(undef, space(Q, 1) physicalspace(W) physicalspace(W) SumSpace{S}())
137+
Q2 = transpose(Q, ((1, 3), (2,)))
138+
end
139+
H[i] = JordanMPOTensor(V physicalspace(W) domain(W), Q1, Q2, W.C, W.D)
140+
else
141+
tmp = transpose(cat(insertleftunit(B′, 4), W.A; dims = 4), ((1,), (3, 4, 2)))
142+
if trscheme == notrunc()
143+
R, Q = rightorth!(tmp; alg)
144+
else
145+
@assert alg == SVD() || alg == SDD()
146+
U, Σ, Q = tsvd!(tmp; alg, trunc = trscheme)
147+
R = U * Σ
148+
end
149+
if dim(R) == 0
150+
V = BlockTensorKit.oplus(oneunit(S), oneunit(S))
151+
Q1 = typeof(W.A)(undef, SumSpace{S}() physicalspace(W) physicalspace(W) SumSpace{S}())
152+
Q2 = typeof(W.B)(undef, SumSpace{S}() physicalspace(W) physicalspace(W))
153+
else
154+
scale!(Q, d)
155+
scale!(R, inv(d))
156+
Q′ = transpose(Q, ((1, 4), (2, 3)))
157+
Q1 = SparseBlockTensorMap(Q′[1, 1, 1, 2:end])
158+
Q2 = removeunit(SparseBlockTensorMap(Q′[1, 1, 1, 1:1]), 4)
159+
V = BlockTensorKit.oplus(oneunit(S), left_virtualspace(Q′), oneunit(S))
160+
end
161+
H[i] = JordanMPOTensor(V physicalspace(W) domain(W), Q1, Q2, W.C, W.D)
162+
end
163+
164+
# absorb into previous site
165+
W′ = H[i - 1]
166+
167+
if size(W′, 1) > 1 && dim(R) != 0
168+
@plansor A′[l p; p' r] := W′.A[l p; p' r'] * R[r'; r]
169+
else
170+
A′ = similar(W′.A, codomain(W′.A) physicalspace(W′.A) left_virtualspace(H[i].A))
171+
end
172+
173+
if size(W′, 1) > 1
174+
@plansor B′[l p; p' r] := W′.A[l p; p' r'] * t[r'; r]
175+
B′ = add!(removeunit(B′, 4), W′.B)
176+
else
177+
B′ = W′.B
178+
end
179+
180+
if dim(R) != 0
181+
@plansor C′[p; p' r] := W′.C[p; p' r'] * R[r'; r]
182+
else
183+
C′ = similar(W′.C, codomain(W′.C) physicalspace(W′) left_virtualspace(H[i].A))
184+
end
185+
186+
@plansor D′[p; p' r] := W′.C[p; p' r'] * t[r'; r]
187+
D′ = add!(removeunit(D′, 3), W′.D)
188+
H[i - 1] = JordanMPOTensor(codomain(W′) physicalspace(W′) V, A′, B′, C′, D′)
189+
return H
190+
end

test/operators.jl

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,13 @@ module TestOperators
124124
@testset "Finite MPOHamiltonian" begin
125125
L = 3
126126
T = ComplexF64
127-
for V in (ℂ^2, U1Space(0 => 1, 1 => 1))
127+
for V in (ℂ^2, U1Space(-1 => 1, 0 => 1, 1 => 1))
128128
lattice = fill(V, L)
129-
O₁ = rand(T, V, V)
129+
O₁ = randn(T, V, V)
130+
O₁ += O₁'
130131
E = id(storagetype(O₁), domain(O₁))
131-
O₂ = rand(T, V^2 V^2)
132+
O₂ = randn(T, V^2 V^2)
133+
O₂ += O₂'
132134

133135
H1 = FiniteMPOHamiltonian(lattice, i => O₁ for i in 1:L)
134136
H2 = FiniteMPOHamiltonian(lattice, (i, i + 1) => O₂ for i in 1:(L - 1))
@@ -164,6 +166,9 @@ module TestOperators
164166
FiniteMPOHamiltonian(lattice, 3 => O₁)
165167
@test 0.8 * H1 + 0.2 * H1 H1 atol = 1.0e-6
166168
@test convert(TensorMap, H1 + H2) convert(TensorMap, H1) + convert(TensorMap, H2) atol = 1.0e-6
169+
H1_trunc = changebonds(H1, SvdCut(; trscheme = truncdim(0)))
170+
@test H1_trunc H1
171+
@test all(left_virtualspace(H1_trunc) .== left_virtualspace(H1))
167172

168173
# test dot and application
169174
state = rand(T, prod(lattice))
@@ -181,12 +186,21 @@ module TestOperators
181186
vertical_operators = Dict(
182187
(I, I + I_vertical) => O₂ for I in eachindex(IndexCartesian(), square) if I[1] < size(square, 1)
183188
)
184-
operators = merge(local_operators, vertical_operators)
189+
I_horizontal = CartesianIndex(0, 1)
190+
horizontal_operators = Dict(
191+
(I, I + I_horizontal) => O₂ for I in eachindex(IndexCartesian(), square) if I[2] < size(square, 1)
192+
)
193+
operators = merge(local_operators, vertical_operators, horizontal_operators)
185194
H4 = FiniteMPOHamiltonian(grid, operators)
186195

187196
@test H4
188197
FiniteMPOHamiltonian(grid, local_operators) +
189-
FiniteMPOHamiltonian(grid, vertical_operators)
198+
FiniteMPOHamiltonian(grid, vertical_operators) +
199+
FiniteMPOHamiltonian(grid, horizontal_operators) atol = 1.0e-4
200+
201+
H5 = changebonds(H4 / 3 + 2H4 / 3, SvdCut(; trscheme = truncbelow(1.0e-12)))
202+
psi = FiniteMPS(physicalspace(H5), V oneunit(V))
203+
@test expectation_value(psi, H4) expectation_value(psi, H5)
190204
end
191205
end
192206

0 commit comments

Comments
 (0)