Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions tensorcircuit/cons.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@
)


def _algebraic_base_contraction(

Check failure on line 576 in tensorcircuit/cons.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=tensorcircuit_tensorcircuit-ng&issues=AZ5E7xuho54AYgzJugx4&open=AZ5E7xuho54AYgzJugx4&pullRequest=114
nodes: List[tn.Node],
algorithm: Any,
output_edge_order: Optional[Sequence[tn.Edge]] = None,
Expand All @@ -587,9 +587,16 @@

raw_tensors, input_sets, output_set, size_dict = _extract_topology(nodes)
# Use the backend of the first node
be = nodes[0].backend
if len(nodes) > 0:
be = nodes[0].backend
else:
be = get_backend(get_default_backend())

if len(raw_tensors) == 1:
if len(raw_tensors) == 0:
# Avoid cotengra bug for empty contraction paths
final_raw_tensor = be.ones([])
exponent = 0.0
Comment on lines +595 to +598

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check len(raw_tensors) == 0 correctly handles the case where nodes is empty, but it also catches cases where nodes contains only CopyNodes (hyperedges). In the latter case, if there are dangling edges, returning a scalar ones([]) will cause a crash at line 632 because the tensor rank (0) will not match the number of dangling edges. It is safer to explicitly check if nodes is empty for the scalar 1.0 result, or handle the CopyNode-only case specifically.

Suggested change
if len(raw_tensors) == 0:
# Avoid cotengra bug for empty contraction paths
final_raw_tensor = be.ones([])
exponent = 0.0
if len(nodes) == 0:
# Avoid cotengra bug for empty contraction paths
final_raw_tensor = be.ones([])
exponent = 0.0
elif len(raw_tensors) == 0:
# Networks with only CopyNodes are not yet supported in the algebraic path
# as they require constructing a dense delta tensor.
raise ValueError("Algebraic contraction of networks with only CopyNodes is not supported.")

elif len(raw_tensors) == 1:
# Avoid cotengra bug for empty contraction paths
final_raw_tensor = be.einsum(input_sets[0] + "->" + output_set, *raw_tensors)
exponent = 0.0
Expand Down
21 changes: 21 additions & 0 deletions tests/test_hyperedge.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,3 +545,24 @@
qir = c.to_qir()
c2 = tc.Circuit.from_qir(qir, circuit_params={"nqubits": n})
np.testing.assert_allclose(c.state(), c2.state(), atol=1e-5)

@pytest.mark.parametrize("contractor_setup", [("cotengra", {"use_primitives": True})], indirect=True)
def test_algebraic_contraction_edge_cases(contractor_setup, backend_setup):
from tensorcircuit.cons import _algebraic_base_contraction
import opt_einsum
Comment on lines +551 to +552

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It is generally preferred to place imports at the top of the file to adhere to PEP 8 guidelines and improve readability, unless there is a specific reason for a local import (e.g., avoiding circular dependencies). Since tensorcircuit is already imported as tc, you can also access the function via tc.cons._algebraic_base_contraction.

References
  1. Imports should be at the top of the file. (link)


# 0 nodes case
res0 = _algebraic_base_contraction([], opt_einsum.paths.greedy)
np.testing.assert_allclose(tc.backend.numpy(res0.tensor), 1.0)

# 1 node case
a = tn.Node(tc.backend.convert_to_tensor(np.array([1.0, 2.0])))
res1 = _algebraic_base_contraction([a], opt_einsum.paths.greedy)
np.testing.assert_allclose(tc.backend.numpy(res1.tensor), np.array([1.0, 2.0]))

# 1 node with self-loop (trace)
# _extract_topology handles traces by mapping them to symbols
b = tn.Node(tc.backend.convert_to_tensor(np.eye(2)))
b[0] ^ b[1]

Check warning on line 566 in tests/test_hyperedge.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove or refactor this statement; it has no side effects.

See more on https://sonarcloud.io/project/issues?id=tensorcircuit_tensorcircuit-ng&issues=AZ5E7xxro54AYgzJugx5&open=AZ5E7xxro54AYgzJugx5&pullRequest=114
res1_trace = _algebraic_base_contraction([b], opt_einsum.paths.greedy)
np.testing.assert_allclose(tc.backend.numpy(res1_trace.tensor), 2.0)
Loading