Skip to content

Commit 2e940cc

Browse files
committed
generalize observable evaluation
Based on PR QuantumKitHub#117 of PEPSKit, this generalizes the expectation value for InfinitePEPOs and InfinitePartitionFunctions
1 parent dc10125 commit 2e940cc

File tree

5 files changed

+314
-13
lines changed

5 files changed

+314
-13
lines changed

src/PEPSKit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ include("states/infinitepeps.jl")
3232
include("states/infiniteweightpeps.jl")
3333
include("states/infinitepartitionfunction.jl")
3434

35+
include("operators/localoperator.jl")
3536
include("operators/infinitepepo.jl")
3637
include("operators/transfermatrix.jl")
37-
include("operators/localoperator.jl")
3838
include("operators/lattices/squarelattice.jl")
3939
include("operators/models.jl")
4040

src/algorithms/contractions/localoperator.jl

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,42 @@ function _contract_edge_expr(rowrange, colrange)
126126
return edges_N, edges_E, edges_S, edges_W
127127
end
128128

129+
function _contract_pf_edge_expr(rowrange, colrange)
130+
rmin, rmax = extrema(rowrange)
131+
cmin, cmax = extrema(colrange)
132+
gridsize = (rmax - rmin + 1, cmax - cmin + 1)
133+
134+
edges_N = map(1:gridsize[2]) do i
135+
E_N = :(env.edges[NORTH, mod1($(rmin - 1), end), mod1($(cmin + i - 1), end)])
136+
return tensorexpr(
137+
E_N, (envlabel(NORTH, i - 1), virtuallabel(NORTH, i)), envlabel(NORTH, i)
138+
)
139+
end
140+
141+
edges_E = map(1:gridsize[1]) do i
142+
E_E = :(env.edges[EAST, mod1($(rmin + i - 1), end), mod1($(cmax + 1), end)])
143+
return tensorexpr(
144+
E_E, (envlabel(EAST, i - 1), virtuallabel(EAST, i)), envlabel(EAST, i)
145+
)
146+
end
147+
148+
edges_S = map(1:gridsize[2]) do i
149+
E_S = :(env.edges[SOUTH, mod1($(rmax + 1), end), mod1($(cmin + i - 1), end)])
150+
return tensorexpr(
151+
E_S, (envlabel(SOUTH, i), virtuallabel(SOUTH, i)), envlabel(SOUTH, i - 1)
152+
)
153+
end
154+
155+
edges_W = map(1:gridsize[1]) do i
156+
E_W = :(env.edges[WEST, mod1($(rmin + i - 1), end), mod1($(cmin - 1), end)])
157+
return tensorexpr(
158+
E_W, (envlabel(WEST, i), virtuallabel(WEST, i)), envlabel(WEST, i - 1)
159+
)
160+
end
161+
162+
return edges_N, edges_E, edges_S, edges_W
163+
end
164+
129165
function _contract_state_expr(rowrange, colrange, cartesian_inds=nothing)
130166
rmin, rmax = extrema(rowrange)
131167
cmin, cmax = extrema(colrange)
@@ -173,6 +209,96 @@ function _contract_state_expr(rowrange, colrange, cartesian_inds=nothing)
173209
end
174210
end
175211

212+
function pf_tensor_expr(tensor_label, label_west, label_south, label_north, label_east, (len,place))
213+
if len == 1
214+
a = tensorexpr(
215+
tensor_label,
216+
((label_west),(label_south),),
217+
((label_north),(label_east),),
218+
)
219+
else
220+
if place == 1
221+
a = tensorexpr(
222+
tensor_label,
223+
((label_west), (label_south),),
224+
((label_north), (label_east), (virtuallabel(:op, len, place)),),
225+
)
226+
elseif place == len
227+
a = tensorexpr(
228+
tensor_label,
229+
((virtuallabel(:op, len, place - 1)), (label_west), (label_south),),
230+
((label_north), (label_east),),
231+
)
232+
else
233+
a = tensorexpr(
234+
tensor_label,
235+
((virtuallabel(:op, len, place - 1)), (label_west), (label_south),),
236+
((label_north), (label_east), (virtuallabel(:op, len, place)),),
237+
)
238+
end
239+
end
240+
return a
241+
end
242+
243+
function _contract_tensor_expr(O, pos, rowrange, colrange, cartesian_inds=nothing)
244+
rmin, rmax = extrema(rowrange)
245+
cmin, cmax = extrema(colrange)
246+
gridsize = (rmax - rmin + 1, cmax - cmin + 1)
247+
return map(Iterators.product(1:gridsize[1], 1:gridsize[2])) do (i, j)
248+
inds_id = if isnothing(cartesian_inds)
249+
nothing
250+
else
251+
findfirst(==(CartesianIndex(rmin + i - 1, cmin + j - 1)), cartesian_inds)
252+
end
253+
tensor_label = if isnothing(inds_id) || O <: Nothing
254+
:(pf[mod1($(rmin + i - 1), end), mod1($(cmin + j - 1), end)])
255+
else
256+
:(O[$inds_id])
257+
end
258+
259+
label_west = if j == 1
260+
virtuallabel(WEST, i)
261+
else
262+
virtuallabel(:horizontal, i, j - 1)
263+
end
264+
label_south = if i == gridsize[1]
265+
virtuallabel(SOUTH, j)
266+
else
267+
virtuallabel(:vertical, i, j)
268+
end
269+
label_north = if i == 1
270+
virtuallabel(NORTH, j)
271+
else
272+
virtuallabel(:vertical, i - 1, j)
273+
end
274+
label_east = if j == gridsize[2]
275+
virtuallabel(EAST, i)
276+
else
277+
virtuallabel(:horizontal, i, j)
278+
end
279+
if isnothing(inds_id) || O <: Nothing
280+
pf_tensor_expr(
281+
tensor_label,
282+
label_west,
283+
label_south,
284+
label_north,
285+
label_east,
286+
(1,1),
287+
)
288+
289+
else
290+
pf_tensor_expr(
291+
tensor_label,
292+
label_west,
293+
label_south,
294+
label_north,
295+
label_east,
296+
pos[inds_id],
297+
)
298+
end
299+
end
300+
end
301+
176302
@generated function _contract_local_operator(
177303
inds::NTuple{N,Val},
178304
O::AbstractTensorMap{T,S,N,N},
@@ -270,3 +396,70 @@ end
270396
end
271397
return macroexpand(@__MODULE__, returnex)
272398
end
399+
400+
# Partition function contractions
401+
402+
"""
403+
contract_local_tensors(inds, [O], pf, env)
404+
405+
Contract a local tensor `O` inserted into a partition function `pf` at position `inds`,
406+
using the environment `env`.
407+
"""
408+
function contract_local_tensors(
409+
inds::NTuple{N,CartesianIndex{2}},
410+
O::Union{Nothing,NTuple{N,AbstractTensorMap{T,S}}},
411+
pos::Union{Nothing,NTuple{N,Tuple{Int,Int}}},
412+
pf::InfinitePartitionFunction,
413+
env::CTMRGEnv{C,<:CTMRG_PF_EdgeTensor},
414+
) where {N,T,S,C}
415+
static_inds = Val.(inds)
416+
static_pos = Val.(pos)
417+
return _contract_local_tensors(static_inds, O, static_pos, pf, env)
418+
end
419+
function contract_local_tensors(
420+
inds::NTuple{N,Tuple{Int,Int}},
421+
O::Union{Nothing,NTuple{N,AbstractTensorMap{T,S}}},
422+
pos::Union{Nothing,NTuple{N,Tuple{Int,Int}}},
423+
pf::InfinitePartitionFunction,
424+
env::CTMRGEnv{C,<:CTMRG_PF_EdgeTensor},
425+
) where {N,T,S,C}
426+
return contract_local_tensors(CartesianIndex.(inds), O, pos, pf, env)
427+
end
428+
@generated function _contract_local_tensors(
429+
inds::NTuple{N,Val},
430+
O::Union{Nothing,NTuple{N,AbstractTensorMap{T,S}}},
431+
pos::Union{Nothing,NTuple{N,Val}},
432+
pf::InfinitePartitionFunction,
433+
env::CTMRGEnv{C,<:CTMRG_PF_EdgeTensor},
434+
) where {N,T,S,C}
435+
# weird hack to extract information from Val
436+
cartesian_inds = collect(CartesianIndex{2}, map(x -> x.parameters[1], inds.parameters))
437+
cartesian_pos = collect(Tuple{Int,Int}, map(x -> x.parameters[1], pos.parameters)) # weird hack to extract information from Val
438+
allunique(cartesian_inds) ||
439+
throw(ArgumentError("Indices should not overlap: $cartesian_inds."))
440+
rowrange = getindex.(cartesian_inds, 1)
441+
colrange = getindex.(cartesian_inds, 2)
442+
443+
corner_NW, corner_NE, corner_SE, corner_SW = _contract_corner_expr(rowrange, colrange)
444+
edges_N, edges_E, edges_S, edges_W = _contract_pf_edge_expr(rowrange, colrange)
445+
tensors = _contract_tensor_expr(O, Tuple(cartesian_pos), rowrange, colrange, cartesian_inds)
446+
447+
multiplication_ex = Expr(
448+
:call,
449+
:*,
450+
corner_NW,
451+
corner_NE,
452+
corner_SE,
453+
corner_SW,
454+
edges_N...,
455+
edges_E...,
456+
edges_S...,
457+
edges_W...,
458+
tensors...,
459+
)
460+
461+
returnex = quote
462+
@autoopt @tensor opt = $multiplication_ex
463+
end
464+
return macroexpand(@__MODULE__, returnex)
465+
end

src/algorithms/toolbox.jl

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,24 @@ convention.
2525
"""
2626
function MPSKit.expectation_value(
2727
pf::InfinitePartitionFunction,
28-
op::Pair{CartesianIndex{2},<:AbstractTensorMap{T,S,2,2}},
29-
env::CTMRGEnv,
30-
) where {T,S}
31-
return contract_local_tensor(op[1], op[2], env) /
32-
contract_local_tensor(op[1], pf[op[1]], env)
33-
end
34-
function MPSKit.expectation_value(
35-
pf::InfinitePartitionFunction, op::Pair{Tuple{Int,Int}}, env::CTMRGEnv
36-
)
37-
return expectation_value(pf, CartesianIndex(op[1]) => op[2], env)
28+
op::NTuple{N,Array{<:Pair{CartesianIndex{2},<:AbstractTensorMap{T,S}}}},
29+
envs::CTMRGEnv,
30+
) where {N,T,S}
31+
inds = CartesianIndex{2}[]
32+
ops = AbstractTensorMap{T,S}[]
33+
positions = Tuple{Int,Int}[]
34+
for (n, term) = enumerate(op)
35+
for (i,(ind, op)) in enumerate(term)
36+
push!(inds, ind)
37+
push!(ops, op)
38+
push!(positions, (length(term), i))
39+
end
40+
end
41+
# inds = first.(op)
42+
# ops = last.(op)
43+
pos = Tuple(positions)
44+
return contract_local_tensors(Tuple(inds), Tuple(ops), pos, pf, envs) /
45+
contract_local_tensors(Tuple(inds), nothing, pos, pf, envs)
3846
end
3947

4048
"""

src/operators/infinitepepo.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,55 @@ function Base.adjoint(O::InfinitePEPO)
202202
return InfinitePEPO(_dag.(unitcell(O)))
203203
end
204204

205+
function trace_out(O::InfinitePEPO)
206+
function _trace_out(O::PEPOTensor)
207+
@tensor O_tr[-4 -3; -1 -2] := O[1 1; -1 -2 -3 -4]
208+
return O_tr
209+
end
210+
return InfinitePartitionFunction(_trace_out.(unitcell(O))[:,:,1])
211+
end
212+
213+
function MPSKit.expectation_value(O::InfinitePEPO, H::InfinitePEPO, χ, ctm_alg)
214+
network_OH = InfiniteSquareNetwork(_stack_pepos((PEPSKit.unitcell(O), PEPSKit.unitcell(H))))
215+
env0_OH = CTMRGEnv(network_OH, χ)
216+
env_OH, = leading_boundary(env0_OH, network_OH, ctm_alg)
217+
218+
network_OO = InfiniteSquareNetwork(_stack_pepos((PEPSKit.unitcell(O), PEPSKit.unitcell(O))))
219+
env0_OO = CTMRGEnv(network_OO, χ)
220+
env_OO, = leading_boundary(env0_OO, network_OO, ctm_alg)
221+
return network_value(network_OH, env_OH) / network_value(network_OO, env_OO)
222+
end
223+
224+
function _expectation_value(O::InfinitePEPO, gate::Pair{NTuple{1, CartesianIndex{2}}, T}, χ, ctm_alg) where {T<:AbstractTensorMap}
225+
(Nr, Nc) = size(O)
226+
site = (mod1(gate[1][1][1], Nr), mod1(gate[1][1][2], Nc), 1)
227+
228+
@tensor t[-4 -3; -1 -2] := O[site...][1 2; -1 -2 -3 -4] * gate[2][2; 1]
229+
network = trace_out(O)
230+
env0 = CTMRGEnv(network, χ)
231+
env, = leading_boundary(env0, network, ctm_alg)
232+
return expectation_value(network, ([gate[1][1] => t],), env)
233+
end
234+
235+
function _expectation_value(O::InfinitePEPO, gate::Pair{NTuple{2, CartesianIndex{2}}, T}, χ, ctm_alg) where {T<:AbstractTensorMap}
236+
(Nr, Nc) = size(O)
237+
left = (mod1(gate[1][1][1], Nr), mod1(gate[1][1][2], Nc), 1)
238+
right = (mod1(gate[1][2][1], Nr), mod1(gate[1][2][2], Nc), 1)
239+
@tensor t[-1 -2 -3 -4; -5 -6 -7 -8] := O[left...][1 2; -1 -2 -3 -4] * O[right...][3 4; -5 -6 -7 -8] * gate[2][2 4; 1 3]
240+
U, S, V = tsvd(t)
241+
L = permute(U * sqrt(S), ((4,3),(1,2,5)))
242+
R = permute(sqrt(S) * V, ((1,5,4),(2,3)))
243+
244+
network = trace_out(O)
245+
env0 = CTMRGEnv(network, χ)
246+
env, = leading_boundary(env0, network, ctm_alg)
247+
return expectation_value(network, ([gate[1][1] => L, gate[1][2] => R],), env)
248+
end
249+
250+
function MPSKit.expectation_value(O::InfinitePEPO, H::LocalOperator, χ, ctm_alg)
251+
return sum([_expectation_value(O, t, χ, ctm_alg) for t = H.terms])
252+
end
253+
205254
## Vector interface
206255

207256
function VectorInterface.scalartype(::Type{NT}) where {NT<:InfinitePEPO}
@@ -270,3 +319,14 @@ function _stack_tuples(A::Matrix{NTuple{N,T}}) where {N,T}
270319
end
271320
return out
272321
end
322+
323+
function _stack_pepos(A::NTuple{N,Array{T}}) where {N,T}
324+
size_out = size(A[1])
325+
out = Array{T}(undef, size_out[1], size_out[2], N)
326+
for h = 1:N
327+
for (r, c) in Iterators.product(axes(A[1])...)
328+
out[r, c, h] = A[h][r, c]
329+
end
330+
end
331+
return InfinitePEPO(out)
332+
end

test/ctmrg/partition_function.jl

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ projector_algs = [:halfinfinite, :fullinfinite]
101101

102102
# check observables
103103
λ = network_value(Z, env)
104-
m = expectation_value(Z, (1, 1) => M, env)
105-
e = expectation_value(Z, (1, 1) => E, env)
104+
m = expectation_value(Z, ((1, 1) => M,), env)
105+
e = expectation_value(Z, ((1, 1) => E,), env)
106106
f_exact, m_exact, e_exact = classical_ising_exact(; beta)
107107

108108
# should be real-ish
@@ -115,3 +115,43 @@ projector_algs = [:halfinfinite, :fullinfinite]
115115
@test abs(m) abs(m_exact) rtol = 1e-4
116116
@test e e_exact rtol = 1e-1 # accuracy limited by bond dimension and maxiter
117117
end
118+
119+
@testset "Classical Ising correlation functions" begin
120+
ctm_alg = SimultaneousCTMRG(; maxiter=300)
121+
β = [0.1, log(1 + sqrt(2)) / 2 + 0.05, 2.0]
122+
123+
# contract at high, critical and low temperature
124+
O_high, M_high, = classical_ising(; beta=β[1])
125+
Z_high = InfinitePartitionFunction(O_high)
126+
env0_high = CTMRGEnv(Z_high, χenv)
127+
env_high = leading_boundary(env0_high, Z_high, ctm_alg)
128+
129+
O_crit, M_crit, = classical_ising(; beta=β[2])
130+
Z_crit = InfinitePartitionFunction(O_crit)
131+
env0_crit = CTMRGEnv(Z_crit, χenv)
132+
env_crit = leading_boundary(env0_crit, Z_crit, ctm_alg)
133+
134+
O_low, M_low, = classical_ising(; beta=β[3])
135+
Z_low = InfinitePartitionFunction(O_low)
136+
env0_low = CTMRGEnv(Z_low, χenv)
137+
env_low = leading_boundary(env0_low, Z_low, ctm_alg)
138+
139+
# compute correlators
140+
corr_zz_high = expectation_value(Z_high, ((1, 1) => M_high, (2, 1) => M_high), env_high)
141+
corr_zz_crit = expectation_value(Z_crit, ((1, 1) => M_crit, (2, 1) => M_crit), env_crit)
142+
corr_zz_low = expectation_value(Z_low, ((1, 1) => M_low, (2, 1) => M_low), env_low)
143+
@test abs(corr_zz_high) < abs(corr_zz_crit) < abs(corr_zz_low)
144+
@test abs(corr_zz_low) 1.0 rtol = 1e-6
145+
146+
corr_zzz_high = expectation_value(
147+
Z_high, ((1, 1) => M_high, (2, 1) => M_high, (1, 2) => M_high), env_high
148+
)
149+
corr_zzz_crit = expectation_value(
150+
Z_crit, ((1, 1) => M_crit, (2, 1) => M_crit, (1, 2) => M_crit), env_crit
151+
)
152+
corr_zzz_low = expectation_value(
153+
Z_low, ((1, 1) => M_low, (2, 1) => M_low, (1, 2) => M_low), env_low
154+
)
155+
@test abs(corr_zzz_high) 0.0 atol = 1e-6
156+
@test abs(corr_zzz_low) 1.0 rtol = 1e-6
157+
end

0 commit comments

Comments
 (0)