Skip to content

Commit 59a7f9d

Browse files
authored
Reduced density matrices (#230)
* Switch expectation_value to reduced densitymatrix * Minor improvement * Use AD compatible normalization * FFF: Fix the f'ing fermions * Fix bug in contraction expressions * Special case 1x1 density matrix * Special case 2x1 density matrix * Safeguard fermionic non-canonical arrows * Special case 1x2 density matrix * Add utility syntax bra=ket * Export `reduced_densitymatrix`
1 parent 66f56f1 commit 59a7f9d

File tree

4 files changed

+252
-7
lines changed

4 files changed

+252
-7
lines changed

src/PEPSKit.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ export CTMRGEnv, SequentialCTMRG, SimultaneousCTMRG
9090
export FixedSpaceTruncation, SiteDependentTruncation
9191
export HalfInfiniteProjector, FullInfiniteProjector
9292
export LocalOperator, physicalspace
93-
export expectation_value, cost_function, product_peps, correlation_length, network_value
94-
export correlator
93+
export product_peps
94+
export reduced_densitymatrix, expectation_value, network_value, cost_function
95+
export correlator, correlation_length
9596
export leading_boundary
9697
export PEPSOptimize, GeomSum, ManualIter, LinSolver, EigSolver
9798
export fixedpoint

src/algorithms/contractions/localoperator.jl

Lines changed: 222 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ function _contract_state_expr(rowrange, colrange, cartesian_inds=nothing)
144144
physicallabel(:O, side, inds_id)
145145
end
146146
return tensorexpr(
147-
:(bra[mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)]),
147+
:($(side)[mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)]),
148148
(physical_label,),
149149
(
150150
if i == 1
@@ -212,7 +212,7 @@ end
212212
)
213213

214214
returnex = quote
215-
@autoopt @tensor opt = $multiplication_ex
215+
@autoopt @tensor $multiplication_ex
216216
end
217217
return macroexpand(@__MODULE__, returnex)
218218
end
@@ -266,7 +266,226 @@ end
266266
)
267267

268268
returnex = quote
269-
@autoopt @tensor opt = $multiplication_ex
269+
@autoopt @tensor $multiplication_ex
270270
end
271271
return macroexpand(@__MODULE__, returnex)
272272
end
273+
274+
"""
275+
$(SIGNATURES)
276+
277+
Construct the reduced density matrix `ρ` of the PEPS `peps` with open indices `inds` using the environment `env`.
278+
279+
This works by generating the appropriate contraction on a rectangular patch with its corners
280+
specified by `inds`. The result is normalized such that `tr(ρ) = 1`.
281+
"""
282+
function reduced_densitymatrix(
283+
inds::NTuple{N,CartesianIndex{2}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
284+
) where {N}
285+
static_inds = Val.(inds)
286+
return _contract_densitymatrix(static_inds, ket, bra, env)
287+
end
288+
function reduced_densitymatrix(
289+
inds::NTuple{N,Tuple{Int,Int}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
290+
) where {N}
291+
return reduced_densitymatrix(CartesianIndex.(inds), ket, bra, env)
292+
end
293+
function reduced_densitymatrix(inds, ket::InfinitePEPS, env::CTMRGEnv)
294+
return reduced_densitymatrix(inds, ket, ket, env)
295+
end
296+
297+
# Special case 1x1 density matrix:
298+
# Keep contraction order but try to optimize intermediate permutations:
299+
# EE_SWA is largest object so keep largest legs to the front there
300+
function reduced_densitymatrix(
301+
inds::Tuple{CartesianIndex{2}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
302+
)
303+
row, col = Tuple(inds[1])
304+
305+
# Unpack variables and absorb corners
306+
A = ket[mod1(row, end), mod1(col, end)]
307+
= bra[mod1(row, end), mod1(col, end)]
308+
309+
E_north =
310+
env.edges[NORTH, mod1(row - 1, end), mod1(col, end)] *
311+
twistdual(env.corners[NORTHEAST, mod1(row - 1, end), mod1(col + 1, end)], 1)
312+
E_east =
313+
env.edges[EAST, mod1(row, end), mod1(col + 1, end)] *
314+
twistdual(env.corners[SOUTHEAST, mod1(row + 1, end), mod1(col + 1, end)], 1)
315+
E_south =
316+
env.edges[SOUTH, mod1(row + 1, end), mod1(col, end)] *
317+
twistdual(env.corners[SOUTHWEST, mod1(row + 1, end), mod1(col - 1, end)], 1)
318+
E_west =
319+
env.edges[WEST, mod1(row, end), mod1(col - 1, end)] *
320+
twistdual(env.corners[NORTHWEST, mod1(row - 1, end), mod1(col - 1, end)], 1)
321+
322+
@tensor EE_SW[χSE χNW DSb DWb; DSt DWt] :=
323+
E_south[χSE DSt DSb; χSW] * E_west[χSW DWt DWb; χNW]
324+
325+
@tensor EE_SWA[χSE χNW DNt DEt; dt DSb DWb] :=
326+
EE_SW[χSE χNW DSb DWb; DSt DWt] * A[dt; DNt DEt DSt DWt]
327+
328+
@tensor EE_NE[DNb DEb; χSE χNW DNt DEt] :=
329+
E_north[χNW DNt DNb; χNE] * E_east[χNE DEt DEb; χSE]
330+
331+
@tensor EEAEE[dt; DNb DEb DSb DWb] :=
332+
EE_NE[DNb DEb; χSE χNW DNt DEt] * EE_SWA[χSE χNW DNt DEt; dt DSb DWb]
333+
334+
@tensor ρ[dt; db] := EEAEE[dt; DNb DEb DSb DWb] * conj(Ā[db; DNb DEb DSb DWb])
335+
336+
return ρ / str(ρ)
337+
end
338+
339+
function reduced_densitymatrix(
340+
inds::NTuple{2,CartesianIndex{2}}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
341+
)
342+
if inds[2] - inds[1] == CartesianIndex(1, 0)
343+
return reduced_densitymatrix2x1(inds[1], ket, bra, env)
344+
elseif inds[2] - inds[1] == CartesianIndex(0, 1)
345+
return reduced_densitymatrix1x2(inds[1], ket, bra, env)
346+
else
347+
static_inds = Val.(inds)
348+
return _contract_densitymatrix(static_inds, ket, bra, env)
349+
end
350+
end
351+
352+
# Special case 2x1 density matrix:
353+
# Keep contraction order but try to optimize intermediate permutations:
354+
function reduced_densitymatrix2x1(
355+
ind::CartesianIndex, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
356+
)
357+
row, col = Tuple(ind)
358+
359+
# Unpack variables and absorb corners
360+
A_north = ket[mod1(row, end), mod1(col, end)]
361+
Ā_north = bra[mod1(row, end), mod1(col, end)]
362+
A_south = ket[mod1(row + 1, end), mod1(col, end)]
363+
Ā_south = bra[mod1(row + 1, end), mod1(col, end)]
364+
365+
E_north =
366+
env.edges[NORTH, mod1(row - 1, end), mod1(col, end)] *
367+
twistdual(env.corners[NORTHEAST, mod1(row - 1, end), mod1(col + 1, end)], 1)
368+
E_northeast = env.edges[EAST, mod1(row, end), mod1(col + 1, end)]
369+
E_southeast =
370+
env.edges[EAST, mod1(row + 1, end), mod1(col + 1, end)] *
371+
twistdual(env.corners[SOUTHEAST, mod1(row + 2, end), mod1(col + 1, end)], 1)
372+
E_south =
373+
env.edges[SOUTH, mod1(row + 2, end), mod1(col, end)] *
374+
twistdual(env.corners[SOUTHWEST, mod1(row + 2, end), mod1(col - 1, end)], 1)
375+
E_southwest = env.edges[WEST, mod1(row + 1, end), mod1(col - 1, end)]
376+
E_northwest =
377+
env.edges[WEST, mod1(row, end), mod1(col - 1, end)] *
378+
twistdual(env.corners[NORTHWEST, mod1(row - 1, end), mod1(col - 1, end)], 1)
379+
380+
@tensor EE_NW[χW χNE DNWt DNt; DNWb DNb] :=
381+
E_northwest[χW DNWt DNWb; χNW] * E_north[χNW DNt DNb; χNE]
382+
@tensor EEA_NW[χW DMb dNb χNE DNEb; DNWt DNt] :=
383+
EE_NW[χW χNE DNWt DNt; DNWb DNb] * conj(Ā_north[dNb; DNb DNEb DMb DNWb])
384+
@tensor EEAA_NW[χW DMb dNb dNt DMt; χNE DNEt DNEb] :=
385+
EEA_NW[χW DMb dNb χNE DNEb; DNWt DNt] * A_north[dNt; DNt DNEt DMt DNWt]
386+
@tensor EEEAA_N[dNt dNb; χW DMt DMb χE] :=
387+
EEAA_NW[χW DMb dNb dNt DMt; χNE DNEt DNEb] * E_northeast[χNE DNEt DNEb; χE]
388+
389+
@tensor EE_SE[χE χSW DSEt DSt; DSEb DSb] :=
390+
E_southeast[χE DSEt DSEb; χSE] * E_south[χSE DSt DSb; χSW]
391+
@tensor EEA_SE[χE DMb dSb χSW DSWb; DSEt DSt] :=
392+
EE_SE[χE χSW DSEt DSt; DSEb DSb] * conj(Ā_south[dSb; DMb DSEb DSb DSWb])
393+
@tensor EEAA_SE[χE DMb dSb dSt DMt; χSW DSWt DSWb] :=
394+
EEA_SE[χE DMb dSb χSW DSWb; DSEt DSt] * A_south[dSt; DMt DSEt DSt DSWt]
395+
@tensor EEEAA_S[χW DMt DMb χE; dSt dSb] :=
396+
EEAA_SE[χE DMb dSb dSt DMt; χSW DSWt DSWb] * E_southwest[χSW DSWt DSWb; χW]
397+
398+
@tensor ρ[dNt dSt; dNb dSb] :=
399+
EEEAA_N[dNt dNb; χW DMt DMb χE] * EEEAA_S[χW DMt DMb χE; dSt dSb]
400+
401+
return ρ / str(ρ)
402+
end
403+
404+
function reduced_densitymatrix1x2(
405+
ind::CartesianIndex, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
406+
)
407+
row, col = Tuple(ind)
408+
409+
# Unpack variables and absorb corners
410+
A_west = ket[mod1(row, end), mod1(col, end)]
411+
Ā_west = bra[mod1(row, end), mod1(col, end)]
412+
A_east = ket[mod1(row, end), mod1(col + 1, end)]
413+
Ā_east = bra[mod1(row, end), mod1(col + 1, end)]
414+
415+
E_northwest = env.edges[NORTH, mod1(row - 1, end), mod1(col, end)]
416+
E_northeast =
417+
env.edges[NORTH, mod1(row - 1, end), mod1(col + 1, end)] *
418+
twistdual(env.corners[NORTHEAST, mod1(row - 1, end), mod1(col + 2, end)], 1)
419+
E_east =
420+
env.edges[EAST, mod1(row, end), mod1(col + 2, end)] *
421+
twistdual(env.corners[SOUTHEAST, mod1(row + 1, end), mod1(col + 2, end)], 1)
422+
E_southeast = env.edges[SOUTH, mod1(row + 1, end), mod1(col + 1, end)]
423+
E_southwest =
424+
env.edges[SOUTH, mod1(row + 1, end), mod1(col, end)] *
425+
twistdual(env.corners[SOUTHWEST, mod1(row + 1, end), mod1(col - 1, end)], 1)
426+
E_west =
427+
env.edges[WEST, mod1(row, end), mod1(col - 1, end)] *
428+
twistdual(env.corners[NORTHWEST, mod1(row - 1, end), mod1(col - 1, end)], 1)
429+
430+
@tensor EE_SW[χS χNW DSWt DWt; DSWb DWb] :=
431+
E_southwest[χS DSWt DSWb; χSW] * E_west[χSW DWt DWb; χNW]
432+
@tensor EEA_SW[χS DMb dWb χNW DNWb; DSWt DWt] :=
433+
EE_SW[χS χNW DSWt DWt; DSWb DWb] * conj(Ā_west[dWb; DNWb DMb DSWb DWb])
434+
@tensor EEAA_SW[χS DMb dWb dWt DMt; χNW DNWt DNWb] :=
435+
EEA_SW[χS DMb dWb χNW DNWb; DSWt DWt] * A_west[dWt; DNWt DMt DSWt DWt]
436+
@tensor EEEAA_W[dWt dWb; χS DMt DMb χN] :=
437+
EEAA_SW[χS DMb dWb dWt DMt; χNW DNWt DNWb] * E_northwest[χNW DNWt DNWb; χN]
438+
439+
@tensor EE_NE[χN χSE DNEt DEt; DNEb DEb] :=
440+
E_northeast[χN DNEt DNEb; χNE] * E_east[χNE DEt DEb; χSE]
441+
@tensor EEA_NE[χN DMb dEb χSE DSEb; DNEt DEt] :=
442+
EE_NE[χN χSE DNEt DEt; DNEb DEb] * conj(Ā_east[dEb; DNEb DEb DSEb DMb])
443+
@tensor EEAA_NE[χN DMb dEb dEt DMt; χSE DSEt DSEb] :=
444+
EEA_NE[χN DMb dEb χSE DSEb; DNEt DEt] * A_east[dEt; DNEt DEt DSEt DMt]
445+
@tensor EEEAA_E[χS DMt DMb χN; dEt dEb] :=
446+
EEAA_NE[χN DMb dEb dEt DMt; χSE DSEt DSEb] * E_southeast[χSE DSEt DSEb; χS]
447+
448+
@tensor ρ[dWt dEt; dWb dEb] :=
449+
EEEAA_W[dWt dWb; χS DMt DMb χN] * EEEAA_E[χS DMt DMb χN; dEt dEb]
450+
451+
return ρ / str(ρ)
452+
end
453+
454+
@generated function _contract_densitymatrix(
455+
inds::NTuple{N,Val}, ket::InfinitePEPS, bra::InfinitePEPS, env::CTMRGEnv
456+
) where {N}
457+
cartesian_inds = collect(CartesianIndex{2}, map(x -> x.parameters[1], inds.parameters)) # weird hack to extract information from Val
458+
allunique(cartesian_inds) ||
459+
throw(ArgumentError("Indices should not overlap: $cartesian_inds."))
460+
rowrange = getindex.(cartesian_inds, 1)
461+
colrange = getindex.(cartesian_inds, 2)
462+
463+
corner_NW, corner_NE, corner_SE, corner_SW = _contract_corner_expr(rowrange, colrange)
464+
edges_N, edges_E, edges_S, edges_W = _contract_edge_expr(rowrange, colrange)
465+
result = tensorexpr(
466+
:ρ,
467+
ntuple(i -> physicallabel(:O, :ket, i), N),
468+
ntuple(i -> physicallabel(:O, :bra, i), N),
469+
)
470+
bra, ket = _contract_state_expr(rowrange, colrange, cartesian_inds)
471+
472+
multiplication_ex = Expr(
473+
:call,
474+
:*,
475+
corner_NW,
476+
corner_NE,
477+
corner_SE,
478+
corner_SW,
479+
edges_N...,
480+
edges_E...,
481+
edges_S...,
482+
edges_W...,
483+
ket...,
484+
map(x -> Expr(:call, :conj, x), bra)...,
485+
)
486+
multex = :(@autoopt @tensor $result := $multiplication_ex)
487+
return quote
488+
$(macroexpand(@__MODULE__, multex))
489+
return ρ / str(ρ)
490+
end
491+
end

src/algorithms/toolbox.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ for a PEPS `peps` using a given CTMRG environment `env`.
77
function MPSKit.expectation_value(peps::InfinitePEPS, O::LocalOperator, env::CTMRGEnv)
88
checklattice(peps, O)
99
term_vals = dtmap([O.terms...]) do (inds, operator) # OhMyThreads can't iterate over O.terms directly
10-
contract_local_operator(inds, operator, peps, peps, env) /
11-
contract_local_norm(inds, peps, peps, env)
10+
ρ = reduced_densitymatrix(inds, peps, peps, env)
11+
return trmul(operator, ρ)
1212
end
1313
return sum(term_vals)
1414
end

src/utility/util.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,31 @@ function twistdual!(t::AbstractTensorMap, is)
131131
end
132132
twistdual(t::AbstractTensorMap, is) = twistdual!(copy(t), is)
133133
134+
"""
135+
str(t)
136+
137+
Fermionic supertrace by using `@tensor`.
138+
"""
139+
str(t::AbstractTensorMap) = _str(BraidingStyle(sectortype(t)), t)
140+
_str(::Bosonic, t::AbstractTensorMap) = tr(t)
141+
@generated function _str(::Fermionic, t::AbstractTensorMap{<:Any,<:Any,N,N}) where {N}
142+
tex = tensorexpr(:t, ntuple(identity, N), ntuple(identity, N))
143+
return macroexpand(@__MODULE__, :(@tensor $tex))
144+
end
145+
146+
"""
147+
trmul(H, ρ)
148+
149+
Compute `tr(H * ρ)` without forming `H * ρ`.
150+
"""
151+
@generated function trmul(
152+
H::AbstractTensorMap{<:Any,S,N,N}, ρ::AbstractTensorMap{<:Any,S,N,N}
153+
) where {S,N}
154+
Hex = tensorexpr(:H, ntuple(identity, N), ntuple(i -> i + N, N))
155+
ρex = tensorexpr(:ρ, ntuple(i -> i + N, N), ntuple(identity, N))
156+
return macroexpand(@__MODULE__, :(@tensor $Hex * $ρex))
157+
end
158+
134159
# Check whether diagonals contain degenerate values up to absolute or relative tolerance
135160
function is_degenerate_spectrum(
136161
S; atol::Real=0, rtol::Real=atol > 0 ? 0 : sqrt(eps(scalartype(S)))

0 commit comments

Comments
 (0)