Skip to content

Commit 9320e4d

Browse files
authored
Merge pull request #42 from TohgoRena/fix/qiskit-stats-counters
Fix/qiskit stats counters
2 parents 8f73447 + c24894e commit 9320e4d

File tree

3 files changed

+1268
-1153
lines changed

3 files changed

+1268
-1153
lines changed

src/tranqu/transpiler/qiskit_stats_extractor.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
1-
from qiskit import QuantumCircuit # type: ignore[import-untyped]
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, ClassVar
4+
5+
if TYPE_CHECKING:
6+
from qiskit import QuantumCircuit # type: ignore[import-untyped]
7+
from qiskit.circuit.controlflow import ( # type: ignore[import-untyped]
8+
CONTROL_FLOW_OP_NAMES,
9+
)
210

311

412
class QiskitStatsExtractor:
513
"""Extract statistical information from Qiskit quantum circuits."""
614

15+
_NON_GATE_OPERATION: ClassVar[set[str]] = {
16+
"measure",
17+
"reset",
18+
"barrier",
19+
"delay",
20+
"initialize",
21+
*CONTROL_FLOW_OP_NAMES, # 'if_else','for_loop','while_loop','switch_case'
22+
}
23+
724
def extract_stats_from(self, program: QuantumCircuit) -> dict[str, int]:
825
"""Extract statistical information from a Qiskit quantum circuit.
926
@@ -16,16 +33,46 @@ def extract_stats_from(self, program: QuantumCircuit) -> dict[str, int]:
1633
"""
1734
stats = {}
1835
stats["n_qubits"] = program.num_qubits
19-
stats["n_gates"] = len(program.data)
36+
stats["n_gates"] = self._count_gates(program)
2037
stats["n_gates_1q"] = self._count_single_qubit_gates(program)
2138
stats["n_gates_2q"] = self._count_two_qubit_gates(program)
2239
stats["depth"] = program.depth()
2340
return stats
2441

42+
@staticmethod
43+
def _count_gates(program: QuantumCircuit) -> int:
44+
# sum non_gate operation
45+
return sum(
46+
1
47+
for instruction in program.data
48+
if instruction.operation.name
49+
not in QiskitStatsExtractor._NON_GATE_OPERATION
50+
)
51+
2552
@staticmethod
2653
def _count_single_qubit_gates(program: QuantumCircuit) -> int:
27-
return sum(1 for instruction in program.data if len(instruction.qubits) == 1)
54+
data = program.data
55+
count = 0
56+
for instruction in data:
57+
# is 1 qubit?
58+
if len(instruction.qubits) != 1:
59+
continue
60+
# is non gate opration?
61+
if instruction.operation.name in QiskitStatsExtractor._NON_GATE_OPERATION:
62+
continue
63+
count += 1
64+
return count
2865

2966
@staticmethod
3067
def _count_two_qubit_gates(program: QuantumCircuit) -> int:
31-
return sum(1 for instruction in program.data if len(instruction.qubits) == 2) # noqa: PLR2004
68+
data = program.data
69+
count = 0
70+
for instruction in data:
71+
# is 2 qubit?
72+
if len(instruction.qubits) != 2: # noqa: PLR2004
73+
continue
74+
# is non gate opration?
75+
if instruction.operation.name in QiskitStatsExtractor._NON_GATE_OPERATION:
76+
continue
77+
count += 1
78+
return count
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# mypy: disable-error-code="import-untyped"
2+
3+
from __future__ import annotations
4+
5+
from qiskit import QuantumCircuit
6+
7+
from tranqu.transpiler.qiskit_stats_extractor import QiskitStatsExtractor
8+
9+
10+
class TestQiskitStatsExtractor:
11+
def test_simple_circuit_counts(self) -> None:
12+
"""Check that basic 1-qubit / 2-qubit gate counts are correct."""
13+
circuit = QuantumCircuit(2)
14+
circuit.h(0)
15+
circuit.x(0)
16+
circuit.cx(0, 1)
17+
18+
extractor = QiskitStatsExtractor()
19+
stats = extractor.extract_stats_from(circuit)
20+
21+
# Number of qubits
22+
assert stats["n_qubits"] == 2
23+
# Only H, X, and CX (3 operations) are counted as gates
24+
assert stats["n_gates"] == 3
25+
# 1-qubit gates: H and X (2 operations)
26+
assert stats["n_gates_1q"] == 2
27+
# 2-qubit gates: only CX (1 operation)
28+
assert stats["n_gates_2q"] == 1
29+
# Depth should match QuantumCircuit.depth()
30+
assert stats["depth"] == circuit.depth()
31+
32+
def test_non_gate_operations_are_ignored(self) -> None:
33+
"""Check that non-gate operations."""
34+
circuit = QuantumCircuit(2, 2)
35+
# Gate
36+
circuit.h(0)
37+
circuit.x(0)
38+
circuit.cx(0, 1)
39+
# Various non-gate operations
40+
circuit.barrier()
41+
circuit.measure(0, 0)
42+
circuit.reset(1)
43+
circuit.delay(100, 0)
44+
circuit.initialize([1, 0], 0)
45+
46+
extractor = QiskitStatsExtractor()
47+
stats = extractor.extract_stats_from(circuit)
48+
49+
# Number of qubits is still correct
50+
assert stats["n_qubits"] == 2
51+
# Only H, X, and CX (3 operations) are counted as gates
52+
assert stats["n_gates"] == 3
53+
assert stats["n_gates_1q"] == 2
54+
assert stats["n_gates_2q"] == 1
55+
56+
def test_empty_circuit(self) -> None:
57+
"""Check that an empty circuit does not raise and all counts are zero."""
58+
circuit = QuantumCircuit(3)
59+
60+
extractor = QiskitStatsExtractor()
61+
stats = extractor.extract_stats_from(circuit)
62+
63+
assert stats["n_qubits"] == 3
64+
assert stats["n_gates"] == 0
65+
assert stats["n_gates_1q"] == 0
66+
assert stats["n_gates_2q"] == 0
67+
assert stats["depth"] == 0

0 commit comments

Comments
 (0)