Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix use of custom dt in transpile when target is provided #11995

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions qiskit/transpiler/passes/scheduling/time_unit_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@ def __init__(self, inst_durations: InstructionDurations = None, target: Target =
Args:
inst_durations (InstructionDurations): A dictionary of durations of instructions.
target: The :class:`~.Target` representing the target backend, if both
``inst_durations`` and ``target`` are specified then this argument will take
precedence and ``inst_durations`` will be ignored.


``inst_durations`` and ``target`` are specified, the ``inst_durations`` argument
will take precedence and ``target`` will be ignored.
"""
super().__init__()
self.inst_durations = inst_durations or InstructionDurations()
if target is not None:
self.inst_durations = target.durations()
if inst_durations is not None:
self.inst_durations = inst_durations
else:
self.inst_durations = (
target.durations() if target is not None else InstructionDurations()
)

def run(self, dag: DAGCircuit):
"""Run the TimeUnitAnalysis pass on `dag`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
The :class:`.TimeUnitConversion` transformation pass now gives precedence to the ``inst_durations``
argument over the ``target`` argument. This fixes an unexpected behavior in :meth:`.transpile` where
custom ``dt`` inputs would be ignored if a ``target`` was provided too (directly or as part of a
:class:`.BackendV2` instance).
94 changes: 52 additions & 42 deletions test/python/circuit/test_scheduled_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from qiskit.circuit import Parameter
from qiskit.circuit.duration import convert_durations_to_dt
from qiskit.providers.fake_provider import Fake27QPulseV1, GenericBackendV2
from qiskit.providers.backend_compat import BackendV2Converter
from qiskit.providers.basic_provider import BasicSimulator
from qiskit.scheduler import ScheduleConfig
from qiskit.transpiler.exceptions import TranspilerError
Expand All @@ -44,6 +45,9 @@ def setUp(self):
# run when alignment values different to 1 are found.
self.backend_with_dt.configuration().timing_constraints = {}
self.backend_without_dt.configuration().timing_constraints = {}
# Generate V2 backends
self.backend_with_dt_v2 = BackendV2Converter(self.backend_with_dt)
self.backend_without_dt_v2 = BackendV2Converter(self.backend_without_dt)
self.dt = 2.2222222222222221e-10
self.simulator_backend = BasicSimulator()

Expand Down Expand Up @@ -79,24 +83,27 @@ def test_schedule_circuit_when_transpile_option_tells_dt(self):
qc.delay(100, 0, unit="ns") # 450[dt]
qc.h(0)
qc.h(1)
sc = transpile(
qc,
self.backend_without_dt,
scheduling_method="alap",
dt=self.dt,
layout_method="trivial",
)
self.assertEqual(sc.duration, 450546)
self.assertEqual(sc.unit, "dt")
self.assertEqual(sc.data[0].operation.name, "delay")
self.assertEqual(sc.data[0].operation.duration, 450450)
self.assertEqual(sc.data[0].operation.unit, "dt")
self.assertEqual(sc.data[1].operation.name, "rz")
self.assertEqual(sc.data[1].operation.duration, 0)
self.assertEqual(sc.data[1].operation.unit, "dt")
self.assertEqual(sc.data[4].operation.name, "delay")
self.assertEqual(sc.data[4].operation.duration, 450450)
self.assertEqual(sc.data[4].operation.unit, "dt")

for backend in [self.backend_without_dt, self.backend_without_dt_v2]:
with self.subTest(backend=backend):
sc = transpile(
qc,
backend,
scheduling_method="alap",
dt=self.dt,
layout_method="trivial",
)
self.assertEqual(sc.duration, 450546)
self.assertEqual(sc.unit, "dt")
self.assertEqual(sc.data[0].operation.name, "delay")
self.assertEqual(sc.data[0].operation.duration, 450450)
self.assertEqual(sc.data[0].operation.unit, "dt")
self.assertEqual(sc.data[1].operation.name, "rz")
self.assertEqual(sc.data[1].operation.duration, 0)
self.assertEqual(sc.data[1].operation.unit, "dt")
self.assertEqual(sc.data[4].operation.name, "delay")
self.assertEqual(sc.data[4].operation.duration, 450450)
self.assertEqual(sc.data[4].operation.unit, "dt")

def test_schedule_circuit_in_sec_when_no_one_tells_dt(self):
"""dt is unknown and all delays and gate times are in SI"""
Expand Down Expand Up @@ -245,23 +252,26 @@ def test_unit_seconds_when_using_backend_durations(self):
qc.h(0)
qc.delay(500 * self.dt, 1, "s")
qc.cx(0, 1)
# usual case
scheduled = transpile(
qc, backend=self.backend_with_dt, scheduling_method="alap", layout_method="trivial"
)
self.assertEqual(scheduled.duration, 1876)

# update durations
durations = InstructionDurations.from_backend(self.backend_with_dt)
durations.update([("cx", [0, 1], 1000 * self.dt, "s")])
scheduled = transpile(
qc,
backend=self.backend_with_dt,
scheduling_method="alap",
instruction_durations=durations,
layout_method="trivial",
)
self.assertEqual(scheduled.duration, 1500)
for backend in [self.backend_with_dt, self.backend_with_dt_v2]:
with self.subTest(backend=backend):
# usual case
scheduled = transpile(
qc, backend=backend, scheduling_method="alap", layout_method="trivial"
)
self.assertEqual(scheduled.duration, 1876)

# update durations
durations = InstructionDurations.from_backend(self.backend_with_dt)
durations.update([("cx", [0, 1], 1000 * self.dt, "s")])
scheduled = transpile(
qc,
backend=backend,
scheduling_method="alap",
instruction_durations=durations,
layout_method="trivial",
)
self.assertEqual(scheduled.duration, 1500)

def test_per_qubit_durations(self):
"""See: https://github.com/Qiskit/qiskit-terra/issues/5109"""
Expand Down Expand Up @@ -348,15 +358,15 @@ def test_change_dt_in_transpile(self):
qc = QuantumCircuit(1, 1)
qc.x(0)
qc.measure(0, 0)
# default case
scheduled = transpile(qc, backend=self.backend_with_dt, scheduling_method="asap")
org_duration = scheduled.duration

# halve dt in sec = double duration in dt
scheduled = transpile(
qc, backend=self.backend_with_dt, scheduling_method="asap", dt=self.dt / 2
)
self.assertEqual(scheduled.duration, org_duration * 2)
for backend in [self.backend_with_dt, self.backend_with_dt_v2]:
with self.subTest(backend=backend):
# default case
scheduled = transpile(qc, backend=backend, scheduling_method="asap")
org_duration = scheduled.duration
# halve dt in sec = double duration in dt
scheduled = transpile(qc, backend=backend, scheduling_method="asap", dt=self.dt / 2)
self.assertEqual(scheduled.duration, org_duration * 2)

@data("asap", "alap")
def test_duration_on_same_instruction_instance(self, scheduling_method):
Expand Down
Loading