Skip to content

Commit c8d03b9

Browse files
mtfishmanclaude
andcommitted
Test simplification: use norm_sqr_network for BP and apply tests
Both `test/test_belief_propagation.jl` and `test/test_apply.jl` now build their `ψψ` through `norm_sqr_network(ψ)` (a QuadraticFormNetwork), so the auto-init in `initialize_cache(::bp, ::QFN)` handles the identity-messages construction directly — no hand-rolled pairings or LFN-specific helpers in the tests. The 2-site RDM construction in `test_belief_propagation.jl` is also rewritten for the QFN layout: ask `environment` for all three layers (bra/ket/operator) at the selected vertices, then contract with only the bra and ket tensors at those vertices. Dropping the per-site identity operator leaves the bra (primed) and ket (unprimed) site inds open, which is exactly the reduced density matrix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6d09a71 commit c8d03b9

2 files changed

Lines changed: 71 additions & 103 deletions

File tree

test/test_apply.jl

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,15 @@
11
using Compat: Compat
2-
using Dictionaries: Dictionary, set!
3-
using Graphs: dst, src, vertices
4-
using ITensorNetworks: BeliefPropagationCache, apply, environment, identity_messages,
5-
inner_network, siteinds, update
6-
using ITensors: ITensors, ITensor, Index, commoninds, dag, inner, op, prime
2+
using Graphs: vertices
3+
using ITensorNetworks: BeliefPropagationCache, apply, environment, initialize_cache,
4+
norm_sqr_network, siteinds, update
5+
using ITensors: ITensors, Algorithm, ITensor, inner, op
76
using NamedGraphs.NamedGraphGenerators: named_grid
8-
using NamedGraphs.PartitionedGraphs:
9-
PartitionedGraph, QuotientEdge, partitioned_vertices, quotientedges
7+
using NamedGraphs.PartitionedGraphs: PartitionedGraph
108
using SplitApplyCombine: group
119
using StableRNGs: StableRNG
1210
using TensorOperations: TensorOperations
1311
using Test: @test, @test_throws, @testset
1412
include("utils.jl")
15-
16-
# Build identity-style initial messages on each quotient edge of an LFN
17-
# (where bra = `dag(prime(ket))`) by pairing each ket Index `k` with its
18-
# bra counterpart `dag(prime(k))` directly, so the construction is
19-
# robust to multiple link indices per edge.
20-
function _lfn_identity_messages(ψψ, ptn::PartitionedGraph)
21-
pairings = Dictionary{QuotientEdge, Pair{Vector{Index}, Vector{Index}}}()
22-
pv = partitioned_vertices(ptn)
23-
for pe in quotientedges(ptn)
24-
src_orig = unique(first.(filter(v -> last(v) == "ket", pv[parent(src(pe))])))
25-
dst_orig = unique(first.(filter(v -> last(v) == "ket", pv[parent(dst(pe))])))
26-
for (from_orig, to_orig, e) in (
27-
(src_orig, dst_orig, pe),
28-
(dst_orig, src_orig, reverse(pe)),
29-
)
30-
kets = Index[]
31-
for v_from in from_orig, v_to in to_orig
32-
append!(kets, commoninds(ψψ[(v_from, "ket")], ψψ[(v_to, "ket")]))
33-
end
34-
bras = dag.(prime.(kets))
35-
set!(pairings, e, bras => kets)
36-
end
37-
end
38-
return identity_messages(ITensors.scalartype(ψψ), pairings)
39-
end
40-
4113
@testset "apply" begin
4214
g_dims = (2, 2)
4315
g = named_grid(g_dims)
@@ -46,23 +18,38 @@ end
4618
rng = StableRNG(1234)
4719
ψ = random_tensornetwork(rng, s; link_space = χ)
4820
v1, v2 = (2, 2), (1, 2)
49-
ψψ = inner_network(ψ, ψ)
50-
# Simple Belief Propagation grouping (one bra+ket per partition) gives
51-
# a product environment around `[v1, v2]`, which is what `apply` requires.
21+
ψψ = norm_sqr_network(ψ)
22+
# Vertices of `[v1, v2]` across all layers of `ψψ` (bra/ket/operator),
23+
# so the environment around them is just the incoming BP messages —
24+
# the per-site operator tensors aren't pulled in as central tensors.
25+
env_verts(vs) = [
26+
(v, suffix) for v in vs for suffix in ("bra", "ket", "operator")
27+
]
28+
# Simple Belief Propagation grouping (one bra/ket/operator triple per
29+
# partition) gives a product environment around `[v1, v2]`, which is
30+
# what `apply` requires.
5231
ptn_SBP = PartitionedGraph(ψψ, group(v -> v[1], vertices(ψψ)))
53-
bp_cache =
54-
BeliefPropagationCache(ptn_SBP; messages = _lfn_identity_messages(ψψ, ptn_SBP))
55-
bp_cache = update(bp_cache; maxiter = 20)
56-
envsSBP = environment(bp_cache, [(v1, "bra"), (v1, "ket"), (v2, "bra"), (v2, "ket")])
32+
bp_cache = update(
33+
initialize_cache(
34+
Algorithm("bp"),
35+
ψψ;
36+
partitioned_vertices = ptn_SBP.partitioned_vertices
37+
);
38+
maxiter = 20
39+
)
40+
envsSBP = environment(bp_cache, env_verts((v1, v2)))
5741
# Column-grouping (one whole column per partition) gives a non-product
5842
# environment; `apply` should reject it.
5943
ptn_col = PartitionedGraph(ψψ, group(v -> v[1][1], vertices(ψψ)))
60-
bp_cache_col =
61-
BeliefPropagationCache(ptn_col; messages = _lfn_identity_messages(ψψ, ptn_col))
62-
bp_cache_col = update(bp_cache_col; maxiter = 20)
63-
envsGBP = environment(
64-
bp_cache_col, [(v1, "bra"), (v1, "ket"), (v2, "bra"), (v2, "ket")]
44+
bp_cache_col = update(
45+
initialize_cache(
46+
Algorithm("bp"),
47+
ψψ;
48+
partitioned_vertices = ptn_col.partitioned_vertices
49+
);
50+
maxiter = 20
6551
)
52+
envsGBP = environment(bp_cache_col, env_verts((v1, v2)))
6653
inner_alg = "exact"
6754
ngates = 5
6855
truncerr = 0.0

test/test_belief_propagation.jl

Lines changed: 39 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
using Compat: Compat
2-
using Dictionaries: Dictionary, set!
3-
using Graphs: dst, src, vertices
2+
using Graphs: vertices
43
using ITensorNetworks: ITensorNetworks, BeliefPropagationCache, QuadraticFormNetwork,
5-
contract, contraction_sequence, environment, identity_messages, inner_network, message,
6-
message_diff, partitioned_tensornetwork, scalar, siteinds, tensornetwork, update,
7-
update_factor, updated_message,
4+
bra_vertex, contract, contraction_sequence, default_partitioned_vertices, environment,
5+
identity_messages, ket_vertex, message, message_diff, norm_sqr_network, operator_vertex,
6+
partitioned_tensornetwork, scalar, siteinds, tensornetwork, update, update_factor,
7+
updated_message
88
include("utils.jl")
99
using ITensors.NDTensors: array
10-
using ITensors: ITensors, Algorithm, ITensor, Index, combiner, commoninds, dag, inds, inner,
11-
op, prime, random_itensor
10+
using ITensors: ITensors, ITensor, combiner, dag, inds, inner, prime, random_itensor
1211
using LinearAlgebra: eigvals, tr
13-
using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid
14-
using NamedGraphs.PartitionedGraphs: PartitionedGraph, QuotientEdge, quotientedges
15-
using NamedGraphs: NamedEdge, NamedGraph
16-
using SplitApplyCombine: group
12+
using NamedGraphs.NamedGraphGenerators: named_grid
13+
using NamedGraphs.PartitionedGraphs: PartitionedGraph, quotientedges
1714
using StableRNGs: StableRNG
1815
using TensorOperations: TensorOperations
1916
using Test: @test, @testset
@@ -28,38 +25,19 @@ using Test: @test, @testset
2825
χ = 2
2926
rng = StableRNG(1234)
3027
ψ = random_tensornetwork(rng, elt, s; link_space = χ)
31-
ψψ = ψ prime(dag(ψ); sites = [])
32-
pv = group(v -> first(v), vertices(ψψ))
33-
ptn = PartitionedGraph(ψψ, pv)
34-
# Build identity initial messages by hand: layer 1 is the ket
35-
# (original ψ) and layer 2 is the bra (`dag(prime(ψ; sites = []))`).
36-
# Pair the bra/ket link inds ordinally on each quotient edge so the
37-
# resulting `delta(b, dag(k))` has opposite QN flow.
38-
pairings = Dictionary{QuotientEdge, Pair{Vector{Index}, Vector{Index}}}()
39-
for pe in quotientedges(ptn)
40-
v_src, v_dst = parent(src(pe)), parent(dst(pe))
41-
for (v_from, v_to, e) in (
42-
(v_src, v_dst, pe),
43-
(v_dst, v_src, reverse(pe)),
44-
)
45-
# ψψ layer 2 is `dag(prime(ψ; sites = []))`, so each bra
46-
# link Index is `dag(prime(k))` for the matching ket k.
47-
# Build the pair explicitly to avoid relying on
48-
# `commoninds` ordering across the two layers.
49-
kets = collect(commoninds(ψψ[(v_from, 1)], ψψ[(v_to, 1)]))
50-
bras = dag.(prime.(kets))
51-
set!(pairings, e, bras => kets)
52-
end
53-
end
54-
bpc = BeliefPropagationCache(ptn; messages = identity_messages(elt, pairings))
28+
ψψ = norm_sqr_network(ψ)
29+
ptn = PartitionedGraph(ψψ, default_partitioned_vertices(ψψ))
30+
bpc = BeliefPropagationCache(ptn; messages = identity_messages(ψψ, ptn))
5531

56-
#Test updating the tensors in the cache
57-
vket, vbra = ((1, 1), 1), ((1, 1), 2)
32+
# Test updating the tensors in the cache. QFN bra has both site
33+
# and link inds primed (relative to the ket), so the bra-side
34+
# tensor is constructed as `dag(prime(new_A))` directly.
35+
v = (1, 1)
36+
vket = ket_vertex(ψψ, v)
37+
vbra = bra_vertex(ψψ, v)
5838
A = bpc[vket]
5939
new_A = random_itensor(elt, inds(A))
60-
new_A_dag = ITensors.replaceind(
61-
dag(prime(new_A)), only(s[first(vket)])', only(s[first(vket)])
62-
)
40+
new_A_dag = dag(prime(new_A))
6341
bpc[vket] = new_A
6442
bpc[vbra] = new_A_dag
6543
@test bpc[vket] == new_A
@@ -73,28 +51,31 @@ using Test: @test, @testset
7351
@test eltype(only(message(bpc, pe))) == elt
7452
end
7553
#Test updating the underlying tensornetwork in the cache
76-
v = first(vertices(ψψ))
54+
v_any = first(vertices(ψψ))
7755
rng = StableRNG(1234)
78-
new_tensor = random_itensor(rng, inds(ψψ[v]))
79-
bpc_updated = update_factor(bpc, v, new_tensor)
56+
new_tensor = random_itensor(rng, inds(ψψ[v_any]))
57+
bpc_updated = update_factor(bpc, v_any, new_tensor)
8058
ψψ_updated = tensornetwork(bpc_updated)
81-
@test ψψ_updated[v] == new_tensor
59+
@test ψψ_updated[v_any] == new_tensor
8260

83-
#Test forming a two-site RDM. Check it has the correct size, trace 1 and is PSD
61+
# Two-site RDM at `vs`: ask for the environment around all three
62+
# layers (bra/ket/operator) at `vs` so the BP env is just incoming
63+
# messages, then contract with only the bra and ket tensors at
64+
# `vs` — dropping the per-site identity operator at those
65+
# vertices leaves the bra (primed) and ket (unprimed) site inds
66+
# open, which is exactly the RDM.
8467
vs = [(2, 2), (2, 3)]
85-
86-
# Prime the bra-ket shared site indices on the ket side at the
87-
# selected vertices, so the contracted RDM has open primed/unprimed
88-
# legs there. Mutates a copy of `ψψ` in place; no graph edits.
89-
ψψsplit = copy(ψψ)
90-
for v in vs
91-
common = commoninds(ψψsplit[(v, 1)], ψψsplit[(v, 2)])
92-
ψψsplit[(v, 2)] = prime(ψψsplit[(v, 2)], common)
93-
end
94-
env_tensors = environment(bpc, [(v, 2) for v in vs])
95-
rdm =
96-
contract(vcat(env_tensors, ITensor[ψψsplit[vp] for vp in [(v, 2) for v in vs]]))
97-
68+
env_vs = vcat(
69+
[bra_vertex(ψψ, v) for v in vs],
70+
[ket_vertex(ψψ, v) for v in vs],
71+
[operator_vertex(ψψ, v) for v in vs]
72+
)
73+
env_tensors = environment(bpc, env_vs)
74+
local_tensors = vcat(
75+
ITensor[ψψ[bra_vertex(ψψ, v)] for v in vs],
76+
ITensor[ψψ[ket_vertex(ψψ, v)] for v in vs]
77+
)
78+
rdm = contract(vcat(env_tensors, local_tensors))
9879
rdm = array(
9980
(rdm * combiner(inds(rdm; plev = 0)...)) * combiner(inds(rdm; plev = 1)...)
10081
)

0 commit comments

Comments
 (0)