@@ -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)
3838end
3939function 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
6969end
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
125113end
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
172160end
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)
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)
279282end
280283function 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)
284287end
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, :* ,
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
316319This works by generating the appropriate contraction on a rectangular patch with its corners
317320specified 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)
325335end
326336function 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)
333343end
334344function reduced_densitymatrix(
335345 inds:: NTuple{N, Tuple{Int, Int}} , ket:: InfinitePEPS , bra:: InfinitePEPS , env:: CTMRGEnv
344354function reduced_densitymatrix(inds, ket:: InfinitePEPS , env:: CTMRGEnv )
345355 return reduced_densitymatrix(inds, ket, ket, env)
346356end
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
401416end
402417
@@ -503,7 +518,7 @@ function reduced_densitymatrix1x2(
503518end
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
534549end
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))
0 commit comments