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 loose custom basis gates in preset pm pipieline #13394

Merged
merged 7 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@
import copy
import warnings

from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES
from qiskit.circuit.controlflow import (
CONTROL_FLOW_OP_NAMES,
IfElseOp,
WhileLoopOp,
ForLoopOp,
SwitchCaseOp,
)
from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping
from qiskit.circuit.quantumregister import Qubit
from qiskit.providers.backend import Backend
Expand Down Expand Up @@ -312,6 +318,7 @@ def generate_preset_pass_manager(
_skip_target = (
target is None
and backend is None
# Note: instruction_durations is deprecated and will be removed in 2.0 (no need for alternative)
and (basis_gates is None or coupling_map is None or instruction_durations is not None)
)

Expand All @@ -336,23 +343,31 @@ def generate_preset_pass_manager(
# Only parse backend properties when the target isn't skipped to
# preserve the former behavior of transpile.
backend_properties = _parse_backend_properties(backend_properties, backend)
# Build target from constraints.
target = Target.from_configuration(
basis_gates=basis_gates,
num_qubits=backend.num_qubits if backend is not None else None,
coupling_map=coupling_map,
# If the instruction map has custom gates, do not give as config, the information
# will be added to the target with update_from_instruction_schedule_map
inst_map=inst_map if inst_map and not inst_map.has_custom_gate() else None,
backend_properties=backend_properties,
instruction_durations=instruction_durations,
concurrent_measurements=(
backend.target.concurrent_measurements if backend is not None else None
),
dt=dt,
timing_constraints=timing_constraints,
custom_name_mapping=name_mapping,
)
with warnings.catch_warnings():
# TODO: inst_map will be removed in 2.0
warnings.filterwarnings(
"ignore",
category=DeprecationWarning,
message=".*``inst_map`` is deprecated as of Qiskit 1.3.*",
module="qiskit",
)
# Build target from constraints.
target = Target.from_configuration(
basis_gates=basis_gates,
num_qubits=backend.num_qubits if backend is not None else None,
coupling_map=coupling_map,
# If the instruction map has custom gates, do not give as config, the information
# will be added to the target with update_from_instruction_schedule_map
inst_map=inst_map if inst_map and not inst_map.has_custom_gate() else None,
backend_properties=backend_properties,
instruction_durations=instruction_durations,
concurrent_measurements=(
backend.target.concurrent_measurements if backend is not None else None
),
dt=dt,
timing_constraints=timing_constraints,
custom_name_mapping=name_mapping,
)

# Update target with custom gate information. Note that this is an exception to the priority
# order (target > loose constraints), added to handle custom gates for scheduling passes.
Expand Down Expand Up @@ -433,9 +448,14 @@ def generate_preset_pass_manager(
def _parse_basis_gates(basis_gates, backend, inst_map, skip_target):
name_mapping = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead code now

standard_gates = get_standard_gate_name_mapping()
# Add control flow gates by default to basis set
# Add control flow gates by default to basis set and name mapping
default_gates = {"measure", "delay", "reset"}.union(CONTROL_FLOW_OP_NAMES)

name_mapping = {
"if_else": IfElseOp,
"while_loop": WhileLoopOp,
"for_loop": ForLoopOp,
"switch_case": SwitchCaseOp,
}
try:
instructions = set(basis_gates)
for name in default_gates:
Expand All @@ -450,7 +470,15 @@ def _parse_basis_gates(basis_gates, backend, inst_map, skip_target):
return None, name_mapping, skip_target

for inst in instructions:
if inst not in standard_gates or inst not in default_gates:
if inst not in standard_gates and inst not in default_gates:
warnings.warn(
category=DeprecationWarning,
message="Providing custom gates through the ``basis_gates`` argument is deprecated "
"for both ``transpile`` and ``generate_preset_pass_manager`` as of Qiskit 1.3.0. "
"It will be removed in Qiskit 2.0. The ``target`` parameter should be used instead. "
"You can build a target instance using ``Target.from_configuration()`` and provide"
"custom gate definitions with the ``custom_name_mapping`` argument.",
)
skip_target = True
break

Expand All @@ -463,7 +491,18 @@ def _parse_basis_gates(basis_gates, backend, inst_map, skip_target):

# Check for custom instructions before removing calibrations
for inst in instructions:
if inst not in standard_gates or inst not in default_gates:
if inst not in standard_gates and inst not in default_gates:
if inst not in backend.operation_names:
# do not raise warning when the custom instruction comes from the backend
# (common case with BasicSimulator)
warnings.warn(
category=DeprecationWarning,
message="Providing custom gates through the ``basis_gates`` argument is deprecated "
"for both ``transpile`` and ``generate_preset_pass_manager`` as of Qiskit 1.3.0. "
"It will be removed in Qiskit 2.0. The ``target`` parameter should be used instead. "
"You can build a target instance using ``Target.from_configuration()`` and provide"
"custom gate definitions with the ``custom_name_mapping`` argument.",
)
skip_target = True
break

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
deprecations_transpiler:
- |
Providing custom gates through the ``basis_gates`` argument is deprecated
for both :func:`.transpile` and :func:`generate_preset_pass_manager`, this functionality
will be removed in Qiskit 2.0. Custom gates are still supported in the :class:`.Target` model,
and can be provided through the ``target`` argument. One can build a :class:`.Target` instance
from scratch or use the :meth:`.Target.from_configuration` method with the ``custom_name_mapping``
argument. For example::

from qiskit.circuit.library import XGate
from qiskit.transpiler.target import Target

basis_gates = ["my_x", "cx"]
custom_name_mapping = {"my_x": XGate()}
target = Target.from_configuration(
basis_gates=basis_gates, num_qubits=2, custom_name_mapping=custom_name_mapping
)
37 changes: 37 additions & 0 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1735,3 +1735,40 @@ def test_unsupported_targets_raise(self, optimization_level):
pass
with self.assertRaisesRegex(TranspilerError, "The control-flow construct.*not supported"):
transpile(qc, target=target, optimization_level=optimization_level)

@data(0, 1, 2, 3)
def test_custom_basis_gates_raise(self, optimization_level):
"""Test that trying to provide a list of custom basis gates to generate_preset_pass_manager
raises a deprecation warning."""

with self.subTest(msg="no warning"):
# check that the warning isn't raised if the basis gates aren't custom
basis_gates = ["x", "cx"]
_ = generate_preset_pass_manager(
optimization_level=optimization_level, basis_gates=basis_gates
)

with self.subTest(msg="warning only basis gates"):
# check that the warning is raised if they are custom
basis_gates = ["my_gate"]
with self.assertWarnsRegex(
DeprecationWarning,
"Providing custom gates through the ``basis_gates`` argument is deprecated",
):
_ = generate_preset_pass_manager(
optimization_level=optimization_level, basis_gates=basis_gates
)

with self.subTest(msg="no warning custom basis gates in backend"):
# check that the warning is not raised if a loose custom gate is found in the backend
backend = GenericBackendV2(num_qubits=2)
gate = Gate(name="my_gate", num_qubits=1, params=[])
backend.target.add_instruction(gate)
self.assertEqual(
backend.operation_names,
["cx", "id", "rz", "sx", "x", "reset", "delay", "measure", "my_gate"],
)
basis_gates = ["my_gate"]
_ = generate_preset_pass_manager(
optimization_level=optimization_level, basis_gates=basis_gates, backend=backend
)