@@ -171,6 +171,62 @@ 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 (: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)
193+ else
194+ physicallabel (:O , side, inds_id)
195+ end
196+ 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),
203+ (
204+ if i == 1
205+ virtuallabel (NORTH, side, j)
206+ else
207+ virtuallabel (:vertical , side, i - 1 , j)
208+ end ,
209+ if j == gridsize[2 ]
210+ virtuallabel (EAST, side, i)
211+ else
212+ virtuallabel (:horizontal , side, i, j)
213+ end ,
214+ if i == gridsize[1 ]
215+ virtuallabel (SOUTH, side, j)
216+ else
217+ virtuallabel (:vertical , side, i, j)
218+ end ,
219+ if j == 1
220+ virtuallabel (WEST, side, i)
221+ else
222+ virtuallabel (:horizontal , side, i, j - 1 )
223+ end ,
224+ ),
225+ )
226+ end
227+ end
228+ end
229+
174230@generated function _contract_local_operator (
175231 inds:: NTuple{N, Val} ,
176232 O:: AbstractTensorMap{T, S, N, N} ,
@@ -251,25 +307,40 @@ end
251307 return macroexpand (@__MODULE__ , returnex)
252308end
253309
254- """
310+ @doc """
255311$(SIGNATURES)
256312
257313Construct 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`.
258315
259316This works by generating the appropriate contraction on a rectangular patch with its corners
260317specified by `inds`. The result is normalized such that `tr(ρ) = 1`.
261- """
318+ """ reduced_densitymatrix
319+
262320function reduced_densitymatrix (
263321 inds:: NTuple{N, CartesianIndex{2}} , ket:: InfinitePEPS , bra:: InfinitePEPS , env:: CTMRGEnv
264322 ) where {N}
265323 static_inds = Val .(inds)
266324 return _contract_densitymatrix (static_inds, ket, bra, env)
267325end
326+ function reduced_densitymatrix (
327+ inds:: NTuple{N, CartesianIndex{2}} , ket:: InfinitePEPO , bra:: InfinitePEPO , env:: CTMRGEnv
328+ ) where {N}
329+ size (ket) == size (bra) || throw (DimensionMismatch (" incompatible bra and ket dimensions" ))
330+ size (ket, 3 ) == 1 || throw (DimensionMismatch (" only single-layer densitymatrices are supported" ))
331+ static_inds = Val .(inds)
332+ return _contract_densitymatrix (static_inds, ket, bra, env)
333+ end
268334function reduced_densitymatrix (
269335 inds:: NTuple{N, Tuple{Int, Int}} , ket:: InfinitePEPS , bra:: InfinitePEPS , env:: CTMRGEnv
270336 ) where {N}
271337 return reduced_densitymatrix (CartesianIndex .(inds), ket, bra, env)
272338end
339+ function reduced_densitymatrix (
340+ inds:: NTuple{N, Tuple{Int, Int}} , ket:: InfinitePEPO , bra:: InfinitePEPO , env:: CTMRGEnv
341+ ) where {N}
342+ return reduced_densitymatrix (CartesianIndex .(inds), ket, bra, env)
343+ end
273344function reduced_densitymatrix (inds, ket:: InfinitePEPS , env:: CTMRGEnv )
274345 return reduced_densitymatrix (inds, ket, ket, env)
275346end
461532 return ρ / str (ρ)
462533 end
463534end
535+
536+ @generated function _contract_densitymatrix (
537+ inds:: NTuple{N, Val} , ket:: InfinitePEPO , bra:: InfinitePEPO , env:: CTMRGEnv
538+ ) where {N}
539+ cartesian_inds = collect (CartesianIndex{2 }, map (x -> x. parameters[1 ], inds. parameters)) # weird hack to extract information from Val
540+ allunique (cartesian_inds) ||
541+ throw (ArgumentError (" Indices should not overlap: $cartesian_inds ." ))
542+ rowrange = getindex .(cartesian_inds, 1 )
543+ colrange = getindex .(cartesian_inds, 2 )
544+
545+ 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)
547+ result = tensorexpr (
548+ :ρ ,
549+ ntuple (i -> physicallabel (:O , :ket , i), N),
550+ ntuple (i -> physicallabel (:O , :bra , i), N),
551+ )
552+ bra, ket = _contract_pepo_state_expr (rowrange, colrange, cartesian_inds)
553+
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+ )
560+ multex = :(@autoopt @tensor contractcheck = true $ result := $ multiplication_ex)
561+ return quote
562+ $ (macroexpand (@__MODULE__ , multex))
563+ return ρ / str (ρ)
564+ end
565+ end
0 commit comments