Skip to content

Commit

Permalink
Bugfix: transpile() fails in a circuit with delay ops. (#151)
Browse files Browse the repository at this point in the history
* Bugfix: transpile() now accepts delay ops.

---------

Co-authored-by: Ville Bergholm <[email protected]>
  • Loading branch information
smite and Ville Bergholm authored Mar 5, 2025
1 parent 230b93e commit d671d72
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 30 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
Changelog
=========

Version 17.4
============

* Bugfix: The ``delay`` operation is now accepted by the standard transpiler.
`#151 <https://github.com/iqm-finland/qiskit-on-iqm/pull/151>`_

Version 17.3
============

Expand Down
8 changes: 5 additions & 3 deletions src/iqm/qiskit_iqm/iqm_transpilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
dag = Optimize1qGatesDecomposition(self._intermediate_basis).run(dag)
for node in dag.topological_op_nodes():
if node.name == 'u':
# convert into PRX + RZ
qubit_index = dag.find_bit(node.qargs[0])[0]
if math.isclose(node.op.params[0], 0, abs_tol=TOLERANCE):
dag.remove_op_node(node)
Expand All @@ -97,13 +98,14 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
# makes no sense to convert it into active z rotations if we hit a barrier?
pass
elif node.name == 'move':
# acts like iSWAP with RZ, moving it to the other component
qb, res = dag.find_bit(node.qargs[0])[0], dag.find_bit(node.qargs[1])[0]
rz_angles[res], rz_angles[qb] = rz_angles[qb], rz_angles[res]
elif node.name == 'cz':
pass # rz_angles are commute through CZ gates
elif node.name in {'cz', 'delay'}:
pass # commutes with RZ gates
else:
raise ValueError(
f'Unexpected operation {node.name} in circuit given to IQMOptimizeSingleQubitGates pass'
f"Unexpected operation '{node.name}' in circuit given to IQMOptimizeSingleQubitGates pass"
)

if not self._drop_final_rz:
Expand Down
54 changes: 27 additions & 27 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,21 @@ def linear_3q_architecture_static():
)


def _1q_loci(qubits: list[str]) -> tuple[tuple[str, ...], ...]:
"""One-qubit loci for the given qubits."""
return tuple((q,) for q in qubits)


@pytest.fixture
def linear_3q_architecture():
qubits = ['QB1', 'QB2', 'QB3']
return DynamicQuantumArchitecture(
calibration_set_id=UUID('59478539-dcef-4b2e-80c8-122d7ec3fc89'),
qubits=['QB1', 'QB2', 'QB3'],
qubits=qubits,
computational_resonators=[],
gates={
'prx': GateInfo(
implementations={'drag_gaussian': GateImplementationInfo(loci=(('QB1',), ('QB2',), ('QB3',)))},
implementations={'drag_gaussian': GateImplementationInfo(loci=_1q_loci(qubits))},
default_implementation='drag_gaussian',
override_default_implementation={},
),
Expand All @@ -63,7 +69,7 @@ def linear_3q_architecture():
override_default_implementation={},
),
'measure': GateInfo(
implementations={'constant': GateImplementationInfo(loci=(('QB1',), ('QB2',), ('QB3',)))},
implementations={'constant': GateImplementationInfo(loci=_1q_loci(qubits))},
default_implementation='constant',
override_default_implementation={},
),
Expand All @@ -73,22 +79,19 @@ def linear_3q_architecture():

@pytest.fixture
def adonis_architecture():
qubits = ['QB1', 'QB2', 'QB3', 'QB4', 'QB5']
return DynamicQuantumArchitecture(
calibration_set_id=UUID('59478539-dcef-4b2e-80c8-122d7ec3fc89'),
qubits=['QB1', 'QB2', 'QB3', 'QB4', 'QB5'],
qubits=qubits,
computational_resonators=[],
gates={
'prx': GateInfo(
implementations={
'drag_gaussian': GateImplementationInfo(loci=(('QB1',), ('QB2',), ('QB3',), ('QB4',), ('QB5',)))
},
implementations={'drag_gaussian': GateImplementationInfo(loci=_1q_loci(qubits))},
default_implementation='drag_gaussian',
override_default_implementation={},
),
'cc_prx': GateInfo(
implementations={
'prx_composite': GateImplementationInfo(loci=(('QB1',), ('QB2',), ('QB3',), ('QB4',), ('QB5',)))
},
implementations={'prx_composite': GateImplementationInfo(loci=_1q_loci(qubits))},
default_implementation='prx_composite',
override_default_implementation={},
),
Expand All @@ -102,9 +105,7 @@ def adonis_architecture():
override_default_implementation={},
),
'measure': GateInfo(
implementations={
'constant': GateImplementationInfo(loci=(('QB1',), ('QB2',), ('QB3',), ('QB4',), ('QB5',)))
},
implementations={'constant': GateImplementationInfo(loci=_1q_loci(qubits))},
default_implementation='constant',
override_default_implementation={},
),
Expand All @@ -114,20 +115,24 @@ def adonis_architecture():

@pytest.fixture
def move_architecture():
qubits = ['QB1', 'QB2', 'QB3', 'QB4', 'QB5', 'QB6']
return DynamicQuantumArchitecture(
calibration_set_id=UUID('26c5e70f-bea0-43af-bd37-6212ec7d04cb'),
qubits=['QB1', 'QB2', 'QB3', 'QB4', 'QB5', 'QB6'],
qubits=qubits,
computational_resonators=['CR1'],
gates={
'prx': GateInfo(
implementations={
'drag_gaussian': GateImplementationInfo(
loci=(('QB1',), ('QB2',), ('QB3',), ('QB4',), ('QB5',), ('QB6',))
),
'drag_gaussian': GateImplementationInfo(loci=_1q_loci(qubits)),
},
default_implementation='drag_gaussian',
override_default_implementation={},
),
'cc_prx': GateInfo(
implementations={'prx_composite': GateImplementationInfo(loci=_1q_loci(qubits))},
default_implementation='prx_composite',
override_default_implementation={},
),
'cz': GateInfo(
implementations={
'tgss': GateImplementationInfo(
Expand All @@ -152,9 +157,7 @@ def move_architecture():
),
'measure': GateInfo(
implementations={
'constant': GateImplementationInfo(
loci=(('QB1',), ('QB2',), ('QB3',), ('QB4',), ('QB5',), ('QB6',))
),
'constant': GateImplementationInfo(loci=_1q_loci(qubits)),
},
default_implementation='constant',
override_default_implementation={},
Expand Down Expand Up @@ -241,16 +244,15 @@ def hypothetical_fake_architecture():

@pytest.fixture
def ndonis_architecture():
qubits = ['QB1', 'QB2', 'QB3', 'QB4', 'QB5', 'QB6']
return DynamicQuantumArchitecture(
calibration_set_id=UUID('26c5e70f-bea0-43af-bd37-6212ec7d04cb'),
qubits=['QB1', 'QB2', 'QB3', 'QB4', 'QB5', 'QB6'],
qubits=qubits,
computational_resonators=['CR1'],
gates={
'prx': GateInfo(
implementations={
'drag_gaussian': GateImplementationInfo(
loci=(('QB1',), ('QB2',), ('QB3',), ('QB4',), ('QB5',), ('QB6',))
),
'drag_gaussian': GateImplementationInfo(loci=_1q_loci(qubits)),
},
default_implementation='drag_gaussian',
override_default_implementation={},
Expand Down Expand Up @@ -289,9 +291,7 @@ def ndonis_architecture():
),
'measure': GateInfo(
implementations={
'constant': GateImplementationInfo(
loci=(('QB1',), ('QB2',), ('QB3',), ('QB4',), ('QB5',), ('QB6',))
),
'constant': GateImplementationInfo(loci=_1q_loci(qubits)),
},
default_implementation='constant',
override_default_implementation={},
Expand Down
4 changes: 4 additions & 0 deletions tests/test_iqm_transpilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,12 @@ def test_optimize_single_qubit_gates_preserves_layout(backend):
def test_qiskit_native_transpiler(move_architecture, optimization_level):
"""Tests that a simple circuit is transpiled correctly using the Qiskit transpiler."""
backend, _ = get_mocked_backend(move_architecture)
# circuit should contain all our supported operations to make sure the transpiler can handle them
qc = QuantumCircuit(2)
qc.h(0)
qc.barrier(0, 1)
qc.delay(10, 0, unit='ns')
qc.reset(0)
qc.cx(0, 1)
qc.measure_all()
transpiled_circuit = transpile(qc, backend=backend, optimization_level=optimization_level)
Expand Down

0 comments on commit d671d72

Please sign in to comment.