diff --git a/src/qc_compiler/__init__.py b/src/qc_compiler/__init__.py index 84a83bb..8912f82 100644 --- a/src/qc_compiler/__init__.py +++ b/src/qc_compiler/__init__.py @@ -28,11 +28,13 @@ DEFAULT_TWO_QUBIT_ERROR, DEFAULT_TWO_QUBIT_GATE_TIME, TWO_QUBIT_GATES, + clbit_index, compute_circuit_depth, compute_cnot_count, compute_idle_fraction, get_avg_gate_time, get_backend_properties, + qubit_index, ) __all__ = [ @@ -63,9 +65,11 @@ "QCompilerResult", "ScheduleResult", "TranspileConfig", + "clbit_index", "compute_circuit_depth", "compute_cnot_count", "compute_idle_fraction", "get_avg_gate_time", "get_backend_properties", + "qubit_index", ] \ No newline at end of file diff --git a/src/qc_compiler/batching.py b/src/qc_compiler/batching.py index 97bd9fe..a377153 100644 --- a/src/qc_compiler/batching.py +++ b/src/qc_compiler/batching.py @@ -26,6 +26,7 @@ from qiskit import QuantumCircuit from qc_compiler.cost_model import CostModel +from qc_compiler.utils import qubit_index @dataclass @@ -298,7 +299,7 @@ def _compute_core_hash(self, circuit: QuantumCircuit) -> int: gate_name = instr.operation.name if gate_name in ("measure", "barrier", "reset", "snapshot"): continue - qubits = tuple(circuit.find_bit(q).index for q in instr.qubits) + qubits = tuple(qubit_index(circuit, q) for q in instr.qubits) params = tuple( float(p) if hasattr(p, '__float__') else str(p) for p in instr.operation.params @@ -328,7 +329,7 @@ def _detect_measurement_basis( if instr.operation.name == "measure": measure_indices.add(idx) for q in instr.qubits: - measured_qubits.add(circuit.find_bit(q).index) + measured_qubits.add(qubit_index(circuit, q)) if not measured_qubits: return "none" @@ -347,7 +348,7 @@ def _detect_measurement_basis( if instr.operation.name in ("measure", "barrier", "reset", "snapshot"): continue if len(instr.qubits) == 1: - qubit_idx = circuit.find_bit(instr.qubits[0]).index + qubit_idx = qubit_index(circuit, instr.qubits[0]) if qubit_idx in measured_qubits: pre_measure_gates[qubit_idx].append( instr.operation.name diff --git a/src/qc_compiler/cost_model.py b/src/qc_compiler/cost_model.py index 3ff22e7..8a964ca 100644 --- a/src/qc_compiler/cost_model.py +++ b/src/qc_compiler/cost_model.py @@ -26,6 +26,7 @@ TWO_QUBIT_GATES, compute_circuit_depth, get_backend_properties, + qubit_index, ) SINGLE_QUBIT_GATES = {"id", "rz", "sx", "x", "h", "s", "t", "p", "u", "u1", "u2", "u3"} @@ -212,7 +213,7 @@ def estimate_gate_error( for instr in circuit.data: gate_name = instr.operation.name qubits = tuple( - circuit.find_bit(q).index for q in instr.qubits + qubit_index(circuit, q) for q in instr.qubits ) if len(qubits) == 2 and gate_name in TWO_QUBIT_GATES: fidelity = self._get_gate_fidelity_for_pair( @@ -473,7 +474,7 @@ def _get_measured_qubits(self, circuit: QuantumCircuit) -> list[int]: for instr in circuit.data: if instr.operation.name == "measure": for qubit in instr.qubits: - measured.add(circuit.find_bit(qubit).index) + measured.add(qubit_index(circuit, qubit)) if not measured: return [] return sorted(measured) diff --git a/src/qc_compiler/cutting.py b/src/qc_compiler/cutting.py index 269e557..630c153 100644 --- a/src/qc_compiler/cutting.py +++ b/src/qc_compiler/cutting.py @@ -49,6 +49,7 @@ DEFAULT_T2_TIME, DEFAULT_TWO_QUBIT_ERROR, TWO_QUBIT_GATES, + qubit_index, ) @@ -367,7 +368,7 @@ def _find_cut_candidates( for idx, instr in enumerate(circuit.data): gate_name = instr.operation.name qubits = tuple( - circuit.find_bit(q).index for q in instr.qubits + qubit_index(circuit, q) for q in instr.qubits ) if gate_name in TWO_QUBIT_GATES and len(qubits) == 2: @@ -596,7 +597,7 @@ def union(x, y): for idx, instr in enumerate(circuit.data): qubits = tuple( - circuit.find_bit(q).index for q in instr.qubits + qubit_index(circuit, q) for q in instr.qubits ) if len(qubits) == 2: q0, q1 = qubits @@ -638,7 +639,7 @@ def _extract_subcircuit( for idx, instr in enumerate(circuit.data): gate_qubits = tuple( - circuit.find_bit(q).index for q in instr.qubits + qubit_index(circuit, q) for q in instr.qubits ) if idx in cut_gate_indices: diff --git a/src/qc_compiler/fusion.py b/src/qc_compiler/fusion.py index d2e00bc..1dca341 100644 --- a/src/qc_compiler/fusion.py +++ b/src/qc_compiler/fusion.py @@ -37,6 +37,7 @@ from qiskit.synthesis import OneQubitEulerDecomposer from qc_compiler.cost_model import CircuitMetrics, CostModel +from qc_compiler.utils import qubit_index @dataclass @@ -302,7 +303,7 @@ def _find_single_qubit_chains( affected_qubits = set() for q in instr.qubits: - qidx = circuit.find_bit(q).index + qidx = qubit_index(circuit, q) affected_qubits.add(qidx) for q in affected_qubits: if q in active_chains: @@ -314,7 +315,7 @@ def _find_single_qubit_chains( continue if len(instr.qubits) == 1: - qubit_idx = circuit.find_bit(instr.qubits[0]).index + qubit_idx = qubit_index(circuit, instr.qubits[0]) if qubit_idx in active_chains: active_chains[qubit_idx][2].append(idx) diff --git a/src/qc_compiler/scheduling.py b/src/qc_compiler/scheduling.py index a3b2d37..5e6cbeb 100644 --- a/src/qc_compiler/scheduling.py +++ b/src/qc_compiler/scheduling.py @@ -33,7 +33,7 @@ from qiskit import QuantumCircuit from qc_compiler.cost_model import CostModel -from qc_compiler.utils import DEFAULT_SINGLE_QUBIT_GATE_TIME +from qc_compiler.utils import DEFAULT_SINGLE_QUBIT_GATE_TIME, clbit_index, qubit_index @dataclass @@ -214,14 +214,14 @@ def _asap_schedule(self, circuit: QuantumCircuit) -> QuantumCircuit: for instr in circuit.data: gate = instr.operation - qubits = [circuit.find_bit(q).index for q in instr.qubits] + qubits = [qubit_index(circuit, q) for q in instr.qubits] earliest_cycle = max(qubit_next_cycle[q] for q in qubits) if qubits else 0 gate_duration = self._get_gate_duration(gate.name, qubits) target_qubits = [circuit.qubits[q] for q in qubits] - target_clbits = [circuit.find_bit(c).index for c in instr.clbits] + target_clbits = [clbit_index(circuit, c) for c in instr.clbits] clbit_refs = [scheduled.clbits[i] for i in target_clbits] if target_clbits else [] scheduled.append(gate, target_qubits, clbit_refs) @@ -271,7 +271,7 @@ def _alap_schedule(self, circuit: QuantumCircuit) -> QuantumCircuit: for i in range(len(circuit.data) - 1, -1, -1): instr = circuit.data[i] - qubits = [circuit.find_bit(q).index for q in instr.qubits] + qubits = [qubit_index(circuit, q) for q in instr.qubits] if not qubits: gate_latest_start[i] = 0 @@ -299,9 +299,9 @@ def _alap_schedule(self, circuit: QuantumCircuit) -> QuantumCircuit: for i in gate_order: instr = circuit.data[i] gate = instr.operation - qubits = [circuit.find_bit(q).index for q in instr.qubits] + qubits = [qubit_index(circuit, q) for q in instr.qubits] target_qubits = [circuit.qubits[q] for q in qubits] - target_clbits = [circuit.find_bit(c).index for c in instr.clbits] + target_clbits = [clbit_index(circuit, c) for c in instr.clbits] clbit_refs = [scheduled.clbits[j] for j in target_clbits] if target_clbits else [] scheduled.append(gate, target_qubits, clbit_refs) @@ -351,13 +351,13 @@ def _coherence_aware_schedule( qubit_first_gate = [-1] * circuit.num_qubits for i, instr in enumerate(circuit.data): - qubits = [circuit.find_bit(q).index for q in instr.qubits] + qubits = [qubit_index(circuit, q) for q in instr.qubits] for q in qubits: if qubit_first_gate[q] == -1: qubit_first_gate[q] = i for i, instr in enumerate(circuit.data): - qubits = [circuit.find_bit(q).index for q in instr.qubits] + qubits = [qubit_index(circuit, q) for q in instr.qubits] for q in qubits: if qubit_latest_gate[q] >= 0: predecessors[i].add(qubit_latest_gate[q]) @@ -365,9 +365,9 @@ def _coherence_aware_schedule( for i in range(num_gates - 1): if circuit.data[i].operation.name == "barrier": - for q in [circuit.find_bit(q).index for q in circuit.data[i].qubits]: + for q in [qubit_index(circuit, q) for q in circuit.data[i].qubits]: for j in range(i + 1, num_gates): - jqubits = [circuit.find_bit(q).index for q in circuit.data[j].qubits] + jqubits = [qubit_index(circuit, q) for q in circuit.data[j].qubits] if q in jqubits: predecessors[j].add(i) @@ -391,15 +391,15 @@ def _coherence_aware_schedule( if not ready: break - ready.sort(key=lambda i: min(t2_priority.get(q, float('inf')) for q in [circuit.find_bit(q).index for q in circuit.data[i].qubits]) if circuit.data[i].qubits else float('inf')) + ready.sort(key=lambda i: min(t2_priority.get(q, float('inf')) for q in [qubit_index(circuit, q) for q in circuit.data[i].qubits]) if circuit.data[i].qubits else float('inf')) gate_idx = ready[0] instr = circuit.data[gate_idx] gate = instr.operation - qubits = [circuit.find_bit(q).index for q in instr.qubits] + qubits = [qubit_index(circuit, q) for q in instr.qubits] target_qubits = [circuit.qubits[q] for q in qubits] - target_clbits = [circuit.find_bit(c).index for c in instr.clbits] + target_clbits = [clbit_index(circuit, c) for c in instr.clbits] clbit_refs = [scheduled.clbits[j] for j in target_clbits] if target_clbits else [] scheduled.append(gate, target_qubits, clbit_refs) @@ -440,7 +440,7 @@ def _compute_idle_times( qubit_active_cycles = {q: 0 for q in range(circuit.num_qubits)} for instr in circuit.data: for qubit in instr.qubits: - qidx = circuit.find_bit(qubit).index + qidx = qubit_index(circuit, qubit) qubit_active_cycles[qidx] += 1 idle_times = {} @@ -464,7 +464,7 @@ def _compute_asap_starts(self, circuit: QuantumCircuit) -> list[int]: starts = [] for instr in circuit.data: - qubits = [circuit.find_bit(q).index for q in instr.qubits] + qubits = [qubit_index(circuit, q) for q in instr.qubits] if not qubits: starts.append(0) continue diff --git a/src/qc_compiler/utils.py b/src/qc_compiler/utils.py index f7dd805..4b6ad03 100644 --- a/src/qc_compiler/utils.py +++ b/src/qc_compiler/utils.py @@ -21,6 +21,35 @@ DEFAULT_TWO_QUBIT_GATE_TIME = 300e-9 +def qubit_index(circuit: QuantumCircuit, qubit) -> int: + """Get the integer index of a qubit in a circuit. + + Uses circuit.find_bit() for compatibility, but can be updated + if Qiskit deprecates find_bit in a future release. + + Args: + circuit: The quantum circuit. + qubit: A qubit reference from circuit.data. + + Returns: + Integer index of the qubit. + """ + return circuit.find_bit(qubit).index + + +def clbit_index(circuit: QuantumCircuit, clbit) -> int: + """Get the integer index of a classical bit in a circuit. + + Args: + circuit: The quantum circuit. + clbit: A classical bit reference from circuit.data. + + Returns: + Integer index of the classical bit. + """ + return circuit.find_bit(clbit).index + + def get_backend_properties(backend: BackendV2) -> dict: """Extract calibration properties from a quantum backend.