@@ -171,6 +171,58 @@ function _contract_state_expr(rowrange, colrange, cartesian_inds = nothing)
171171 end
172172end
173173
174+ function _contract_pepo_state_expr (rowrange, colrange, cartesian_inds = nothing )
175+ rmin, rmax = extrema (rowrange)
176+ cmin, cmax = extrema (colrange)
177+ gridsize = (rmax - rmin + 1 , cmax - cmin + 1 )
178+
179+ return map ((:bra , :ket )) do side
180+ return map (Iterators. product (1 : gridsize[1 ], 1 : gridsize[2 ])) do (i, j)
181+ inds_id = if isnothing (cartesian_inds)
182+ nothing
183+ else
184+ findfirst (== (CartesianIndex (rmin + i - 1 , cmin + j - 1 )), cartesian_inds)
185+ end
186+ physical_label_in = if isnothing (inds_id)
187+ physicallabel (side === :bra ? :in : :out , i, j)
188+ else
189+ physicallabel (:Oopen , inds_id)
190+ end
191+ physical_label_out = if isnothing (inds_id)
192+ physicallabel (side === :bra ? :in : :out , i, j)
193+ else
194+ physicallabel (:O , side, inds_id)
195+ end
196+ return tensorexpr (
197+ :($ (side)[mod1 ($ (rmin + i - 1 ), end ), mod1 ($ (cmin + j - 1 ), end )]),
198+ (physical_label_out, physical_label_in),
199+ (
200+ if i == 1
201+ virtuallabel (NORTH, side, j)
202+ else
203+ virtuallabel (:vertical , side, i - 1 , j)
204+ end ,
205+ if j == gridsize[2 ]
206+ virtuallabel (EAST, side, i)
207+ else
208+ virtuallabel (:horizontal , side, i, j)
209+ end ,
210+ if i == gridsize[1 ]
211+ virtuallabel (SOUTH, side, j)
212+ else
213+ virtuallabel (:vertical , side, i, j)
214+ end ,
215+ if j == 1
216+ virtuallabel (WEST, side, i)
217+ else
218+ virtuallabel (:horizontal , side, i, j - 1 )
219+ end ,
220+ ),
221+ )
222+ end
223+ end
224+ end
225+
174226@generated function _contract_local_operator (
175227 inds:: NTuple{N, Val} ,
176228 O:: AbstractTensorMap{T, S, N, N} ,
@@ -251,25 +303,40 @@ end
251303 return macroexpand (@__MODULE__ , returnex)
252304end
253305
254- """
306+ @doc """
255307$(SIGNATURES)
256308
257309Construct the reduced density matrix `ρ` of the PEPS `peps` with open indices `inds` using the environment `env`.
310+ Alternatively, construct the reduced density matrix `ρ` of a full density matrix PEPO with open indices `inds` using the environment `env`.
258311
259312This works by generating the appropriate contraction on a rectangular patch with its corners
260313specified by `inds`. The result is normalized such that `tr(ρ) = 1`.
261- """
314+ """ reduced_densitymatrix
315+
262316function reduced_densitymatrix (
263317 inds:: NTuple{N, CartesianIndex{2}} , ket:: InfinitePEPS , bra:: InfinitePEPS , env:: CTMRGEnv
264318 ) where {N}
265319 static_inds = Val .(inds)
266320 return _contract_densitymatrix (static_inds, ket, bra, env)
267321end
322+ function reduced_densitymatrix (
323+ inds:: NTuple{N, CartesianIndex{2}} , ket:: InfinitePEPO , bra:: InfinitePEPO , env:: CTMRGEnv
324+ ) where {N}
325+ size (ket) == size (bra) || throw (DimensionMismatch (" incompatible bra and ket dimensions" ))
326+ size (ket, 3 ) == 1 || throw (DimensionMismatch (" only single-layer densitymatrices are supported" ))
327+ static_inds = Val .(inds)
328+ return _contract_densitymatrix (static_inds, ket, bra, env)
329+ end
268330function reduced_densitymatrix (
269331 inds:: NTuple{N, Tuple{Int, Int}} , ket:: InfinitePEPS , bra:: InfinitePEPS , env:: CTMRGEnv
270332 ) where {N}
271333 return reduced_densitymatrix (CartesianIndex .(inds), ket, bra, env)
272334end
335+ function reduced_densitymatrix (
336+ inds:: NTuple{N, Tuple{Int, Int}} , ket:: InfinitePEPO , bra:: InfinitePEPO , env:: CTMRGEnv
337+ ) where {N}
338+ return reduced_densitymatrix (CartesianIndex .(inds), ket, bra, env)
339+ end
273340function reduced_densitymatrix (inds, ket:: InfinitePEPS , env:: CTMRGEnv )
274341 return reduced_densitymatrix (inds, ket, ket, env)
275342end
461528 return ρ / str (ρ)
462529 end
463530end
531+
532+ @generated function _contract_densitymatrix (
533+ inds:: NTuple{N, Val} , ket:: InfinitePEPO , bra:: InfinitePEPO , env:: CTMRGEnv
534+ ) where {N}
535+ cartesian_inds = collect (CartesianIndex{2 }, map (x -> x. parameters[1 ], inds. parameters)) # weird hack to extract information from Val
536+ allunique (cartesian_inds) ||
537+ throw (ArgumentError (" Indices should not overlap: $cartesian_inds ." ))
538+ rowrange = getindex .(cartesian_inds, 1 )
539+ colrange = getindex .(cartesian_inds, 2 )
540+
541+ corner_NW, corner_NE, corner_SE, corner_SW = _contract_corner_expr (rowrange, colrange)
542+ edges_N, edges_E, edges_S, edges_W = _contract_edge_expr (rowrange, colrange)
543+ result = tensorexpr (
544+ :ρ ,
545+ ntuple (i -> physicallabel (:O , :ket , i), N),
546+ ntuple (i -> physicallabel (:O , :bra , i), N),
547+ )
548+ bra, ket = _contract_pepo_state_expr (rowrange, colrange, cartesian_inds)
549+
550+ multiplication_ex = Expr (
551+ :call , :* ,
552+ corner_NW, corner_NE, corner_SE, corner_SW,
553+ edges_N... , edges_E... , edges_S... , edges_W... ,
554+ ket... , map (x -> Expr (:call , :conj , x), bra)... ,
555+ )
556+ multex = :(@autoopt @tensor contractcheck = true $ result := $ multiplication_ex)
557+ return quote
558+ $ (macroexpand (@__MODULE__ , multex))
559+ return ρ / str (ρ)
560+ end
561+ end
0 commit comments