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

Deprecate instruction_durations, timing_constraints and backend_properties #13338

Merged
merged 16 commits into from
Oct 30, 2024
Merged
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
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