Skip to content

Commit 6e10f3e

Browse files
⚡ Speed up HRS and graph-state construction (#1119)
* perf: reuse HRS gates and build graph states from edges * docs: link construction improvements to PR 1119 --------- Signed-off-by: simon1hofmann <119581649+simon1hofmann@users.noreply.github.com>
1 parent b0a59dc commit 6e10f3e

4 files changed

Lines changed: 28 additions & 9 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ that minor releases may include breaking changes.
3636

3737
### Changed
3838

39+
- ⚡ Reuse the controlled adder in HRS multipliers and build graph-state gates
40+
directly from sparse edges ([#1119]) ([**@simon1hofmann**])
3941
- ⚡ Test all Python versions on Linux and Python 3.15 on other platforms for
4042
ready pull requests. Keep the full matrix on main, merge queues, and manual
4143
runs ([#1112]) ([**@simon1hofmann**])
@@ -282,6 +284,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._
282284

283285
<!-- PR links -->
284286

287+
[#1119]: https://github.com/munich-quantum-toolkit/bench/pull/1119
285288
[#1118]: https://github.com/munich-quantum-toolkit/bench/pull/1118
286289
[#1117]: https://github.com/munich-quantum-toolkit/bench/pull/1117
287290
[#1116]: https://github.com/munich-quantum-toolkit/bench/pull/1116

‎src/mqt/bench/benchmarks/graphstate.py‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import networkx as nx
1414
from qiskit.circuit import QuantumCircuit, QuantumRegister
15-
from qiskit.circuit.library import GraphStateGate
1615

1716
from ._registry import register_benchmark
1817

@@ -35,7 +34,11 @@ def create_circuit(num_qubits: int, degree: int = 2, seed: int | None = None) ->
3534
qc = QuantumCircuit(q, name="graphstate")
3635

3736
g = nx.random_regular_graph(degree, num_qubits, seed=seed)
38-
a = nx.convert_matrix.to_numpy_array(g)
39-
qc.compose(GraphStateGate(a), inplace=True)
37+
# Keep NetworkX's node order to preserve seeded wire assignments.
38+
indices = {node: index for index, node in enumerate(g)}
39+
edges = sorted(sorted((indices[a], indices[b])) for a, b in g.edges)
40+
qc.h(q)
41+
for a, b in edges:
42+
qc.cz(a, b)
4043
qc.measure_all()
41-
return qc.decompose(gates_to_decompose="graph_state")
44+
return qc

‎src/mqt/bench/benchmarks/hrs_cumulative_multiplier.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def create_circuit(num_qubits: int) -> QuantumCircuit:
6060

6161
# prepare adder as controlled gate
6262
adder = adder_ripple_c04(num_state_qubits, kind="half")
63+
controlled_adder = adder.to_gate().control(1)
6364

6465
# get the number of helper qubits needed
6566
num_helper_qubits = adder.num_ancillas
@@ -73,10 +74,7 @@ def create_circuit(num_qubits: int) -> QuantumCircuit:
7374
qc = QuantumCircuit(*qregs)
7475

7576
for i in range(num_state_qubits):
76-
num_adder_qubits = num_state_qubits
77-
adder_for_current_step = adder
78-
controlled_adder = adder_for_current_step.to_gate().control(1)
79-
qr_list = [qr_a[i], *qr_b[:num_adder_qubits], *qr_out[i : num_state_qubits + i + 1]]
77+
qr_list = [qr_a[i], *qr_b[:], *qr_out[i : num_state_qubits + i + 1]]
8078
if num_helper_qubits > 0 and qr_helper:
8179
qr_list.extend(qr_helper[:])
8280
qc.append(controlled_adder, qr_list)

‎tests/test_bench.py‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
from importlib import metadata
2020
from typing import TYPE_CHECKING, NoReturn, cast
2121

22+
import networkx as nx
2223
import numpy as np
2324
import pytest
2425
from qiskit import QuantumCircuit, qasm2, qasm3, qpy
2526
from qiskit.circuit import ClassicalRegister, ForLoopOp, IfElseOp, Parameter
26-
from qiskit.circuit.library import CXGate, HGate, RXGate, RZGate, TGate, XGate
27+
from qiskit.circuit.library import CXGate, GraphStateGate, HGate, RXGate, RZGate, TGate, XGate
2728
from qiskit.compiler import transpile
2829
from qiskit.converters import circuit_to_dag
2930
from qiskit.primitives import StatevectorSampler
@@ -212,6 +213,7 @@ def test_arithmetic_circuits(benchmark_name: str, input_value: int) -> None:
212213
("multiplier", 8, None, (4, 5, 6, 7), [1, 3], "0100"), # 2 * 2 = 4
213214
("rg_qft_multiplier", 8, None, (4, 5, 6, 7), [1, 3], "0100"), # 2 * 2 = 4
214215
("hrs_cumulative_multiplier", 9, None, (4, 5, 6, 7), [1, 3], "0100"), # 2 * 2 = 4
216+
("hrs_cumulative_multiplier", 9, None, (4, 5, 6, 7), [0, 1, 2, 3], "1001"), # 3 * 3 = 9
215217
],
216218
)
217219
@pytest.mark.parametrize("level", list(BenchmarkLevel))
@@ -678,6 +680,19 @@ def test_dj_constant_oracle() -> None:
678680
assert Statevector(qc).probabilities([0, 1, 2, 3])[0] == pytest.approx(1)
679681

680682

683+
@pytest.mark.parametrize(("num_qubits", "degree", "seed"), [(1, 0, 42), (6, 2, 0), (6, 3, 42)])
684+
def test_graphstate_seeded_operator(num_qubits: int, degree: int, seed: int) -> None:
685+
"""Direct graph-state construction preserves the seeded adjacency-matrix operator."""
686+
graph = nx.random_regular_graph(degree, num_qubits, seed=seed)
687+
expected = GraphStateGate(nx.to_numpy_array(graph))
688+
circuit = get_benchmark_alg("graphstate", num_qubits, degree=degree, seed=seed)
689+
690+
assert circuit.count_ops().get("cz", 0) == num_qubits * degree // 2
691+
assert circuit.count_ops()["measure"] == num_qubits
692+
circuit.remove_final_measurements()
693+
assert Operator(circuit).equiv(Operator(expected))
694+
695+
681696
@pytest.mark.parametrize("benchmark", ["graphstate", "randomcircuit", "vqe_real_amp"])
682697
def test_benchmark_seed(benchmark: str) -> None:
683698
"""The seed controls both random construction and parameter binding."""

0 commit comments

Comments
 (0)