Skip to content

Commit

Permalink
Deprecate lists for argument input on transpile() (#8835)
Browse files Browse the repository at this point in the history
* Deprecate lists for argument input on transpile()

Right now the support for the argument broadcasting with list inputs for
various transpiler options on the transpile() function causes a
significant performance overhead to support, primarily do to how we have
to handle the multiple arguments across a parallel dispatch boundary. It
also significantly increases the code complexity of the function to
support more than one input for each argument (except circuits). The
utility of doing this type of argument handling is quite limited since a
similar result can be achieved with a for loop and would like be simpler
for users to reason about. When weighing all these factors the best path
forward is to just remove this functionality. This commit starts the
process of removing this feature by marking it as deprecated. Once the
deprecation cycle is complete we can greatly simplify the code in
transpile and primarily replace it with a call to
generate_preset_pass_manager() and passmanager.run() (the only thing I
think we'll have to handle out of band is faulty qubits defined in a
BackendProperties for BackendV1).

Related to #7741

* Update qiskit/compiler/transpiler.py

Co-authored-by: Kevin Hartman <[email protected]>

* Update qiskit/compiler/transpiler.py

Co-authored-by: Kevin Hartman <[email protected]>

* Fix release note typo

Co-authored-by: Kevin Hartman <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 17, 2022
1 parent c34e0b9 commit 696e53d
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 11 deletions.
59 changes: 48 additions & 11 deletions qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,22 @@ def transpile(
) -> Union[QuantumCircuit, List[QuantumCircuit]]:
"""Transpile one or more circuits, according to some desired transpilation targets.
All arguments may be given as either a singleton or list. In case of a list,
the length must be equal to the number of circuits being transpiled.
.. deprecated:: 0.23.0
Previously, all arguments accepted lists of the same length as ``circuits``,
which was used to specialize arguments for circuits at the corresponding
indices. Support for using such argument lists is now deprecated and will
be removed in the 0.25.0 release. If you need to use multiple values for an
argument, you can use multiple :func:`~.transpile` calls (and potentially
:func:`~.parallel_map` to leverage multiprocessing if needed).
Transpilation is done in parallel using multiprocessing.
Args:
circuits: Circuit(s) to transpile
backend: If set, transpiler options are automatically grabbed from
``backend.configuration()`` and ``backend.properties()``.
If any other option is explicitly set (e.g., ``coupling_map``), it
backend: If set, the transpiler will compile the input circuit to this target
device. If any other option is explicitly set (e.g., ``coupling_map``), it
will override the backend's.
.. note::
The backend arg is purely for convenience. The resulting
circuit may be run on any backend as long as it is compatible.
basis_gates: List of basis gate names to unroll to
(e.g: ``['u1', 'u2', 'u3', 'cx']``). If ``None``, do not unroll.
inst_map: Mapping of unrolled gates to pulse schedules. If this is not provided,
Expand Down Expand Up @@ -626,7 +626,9 @@ def _parse_transpile_args(
# Each arg could be single or a list. If list, it must be the same size as
# number of circuits. If single, duplicate to create a list of that size.
num_circuits = len(circuits)

user_input_durations = instruction_durations
user_input_timing_constraints = timing_constraints
user_input_initial_layout = initial_layout
# If a target is specified have it override any implicit selections from a backend
# but if an argument is explicitly passed use that instead of the target version
if target is not None:
Expand Down Expand Up @@ -702,6 +704,41 @@ def _parse_transpile_args(
"hls_config": hls_config,
}.items():
if isinstance(value, list):
# This giant if-statement detects deprecated use of argument
# broadcasting. For arguments that previously supported broadcast
# but were not themselves of type list (the majority), we simply warn
# when the user provides a list. For the others, special handling is
# required to disambiguate an expected value of type list from
# an attempt to provide multiple values for broadcast. This path is
# super buggy in general (outside of the warning) and since we're
# deprecating this it's better to just remove it than try to clean it up.
# pylint: disable=too-many-boolean-expressions
if (
key not in {"instruction_durations", "timing_constraints", "initial_layout"}
or (
key == "initial_layout"
and user_input_initial_layout
and isinstance(user_input_initial_layout, list)
and isinstance(user_input_initial_layout[0], (Layout, dict, list))
)
or (
key == "instruction_durations"
and user_input_durations
and isinstance(user_input_durations, list)
and isinstance(user_input_durations[0], (list, InstructionDurations))
)
or (
key == "timing_constraints"
and user_input_timing_constraints
and isinstance(user_input_timing_constraints, list)
)
):
warnings.warn(
f"Passing in a list of arguments for {key} is deprecated and will no longer work "
"starting in the 0.25.0 release.",
DeprecationWarning,
stacklevel=3,
)
unique_dict[key] = value
else:
shared_dict[key] = value
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
deprecations:
- |
Support for passing in lists of argument values to the :func:`~.transpile`
function is deprecated and will be removed in the 0.25.0 release. This
is being done to facilitate greatly reducing the overhead for parallel
execution for transpiling multiple circuits at once. If you're using
this functionality currently you can call :func:`~.transpile` multiple
times instead. For example if you were previously doing something like::
from qiskit.transpiler import CouplingMap
from qiskit import QuantumCircuit
from qiskit import transpile
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
cmaps = [CouplingMap.from_heavy_hex(d) for d in range(3, 15, 2)]
results = transpile([qc] * 6, coupling_map=cmaps)
instead you should run something like::
from itertools import cycle
from qiskit.transpiler import CouplingMap
from qiskit import QuantumCircuit
from qiskit import transpile
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
cmaps = [CouplingMap.from_heavy_hex(d) for d in range(3, 15, 2)]
results = []
for qc, cmap in zip(cycle([qc]), cmaps):
results.append(transpile(qc, coupling_map=cmap))
You can also leverage :func:`~.parallel_map` or ``multiprocessing`` from
the Python standard library if you want to run this in parallel.

0 comments on commit 696e53d

Please sign in to comment.