diff --git a/qiskit/circuit/duration.py b/qiskit/circuit/duration.py index 88ce7af968e..6acb230baad 100644 --- a/qiskit/circuit/duration.py +++ b/qiskit/circuit/duration.py @@ -78,8 +78,15 @@ def convert_durations_to_dt(qc: QuantumCircuit, dt_in_sec: float, inplace=True): operation.duration = duration_in_dt(duration, dt_in_sec) operation.unit = "dt" - if circ.duration is not None: - circ.duration = duration_in_dt(circ.duration, dt_in_sec) + if circ.duration is not None and circ.unit != "dt": + if not circ.unit.endswith("s"): + raise CircuitError(f"Invalid time unit: '{circ.unit}'") + + duration = circ.duration + if circ.unit != "s": + duration = apply_prefix(duration, circ.unit) + + circ.duration = duration_in_dt(duration, dt_in_sec) circ.unit = "dt" if not inplace: diff --git a/releasenotes/notes/fix-scheduling-units-59477912b47d3dc1.yaml b/releasenotes/notes/fix-scheduling-units-59477912b47d3dc1.yaml new file mode 100644 index 00000000000..72f4b46fc73 --- /dev/null +++ b/releasenotes/notes/fix-scheduling-units-59477912b47d3dc1.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + A bug has been fixed in :func:`.convert_durations_to_dt` where the function would blindly apply + a conversion from seconds to ``dt`` on circuit durations, independently of the original units of the attribute. + This could lead to wrong orders of magnitude in the reported circuit durations. diff --git a/test/python/circuit/test_scheduled_circuit.py b/test/python/circuit/test_scheduled_circuit.py index 35441076290..ec4bc72ffd0 100644 --- a/test/python/circuit/test_scheduled_circuit.py +++ b/test/python/circuit/test_scheduled_circuit.py @@ -18,8 +18,10 @@ from qiskit import QuantumCircuit, QiskitError from qiskit import transpile, assemble from qiskit.circuit import Parameter -from qiskit.providers.fake_provider import Fake27QPulseV1 +from qiskit.circuit.duration import convert_durations_to_dt +from qiskit.providers.fake_provider import Fake27QPulseV1, GenericBackendV2 from qiskit.providers.basic_provider import BasicSimulator +from qiskit.scheduler import ScheduleConfig from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.instruction_durations import InstructionDurations from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -300,6 +302,48 @@ def test_per_qubit_durations(self): self.assertEqual(sc.qubit_start_time(*q), 300) self.assertEqual(sc.qubit_stop_time(*q), 2400) + def test_convert_duration_to_dt(self): + """Test that circuit duration unit conversion is applied only when necessary. + Tests fix for bug reported in PR #11782.""" + + backend = GenericBackendV2(num_qubits=3, calibrate_instructions=True, seed=10) + schedule_config = ScheduleConfig( + inst_map=backend.target.instruction_schedule_map(), + meas_map=backend.meas_map, + dt=backend.dt, + ) + + circ = QuantumCircuit(2) + circ.cx(0, 1) + circ.measure_all() + + circuit_dt = transpile(circ, backend, scheduling_method="asap") + # reference duration and unit in dt + ref_duration = circuit_dt.duration + ref_unit = circuit_dt.unit + + circuit_s = circuit_dt.copy() + circuit_s.duration *= backend.dt + circuit_s.unit = "s" + + circuit_ms = circuit_s.copy() + circuit_ms.duration *= 1000 + circuit_ms.unit = "ms" + + for circuit in [circuit_dt, circuit_s, circuit_ms]: + with self.subTest(circuit=circuit): + converted_circ = convert_durations_to_dt( + circuit, dt_in_sec=schedule_config.dt, inplace=False + ) + self.assertEqual( + converted_circ.duration, + ref_duration, + ) + self.assertEqual( + converted_circ.unit, + ref_unit, + ) + def test_change_dt_in_transpile(self): qc = QuantumCircuit(1, 1) qc.x(0)