diff --git a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py index d53c3fc4ef6a..e0931133c52e 100644 --- a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py +++ b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py @@ -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`. diff --git a/releasenotes/notes/transpile-custom-dt-precedence-3c7354e40bbd1107.yaml b/releasenotes/notes/transpile-custom-dt-precedence-3c7354e40bbd1107.yaml new file mode 100644 index 000000000000..c1436948f953 --- /dev/null +++ b/releasenotes/notes/transpile-custom-dt-precedence-3c7354e40bbd1107.yaml @@ -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). \ No newline at end of file diff --git a/test/python/circuit/test_scheduled_circuit.py b/test/python/circuit/test_scheduled_circuit.py index ec4bc72ffd06..9a3ebd5215da 100644 --- a/test/python/circuit/test_scheduled_circuit.py +++ b/test/python/circuit/test_scheduled_circuit.py @@ -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 @@ -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() @@ -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""" @@ -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""" @@ -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):