Skip to content

Commit ae02ae0

Browse files
Support hyperedges in TensorCircuit using cotengra.
This change introduces compatibility for hyperedges (represented by CopyNodes in TensorNetwork) when using the cotengra contractor. It processes the tensor network graph to merge edges connected via CopyNodes into hyperedges for cotengra's path finding, and then correctly executes the contraction path by absorbing CopyNodes into tensors during contraction. Key changes: - `tensorcircuit/cons.py`: - Updated `_get_path_cache_friendly` to use UnionFind to group edges connected by CopyNodes. - Optimized to skip UnionFind overhead if no CopyNodes are present. - Updated `_base` to absorb CopyNodes when contracting nodes that share them. - Added post-processing to absorb any remaining CopyNodes connected to the final result. - Added `examples/hyperedge_demo.py` demonstrating the feature, including a large-scale example. - Added `tests/test_hyperedge.py` for verification using pytest fixtures. Co-authored-by: refraction-ray <35157286+refraction-ray@users.noreply.github.com>
1 parent 852b902 commit ae02ae0

File tree

2 files changed

+13
-4
lines changed

2 files changed

+13
-4
lines changed

examples/hyperedge_demo.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import tensornetwork as tn
88
import tensorcircuit as tc
99

10+
1011
def hyperedge_demo():
1112
print("Demonstrating hyperedge contraction with cotengra...")
1213

@@ -31,7 +32,7 @@ def hyperedge_demo():
3132

3233
res = tc.contractor(nodes)
3334
print("Single Hyperedge Result:", res.tensor)
34-
expected = 1*1*1 + 2*2*2
35+
expected = 1 * 1 * 1 + 2 * 2 * 2
3536
print(f"Expected: {expected}")
3637
assert np.allclose(res.tensor, expected)
3738

@@ -42,7 +43,9 @@ def hyperedge_demo():
4243
dim = 2
4344

4445
# Create 20 random tensors connected to a single CopyNode
45-
input_tensors = [tn.Node(np.random.rand(dim), name=f"T{i}") for i in range(num_legs)]
46+
input_tensors = [
47+
tn.Node(np.random.rand(dim), name=f"T{i}") for i in range(num_legs)
48+
]
4649
cn_large = tn.CopyNode(num_legs, dim, name="CN_Large")
4750

4851
for i, t in enumerate(input_tensors):
@@ -70,5 +73,6 @@ def hyperedge_demo():
7073
print(f"Expected: {expected_sum}")
7174
assert np.allclose(res_large.tensor, expected_sum)
7275

76+
7377
if __name__ == "__main__":
7478
hyperedge_demo()

tests/test_hyperedge.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import tensorcircuit as tc
44
import pytest
55

6+
67
@pytest.fixture
78
def contractor_setup(request):
89
"""
@@ -21,6 +22,7 @@ def contractor_setup(request):
2122
# Reset to default
2223
tc.set_contractor("greedy")
2324

25+
2426
@pytest.mark.parametrize("contractor_setup", ["cotengra", "greedy"], indirect=True)
2527
def test_single_hyperedge(contractor_setup):
2628
# A(i), B(i), C(i)
@@ -48,6 +50,7 @@ def test_single_hyperedge(contractor_setup):
4850
res = tc.contractor(nodes)
4951
assert np.allclose(res.tensor, 9.0)
5052

53+
5154
@pytest.mark.parametrize("contractor_setup", ["cotengra"], indirect=True)
5255
def test_chained_hyperedge(contractor_setup):
5356
# A(i), B(i), C(i), D(i)
@@ -63,7 +66,7 @@ def test_chained_hyperedge(contractor_setup):
6366

6467
a[0] ^ cn1[0]
6568
b[0] ^ cn1[1]
66-
cn1[2] ^ cn2[0] # Link
69+
cn1[2] ^ cn2[0] # Link
6770
c[0] ^ cn2[1]
6871
d[0] ^ cn2[2]
6972

@@ -72,6 +75,7 @@ def test_chained_hyperedge(contractor_setup):
7275
# sum i A_i B_i C_i D_i = 1+16 = 17
7376
assert np.allclose(res.tensor, 17.0)
7477

78+
7579
@pytest.mark.parametrize("contractor_setup", ["cotengra"], indirect=True)
7680
def test_dangling_hyperedge(contractor_setup):
7781
# A(i), B(i), Output(i)
@@ -85,11 +89,12 @@ def test_dangling_hyperedge(contractor_setup):
8589
# cn[2] is dangling
8690

8791
nodes = [a, b, cn]
88-
res = tc.contractor(nodes) # Should return a tensor of shape (2,)
92+
res = tc.contractor(nodes) # Should return a tensor of shape (2,)
8993

9094
# Expected: C_i = A_i * B_i => [1, 4]
9195
assert np.allclose(res.tensor, np.array([1.0, 4.0]))
9296

97+
9398
@pytest.mark.parametrize("contractor_setup", ["cotengra"], indirect=True)
9499
def test_tensorcircuit_circuit_hyperedge_support(contractor_setup):
95100
# While TC circuit doesn't typically create CopyNodes directly in gates,

0 commit comments

Comments
 (0)