Skip to content

Commit aa5875c

Browse files
Add bipartite specialization of belief propagation and gauging (#319)
* Add bipartite specialization of belief propagation * Fix formatting * Fix bipartite SU test * Rename variable * Avoid double `map` calls
1 parent 7de6692 commit aa5875c

File tree

8 files changed

+88
-15
lines changed

8 files changed

+88
-15
lines changed

src/algorithms/bp/beliefpropagation.jl

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ $(TYPEDFIELDS)
2020
"Toggle for projecting messages onto the hermitian subspace immediately after update through BP equation"
2121
project_hermitian::Bool = true
2222

23+
"When true, preserve bipartite structure of BPEnv inherited from input network"
24+
bipartite::Bool = false
25+
2326
"Output verbosity level"
2427
verbosity::Int = 2
2528
end
@@ -54,21 +57,43 @@ function leading_boundary(env₀::BPEnv, network::InfiniteSquareNetwork, alg::Be
5457
return env, ϵ
5558
end
5659
end
57-
function leading_boundary(env₀::BPEnv, state, args...)
58-
return leading_boundary(env₀, InfiniteSquareNetwork(state), args...)
60+
function leading_boundary(env₀::BPEnv, state::InfiniteState, alg::BeliefPropagation)
61+
if alg.bipartite
62+
@assert _state_bipartite_check(state)
63+
end
64+
return leading_boundary(env₀, InfiniteSquareNetwork(state), alg)
5965
end
6066

6167
"""
6268
One iteration to update the BP environment.
6369
"""
6470
function bp_iteration(network::InfiniteSquareNetwork, env::BPEnv, alg::BeliefPropagation)
65-
messages = map(eachindex(env)) do I
66-
M = update_message(I, network, env)
67-
normalize!(M)
68-
alg.project_hermitian && (M = project_hermitian!!(M))
69-
return M
71+
if alg.bipartite
72+
@assert size(network, 1) == size(network, 2) == 2
73+
messages = similar(env.messages)
74+
for (d, r) in Iterators.product(1:4, 1:2)
75+
# update BP env around 1st column of state
76+
# [N/S, 1:2, 1], [E/W, 1:2, 2]
77+
c = (d == NORTH || d == SOUTH) ? 1 : 2
78+
I = CartesianIndex(d, r, c)
79+
M = update_message(I, network, env)
80+
normalize!(M)
81+
alg.project_hermitian && (M = project_hermitian!!(M))
82+
messages[I] = M
83+
# copy to the other column
84+
I′ = CartesianIndex(d, _next(r, 2), _next(c, 2))
85+
messages[I′] = copy(M)
86+
end
87+
return BPEnv(messages)
88+
else
89+
messages = map(eachindex(env)) do I
90+
M = update_message(I, network, env)
91+
normalize!(M)
92+
alg.project_hermitian && (M = project_hermitian!!(M))
93+
return M
94+
end
95+
return BPEnv(messages)
7096
end
71-
return BPEnv(messages)
7297
end
7398

7499
"""

src/algorithms/bp/gaugefix.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ Algorithm for gauging PEPS with belief propagation fixed point messages.
77
# TODO: add options
88
end
99

10+
function _bpenv_bipartite_check(env::BPEnv)
11+
for (r, c) in Iterators.product(1:2, 1:2)
12+
r′, c′ = _next(r, 2), _next(c, 2)
13+
if !all(env[:, r, c] .== env[:, r′, c′])
14+
return false
15+
end
16+
end
17+
return true
18+
end
19+
1020
"""
1121
gauge_fix(psi::Union{InfinitePEPS, InfinitePEPO}, alg::BPGauge, env::BPEnv)
1222
@@ -15,11 +25,19 @@ an [`InfinitePEPO`](@ref) interpreted as purified state with two physical legs)
1525
using fixed point environment `env` of belief propagation.
1626
"""
1727
function gauge_fix(psi::InfinitePEPS, alg::BPGauge, env::BPEnv)
28+
bipartite = _state_bipartite_check(psi) && _bpenv_bipartite_check(env)
1829
psi′ = copy(psi)
1930
XXinv = map(eachcoordinate(psi, 1:2)) do I
2031
_, X, Xinv = _bp_gauge_fix!(CartesianIndex(I), psi′, env)
2132
return X, Xinv
2233
end
34+
if bipartite
35+
# copy 1st column to 2nd column to eliminate differences
36+
# caused by order of applying gauge transformations
37+
for r in 1:2
38+
psi′[_next(r, 2), 2] = copy(psi′[r, 1])
39+
end
40+
end
2341
return psi′, XXinv
2442
end
2543
function gauge_fix(psi::InfinitePEPO, alg::BPGauge, env::BPEnv)

src/algorithms/time_evolution/gaugefix_su.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ function _trivial_gates(elt::Type{<:Number}, lattice::Matrix{S}) where {S <: Ele
3636
end
3737

3838
"""
39-
$(SIGNATURES)
39+
gauge_fix(psi::Union{InfinitePEPS, InfinitePEPO}, alg::SUGauge)
4040
4141
Fix the gauge of `psi` using trivial simple update.
4242
"""
43-
function gauge_fix(psi::InfinitePEPS, alg::SUGauge)
43+
function gauge_fix(psi::InfiniteState, alg::SUGauge)
4444
gates = _trivial_gates(scalartype(psi), physicalspace(psi))
45-
su_alg = SimpleUpdate(; trunc = FixedSpaceTruncation())
45+
su_alg = SimpleUpdate(; trunc = FixedSpaceTruncation(), bipartite = _state_bipartite_check(psi))
4646
wts0 = SUWeight(psi)
4747
# use default constructor to avoid calculation of exp(-H * 0)
4848
evolver = TimeEvolver(su_alg, 0.0, alg.maxiter, gates, SUState(0, 0.0, psi, wts0))

src/algorithms/time_evolution/simpleupdate.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ $(TYPEDFIELDS)
1414
imaginary_time::Bool = true
1515
"When true, force the usage of 3-site simple update"
1616
force_3site::Bool = false
17-
"(Only applicable to InfinitePEPS) When true, assume bipartite unit cell structure"
17+
"When true, assume bipartite unit cell structure"
1818
bipartite::Bool = false
1919
"(Only applicable to InfinitePEPO)
2020
When true, the PEPO is regarded as a purified PEPS, and updated as

src/algorithms/time_evolution/time_evolve.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ end
3030

3131
Base.iterate(it::TimeEvolver) = iterate(it, it.state)
3232

33+
function _state_bipartite_check(psi::InfiniteState)
34+
if isa(psi, InfinitePEPO)
35+
@assert size(psi, 3) == 1 "Input InfinitePEPO is expected to have only one layer."
36+
end
37+
if !(size(psi, 1) == size(psi, 2) == 2)
38+
return false
39+
end
40+
for (r, c) in Iterators.product(1:2, 1:2)
41+
r′, c′ = _next(r, 2), _next(c, 2)
42+
if psi[r, c] != psi[r′, c′]
43+
return false
44+
end
45+
end
46+
return true
47+
end
48+
3349
function _timeevol_sanity_check(
3450
ψ₀::InfiniteState, Pspaces::M, alg::A
3551
) where {A <: TimeEvolution, M <: AbstractMatrix{<:ElementarySpace}}
@@ -40,7 +56,7 @@ function _timeevol_sanity_check(
4056
@assert ψ₀ isa InfinitePEPO "alg.purified = false is only applicable to PEPO."
4157
end
4258
if hasfield(typeof(alg), :bipartite) && alg.bipartite
43-
@assert Nr == Nc == 2 "`bipartite = true` requires 2 x 2 unit cell size."
59+
@assert _state_bipartite_check(ψ₀) "Input state is not bipartite with 2 x 2 unit cell."
4460
end
4561
return nothing
4662
end

test/timeevol/cluster_projectors.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ using PEPSKit
44
using LinearAlgebra
55
using Random
66
import MPSKitModels: hubbard_space
7-
using PEPSKit: sdiag_pow, _cluster_truncate!, _flip_virtuals!
7+
using PEPSKit: sdiag_pow, _cluster_truncate!, _flip_virtuals!, _next
88
using MPSKit: GenericMPSTensor, MPSBondTensor
99
include("cluster_tools.jl")
1010

@@ -112,6 +112,10 @@ end
112112
trunc_env0 = truncerror(; atol = 1.0e-12) & truncrank(4)
113113
trunc_env = truncerror(; atol = 1.0e-12) & truncrank(16)
114114
peps = InfinitePEPS(rand, Float64, Pspace, Vspace, Vspace'; unitcell = (Nr, Nc))
115+
# make state bipartite
116+
for r in 1:2
117+
peps.A[_next(r, 2), 2] = copy(peps.A[r, 1])
118+
end
115119
wts = SUWeight(peps)
116120
ham = real(
117121
hubbard_model(

test/timeevol/sitedep_truncation.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using LinearAlgebra
33
using Random
44
using TensorKit
55
using PEPSKit
6-
using PEPSKit: NORTH, EAST
6+
using PEPSKit: NORTH, EAST, _next
77

88
function get_bonddims(peps::InfinitePEPS)
99
xdims = collect(dim(domain(t, EAST)) for t in peps.A)
@@ -22,6 +22,10 @@ end
2222
ham = real(heisenberg_XYZ(InfiniteSquare(Nr, Nc); Jx = 1.0, Jy = 1.0, Jz = 1.0))
2323
Random.seed!(100)
2424
peps0 = InfinitePEPS(rand, Float64, ℂ^2, ℂ^10; unitcell = (Nr, Nc))
25+
# make state bipartite
26+
for r in 1:2
27+
peps0.A[_next(r, 2), 2] = copy(peps0.A[r, 1])
28+
end
2529
env0 = SUWeight(peps0)
2630
normalize!.(peps0.A, Inf)
2731
# set trunc to be compatible with bipartite structure

test/timeevol/tf_ising_finiteT.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ dt, nstep = 1.0e-3, 400
5959
# PEPO approach: results at β, or T = 2.5
6060
alg = SimpleUpdate(; trunc = trunc_pepo, purified = false, bipartite)
6161
pepo, wts, info = time_evolve(pepo0, ham, dt, nstep, alg, wts0)
62+
63+
## BP gauge fixing
64+
bp_alg = BeliefPropagation(; maxiter = 100, tol = 1.0e-9, bipartite)
65+
bp_env, = leading_boundary(BPEnv(ones, Float64, pepo), pepo, bp_alg)
66+
pepo, = gauge_fix(pepo, BPGauge(), bp_env)
67+
6268
env = converge_env(InfinitePartitionFunction(pepo), 16)
6369
result_β = measure_mag(pepo, env)
6470
@info "tr(σ(x,z)ρ) at T = $(1 / β): $(result_β)."

0 commit comments

Comments
 (0)