Skip to content

Commit adbedf0

Browse files
committed
Merge branch 'master' into lb/fix_charge_shift
2 parents 8031e6a + 1ac5857 commit adbedf0

File tree

9 files changed

+215
-8
lines changed

9 files changed

+215
-8
lines changed

.github/workflows/Tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
- utility
3434
- bondenv
3535
- timeevol
36+
- toolbox
3637
os:
3738
- ubuntu-latest
3839
- macOS-latest

src/algorithms/contractions/localoperator.jl

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,62 @@ function _contract_state_expr(rowrange, colrange, cartesian_inds = nothing)
171171
end
172172
end
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)
252308
end
253309

254-
"""
310+
@doc """
255311
$(SIGNATURES)
256312
257313
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`.
258315
259316
This works by generating the appropriate contraction on a rectangular patch with its corners
260317
specified by `inds`. The result is normalized such that `tr(ρ) = 1`.
261-
"""
318+
""" reduced_densitymatrix
319+
262320
function 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)
267325
end
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
268334
function 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)
272338
end
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
273344
function reduced_densitymatrix(inds, ket::InfinitePEPS, env::CTMRGEnv)
274345
return reduced_densitymatrix(inds, ket, ket, env)
275346
end
@@ -461,3 +532,34 @@ end
461532
return ρ / str(ρ)
462533
end
463534
end
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

src/algorithms/toolbox.jl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
"""
2-
expectation_value(peps::InfinitePEPS, O::LocalOperator, env::CTMRGEnv)
2+
expectation_value(state, O::LocalOperator, env::CTMRGEnv)
3+
expectation_value(bra, O::LocalOperator, ket, env::CTMRGEnv)
34
4-
Compute the expectation value ⟨peps|O|peps⟩ / ⟨peps|peps⟩ of a [`LocalOperator`](@ref) `O`
5-
for a PEPS `peps` using a given CTMRG environment `env`.
5+
Compute the expectation value ⟨bra|O|ket⟩ / ⟨bra|ket⟩ of a [`LocalOperator`](@ref) `O`.
6+
This can be done either for a PEPS, or alternatively for a density matrix PEPO.
7+
In the latter case the first signature corresponds to a single layer PEPO contraction, while
8+
the second signature yields a bilayer contraction instead.
69
"""
7-
function MPSKit.expectation_value(peps::InfinitePEPS, O::LocalOperator, env::CTMRGEnv)
8-
checklattice(peps, O)
10+
function MPSKit.expectation_value(
11+
bra::Union{InfinitePEPS, InfinitePEPO}, O::LocalOperator,
12+
ket::Union{InfinitePEPS, InfinitePEPO}, env::CTMRGEnv
13+
)
14+
checklattice(bra, O, ket)
915
term_vals = dtmap([O.terms...]) do (inds, operator) # OhMyThreads can't iterate over O.terms directly
10-
ρ = reduced_densitymatrix(inds, peps, peps, env)
16+
ρ = reduced_densitymatrix(inds, ket, bra, env)
1117
return trmul(operator, ρ)
1218
end
1319
return sum(term_vals)
1420
end
21+
MPSKit.expectation_value(peps::InfinitePEPS, O::LocalOperator, env::CTMRGEnv) = expectation_value(peps, O, peps, env)
22+
1523
"""
1624
expectation_value(pf::InfinitePartitionFunction, inds => O, env::CTMRGEnv)
1725

src/networks/tensors.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,23 @@ function physicalspace(t::PEPOTensor)
123123
return codomain_physicalspace(t)
124124
end
125125
virtualspace(t::PEPOTensor, dir) = space(t, dir + 2)
126+
127+
"""
128+
$(SIGNATURES)
129+
130+
Fuse the physical indices of a PEPO tensor, obtaining a PEPS tensor.
131+
"""
132+
function fuse_physicalspaces(O::PEPOTensor)
133+
F = isomorphism(Int, fuse(codomain(O)), codomain(O))
134+
return F * O, F
135+
end
136+
137+
"""
138+
$(SIGNATURES)
139+
140+
Trace out the physical indices of a PEPO tensor, obtaining a partition function tensor.
141+
"""
142+
function trace_physicalspaces(O::PEPOTensor)
143+
@plansor t[W S; N E] := O[p p; N E S W]
144+
return t
145+
end

src/operators/infinitepepo.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ domain_physicalspace(T::InfinitePEPO, r::Int, c::Int) = domain_physicalspace(T[r
153153
function codomain_physicalspace(T::InfinitePEPO, r::Int, c::Int)
154154
return codomain_physicalspace(T[r, c, end])
155155
end
156+
physicalspace(T::InfinitePEPO) = physicalspace.(Ref(T), 1:size(T, 1), 1:size(T, 2))
156157
function physicalspace(T::InfinitePEPO, r::Int, c::Int)
157158
codomain_physicalspace(T, r, c) == domain_physicalspace(T, r, c) || throw(
158159
SpaceMismatch(
@@ -173,6 +174,23 @@ function InfiniteSquareNetwork(top::InfinitePEPS, mid::InfinitePEPO, bot::Infini
173174
)
174175
end
175176

177+
## Conversion to states
178+
179+
function InfinitePartitionFunction::InfinitePEPO)
180+
size(ρ, 3) == 1 || throw(DimensionMismatch("Only single-layer density matrices can be converted to partition functions"))
181+
return InfinitePartitionFunction(
182+
trace_physicalspaces.(reshape(unitcell(ρ), size(ρ, 1), size(ρ, 2)))
183+
)
184+
end
185+
186+
function InfinitePEPS::InfinitePEPO)
187+
size(ρ, 3) == 1 || throw(DimensionMismatch("Only single-layer density matrices can be converted to partition functions"))
188+
return InfinitePEPS(
189+
first.(fuse_physicalspaces.(reshape(unitcell(ρ), size(ρ, 1), size(ρ, 2))))
190+
)
191+
end
192+
193+
176194
## Vector interface
177195

178196
VI.scalartype(::Type{NT}) where {NT <: InfinitePEPO} = scalartype(eltype(NT))

src/operators/localoperator.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ while the second version throws an error if the lattices do not match.
7474
function checklattice(args...)
7575
return checklattice(Bool, args...) || throw(ArgumentError("Lattice mismatch."))
7676
end
77+
checklattice(::Type{Bool}, arg) = true
78+
function checklattice(::Type{Bool}, arg1, arg2, args...)
79+
return checklattice(Bool, arg1, arg2) && checklattice(Bool, arg2, args...)
80+
end
7781
function checklattice(::Type{Bool}, H1::LocalOperator, H2::LocalOperator)
7882
return H1.lattice == H2.lattice
7983
end
@@ -83,6 +87,12 @@ end
8387
function checklattice(::Type{Bool}, H::LocalOperator, peps::InfinitePEPS)
8488
return checklattice(Bool, peps, H)
8589
end
90+
function checklattice(::Type{Bool}, pepo::InfinitePEPO, O::LocalOperator)
91+
return size(pepo, 3) == 1 && reshape(physicalspace(pepo), size(pepo, 1), size(pepo, 2)) == physicalspace(O)
92+
end
93+
function checklattice(::Type{Bool}, O::LocalOperator, pepo::InfinitePEPO)
94+
return checklattice(Bool, pepo, O)
95+
end
8696
@non_differentiable checklattice(args...)
8797

8898
function Base.repeat(O::LocalOperator, m::Int, n::Int)

src/states/infinitepeps.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ end
137137
## Spaces
138138

139139
TensorKit.spacetype(::Type{T}) where {T <: InfinitePEPS} = spacetype(eltype(T))
140+
virtualspace(n::InfinitePEPS, dir) = virtualspace.(unitcell(n), dir)
140141
virtualspace(n::InfinitePEPS, r::Int, c::Int, dir) = virtualspace(n[r, c], dir)
142+
physicalspace(n::InfinitePEPS) = physicalspace.(unitcell(n))
141143
physicalspace(n::InfinitePEPS, r::Int, c::Int) = physicalspace(n[r, c])
142144

143145
## InfiniteSquareNetwork interface

test/runtests.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ end
6565
include("timeevol/sitedep_truncation.jl")
6666
end
6767
end
68+
if GROUP == "ALL" || GROUP == "TOOLBOX"
69+
@time @safetestset "Density matrix from double-layer PEPO" begin
70+
include("toolbox/densitymatrices.jl")
71+
end
72+
end
6873
if GROUP == "ALL" || GROUP == "UTILITY"
6974
@time @safetestset "LocalOperator" begin
7075
include("utility/localoperator.jl")

test/toolbox/densitymatrices.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using TensorKit
2+
using PEPSKit
3+
using Test
4+
using TestExtras
5+
6+
ds = Dict(Trivial =>^2, U1Irrep => U1Space(i => d for (i, d) in zip(-1:1, (1, 1, 2))), FermionParity => Vect[FermionParity](0 => 2, 1 => 1))
7+
Ds = Dict(Trivial =>^3, U1Irrep => U1Space(i => D for (i, D) in zip(-1:1, (1, 2, 2))), FermionParity => Vect[FermionParity](0 => 3, 1 => 2))
8+
χs = Dict(Trivial =>^4, U1Irrep => U1Space(i => χ for (i, χ) in zip(-2:2, (1, 3, 2))), FermionParity => Vect[FermionParity](0 => 3, 1 => 2))
9+
10+
@testset "Double-layer densitymatrix contractions ($I)" for I in keys(ds)
11+
d = ds[I]
12+
D = Ds[I]
13+
χ = χs[I]
14+
ρ = InfinitePEPO(d, D)
15+
16+
ρ_peps = @constinferred InfinitePEPS(ρ)
17+
env = CTMRGEnv(ρ_peps, χ)
18+
19+
O = rand(d, d)
20+
F = isomorphism(fuse(d d'), d d')
21+
@tensor O_doubled[-1; -2] := F[-1; 1 2] * O[1; 3] * twist(F', 2)[3 2; -2]
22+
23+
# Single site
24+
O_singlesite = LocalOperator(reshape(physicalspace(ρ), 1, 1), ((1, 1),) => O)
25+
E1 = expectation_value(ρ, O_singlesite, ρ, env)
26+
O_doubled_singlesite = LocalOperator(physicalspace(ρ_peps), ((1, 1),) => O_doubled)
27+
E2 = expectation_value(ρ_peps, O_doubled_singlesite, ρ_peps, env)
28+
@test E1 E2
29+
30+
# two sites
31+
for inds in zip(
32+
[(1, 1), (1, 1), (1, 1), (1, 2), (1, 1)],
33+
[(2, 1), (1, 2), (2, 2), (2, 1), (3, 1)]
34+
)
35+
O_twosite = LocalOperator(reshape(physicalspace(ρ), 1, 1), inds => O O)
36+
E1 = expectation_value(ρ, O_twosite, ρ, env)
37+
O_doubled_twosite = LocalOperator(physicalspace(ρ_peps), inds => O_doubled O_doubled)
38+
E2 = expectation_value(ρ_peps, O_doubled_twosite, ρ_peps, env)
39+
@test E1 E2
40+
end
41+
end

0 commit comments

Comments
 (0)