Skip to content

Commit 76344b9

Browse files
Add single-layer-pepo reduced densitymatrix (#254)
* Generalize to single-layer densitymatrix expectation values * Fix contract_local_operator for PEPS * Update docstring and error messages * Fix `physicalspace` for PEPO * Further improve `physicalspace` --------- Co-authored-by: Yue Zhengyuan <yuezy1997@icloud.com>
1 parent 1ac5857 commit 76344b9

File tree

5 files changed

+154
-80
lines changed

5 files changed

+154
-80
lines changed

src/algorithms/contractions/localoperator.jl

Lines changed: 98 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function contract_local_operator(
3434
env::CTMRGEnv,
3535
) where {T, S, N}
3636
static_inds = Val.(inds)
37-
return _contract_local_operator(static_inds, O, ket, bra, env)
37+
return _contract_local_operator(static_inds, O, (ket, bra), env)
3838
end
3939
function contract_local_operator(
4040
inds::NTuple{N, Tuple{Int, Int}},
@@ -68,7 +68,7 @@ function _contract_corner_expr(rowrange, colrange)
6868
return corner_NW, corner_NE, corner_SE, corner_SW
6969
end
7070

71-
function _contract_edge_expr(rowrange, colrange)
71+
function _contract_edge_expr(rowrange, colrange, height)
7272
rmin, rmax = extrema(rowrange)
7373
cmin, cmax = extrema(colrange)
7474
gridsize = (rmax - rmin + 1, cmax - cmin + 1)
@@ -77,11 +77,7 @@ function _contract_edge_expr(rowrange, colrange)
7777
E_N = :(env.edges[NORTH, mod1($(rmin - 1), end), mod1($(cmin + i - 1), end)])
7878
return tensorexpr(
7979
E_N,
80-
(
81-
envlabel(NORTH, i - 1),
82-
virtuallabel(NORTH, :ket, i),
83-
virtuallabel(NORTH, :bra, i),
84-
),
80+
(envlabel(NORTH, i - 1), virtuallabel.(NORTH, ntuple(identity, height), i)...),
8581
envlabel(NORTH, i),
8682
)
8783
end
@@ -90,11 +86,7 @@ function _contract_edge_expr(rowrange, colrange)
9086
E_E = :(env.edges[EAST, mod1($(rmin + i - 1), end), mod1($(cmax + 1), end)])
9187
return tensorexpr(
9288
E_E,
93-
(
94-
envlabel(EAST, i - 1),
95-
virtuallabel(EAST, :ket, i),
96-
virtuallabel(EAST, :bra, i),
97-
),
89+
(envlabel(EAST, i - 1), virtuallabel.(EAST, ntuple(identity, height), i)...),
9890
envlabel(EAST, i),
9991
)
10092
end
@@ -103,11 +95,7 @@ function _contract_edge_expr(rowrange, colrange)
10395
E_S = :(env.edges[SOUTH, mod1($(rmax + 1), end), mod1($(cmin + i - 1), end)])
10496
return tensorexpr(
10597
E_S,
106-
(
107-
envlabel(SOUTH, i),
108-
virtuallabel(SOUTH, :ket, i),
109-
virtuallabel(SOUTH, :bra, i),
110-
),
98+
(envlabel(SOUTH, i), virtuallabel.(SOUTH, ntuple(identity, height), i)...),
11199
envlabel(SOUTH, i - 1),
112100
)
113101
end
@@ -116,20 +104,20 @@ function _contract_edge_expr(rowrange, colrange)
116104
E_W = :(env.edges[WEST, mod1($(rmin + i - 1), end), mod1($(cmin - 1), end)])
117105
return tensorexpr(
118106
E_W,
119-
(envlabel(WEST, i), virtuallabel(WEST, :ket, i), virtuallabel(WEST, :bra, i)),
107+
(envlabel(WEST, i), virtuallabel.(WEST, ntuple(identity, height), i)...),
120108
envlabel(WEST, i - 1),
121109
)
122110
end
123111

124112
return edges_N, edges_E, edges_S, edges_W
125113
end
126114

127-
function _contract_state_expr(rowrange, colrange, cartesian_inds = nothing)
115+
function _contract_state_expr(rowrange, colrange, height, cartesian_inds = nothing)
128116
rmin, rmax = extrema(rowrange)
129117
cmin, cmax = extrema(colrange)
130118
gridsize = (rmax - rmin + 1, cmax - cmin + 1)
131119

132-
return map((:bra, :ket)) do side
120+
return map(1:height) do side
133121
return map(Iterators.product(1:gridsize[1], 1:gridsize[2])) do (i, j)
134122
inds_id = if isnothing(cartesian_inds)
135123
nothing
@@ -142,7 +130,7 @@ function _contract_state_expr(rowrange, colrange, cartesian_inds = nothing)
142130
physicallabel(:O, side, inds_id)
143131
end
144132
return tensorexpr(
145-
:($(side)[mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)]),
133+
:(state[$(side)][mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)]),
146134
(physical_label,),
147135
(
148136
if i == 1
@@ -171,35 +159,50 @@ function _contract_state_expr(rowrange, colrange, cartesian_inds = nothing)
171159
end
172160
end
173161

174-
function _contract_pepo_state_expr(rowrange, colrange, cartesian_inds = nothing)
162+
function _contract_pepo_state_expr(rowrange, colrange, height, cartesian_inds = nothing)
163+
@assert height == 1 || height == 2 "Contraction with more than 2 layers of PEPO is unintended."
175164
rmin, rmax = extrema(rowrange)
176165
cmin, cmax = extrema(colrange)
177166
gridsize = (rmax - rmin + 1, cmax - cmin + 1)
178-
179-
return map((:bra, :ket)) do side
167+
return map(1:height) do side
180168
return map(Iterators.product(1:gridsize[1], 1:gridsize[2])) do (i, j)
181169
inds_id = if isnothing(cartesian_inds)
182170
nothing
183171
else
184172
findfirst(==(CartesianIndex(rmin + i - 1, cmin + j - 1)), cartesian_inds)
185173
end
186-
physical_label_in = if isnothing(inds_id)
187-
physicallabel(:in, i, j)
188-
else
189-
physicallabel(:Oopen, inds_id)
190-
end
191-
physical_label_out = if isnothing(inds_id)
192-
physicallabel(:out, i, j)
174+
if height == 1
175+
physical_label_in = if isnothing(inds_id)
176+
physicallabel(i, j)
177+
else
178+
physicallabel(:O, 2, inds_id)
179+
end
180+
physical_label_out = if isnothing(inds_id)
181+
physicallabel(i, j)
182+
else
183+
physicallabel(:O, 1, inds_id)
184+
end
185+
tensor_name = :(twistdual(state[1][mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)], 2))
193186
else
194-
physicallabel(:O, side, inds_id)
187+
physical_label_in = if isnothing(inds_id)
188+
physicallabel(:in, i, j)
189+
else
190+
physicallabel(:Oopen, inds_id)
191+
end
192+
physical_label_out = if isnothing(inds_id)
193+
physicallabel(:out, i, j)
194+
else
195+
physicallabel(:O, side, inds_id)
196+
end
197+
tensor_name = if side == 2
198+
:(state[2][mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)])
199+
else
200+
:(twistdual(state[1][mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)], (1, 2)))
201+
end
195202
end
203+
196204
return tensorexpr(
197-
if side == :ket
198-
:(twistdual(ket[mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)], (1, 2)))
199-
else
200-
:(bra[mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)])
201-
end,
202-
(physical_label_out, physical_label_in),
205+
tensor_name, (physical_label_out, physical_label_in),
203206
(
204207
if i == 1
205208
virtuallabel(NORTH, side, j)
@@ -230,7 +233,7 @@ end
230233
@generated function _contract_local_operator(
231234
inds::NTuple{N, Val},
232235
O::AbstractTensorMap{T, S, N, N},
233-
ket::InfinitePEPS, bra::InfinitePEPS,
236+
state::Tuple{InfinitePEPS, InfinitePEPS},
234237
env::CTMRGEnv,
235238
) where {T, S, N}
236239
cartesian_inds = collect(CartesianIndex{2}, map(x -> x.parameters[1], inds.parameters)) # weird hack to extract information from Val
@@ -240,13 +243,13 @@ end
240243
colrange = getindex.(cartesian_inds, 2)
241244

242245
corner_NW, corner_NE, corner_SE, corner_SW = _contract_corner_expr(rowrange, colrange)
243-
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange)
246+
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange, 2)
244247
operator = tensorexpr(
245248
:O,
246-
ntuple(i -> physicallabel(:O, :bra, i), N),
247-
ntuple(i -> physicallabel(:O, :ket, i), N),
249+
ntuple(i -> physicallabel(:O, 2, i), N),
250+
ntuple(i -> physicallabel(:O, 1, i), N),
248251
)
249-
bra, ket = _contract_state_expr(rowrange, colrange, cartesian_inds)
252+
ket, bra = _contract_state_expr(rowrange, colrange, 2, cartesian_inds)
250253

251254
multiplication_ex = Expr(
252255
:call, :*,
@@ -275,24 +278,24 @@ function contract_local_norm(
275278
inds::NTuple{N, CartesianIndex{2}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
276279
) where {N}
277280
static_inds = Val.(inds)
278-
return _contract_local_norm(static_inds, ket, bra, env)
281+
return _contract_local_norm(static_inds, (ket, bra), env)
279282
end
280283
function contract_local_norm(
281284
inds::NTuple{N, Tuple{Int, Int}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
282285
) where {N}
283286
return contract_local_norm(CartesianIndex.(inds), ket, bra, env)
284287
end
285288
@generated function _contract_local_norm(
286-
inds::NTuple{N, Val}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
289+
inds::NTuple{N, Val}, state::Tuple{InfinitePEPS, InfinitePEPS}, env::CTMRGEnv
287290
) where {N}
288291
cartesian_inds = collect(CartesianIndex{2}, map(x -> x.parameters[1], inds.parameters)) # weird hack to extract information from Val
289292
allunique(cartesian_inds) || throw(ArgumentError("Indices should not overlap: $cartesian_inds."))
290293
rowrange = getindex.(cartesian_inds, 1)
291294
colrange = getindex.(cartesian_inds, 2)
292295

293296
corner_NW, corner_NE, corner_SE, corner_SW = _contract_corner_expr(rowrange, colrange)
294-
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange)
295-
bra, ket = _contract_state_expr(rowrange, colrange)
297+
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange, 2)
298+
ket, bra = _contract_state_expr(rowrange, colrange, 2)
296299

297300
multiplication_ex = Expr(
298301
:call, :*,
@@ -310,8 +313,8 @@ end
310313
@doc """
311314
$(SIGNATURES)
312315
313-
Construct the reduced density matrix `ρ` of the PEPS `peps` with open indices `inds` using the environment `env`.
314-
Alternatively, construct the reduced density matrix `ρ` of a full density matrix PEPO with open indices `inds` using the environment `env`.
316+
Construct the reduced density matrix `ρ` of PEPS or PEPO (representing PEPS with ancilla legs) `ket`, `bra` with open indices `inds` using the environment `env`.
317+
Alternatively, construct the reduced density matrix `ρ` of a mixed state specified by the density matrix PEPO `state` with open indices `inds` using the environment `env`.
315318
316319
This works by generating the appropriate contraction on a rectangular patch with its corners
317320
specified by `inds`. The result is normalized such that `tr(ρ) = 1`.
@@ -321,15 +324,22 @@ function reduced_densitymatrix(
321324
inds::NTuple{N, CartesianIndex{2}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
322325
) where {N}
323326
static_inds = Val.(inds)
324-
return _contract_densitymatrix(static_inds, ket, bra, env)
327+
return _contract_densitymatrix(static_inds, (ket, bra), env)
328+
end
329+
function reduced_densitymatrix(
330+
inds::NTuple{N, CartesianIndex{2}}, state::InfinitePEPO, env::CTMRGEnv
331+
) where {N}
332+
size(state, 3) == 1 || throw(DimensionMismatch("only single-layer densitymatrices are supported"))
333+
static_inds = Val.(inds)
334+
return _contract_densitymatrix(static_inds, (state,), env)
325335
end
326336
function reduced_densitymatrix(
327337
inds::NTuple{N, CartesianIndex{2}}, ket::InfinitePEPO, bra::InfinitePEPO, env::CTMRGEnv
328338
) where {N}
329339
size(ket) == size(bra) || throw(DimensionMismatch("incompatible bra and ket dimensions"))
330340
size(ket, 3) == 1 || throw(DimensionMismatch("only single-layer densitymatrices are supported"))
331341
static_inds = Val.(inds)
332-
return _contract_densitymatrix(static_inds, ket, bra, env)
342+
return _contract_densitymatrix(static_inds, (ket, bra), env)
333343
end
334344
function reduced_densitymatrix(
335345
inds::NTuple{N, Tuple{Int, Int}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
@@ -344,6 +354,11 @@ end
344354
function reduced_densitymatrix(inds, ket::InfinitePEPS, env::CTMRGEnv)
345355
return reduced_densitymatrix(inds, ket, ket, env)
346356
end
357+
function reduced_densitymatrix(
358+
inds::NTuple{N, Tuple{Int, Int}}, state::InfinitePEPO, env::CTMRGEnv
359+
) where {N}
360+
return reduced_densitymatrix(CartesianIndex.(inds), state, env)
361+
end
347362

348363
# Special case 1x1 density matrix:
349364
# Keep contraction order but try to optimize intermediate permutations:
@@ -396,7 +411,7 @@ function reduced_densitymatrix(
396411
return reduced_densitymatrix1x2(inds[1], ket, bra, env)
397412
else
398413
static_inds = Val.(inds)
399-
return _contract_densitymatrix(static_inds, ket, bra, env)
414+
return _contract_densitymatrix(static_inds, (ket, bra), env)
400415
end
401416
end
402417

@@ -503,7 +518,7 @@ function reduced_densitymatrix1x2(
503518
end
504519

505520
@generated function _contract_densitymatrix(
506-
inds::NTuple{N, Val}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
521+
inds::NTuple{N, Val}, state::Tuple{InfinitePEPS, InfinitePEPS}, env::CTMRGEnv
507522
) where {N}
508523
cartesian_inds = collect(CartesianIndex{2}, map(x -> x.parameters[1], inds.parameters)) # weird hack to extract information from Val
509524
allunique(cartesian_inds) ||
@@ -512,13 +527,13 @@ end
512527
colrange = getindex.(cartesian_inds, 2)
513528

514529
corner_NW, corner_NE, corner_SE, corner_SW = _contract_corner_expr(rowrange, colrange)
515-
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange)
530+
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange, 2)
516531
result = tensorexpr(
517532
:ρ,
518-
ntuple(i -> physicallabel(:O, :ket, i), N),
519-
ntuple(i -> physicallabel(:O, :bra, i), N),
533+
ntuple(i -> physicallabel(:O, 1, i), N),
534+
ntuple(i -> physicallabel(:O, 2, i), N),
520535
)
521-
bra, ket = _contract_state_expr(rowrange, colrange, cartesian_inds)
536+
ket, bra = _contract_state_expr(rowrange, colrange, 2, cartesian_inds)
522537

523538
multiplication_ex = Expr(
524539
:call, :*,
@@ -534,29 +549,43 @@ end
534549
end
535550

536551
@generated function _contract_densitymatrix(
537-
inds::NTuple{N, Val}, ket::InfinitePEPO, bra::InfinitePEPO, env::CTMRGEnv
538-
) where {N}
552+
inds::NTuple{N, Val}, state::Tuple{InfinitePEPO, Vararg{InfinitePEPO, M}}, env::CTMRGEnv
553+
) where {N, M}
554+
height = M + 1
555+
@assert height == 1 || height == 2 "Contraction with more than 2 layers of PEPO is unintended."
539556
cartesian_inds = collect(CartesianIndex{2}, map(x -> x.parameters[1], inds.parameters)) # weird hack to extract information from Val
540557
allunique(cartesian_inds) ||
541558
throw(ArgumentError("Indices should not overlap: $cartesian_inds."))
542559
rowrange = getindex.(cartesian_inds, 1)
543560
colrange = getindex.(cartesian_inds, 2)
544561

545562
corner_NW, corner_NE, corner_SE, corner_SW = _contract_corner_expr(rowrange, colrange)
546-
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange)
563+
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange, height)
547564
result = tensorexpr(
548565
:ρ,
549-
ntuple(i -> physicallabel(:O, :ket, i), N),
550-
ntuple(i -> physicallabel(:O, :bra, i), N),
566+
ntuple(i -> physicallabel(:O, 1, i), N),
567+
ntuple(i -> physicallabel(:O, 2, i), N),
551568
)
552-
bra, ket = _contract_pepo_state_expr(rowrange, colrange, cartesian_inds)
553569

554-
multiplication_ex = Expr(
555-
:call, :*,
556-
corner_NW, corner_NE, corner_SE, corner_SW,
557-
edges_N..., edges_E..., edges_S..., edges_W...,
558-
ket..., map(x -> Expr(:call, :conj, x), bra)...,
559-
)
570+
pepos = _contract_pepo_state_expr(rowrange, colrange, height, cartesian_inds)
571+
572+
if height == 2
573+
ket, bra = pepos
574+
multiplication_ex = Expr(
575+
:call, :*,
576+
corner_NW, corner_NE, corner_SE, corner_SW,
577+
edges_N..., edges_E..., edges_S..., edges_W...,
578+
ket..., map(x -> Expr(:call, :conj, x), bra)...,
579+
)
580+
else
581+
multiplication_ex = Expr(
582+
:call, :*,
583+
corner_NW, corner_NE, corner_SE, corner_SW,
584+
edges_N..., edges_E..., edges_S..., edges_W...,
585+
pepos[1]...
586+
)
587+
end
588+
560589
multex = :(@autoopt @tensor contractcheck = true $result := $multiplication_ex)
561590
return quote
562591
$(macroexpand(@__MODULE__, multex))

src/algorithms/toolbox.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ function MPSKit.expectation_value(
1919
return sum(term_vals)
2020
end
2121
MPSKit.expectation_value(peps::InfinitePEPS, O::LocalOperator, env::CTMRGEnv) = expectation_value(peps, O, peps, env)
22+
function MPSKit.expectation_value(
23+
state::InfinitePEPO, O::LocalOperator, env::CTMRGEnv
24+
)
25+
checklattice(state, O)
26+
term_vals = dtmap([O.terms...]) do (inds, operator) # OhMyThreads can't iterate over O.terms directly
27+
ρ = reduced_densitymatrix(inds, state, env)
28+
return trmul(operator, ρ)
29+
end
30+
return sum(term_vals)
31+
end
2232

2333
"""
2434
expectation_value(pf::InfinitePartitionFunction, inds => O, env::CTMRGEnv)

src/operators/infinitepepo.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,8 @@ end
150150
TensorKit.spacetype(::Type{P}) where {P <: InfinitePEPO} = spacetype(eltype(P))
151151
virtualspace(T::InfinitePEPO, r::Int, c::Int, h::Int, dir) = virtualspace(T[r, c, h], dir)
152152
domain_physicalspace(T::InfinitePEPO, r::Int, c::Int) = domain_physicalspace(T[r, c, 1])
153-
function codomain_physicalspace(T::InfinitePEPO, r::Int, c::Int)
154-
return codomain_physicalspace(T[r, c, end])
155-
end
156-
physicalspace(T::InfinitePEPO) = physicalspace.(Ref(T), 1:size(T, 1), 1:size(T, 2))
153+
codomain_physicalspace(T::InfinitePEPO, r::Int, c::Int) = codomain_physicalspace(T[r, c, end])
154+
physicalspace(T::InfinitePEPO) = [physicalspace(T, row, col) for row in axes(T, 1), col in axes(T, 2)]
157155
function physicalspace(T::InfinitePEPO, r::Int, c::Int)
158156
codomain_physicalspace(T, r, c) == domain_physicalspace(T, r, c) || throw(
159157
SpaceMismatch(

0 commit comments

Comments
 (0)