Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Add translation stage to IBM backend and update DD to use #483

Merged
merged 11 commits into from
Feb 9, 2023
2 changes: 1 addition & 1 deletion qiskit_ibm_provider/ibm_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ def _deprecate_id_instruction(

def get_translation_stage_plugin(self) -> str:
"""Return the default translation stage plugin name for IBM backends."""
return "ibm_backend"
return "ibm_dynamic_circuits"
Comment on lines 777 to +779
Copy link
Member

Choose a reason for hiding this comment

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

I think doing this might cause problems for users who are using c_if but dynamic=False - the new plugin will convert the operations at optimisation levels 0 and 1, but then I think the job will fail went sent off to the next step in the pipeline?

I'm actually not sure what the solution is here, since dynamic=True and dynamic=False are handled by the same IBMBackend instance. We don't know if the user intends to set dynamic=True until Backend.run is called so get_translation_stage_plugin can't know it.

Copy link
Contributor Author

@taalexander taalexander Feb 1, 2023

Choose a reason for hiding this comment

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

Nobody can use c_if with dynamic=False AFAIK? The only other solution would be to set a different type of backend that is returned to the user from the provider?

Copy link
Member

Choose a reason for hiding this comment

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

Can they not? I thought the old paths had some amount of support for it for like conditional resets? You'd know better than me - if c_if wouldn't have worked before, then I don't think there's any issue with this change.

Copy link
Contributor Author

@taalexander taalexander Feb 1, 2023

Choose a reason for hiding this comment

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

Yah, the old path doesn't support this (outside of simulators I suppose, in which case the simulators support the conversion regardless). The old path does support resets/MCM but these are unaffected.



class IBMRetiredBackend(IBMBackend):
Expand Down
36 changes: 33 additions & 3 deletions qiskit_ibm_provider/transpiler/passes/scheduling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
teleport.x(qr[2])
teleport.measure(qr[2], result)


teleport = transpile(teleport, backend)

scheduled_teleport = pm.run(teleport)
Expand Down Expand Up @@ -115,7 +116,36 @@
qc_c_if.x(0).c_if(0, 1)
qc_c_if.draw(output="mpl")

To work around this please run the pass
The :class:`.IBMBackend` configures a translation plugin
:class:`.IBMTranslationPlugin` to automatically
apply transformations and optimizations for IBM hardware backends when invoking
:func:`~qiskit.compiler.transpile`. This will automatically convert all old style ``c_if``
conditioned gates to new-style control-flow.

.. jupyter-execute::

# Temporary workaround for mock backends. For real backends this is not required.
backend.get_translation_stage_plugin = lambda: "ibm_dynamic_circuits"

qc_c_if_transpiled = transpile(qc_c_if, backend)

We may then schedule the transpiled circuit without further modification.

.. jupyter-execute::

pm = PassManager(
[
ALAPScheduleAnalysis(durations),
PadDynamicalDecoupling(durations, dd_sequence),
]
)

qc_if_dd = pm.run(qc_c_if_transpiled)
qc_if_dd.draw(output="mpl")


If you are not using the transpiler plugin stages to
work around this please manually run the pass
:class:`qiskit.transpiler.passes.ConvertConditionsToIfOps`
prior to your scheduling pass.

Expand All @@ -131,8 +161,8 @@
]
)

qc_if_test = pm.run(qc_c_if)
qc_if_test.draw(output="mpl")
qc_if_dd = pm.run(qc_c_if)
qc_if_dd.draw(output="mpl")


Exploiting IBM backend's local parallel "fast-path"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
upgrade:
- |
The :class:`qiskit_ibm_provider.ibm_backend.IBMBackend` now returns the
``ibm_dynamic_circuits`` translation stage as its plugin
translation stage. This will automatically add custom
transformations when calling the transpiler that are tuned
for IBM quantum hardware.
83 changes: 82 additions & 1 deletion test/unit/transpiler/passes/scheduling/test_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@

"""Test the dynamic circuits scheduling analysis"""

from unittest.mock import patch

from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister, transpile
from qiskit.providers.fake_provider import FakeJakarta
from qiskit.pulse import Schedule, Play, Constant, DriveChannel
from qiskit.transpiler.passes import ConvertConditionsToIfOps
from qiskit.transpiler.passmanager import PassManager
from qiskit.transpiler.exceptions import TranspilerError


from qiskit_ibm_provider.transpiler.passes.scheduling.pad_delay import PadDelay
from qiskit_ibm_provider.transpiler.passes.scheduling.scheduler import (
ALAPScheduleAnalysis,
Expand Down Expand Up @@ -792,6 +793,46 @@ def test_registers(self):

self.assertEqual(expected, scheduled)

def test_c_if_plugin_conversion_with_transpile(self):
"""Verify that old format c_if may be converted and scheduled
after transpilation with the plugin."""
# Patch the test backend with the plugin
with patch.object(
FakeJakarta,
"get_translation_stage_plugin",
return_value="ibm_dynamic_circuits",
taalexander marked this conversation as resolved.
Show resolved Hide resolved
create=True,
):
backend = FakeJakarta()
# Temporary workaround for mock backends. For real backends this is not required.
backend.configuration().basis_gates.append("if_else")

durations = DynamicCircuitInstructionDurations.from_backend(backend)
pm = PassManager([ASAPScheduleAnalysis(durations), PadDelay()])

qr0 = QuantumRegister(1, name="q")
cr = ClassicalRegister(1, name="c")
qc = QuantumCircuit(qr0, cr)
qc.x(qr0[0]).c_if(cr[0], True)

qc_transpiled = transpile(qc, backend, initial_layout=[0])

scheduled = pm.run(qc_transpiled)

qr1 = QuantumRegister(7, name="q")
cr = ClassicalRegister(1, name="c")
expected = QuantumCircuit(qr1, cr)
with expected.if_test((cr[0], True)):
expected.x(qr1[0])
expected.delay(160, qr1[1])
expected.delay(160, qr1[2])
expected.delay(160, qr1[3])
expected.delay(160, qr1[4])
expected.delay(160, qr1[5])
expected.delay(160, qr1[6])

self.assertEqual(expected, scheduled)


class TestALAPSchedulingAndPaddingPass(ControlFlowTestCase):
"""Tests the ALAP Scheduling passes"""
Expand Down Expand Up @@ -1767,3 +1808,43 @@ def test_transpile_mock_backend(self):
expected.delay(160, qr[6])

self.assertEqual(expected, scheduled)

def test_c_if_plugin_conversion_with_transpile(self):
"""Verify that old format c_if may be converted and scheduled after
transpilation with the plugin."""
# Patch the test backend with the plugin
with patch.object(
FakeJakarta,
"get_translation_stage_plugin",
return_value="ibm_dynamic_circuits",
create=True,
):
backend = FakeJakarta()
# Temporary workaround for mock backends. For real backends this is not required.
backend.configuration().basis_gates.append("if_else")

durations = DynamicCircuitInstructionDurations.from_backend(backend)
pm = PassManager([ALAPScheduleAnalysis(durations), PadDelay()])

qr0 = QuantumRegister(1, name="q")
cr = ClassicalRegister(1, name="c")
qc = QuantumCircuit(qr0, cr)
qc.x(qr0[0]).c_if(cr[0], True)

qc_transpiled = transpile(qc, backend, initial_layout=[0])

scheduled = pm.run(qc_transpiled)

qr1 = QuantumRegister(7, name="q")
cr = ClassicalRegister(1, name="c")
expected = QuantumCircuit(qr1, cr)
with expected.if_test((cr[0], True)):
expected.x(qr1[0])
expected.delay(160, qr1[1])
expected.delay(160, qr1[2])
expected.delay(160, qr1[3])
expected.delay(160, qr1[4])
expected.delay(160, qr1[5])
expected.delay(160, qr1[6])

self.assertEqual(expected, scheduled)