Skip to content

Commit

Permalink
Deprecate instruction_durations, timing_constraints and `backend_…
Browse files Browse the repository at this point in the history
…properties` (#13338)

* Deprecate instruction_durations, timing_constraints and backend_properties

* test_scheduling_instruction_constraints

* Fix test/python/transpiler/test_preset_passmanagers.py

* Fix test/python/visualization/timeline/test_core.py

* Fix test/python/compiler/test_transpiler.py

* Fix test/python/circuit/test_scheduled_circuit.py

* single line

* test.python.circuit.test_scheduled_circuit.TestScheduledCircuit

* Apply suggestions from code review

Co-authored-by: Elena Peña Tapia <[email protected]>

* reno

* Update reno example

* tests

---------

Co-authored-by: Elena Peña Tapia <[email protected]>
Co-authored-by: Elena Peña Tapia <[email protected]>
  • Loading branch information
3 people authored Oct 30, 2024
1 parent 91ceee5 commit 2d0e28f
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 122 deletions.
80 changes: 55 additions & 25 deletions qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,40 @@
from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.target import Target
from qiskit.utils import deprecate_arg
from qiskit.utils.deprecate_pulse import deprecate_pulse_arg

logger = logging.getLogger(__name__)

_CircuitT = TypeVar("_CircuitT", bound=Union[QuantumCircuit, List[QuantumCircuit]])


@deprecate_arg(
name="instruction_durations",
since="1.3",
package_name="Qiskit",
removal_timeline="in Qiskit 2.0",
additional_msg="The `target` parameter should be used instead. You can build a `Target` instance "
"with defined instruction durations with "
"`Target.from_configuration(..., instruction_durations=...)`",
)
@deprecate_arg(
name="timing_constraints",
since="1.3",
package_name="Qiskit",
removal_timeline="in Qiskit 2.0",
additional_msg="The `target` parameter should be used instead. You can build a `Target` instance "
"with defined timing constraints with "
"`Target.from_configuration(..., timing_constraints=...)`",
)
@deprecate_arg(
name="backend_properties",
since="1.3",
package_name="Qiskit",
removal_timeline="in Qiskit 2.0",
additional_msg="The `target` parameter should be used instead. You can build a `Target` instance "
"with defined properties with Target.from_configuration(..., backend_properties=...)",
)
@deprecate_pulse_arg("inst_map", predicate=lambda inst_map: inst_map is not None)
def transpile( # pylint: disable=too-many-return-statements
circuits: _CircuitT,
Expand Down Expand Up @@ -367,31 +394,34 @@ def callback_func(**kwargs):
# Edge cases require using the old model (loose constraints) instead of building a target,
# but we don't populate the passmanager config with loose constraints unless it's one of
# the known edge cases to control the execution path.
pm = generate_preset_pass_manager(
optimization_level,
target=target,
backend=backend,
basis_gates=basis_gates,
coupling_map=coupling_map,
instruction_durations=instruction_durations,
backend_properties=backend_properties,
timing_constraints=timing_constraints,
inst_map=inst_map,
initial_layout=initial_layout,
layout_method=layout_method,
routing_method=routing_method,
translation_method=translation_method,
scheduling_method=scheduling_method,
approximation_degree=approximation_degree,
seed_transpiler=seed_transpiler,
unitary_synthesis_method=unitary_synthesis_method,
unitary_synthesis_plugin_config=unitary_synthesis_plugin_config,
hls_config=hls_config,
init_method=init_method,
optimization_method=optimization_method,
dt=dt,
qubits_initially_zero=qubits_initially_zero,
)
with warnings.catch_warnings():
warnings.simplefilter(action="ignore", category=DeprecationWarning)
# Filter instruction_durations, timing_constraints and backend_properties deprecation
pm = generate_preset_pass_manager(
optimization_level,
target=target,
backend=backend,
basis_gates=basis_gates,
coupling_map=coupling_map,
instruction_durations=instruction_durations,
backend_properties=backend_properties,
timing_constraints=timing_constraints,
inst_map=inst_map,
initial_layout=initial_layout,
layout_method=layout_method,
routing_method=routing_method,
translation_method=translation_method,
scheduling_method=scheduling_method,
approximation_degree=approximation_degree,
seed_transpiler=seed_transpiler,
unitary_synthesis_method=unitary_synthesis_method,
unitary_synthesis_plugin_config=unitary_synthesis_plugin_config,
hls_config=hls_config,
init_method=init_method,
optimization_method=optimization_method,
dt=dt,
qubits_initially_zero=qubits_initially_zero,
)

out_circuits = pm.run(circuits, callback=callback, num_processes=num_processes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.target import Target, target_to_backend_properties
from qiskit.transpiler.timing_constraints import TimingConstraints
from qiskit.utils import deprecate_arg
from qiskit.utils.deprecate_pulse import deprecate_pulse_arg

from .level0 import level_0_pass_manager
Expand All @@ -37,6 +38,32 @@
from .level3 import level_3_pass_manager


@deprecate_arg(
name="instruction_durations",
since="1.3",
package_name="Qiskit",
removal_timeline="in Qiskit 2.0",
additional_msg="The `target` parameter should be used instead. You can build a `Target` instance "
"with defined instruction durations with "
"`Target.from_configuration(..., instruction_durations=...)`",
)
@deprecate_arg(
name="timing_constraints",
since="1.3",
package_name="Qiskit",
removal_timeline="in Qiskit 2.0",
additional_msg="The `target` parameter should be used instead. You can build a `Target` instance "
"with defined timing constraints with "
"`Target.from_configuration(..., timing_constraints=...)`",
)
@deprecate_arg(
name="backend_properties",
since="1.3",
package_name="Qiskit",
removal_timeline="in Qiskit 2.0",
additional_msg="The `target` parameter should be used instead. You can build a `Target` instance "
"with defined properties with Target.from_configuration(..., backend_properties=...)",
)
@deprecate_pulse_arg("inst_map", predicate=lambda inst_map: inst_map is not None)
def generate_preset_pass_manager(
optimization_level=2,
Expand Down
14 changes: 14 additions & 0 deletions releasenotes/notes/fixes_13306-f9883a733491a72f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
deprecations_transpiler:
- |
The following :func:`.transpile` and :func:`.generate_preset_pass_manager` arguments are deprecated in favor of
defining a custom :class:`.Target`: ``instruction_durations``, ``timing_constraints``, and ``backend_properties``.
These arguments can be used to build a target with :meth:`.Target.from_configuration`::
Target.from_configuration(
...
backend_properties = backend_properties,
instruction_durations = instruction_durations,
timing_constraints = timing_constraints
)
155 changes: 118 additions & 37 deletions test/python/circuit/test_scheduled_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from qiskit.providers.fake_provider import Fake27QPulseV1, GenericBackendV2
from qiskit.providers.basic_provider import BasicSimulator
from qiskit.scheduler import ScheduleConfig
from qiskit.transpiler import InstructionProperties
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.instruction_durations import InstructionDurations
from test import QiskitTestCase # pylint: disable=wrong-import-order
Expand Down Expand Up @@ -194,12 +195,16 @@ def test_transpile_delay_circuit_without_backend(self):
qc.h(0)
qc.delay(500, 1)
qc.cx(0, 1)
scheduled = transpile(
qc,
scheduling_method="alap",
basis_gates=["h", "cx"],
instruction_durations=[("h", 0, 200), ("cx", [0, 1], 700)],
)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
scheduled = transpile(
qc,
scheduling_method="alap",
basis_gates=["h", "cx"],
instruction_durations=[("h", 0, 200), ("cx", [0, 1], 700)],
)
self.assertEqual(scheduled.duration, 1200)

def test_transpile_circuit_with_custom_instruction(self):
Expand All @@ -210,9 +215,13 @@ def test_transpile_circuit_with_custom_instruction(self):
qc = QuantumCircuit(2)
qc.delay(500, 1)
qc.append(bell.to_instruction(), [0, 1])
scheduled = transpile(
qc, scheduling_method="alap", instruction_durations=[("bell", [0, 1], 1000)]
)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
scheduled = transpile(
qc, scheduling_method="alap", instruction_durations=[("bell", [0, 1], 1000)]
)
self.assertEqual(scheduled.duration, 1500)

def test_transpile_delay_circuit_with_dt_but_without_scheduling_method(self):
Expand Down Expand Up @@ -263,21 +272,29 @@ def test_default_units_for_my_own_duration_users(self):
qc.h(0)
qc.delay(500, 1)
qc.cx(0, 1)
# accept None for qubits
scheduled = transpile(
qc,
basis_gates=["h", "cx", "delay"],
scheduling_method="alap",
instruction_durations=[("h", 0, 200), ("cx", None, 900)],
)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
# accept None for qubits
scheduled = transpile(
qc,
basis_gates=["h", "cx", "delay"],
scheduling_method="alap",
instruction_durations=[("h", 0, 200), ("cx", None, 900)],
)
self.assertEqual(scheduled.duration, 1400)
# prioritize specified qubits over None
scheduled = transpile(
qc,
basis_gates=["h", "cx", "delay"],
scheduling_method="alap",
instruction_durations=[("h", 0, 200), ("cx", None, 900), ("cx", [0, 1], 800)],
)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
# prioritize specified qubits over None
scheduled = transpile(
qc,
basis_gates=["h", "cx", "delay"],
scheduling_method="alap",
instruction_durations=[("h", 0, 200), ("cx", None, 900), ("cx", [0, 1], 800)],
)
self.assertEqual(scheduled.duration, 1300)

def test_unit_seconds_when_using_backend_durations(self):
Expand Down Expand Up @@ -313,19 +330,23 @@ def test_unit_seconds_when_using_backend_durations(self):
)
self.assertEqual(scheduled.duration, 1500)

def test_per_qubit_durations(self):
"""See: https://github.com/Qiskit/qiskit-terra/issues/5109"""
def test_per_qubit_durations_loose_constrain(self):
"""See Qiskit/5109 and Qiskit/13306"""
qc = QuantumCircuit(3)
qc.h(0)
qc.delay(500, 1)
qc.cx(0, 1)
qc.h(1)
sc = transpile(
qc,
scheduling_method="alap",
basis_gates=["h", "cx"],
instruction_durations=[("h", None, 200), ("cx", [0, 1], 700)],
)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
sc = transpile(
qc,
scheduling_method="alap",
basis_gates=["h", "cx"],
instruction_durations=[("h", None, 200), ("cx", [0, 1], 700)],
)
self.assertEqual(sc.qubit_start_time(0), 300)
self.assertEqual(sc.qubit_stop_time(0), 1200)
self.assertEqual(sc.qubit_start_time(1), 500)
Expand All @@ -336,12 +357,21 @@ def test_per_qubit_durations(self):
self.assertEqual(sc.qubit_stop_time(0, 1), 1400)

qc.measure_all()
sc = transpile(
qc,
scheduling_method="alap",
basis_gates=["h", "cx", "measure"],
instruction_durations=[("h", None, 200), ("cx", [0, 1], 700), ("measure", None, 1000)],
)

with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
sc = transpile(
qc,
scheduling_method="alap",
basis_gates=["h", "cx", "measure"],
instruction_durations=[
("h", None, 200),
("cx", [0, 1], 700),
("measure", None, 1000),
],
)
q = sc.qubits
self.assertEqual(sc.qubit_start_time(q[0]), 300)
self.assertEqual(sc.qubit_stop_time(q[0]), 2400)
Expand All @@ -352,6 +382,57 @@ def test_per_qubit_durations(self):
self.assertEqual(sc.qubit_start_time(*q), 300)
self.assertEqual(sc.qubit_stop_time(*q), 2400)

def test_per_qubit_durations(self):
"""Test target with custom instruction_durations"""
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="argument ``calibrate_instructions`` is deprecated",
):
target = GenericBackendV2(
3,
calibrate_instructions=True,
coupling_map=[[0, 1], [1, 2]],
basis_gates=["cx", "h"],
seed=42,
).target
target.update_instruction_properties("cx", (0, 1), InstructionProperties(0.00001))
target.update_instruction_properties("cx", (1, 2), InstructionProperties(0.00001))
target.update_instruction_properties("h", (0,), InstructionProperties(0.000002))
target.update_instruction_properties("h", (1,), InstructionProperties(0.000002))
target.update_instruction_properties("h", (2,), InstructionProperties(0.000002))

qc = QuantumCircuit(3)
qc.h(0)
qc.delay(500, 1)
qc.cx(0, 1)
qc.h(1)

sc = transpile(qc, scheduling_method="alap", target=target)
self.assertEqual(sc.qubit_start_time(0), 500)
self.assertEqual(sc.qubit_stop_time(0), 54554)
self.assertEqual(sc.qubit_start_time(1), 9509)
self.assertEqual(sc.qubit_stop_time(1), 63563)
self.assertEqual(sc.qubit_start_time(2), 0)
self.assertEqual(sc.qubit_stop_time(2), 0)
self.assertEqual(sc.qubit_start_time(0, 1), 500)
self.assertEqual(sc.qubit_stop_time(0, 1), 63563)

qc.measure_all()

target.update_instruction_properties("measure", (0,), InstructionProperties(0.0001))
target.update_instruction_properties("measure", (1,), InstructionProperties(0.0001))

sc = transpile(qc, scheduling_method="alap", target=target)
q = sc.qubits
self.assertEqual(sc.qubit_start_time(q[0]), 500)
self.assertEqual(sc.qubit_stop_time(q[0]), 514013)
self.assertEqual(sc.qubit_start_time(q[1]), 9509)
self.assertEqual(sc.qubit_stop_time(q[1]), 514013)
self.assertEqual(sc.qubit_start_time(q[2]), 63563)
self.assertEqual(sc.qubit_stop_time(q[2]), 514013)
self.assertEqual(sc.qubit_start_time(*q), 500)
self.assertEqual(sc.qubit_stop_time(*q), 514013)

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."""
Expand Down
Loading

0 comments on commit 2d0e28f

Please sign in to comment.