diff --git a/qiskit/passmanager/__init__.py b/qiskit/passmanager/__init__.py index 579a8569b750..a84f37abbf03 100644 --- a/qiskit/passmanager/__init__.py +++ b/qiskit/passmanager/__init__.py @@ -209,7 +209,6 @@ def digit_condition(property_set): .. autosummary:: :toctree: ../stubs/ - FlowController FlowControllerLinear ConditionalController DoWhileController @@ -232,7 +231,6 @@ def digit_condition(property_set): from .passmanager import BasePassManager from .flow_controllers import ( - FlowController, FlowControllerLinear, ConditionalController, DoWhileController, diff --git a/qiskit/passmanager/flow_controllers.py b/qiskit/passmanager/flow_controllers.py index eb06202e8673..c7d952d048dd 100644 --- a/qiskit/passmanager/flow_controllers.py +++ b/qiskit/passmanager/flow_controllers.py @@ -15,9 +15,8 @@ import logging from collections.abc import Callable, Iterable, Generator -from typing import Type, Any +from typing import Any -from qiskit.utils.deprecation import deprecate_func from .base_tasks import BaseController, Task from .compilation_status import PassManagerState, PropertySet from .exceptions import PassManagerError @@ -45,31 +44,6 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: for task in self.tasks: state = yield task @@ -101,31 +75,6 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: max_iteration = self._options.get("max_iteration", 1000) for _ in range(max_iteration): @@ -161,176 +110,7 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: if self.condition(state.property_set): for task in self.tasks: state = yield task - - -class FlowController(BaseController): - """A legacy factory for other flow controllers. - - .. warning:: - - This class is primarily for compatibility with legacy versions of Qiskit, and in general, - you should prefer simply instantiating the controller you want, and adding it to the - relevant :class:`.PassManager` or other controller. Its use is deprecated. - - This allows syntactic sugar for writing pipelines. For example:: - - FlowController.add_flow_controller("my_condition", CustomController) - - controller = FlowController.controller_factory( - [PassA(), PassB()], - {"max_iteration": 1000}, - condition=lambda prop_set: prop_set["x"] == 0, - do_while=lambda prop_set: prop_set["x"] < 100, - my_condition=lambda prop_set: prop_set["y"] = "abc", - ) - - This creates a nested flow controller that runs when the value :code:`x` in the - :class:`.PropertySet` is zero and repeats the pipeline until the value becomes 100. - In each innermost loop, the custom iteration condition provided by - the ``CustomController`` is also evaluated. - - .. warning:: - - :class:`.BaseController` must be directly subclassed to define a custom flow controller. - This class provides a controller factory method, which consumes a class variable - :attr:`.registered_controllers`. Subclassing FlowController may cause - unexpected behavior in the factory method. - Note that factory method implicitly determines the priority of the builtin controllers - when multiple controllers are called together, - and the behavior of generated controller is hardly debugged. - """ - - registered_controllers = { - "condition": ConditionalController, - "do_while": DoWhileController, - } - hierarchy = [ - "condition", - "do_while", - ] - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller object must be explicitly instantiated. " - "Building controller with keyword arguments may yield race condition when " - "multiple keyword arguments are provided together, which is likely unsafe." - ), - ) - def controller_factory( - cls, - passes: Task | list[Task], - options: dict, - **controllers, - ): - """Create a new flow controller with normalization. - - Args: - passes: A list of optimization tasks. - options: Option for this flow controller. - controllers: Dictionary of controller callables keyed on flow controller alias. - - Returns: - An instance of normalized flow controller. - """ - if None in controllers.values(): - raise PassManagerError("The controller needs a callable. Value cannot be None.") - - if isinstance(passes, BaseController): - instance = passes - else: - instance = FlowControllerLinear(passes, options=options) - - if controllers: - # Alias in higher hierarchy becomes outer controller. - for alias in cls.hierarchy[::-1]: - if alias not in controllers: - continue - class_type = cls.registered_controllers[alias] - init_kwargs = { - "options": options, - alias: controllers.pop(alias), - } - instance = class_type([instance], **init_kwargs) - - return instance - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller factory method is deprecated and managing the custom flow controllers " - "with alias no longer helps building the task pipeline. " - "Controllers must be explicitly instantiated and appended to the pipeline." - ), - ) - def add_flow_controller( - cls, - name: str, - controller: Type[BaseController], - ): - """Adds a flow controller. - - Args: - name: Alias of controller class in the namespace. - controller: Flow controller class. - """ - cls.registered_controllers[name] = controller - if name not in cls.hierarchy: - cls.hierarchy.append(name) - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller factory method is deprecated and managing the custom flow controllers " - "with alias no longer helps building the task pipeline. " - "Controllers must be explicitly instantiated and appended to the pipeline." - ), - ) - def remove_flow_controller( - cls, - name: str, - ): - """Removes a flow controller. - - Args: - name: Alias of the controller to remove. - - Raises: - KeyError: If the controller to remove was not registered. - """ - if name not in cls.hierarchy: - raise KeyError("Flow controller not found: %s" % name) - del cls.registered_controllers[name] - cls.hierarchy.remove(name) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 71f596c95dad..bfc0e622d5d2 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -1231,15 +1231,6 @@ InstructionDurations -Fenced Objects --------------- - -.. autosummary:: - :toctree: ../stubs/ - - FencedPropertySet - FencedDAGCircuit - Abstract Passes --------------- @@ -1262,14 +1253,13 @@ # For backward compatibility from qiskit.passmanager import ( - FlowController, ConditionalController, DoWhileController, ) +from qiskit.passmanager.compilation_status import PropertySet from .passmanager import PassManager, StagedPassManager from .passmanager_config import PassManagerConfig -from .propertyset import PropertySet # pylint: disable=no-name-in-module from .exceptions import ( TranspilerError, TranspilerAccessError, @@ -1277,7 +1267,6 @@ LayoutError, CircuitTooWideForTarget, ) -from .fencedobjs import FencedDAGCircuit, FencedPropertySet from .basepasses import AnalysisPass, TransformationPass from .coupling import CouplingMap from .layout import Layout, TranspileLayout diff --git a/qiskit/transpiler/fencedobjs.py b/qiskit/transpiler/fencedobjs.py deleted file mode 100644 index 8113e58d76d9..000000000000 --- a/qiskit/transpiler/fencedobjs.py +++ /dev/null @@ -1,78 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Fenced objects are wraps for raising TranspilerError when they are modified.""" - -from qiskit.utils.deprecation import deprecate_func -from .exceptions import TranspilerError - - -class FencedObject: - """Given an instance and a list of attributes to fence, raises a TranspilerError when one - of these attributes is accessed.""" - - @deprecate_func( - since="0.45.0", - additional_msg=( - "Internal use of FencedObject is already removed from pass manager. " - "Implementation of a task subclass with protection for input object modification " - "is now responsibility of the developer." - ), - pending=True, - ) - def __init__(self, instance, attributes_to_fence): - self._wrapped = instance - self._attributes_to_fence = attributes_to_fence - - def __getattribute__(self, name): - object.__getattribute__(self, "_check_if_fenced")(name) - return getattr(object.__getattribute__(self, "_wrapped"), name) - - def __getitem__(self, key): - object.__getattribute__(self, "_check_if_fenced")("__getitem__") - return object.__getattribute__(self, "_wrapped")[key] - - def __setitem__(self, key, value): - object.__getattribute__(self, "_check_if_fenced")("__setitem__") - object.__getattribute__(self, "_wrapped")[key] = value - - def _check_if_fenced(self, name): - """ - Checks if the attribute name is in the list of attributes to protect. If so, raises - TranspilerError. - - Args: - name (string): the attribute name to check - - Raises: - TranspilerError: when name is the list of attributes to protect. - """ - if name in object.__getattribute__(self, "_attributes_to_fence"): - raise TranspilerError( - "The fenced %s has the property %s protected" - % (type(object.__getattribute__(self, "_wrapped")), name) - ) - - -class FencedPropertySet(FencedObject): - """A property set that cannot be written (via __setitem__)""" - - def __init__(self, property_set_instance): - super().__init__(property_set_instance, ["__setitem__"]) - - -class FencedDAGCircuit(FencedObject): - """A dag circuit that cannot be modified (via remove_op_node)""" - - # FIXME: add more fenced methods of the dag after dagcircuit rewrite - def __init__(self, dag_circuit_instance): - super().__init__(dag_circuit_instance, ["remove_op_node"]) diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py index cf7c44d69a7d..7757282bc10d 100644 --- a/qiskit/transpiler/passmanager.py +++ b/qiskit/transpiler/passmanager.py @@ -16,7 +16,6 @@ import inspect import io import re -import warnings from collections.abc import Iterator, Iterable, Callable from functools import wraps from typing import Union, List, Any @@ -25,14 +24,12 @@ from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.dagcircuit import DAGCircuit from qiskit.passmanager.passmanager import BasePassManager -from qiskit.passmanager.base_tasks import Task, BaseController -from qiskit.passmanager.flow_controllers import FlowController +from qiskit.passmanager.base_tasks import Task +from qiskit.passmanager.flow_controllers import FlowControllerLinear from qiskit.passmanager.exceptions import PassManagerError -from qiskit.utils.deprecation import deprecate_arg from .basepasses import BasePass from .exceptions import TranspilerError from .layout import TranspileLayout -from .runningpassmanager import RunningPassManager _CircuitsT = Union[List[QuantumCircuit], QuantumCircuit] @@ -52,9 +49,6 @@ def __init__( max_iteration: The maximum number of iterations the schedule will be looped if the condition is not met. """ - # For backward compatibility. - self._pass_sets = [] - super().__init__( tasks=passes, max_iteration=max_iteration, @@ -102,140 +96,32 @@ def _passmanager_backend( return out_program - @deprecate_arg( - name="max_iteration", - since="0.25", - additional_msg="'max_iteration' can be set in the constructor.", - pending=True, - package_name="qiskit-terra", - ) def append( self, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: """Append a Pass Set to the schedule of passes. Args: - passes: A set of passes (a pass set) to be added to schedule. A pass set is a list of - passes that are controlled by the same flow controller. If a single pass is - provided, the pass set will only have that pass a single element. - It is also possible to append a :class:`.BaseFlowController` instance and - the rest of the parameter will be ignored. - max_iteration: max number of iterations of passes. - flow_controller_conditions: Dictionary of control flow plugins. - Following built-in controllers are available by default: - - * do_while: The passes repeat until the callable returns False. Corresponds to - :class:`.DoWhileController`. - * condition: The passes run only if the callable returns True. Corresponds to - :class:`.ConditionalController`. - - In general, you have more control simply by creating the controller you want and - passing it to :meth:`append`. + passes: A set of transpiler passes to be added to schedule. Raises: TranspilerError: if a pass in passes is not a proper pass. """ - if max_iteration: - self.max_iteration = max_iteration - - # Backward compatibility as of Terra 0.25 - if isinstance(passes, Task): - passes = [passes] - self._pass_sets.append( - { - "passes": passes, - "flow_controllers": flow_controller_conditions, - } - ) - if flow_controller_conditions: - passes = _legacy_build_flow_controller( - passes, - options={"max_iteration": self.max_iteration}, - **flow_controller_conditions, - ) - - super().append(passes) + super().append(tasks=passes) - @deprecate_arg( - name="max_iteration", - since="0.25", - additional_msg="'max_iteration' can be set in the constructor.", - pending=True, - package_name="qiskit-terra", - ) def replace( self, index: int, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: """Replace a particular pass in the scheduler. Args: index: Pass index to replace, based on the position in passes(). passes: A pass set to be added to the pass manager schedule. - max_iteration: max number of iterations of passes. - flow_controller_conditions: Dictionary of control flow plugins. - See :meth:`qiskit.transpiler.PassManager.append` for details. """ - if max_iteration: - self.max_iteration = max_iteration - - # Backward compatibility as of Terra 0.25 - if isinstance(passes, Task): - passes = [passes] - try: - self._pass_sets[index] = { - "passes": passes, - "flow_controllers": flow_controller_conditions, - } - except IndexError as ex: - raise PassManagerError(f"Index to replace {index} does not exists") from ex - if flow_controller_conditions: - passes = _legacy_build_flow_controller( - passes, - options={"max_iteration": self.max_iteration}, - **flow_controller_conditions, - ) - - super().replace(index, passes) - - def remove(self, index: int) -> None: - super().remove(index) - - # Backward compatibility as of Terra 0.25 - del self._pass_sets[index] - - def __getitem__(self, index): - new_passmanager = super().__getitem__(index) - - # Backward compatibility as of Terra 0.25 - _pass_sets = self._pass_sets[index] - if isinstance(_pass_sets, dict): - _pass_sets = [_pass_sets] - new_passmanager._pass_sets = _pass_sets - return new_passmanager - - def __add__(self, other): - new_passmanager = super().__add__(other) - - # Backward compatibility as of Terra 0.25 - if isinstance(other, self.__class__): - new_passmanager._pass_sets = self._pass_sets - new_passmanager._pass_sets += other._pass_sets - - # When other is not identical type, _pass_sets is also evaluated by self.append. - return new_passmanager - - def to_flow_controller(self) -> RunningPassManager: - # For backward compatibility. - # This method will be resolved to the base class and return FlowControllerLinear - flatten_tasks = list(self._flatten_tasks(self._tasks)) - return RunningPassManager(flatten_tasks) + super().replace(index, tasks=passes) # pylint: disable=arguments-differ def run( @@ -320,22 +206,6 @@ def draw(self, filename=None, style=None, raw=False): return pass_manager_drawer(self, filename=filename, style=style, raw=raw) - def passes(self) -> list[dict[str, BasePass]]: - """Return a list structure of the appended passes and its options. - - Returns: - A list of pass sets, as defined in ``append()``. - """ - ret = [] - for pass_set in self._pass_sets: - item = {"passes": pass_set["passes"]} - if pass_set["flow_controllers"]: - item["flow_controllers"] = set(pass_set["flow_controllers"].keys()) - else: - item["flow_controllers"] = {} - ret.append(item) - return ret - class StagedPassManager(PassManager): """A pass manager pipeline built from individual stages. @@ -462,12 +332,10 @@ def _generate_expanded_stages(self) -> Iterator[str]: def _update_passmanager(self) -> None: self._tasks = [] - self._pass_sets = [] for stage in self.expanded_stages: pm = getattr(self, stage, None) if pm is not None: self._tasks += pm._tasks - self._pass_sets.extend(pm._pass_sets) def __setattr__(self, attr, value): if value == self and attr in self.expanded_stages: @@ -479,8 +347,6 @@ def __setattr__(self, attr, value): def append( self, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: raise NotImplementedError @@ -488,8 +354,6 @@ def replace( self, index: int, passes: BasePass | list[BasePass], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: raise NotImplementedError @@ -504,10 +368,6 @@ def __getitem__(self, index): # It returns instance of self.__class__ which is StagedPassManager. new_passmanager = PassManager(max_iteration=self.max_iteration) new_passmanager._tasks = self._tasks[index] - _pass_sets = self._pass_sets[index] - if isinstance(_pass_sets, dict): - _pass_sets = [_pass_sets] - new_passmanager._pass_sets = _pass_sets return new_passmanager def __len__(self): @@ -520,10 +380,6 @@ def __setitem__(self, index, item): def __add__(self, other): raise NotImplementedError - def passes(self) -> list[dict[str, BasePass]]: - self._update_passmanager() - return super().passes() - def run( self, circuits: _CircuitsT, @@ -533,6 +389,10 @@ def run( self._update_passmanager() return super().run(circuits, output_name, callback) + def to_flow_controller(self) -> FlowControllerLinear: + self._update_passmanager() + return super().to_flow_controller() + def draw(self, filename=None, style=None, raw=False): """Draw the staged pass manager.""" from qiskit.visualization import staged_pass_manager_drawer @@ -577,41 +437,3 @@ def _wrapped_callable(task, passmanager_ir, property_set, running_time, count): ) return _wrapped_callable - - -def _legacy_build_flow_controller( - tasks: list[Task], - options: dict[str, Any], - **flow_controller_conditions, -) -> BaseController: - """A legacy method to build flow controller with keyword arguments. - - Args: - tasks: A list of tasks fed into custom flow controllers. - options: Option for flow controllers. - flow_controller_conditions: Callables keyed on the alias of the flow controller. - - Returns: - A built controller. - """ - warnings.warn( - "Building a flow controller with keyword arguments is going to be deprecated. " - "Custom controllers must be explicitly instantiated and appended to the task list.", - PendingDeprecationWarning, - stacklevel=3, - ) - if isinstance(tasks, Task): - tasks = [tasks] - if any(not isinstance(t, Task) for t in tasks): - raise TypeError("Added tasks are not all valid pass manager task types.") - # Alias in higher hierarchy becomes outer controller. - for alias in FlowController.hierarchy[::-1]: - if alias not in flow_controller_conditions: - continue - class_type = FlowController.registered_controllers[alias] - init_kwargs = { - "options": options, - alias: flow_controller_conditions.pop(alias), - } - tasks = class_type(tasks, **init_kwargs) - return tasks diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index b21c7ac130f5..d24fdca64885 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -43,7 +43,7 @@ from qiskit.transpiler.passes import Depth, Size, FixedPoint, MinimumPoint from qiskit.transpiler.passes.utils.gates_basis import GatesInBasis from qiskit.transpiler.passes.synthesis.unitary_synthesis import UnitarySynthesis -from qiskit.passmanager.flow_controllers import ConditionalController +from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason from qiskit.circuit.library.standard_gates import ( @@ -598,7 +598,7 @@ def _opt_control(property_set): else: raise TranspilerError(f"Invalid optimization_level: {optimization_level}") - unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] + unroll = translation.to_flow_controller() # Build nested Flow controllers def _unroll_condition(property_set): return not property_set["all_gates_in_basis"] @@ -618,7 +618,7 @@ def _unroll_condition(property_set): if optimization_level == 3 else _opt + _unroll_if_out_of_basis + _depth_check + _size_check ) - optimization.append(opt_loop, do_while=_opt_control) + optimization.append(DoWhileController(opt_loop, do_while=_opt_control)) return optimization else: return None @@ -712,13 +712,19 @@ def _swap_mapped(property_set): layout = PassManager() layout.append(_given_layout) if optimization_level == 0: - layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout.append( + ConditionalController( + TrivialLayout(coupling_map), condition=_choose_layout_condition + ) + ) layout += common.generate_embed_passmanager(coupling_map) return layout elif optimization_level == 1: layout.append( - [TrivialLayout(coupling_map), CheckMap(coupling_map)], - condition=_choose_layout_condition, + ConditionalController( + [TrivialLayout(coupling_map), CheckMap(coupling_map)], + condition=_choose_layout_condition, + ) ) choose_layout_1 = VF2Layout( coupling_map=pass_manager_config.coupling_map, @@ -728,7 +734,7 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices ) - layout.append(choose_layout_1, condition=_layout_not_perfect) + layout.append(ConditionalController(choose_layout_1, condition=_layout_not_perfect)) choose_layout_2 = SabreLayout( coupling_map, max_iterations=2, @@ -739,13 +745,15 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_2, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_2, + ], + condition=_vf2_match_not_found, + ) ) elif optimization_level == 2: choose_layout_0 = VF2Layout( @@ -756,7 +764,9 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=25000, # Limits layout scoring to < 10s on ~400 qubit devices ) - layout.append(choose_layout_0, condition=_choose_layout_condition) + layout.append( + ConditionalController(choose_layout_0, condition=_choose_layout_condition) + ) choose_layout_1 = SabreLayout( coupling_map, max_iterations=2, @@ -767,13 +777,15 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_1, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, + ) ) elif optimization_level == 3: choose_layout_0 = VF2Layout( @@ -784,7 +796,9 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=250000, # Limits layout scoring to < 60s on ~400 qubit devices ) - layout.append(choose_layout_0, condition=_choose_layout_condition) + layout.append( + ConditionalController(choose_layout_0, condition=_choose_layout_condition) + ) choose_layout_1 = SabreLayout( coupling_map, max_iterations=4, @@ -795,21 +809,21 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_1, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, + ) ) else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") embed = common.generate_embed_passmanager(coupling_map) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped)) return layout @@ -829,7 +843,9 @@ def _choose_layout_condition(property_set): layout = PassManager() layout.append(_given_layout) - layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout.append( + ConditionalController(TrivialLayout(coupling_map), condition=_choose_layout_condition) + ) layout += common.generate_embed_passmanager(coupling_map) return layout @@ -851,12 +867,14 @@ def _choose_layout_condition(property_set): layout = PassManager() layout.append(_given_layout) layout.append( - DenseLayout( - coupling_map=pass_manager_config.coupling_map, - backend_prop=pass_manager_config.backend_properties, - target=pass_manager_config.target, - ), - condition=_choose_layout_condition, + ConditionalController( + DenseLayout( + coupling_map=pass_manager_config.coupling_map, + backend_prop=pass_manager_config.backend_properties, + target=pass_manager_config.target, + ), + condition=_choose_layout_condition, + ) ) layout += common.generate_embed_passmanager(coupling_map) return layout @@ -924,16 +942,16 @@ def _swap_mapped(property_set): else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - layout_pass, - ], - condition=_choose_layout_condition, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + layout_pass, + ], + condition=_choose_layout_condition, + ) ) embed = common.generate_embed_passmanager(coupling_map) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped)) return layout diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index 9219854a16a3..c00824a70374 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -20,6 +20,7 @@ from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES from qiskit.utils.deprecation import deprecate_func +from qiskit.passmanager.flow_controllers import ConditionalController from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passes import Error from qiskit.transpiler.passes import Unroller @@ -147,9 +148,11 @@ def generate_control_flow_options_check( out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) if bad_options: - out.append(Error(message), condition=_has_control_flow) + out.append(ConditionalController(Error(message), condition=_has_control_flow)) backend_control = _InvalidControlFlowForBackend(basis_gates, target) - out.append(Error(backend_control.message), condition=backend_control.condition) + out.append( + ConditionalController(Error(backend_control.message), condition=backend_control.condition) + ) return out @@ -158,7 +161,7 @@ def generate_error_on_control_flow(message): circuit.""" out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) - out.append(Error(message), condition=_has_control_flow) + out.append(ConditionalController(Error(message), condition=_has_control_flow)) return out @@ -171,8 +174,8 @@ def if_has_control_flow_else(if_present, if_absent): if_absent = if_absent.to_flow_controller() out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) - out.append(if_present, condition=_has_control_flow) - out.append(if_absent, condition=_without_control_flow) + out.append(ConditionalController(if_present, condition=_has_control_flow)) + out.append(ConditionalController(if_absent, condition=_without_control_flow)) return out @@ -328,32 +331,36 @@ def _swap_condition(property_set): if use_barrier_before_measurement: routing.append( - [ - BarrierBeforeFinalMeasurements( - label="qiskit.transpiler.internal.routing.protection.barrier" - ), - routing_pass, - ], - condition=_swap_condition, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + label="qiskit.transpiler.internal.routing.protection.barrier" + ), + routing_pass, + ], + condition=_swap_condition, + ) ) else: - routing.append([routing_pass], condition=_swap_condition) + routing.append(ConditionalController(routing_pass, condition=_swap_condition)) is_vf2_fully_bounded = vf2_call_limit and vf2_max_trials if (target is not None or backend_properties is not None) and is_vf2_fully_bounded: routing.append( - VF2PostLayout( - target, - coupling_map, - backend_properties, - seed_transpiler, - call_limit=vf2_call_limit, - max_trials=vf2_max_trials, - strict_direction=False, - ), - condition=_run_post_layout_condition, + ConditionalController( + VF2PostLayout( + target, + coupling_map, + backend_properties, + seed_transpiler, + call_limit=vf2_call_limit, + max_trials=vf2_max_trials, + strict_direction=False, + ), + condition=_run_post_layout_condition, + ) ) - routing.append(ApplyLayout(), condition=_apply_post_layout_condition) + routing.append(ConditionalController(ApplyLayout(), condition=_apply_post_layout_condition)) def filter_fn(node): return ( @@ -388,7 +395,12 @@ def generate_pre_op_passmanager(target=None, coupling_map=None, remove_reset_in_ def _direction_condition(property_set): return not property_set["is_direction_mapped"] - pre_opt.append([GateDirection(coupling_map, target=target)], condition=_direction_condition) + pre_opt.append( + ConditionalController( + [GateDirection(coupling_map, target=target)], + condition=_direction_condition, + ) + ) if remove_reset_in_zero: pre_opt.append(RemoveResetInZeroState()) return pre_opt @@ -554,7 +566,9 @@ def _contains_delay(property_set): scheduling.append(ContainsInstruction("delay")) scheduling.append( - TimeUnitConversion(instruction_durations, target=target), condition=_contains_delay + ConditionalController( + TimeUnitConversion(instruction_durations, target=target), condition=_contains_delay + ) ) if ( timing_constraints.granularity != 1 @@ -574,11 +588,13 @@ def _require_alignment(property_set): ) ) scheduling.append( - ConstrainedReschedule( - acquire_alignment=timing_constraints.acquire_alignment, - pulse_alignment=timing_constraints.pulse_alignment, - ), - condition=_require_alignment, + ConditionalController( + ConstrainedReschedule( + acquire_alignment=timing_constraints.acquire_alignment, + pulse_alignment=timing_constraints.pulse_alignment, + ), + condition=_require_alignment, + ) ) scheduling.append( ValidatePulseGates( diff --git a/qiskit/transpiler/propertyset.py b/qiskit/transpiler/propertyset.py deleted file mode 100644 index 6ff160b14a43..000000000000 --- a/qiskit/transpiler/propertyset.py +++ /dev/null @@ -1,19 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A property set is maintained by the PassManager to keep information -about the current state of the circuit """ - - -# For backward compatibility -# pylint: disable=unused-import -from qiskit.passmanager import PropertySet diff --git a/qiskit/transpiler/runningpassmanager.py b/qiskit/transpiler/runningpassmanager.py deleted file mode 100644 index 548b49833265..000000000000 --- a/qiskit/transpiler/runningpassmanager.py +++ /dev/null @@ -1,174 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""RunningPassManager class for the transpiler. -This object holds the state of a pass manager during running-time.""" -from __future__ import annotations - -import inspect -import logging -from functools import wraps -from typing import Callable - -from qiskit.circuit import QuantumCircuit -from qiskit.converters import circuit_to_dag, dag_to_circuit -from qiskit.passmanager.compilation_status import PropertySet, WorkflowStatus, PassManagerState -from qiskit.passmanager.base_tasks import Task -from qiskit.passmanager.exceptions import PassManagerError -from qiskit.utils.deprecation import deprecate_func - -# pylint: disable=unused-import -from qiskit.passmanager.flow_controllers import ( - BaseController, - FlowController, - FlowControllerLinear, - # for backward compatibility - ConditionalController, - DoWhileController, -) - -from .exceptions import TranspilerError -from .layout import TranspileLayout - -logger = logging.getLogger(__name__) - - -class RunningPassManager(FlowControllerLinear): - """A RunningPassManager is a running pass manager. - - .. warning:: - - :class:`.RunningPassManager` will be deprecated in the future release. - As of Qiskit Terra 0.25 this class becomes a subclass of the flow controller - with extra methods for backward compatibility. - Relying on a subclass of the running pass manager might break your code stack. - """ - - @deprecate_func( - since="0.45.0", - additional_msg=( - "Building the pipline of the tasks is responsibility of PassManager. " - "RunningPassManager should not modify prepared pipeline at running time." - ), - ) - def append( - self, - passes: Task | list[Task], - **flow_controller_conditions, - ): - """Append a passes to the schedule of passes. - - Args: - passes: A set of passes (a pass set) to be added to schedule. A pass set is a list of - passes that are controlled by the same flow controller. If a single pass is - provided, the pass set will only have that pass a single element. - It is also possible to append a :class:`.BaseFlowController` instance and - the rest of the parameter will be ignored. - flow_controller_conditions: Dictionary of control flow plugins. - Following built-in controllers are available by default: - - * do_while: The passes repeat until the callable returns False. - * condition: The passes run only if the callable returns True. - """ - if not isinstance(passes, BaseController): - normalized_controller = passes - else: - # Backward compatibility. Will be deprecated. - normalized_controller = FlowController.controller_factory( - passes=passes, - options=self._options, - **flow_controller_conditions, - ) - super().append(normalized_controller) - - # pylint: disable=arguments-differ - @deprecate_func( - since="0.45.0", - additional_msg="Now RunningPassManager is a subclass of flow controller.", - pending=True, - ) - def run( - self, - circuit: QuantumCircuit, - output_name: str = None, - callback: Callable = None, - ) -> QuantumCircuit: - """Run all the passes on a QuantumCircuit - - Args: - circuit: Circuit to transform via all the registered passes. - output_name: The output circuit name. If not given, the same as the input circuit. - callback: A callback function that will be called after each pass execution. - - Returns: - QuantumCircuit: Transformed circuit. - """ - initial_status = WorkflowStatus() - property_set = PropertySet() - state = PassManagerState(workflow_status=initial_status, property_set=property_set) - - passmanager_ir = circuit_to_dag(circuit) - passmanager_ir, state = super().execute( - passmanager_ir=passmanager_ir, - state=state, - callback=callback, - ) - - out_circuit = dag_to_circuit(passmanager_ir, copy_operations=False) - out_circuit.name = output_name - - if state.property_set["layout"] is not None: - circuit._layout = TranspileLayout( - initial_layout=state.property_set["layout"], - input_qubit_mapping=state.property_set["original_qubit_indices"], - final_layout=state.property_set["final_layout"], - ) - circuit._clbit_write_latency = state.property_set["clbit_write_latency"] - circuit._conditional_latency = state.property_set["conditional_latency"] - - if state.property_set["node_start_time"]: - # This is dictionary keyed on the DAGOpNode, which is invalidated once - # dag is converted into circuit. So this schedule information is - # also converted into list with the same ordering with circuit.data. - topological_start_times = [] - start_times = state.property_set["node_start_time"] - for dag_node in passmanager_ir.topological_op_nodes(): - topological_start_times.append(start_times[dag_node]) - circuit._op_start_times = topological_start_times - - return circuit - - -# A temporary error handling with slight overhead at class loading. -# This method wraps all class methods to replace PassManagerError with TranspilerError. -# The pass flow controller mechanics raises PassManagerError, as it has been moved to base class. -# PassManagerError is not caught by TranspilerError due to the hierarchy. - - -def _replace_error(meth): - @wraps(meth) - def wrapper(*meth_args, **meth_kwargs): - try: - return meth(*meth_args, **meth_kwargs) - except PassManagerError as ex: - raise TranspilerError(ex.message) from ex - - return wrapper - - -for _name, _method in inspect.getmembers(RunningPassManager, predicate=inspect.isfunction): - if _name.startswith("_"): - # Ignore protected and private. - # User usually doesn't directly execute and catch error from these methods. - continue - _wrapped = _replace_error(_method) - setattr(RunningPassManager, _name, _wrapped) diff --git a/qiskit/visualization/pass_manager_visualization.py b/qiskit/visualization/pass_manager_visualization.py index 6b166ac85ffc..6c91ce9c0099 100644 --- a/qiskit/visualization/pass_manager_visualization.py +++ b/qiskit/visualization/pass_manager_visualization.py @@ -14,11 +14,15 @@ Visualization function for a pass manager. Passes are grouped based on their flow controller, and coloured based on the type of pass. """ +from __future__ import annotations + import os import inspect import tempfile from qiskit.utils import optionals as _optionals +from qiskit.passmanager.base_tasks import BaseController, GenericPass +from qiskit.passmanager.flow_controllers import FlowControllerLinear from qiskit.transpiler.basepasses import AnalysisPass, TransformationPass from .exceptions import VisualizationError @@ -74,8 +78,6 @@ def pass_manager_drawer(pass_manager, filename=None, style=None, raw=False): """ import pydot - passes = pass_manager.passes() - if not style: style = DEFAULT_STYLE @@ -89,7 +91,7 @@ def pass_manager_drawer(pass_manager, filename=None, style=None, raw=False): prev_node = None - for index, controller_group in enumerate(passes): + for index, controller_group in enumerate(pass_manager.to_flow_controller().tasks): subgraph, component_id, prev_node = draw_subgraph( controller_group, component_id, style, prev_node, index ) @@ -174,9 +176,8 @@ def staged_pass_manager_drawer(pass_manager, filename=None, style=None, raw=Fals stage = getattr(pass_manager, st) if stage is not None: - passes = stage.passes() stagegraph = pydot.Cluster(str(st), label=str(st), fontname="helvetica", labeljust="l") - for controller_group in passes: + for controller_group in stage.to_flow_controller().tasks: subgraph, component_id, prev_node = draw_subgraph( controller_group, component_id, style, prev_node, idx ) @@ -193,28 +194,65 @@ def draw_subgraph(controller_group, component_id, style, prev_node, idx): import pydot # label is the name of the flow controller parameter - label = "[{}] {}".format(idx, ", ".join(controller_group["flow_controllers"])) + label = f"[{idx}] " + if isinstance(controller_group, BaseController) and not isinstance( + controller_group, FlowControllerLinear + ): + label += f"{controller_group.__class__.__name__}" # create the subgraph for this controller subgraph = pydot.Cluster(str(component_id), label=label, fontname="helvetica", labeljust="l") component_id += 1 - for pass_ in controller_group["passes"]: - - # label is the name of the pass - node = pydot.Node( - str(component_id), - label=str(type(pass_).__name__), - color=_get_node_color(pass_, style), - shape="rectangle", - fontname="helvetica", - ) + if isinstance(controller_group, BaseController): + # Assume linear pipeline + # TODO: support pipeline branching when such controller is introduced + tasks = getattr(controller_group, "tasks", []) + elif isinstance(controller_group, GenericPass): + tasks = [controller_group] + elif isinstance(controller_group, (list, tuple)): + tasks = controller_group + else: + # Invalid data + return subgraph, component_id, prev_node + + flatten_tasks = [] + for task in tasks: + # Flatten nested linear flow controller. + # This situation often occurs in the builtin pass managers because it constructs + # some stages by appending other pass manager instance converted into a linear controller. + # Flattening inner linear controller tasks doesn't change the execution. + if isinstance(task, FlowControllerLinear): + flatten_tasks.extend(task.tasks) + else: + flatten_tasks.append(task) + + for task in flatten_tasks: + if isinstance(task, BaseController): + # Partly nested flow controller + # TODO recursively inject subgraph into subgraph + node = pydot.Node( + str(component_id), + label="Nested flow controller", + color="k", + shape="rectangle", + fontname="helvetica", + ) + else: + # label is the name of the pass + node = pydot.Node( + str(component_id), + label=str(type(task).__name__), + color=_get_node_color(task, style), + shape="rectangle", + fontname="helvetica", + ) subgraph.add_node(node) component_id += 1 # the arguments that were provided to the pass when it was created - arg_spec = inspect.getfullargspec(pass_.__init__) + arg_spec = inspect.getfullargspec(task.__init__) # 0 is the args, 1: to remove the self arg args = arg_spec[0][1:] diff --git a/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml b/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml new file mode 100644 index 000000000000..be8b0ce26206 --- /dev/null +++ b/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml @@ -0,0 +1,79 @@ +--- +upgrade: + - | + A pattern for the pass piepline construction was upgraded. + The syntactic suger shown below for instantiation of flow controller was removed. + + .. code-block:: python + + from qiskit.transpiler import PassManager + + pm = PassManager() + pm.append(my_pass, condition=condition_callable, do_while=do_while_callable) + + Instead of using this keyword argument pattern, you should explicitly instantiate the + flow controller. + + .. code-block:: python + + from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController + + pm = PassManager() + pm.append( + ConditionalController( + DoWhileController(my_pass, do_while=do_while_callable), + condition=condition_callable, + ) + ) + + Note that you can manage the pecking order of controllers when you want to nest them, + which was not possible with keyword arguments. + You can also build the pipeline with the constructor of the pass manager like below + because there is no reason to call the append method now. + + .. code-block:: python + + pm = PassManager( + ConditionalController( + DoWhileController(my_pass, do_while=do_while_callable), + condition=condition_callable, + ) + ) + + - | + The append method of builtin flow controllers was removed. This includes + + * :meth:`.ConditionalController.append` + * :meth:`.DoWhileController.append` + * :meth:`.FlowControllerLinear.append` + + The task pipeline in a flow controller is frozen, and it must be passed + when the controller instance is created. + + - | + Removed methods :meth:`qiskit.transpiler.PassManager.passes` and + :meth:`qiskit.transpiler.StagedPassManager.passes` that generates + a representation of inset passes in the form of list of dictionary, + however, this format doesn't efficiently represent more complicated pass pipeline, + which may include conditional branching and nested conditions. + Instead of using this representation, please use following pattern + + .. code-block:: python + + pm = PassManager(...) + pm.to_flow_controller().tasks + + This directly returns a linearized base task instances in tuple format. + + - | + The max_iteration argument was removed from :meth:`qiskit.transpiler.PassManager.append` + and :meth:`qiskit.transpiler.PassManager.replace`. + + - | + The following legacy classes were removed from the pass manager and transpiler module. + + * :class:`qiskit.passmanager.flow_controllers.FlowController` + * :class:`qiskit.transpiler.fencedobjs.FencedObject` + * :class:`qiskit.transpiler.fencedobjs.FencedPropertySet` + * :class:`qiskit.transpiler.fencedobjs.FencedDAGCircuit` + * :class:`qiskit.transpiler.runningpassmanager.RunningPassManager` diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py index 577ae886afc7..31931a7fdb4c 100644 --- a/test/python/transpiler/test_commutative_cancellation.py +++ b/test/python/transpiler/test_commutative_cancellation.py @@ -19,6 +19,7 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit.library import U1Gate, RZGate, PhaseGate, CXGate, SXGate from qiskit.circuit.parameter import Parameter +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler.target import Target from qiskit.transpiler import PassManager, PropertySet from qiskit.transpiler.passes import CommutationAnalysis, CommutativeCancellation, FixedPoint, Size @@ -136,8 +137,15 @@ def test_consecutive_cnots2(self): passmanager = PassManager() passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -410,8 +418,15 @@ def test_commutative_circuit3(self): passmanager = PassManager() passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -453,8 +468,15 @@ def test_cnot_cascade(self): passmanager = PassManager() # passmanager.append(CommutativeCancellation()) passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -509,8 +531,15 @@ def test_cnot_cascade1(self): passmanager = PassManager() # passmanager.append(CommutativeCancellation()) passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) diff --git a/test/python/transpiler/test_gates_in_basis_pass.py b/test/python/transpiler/test_gates_in_basis_pass.py index e181423a00ae..4092b312b337 100644 --- a/test/python/transpiler/test_gates_in_basis_pass.py +++ b/test/python/transpiler/test_gates_in_basis_pass.py @@ -16,6 +16,7 @@ from qiskit.circuit.library import HGate, CXGate, UGate, XGate, ZGate from qiskit.circuit.measure import Measure from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary +from qiskit.passmanager.flow_controllers import ConditionalController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import BasisTranslator from qiskit.transpiler.passes import GatesInBasis @@ -86,8 +87,10 @@ def test_all_gates_in_basis_after_translation(self): pm = PassManager() pm.append(analysis_pass) pm.append( - BasisTranslator(SessionEquivalenceLibrary, basis_gates), - condition=lambda property_set: not property_set["all_gates_in_basis"], + ConditionalController( + BasisTranslator(SessionEquivalenceLibrary, basis_gates), + condition=lambda property_set: not property_set["all_gates_in_basis"], + ) ) pm.append(analysis_pass) pm.run(circuit) @@ -200,8 +203,10 @@ def test_all_gates_in_basis_after_translation_with_target(self): pm = PassManager() pm.append(analysis_pass) pm.append( - BasisTranslator(SessionEquivalenceLibrary, basis_gates, target=target), - condition=lambda property_set: not property_set["all_gates_in_basis"], + ConditionalController( + BasisTranslator(SessionEquivalenceLibrary, basis_gates, target=target), + condition=lambda property_set: not property_set["all_gates_in_basis"], + ) ) pm.append(analysis_pass) pm.run(circuit) diff --git a/test/python/transpiler/test_optimize_swap_before_measure.py b/test/python/transpiler/test_optimize_swap_before_measure.py index 75b0079c6650..258796e56929 100644 --- a/test/python/transpiler/test_optimize_swap_before_measure.py +++ b/test/python/transpiler/test_optimize_swap_before_measure.py @@ -15,6 +15,7 @@ import unittest from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -311,8 +312,10 @@ def test_optimize_undone_swap(self): pass_manager = PassManager() pass_manager.append( - [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) @@ -340,8 +343,10 @@ def test_optimize_overlap_swap(self): pass_manager = PassManager() pass_manager.append( - [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_pass_scheduler.py b/test/python/transpiler/test_pass_scheduler.py index a52ad8b1ece2..263db4f64914 100644 --- a/test/python/transpiler/test_pass_scheduler.py +++ b/test/python/transpiler/test_pass_scheduler.py @@ -19,11 +19,7 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.transpiler import PassManager, TranspilerError -from qiskit.transpiler.runningpassmanager import ( - DoWhileController, - ConditionalController, - FlowController, -) +from qiskit.passmanager.flow_controllers import DoWhileController, ConditionalController from qiskit.test import QiskitTestCase from ._dummy_passes import ( PassA_TP_NR_NP, @@ -136,7 +132,9 @@ def test_conditional_passes_true(self): """A pass set with a conditional parameter. The callable is True.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) self.assertScheduler( self.circuit, @@ -171,7 +169,9 @@ def test_conditional_passes_false(self): """A pass set with a conditional parameter. The callable is False.""" self.passmanager.append(PassE_AP_NR_NP(False)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) self.assertScheduler( self.circuit, @@ -183,9 +183,17 @@ def test_conditional_and_loop(self): """Run a conditional first, then a loop.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property"], + ) ) self.assertScheduler( self.circuit, @@ -240,15 +248,19 @@ def test_conditional_and_loop(self): def test_loop_and_conditional(self): """Run a loop first, then a conditional.""" - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("condition") - FlowController.add_flow_controller("condition", ConditionalController) - self.passmanager.append(PassK_check_fixed_point_property()) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: not property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: not property_set["property_fixed_point"], + ) ) self.assertScheduler( self.circuit, @@ -380,8 +392,14 @@ def test_pass_no_return(self): def test_fixed_point_pass(self): """A pass set with a do_while parameter that checks for a fixed point.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) self.assertScheduler( self.circuit, @@ -436,7 +454,11 @@ def test_fixed_point_fc(self): """A fixed point scheduler with flow control.""" self.passmanager.append( DoWhileController( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], do_while=lambda property_set: not property_set["property_fixed_point"], ) ) @@ -492,9 +514,11 @@ def test_fixed_point_pass_max_iteration(self): """A pass set with a do_while parameter that checks that the max_iteration is raised.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - max_iteration=2, + DoWhileController( + [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], + do_while=lambda property_set: not property_set["property_fixed_point"], + options={"max_iteration": 2}, + ), ) self.assertSchedulerRaises( self.circuit, @@ -535,16 +559,18 @@ def test_fresh_initial_state(self): def test_nested_conditional_in_loop(self): """Run a loop with a nested conditional.""" - nested_conditional = [ - ConditionalController( - [PassA_TP_NR_NP()], condition=lambda property_set: property_set["property"] >= 5 - ) - ] + nested_conditional = ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] >= 5 + ) self.passmanager.append( - [PassK_check_fixed_point_property()] - + nested_conditional - + [PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + nested_conditional, + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) expected = [ "run analysis pass PassG_calculates_dag_property", @@ -589,21 +615,6 @@ def test_nested_conditional_in_loop(self): self.assertScheduler(self.circuit, self.passmanager, expected) -class DoXTimesController(FlowController): - """A control-flow plugin for running a set of passes an X amount of times.""" - - def __init__(self, passes, options, do_x_times, **_): - super().__init__(options) - self.passes = passes - self.do_x_times = do_x_times - - # pylint: disable=missing-function-docstring - def iter_tasks(self, metadata): - for _ in range(self.do_x_times(metadata.property_set)): - for pass_ in self.passes: - metadata = yield pass_ - - class TestControlFlowPlugin(SchedulerTestCase): """Testing the control flow plugin system.""" @@ -612,58 +623,6 @@ def setUp(self): self.passmanager = PassManager() self.circuit = QuantumCircuit(QuantumRegister(1)) - def test_control_flow_plugin(self): - """Adds a control flow plugin with a single parameter and runs it.""" - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - self.passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - self.assertScheduler( - self.circuit, - self.passmanager, - [ - "run transformation pass PassA_TP_NR_NP", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - ], - ) - - def test_callable_control_flow_plugin(self): - """Removes do_while, then adds it back. Checks max_iteration still working.""" - controllers_length = len(FlowController.registered_controllers) - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("do_while") - self.assertEqual(controllers_length - 1, len(FlowController.registered_controllers)) - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_while", DoWhileController) - self.assertEqual(controllers_length, len(FlowController.registered_controllers)) - self.passmanager.append( - [PassB_TP_RA_PA(), PassC_TP_RA_PA()], - do_while=lambda property_set: True, - max_iteration=2, - ) - self.assertSchedulerRaises( - self.circuit, - self.passmanager, - [ - "run transformation pass PassA_TP_NR_NP", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - ], - TranspilerError, - ) - - def test_remove_nonexistent_plugin(self): - """Tries to remove a plugin that does not exist.""" - with self.assertRaises(KeyError): - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("foo") - class TestDumpPasses(SchedulerTestCase): """Testing the passes method.""" @@ -673,12 +632,8 @@ def test_passes(self): passmanager = PassManager() passmanager.append(PassC_TP_RA_PA()) passmanager.append(PassB_TP_RA_PA()) - - expected = [ - {"flow_controllers": {}, "passes": [PassC_TP_RA_PA()]}, - {"flow_controllers": {}, "passes": [PassB_TP_RA_PA()]}, - ] - self.assertEqual(expected, passmanager.passes()) + expected = PassC_TP_RA_PA(), PassB_TP_RA_PA() + self.assertEqual(expected, passmanager.to_flow_controller().tasks) def test_passes_in_linear(self): """Dump passes in the same FlowControllerLinear""" @@ -690,54 +645,34 @@ def test_passes_in_linear(self): PassB_TP_RA_PA(), ] ) - - expected = [ - { - "flow_controllers": {}, - "passes": [ - PassC_TP_RA_PA(), - PassB_TP_RA_PA(), - PassD_TP_NR_NP(argument1=[1, 2]), - PassB_TP_RA_PA(), - ], - } - ] - self.assertEqual(expected, passmanager.passes()) - - def test_control_flow_plugin(self): - """Dump passes in a custom flow controller.""" - passmanager = PassManager() - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - - expected = [ - {"passes": [PassB_TP_RA_PA(), PassC_TP_RA_PA()], "flow_controllers": {"do_x_times"}} - ] - self.assertEqual(expected, passmanager.passes()) + expected = ( + PassC_TP_RA_PA(), + PassB_TP_RA_PA(), + PassD_TP_NR_NP(argument1=[1, 2]), + PassB_TP_RA_PA(), + ) + self.assertEqual(expected, passmanager.to_flow_controller().tasks) def test_conditional_and_loop(self): """Dump passes with a conditional and a loop.""" - passmanager = PassManager() - passmanager.append(PassE_AP_NR_NP(True)) - passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property_fixed_point"], - ) - - expected = [ - {"passes": [PassE_AP_NR_NP(True)], "flow_controllers": {}}, - { - "passes": [ + nested_controller = ConditionalController( + DoWhileController( + [ PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property(), ], - "flow_controllers": {"condition", "do_while"}, - }, - ] - self.assertEqual(expected, passmanager.passes()) + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property_fixed_point"], + ) + + passmanager = PassManager() + passmanager.append(PassE_AP_NR_NP(True)) + passmanager.append(nested_controller) + + expected = (PassE_AP_NR_NP(True), nested_controller) + self.assertEqual(expected, passmanager.to_flow_controller().tasks) class StreamHandlerRaiseException(StreamHandler): @@ -801,33 +736,22 @@ def test_passes_in_linear(self): ], ) - def test_control_flow_plugin(self): - """Dump passes in a custom flow controller.""" - passmanager = PassManager() - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - self.assertPassLog( - passmanager, - [ - "PassA_TP_NR_NP", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - ], - ) - def test_conditional_and_loop(self): """Dump passes with a conditional and a loop""" passmanager = PassManager() passmanager.append(PassE_AP_NR_NP(True)) passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property_fixed_point"], + ) ) self.assertPassLog(passmanager, ["PassE_AP_NR_NP"]) @@ -858,7 +782,9 @@ def test_conditional_twice(self): """Run a conditional twice.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) expected = [ @@ -873,8 +799,14 @@ def test_conditional_twice(self): def test_fixed_point_twice(self): """A fixed point scheduler, twice.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) expected = [ @@ -1015,7 +947,10 @@ def test_replace_with_conditional(self): self.passmanager.append(PassB_TP_RA_PA()) self.passmanager.replace( - 1, PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + 1, + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ), ) expected = ["run analysis pass PassE_AP_NR_NP", "set property as False"] @@ -1071,9 +1006,17 @@ def test_accessing_passmanager_by_index_with_condition(self): """test accessing PassManager's conditioned passes by index""" self.passmanager.append(PassF_reduce_dag_property()) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - condition=lambda property_set: True, - do_while=lambda property_set: not property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: True, + ) ) new_passmanager = self.passmanager[1] @@ -1145,7 +1088,10 @@ def test_accessing_passmanager_by_range_with_condition(self): self.passmanager.append(PassB_TP_RA_PA()) self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), + condition=lambda property_set: property_set["property"], + ) ) self.passmanager.append(PassB_TP_RA_PA()) @@ -1194,7 +1140,9 @@ def test_concatenating_passmanagers_with_condition(self): self.passmanager1.append(PassE_AP_NR_NP(True)) self.passmanager1.append(PassB_TP_RA_PA()) self.passmanager2.append( - PassC_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassC_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ) ) self.passmanager2.append(PassB_TP_RA_PA()) @@ -1247,7 +1195,9 @@ def test_adding_list_of_passes_to_passmanager_with_condition(self): """test adding a list of passes to a PassManager that have conditions""" self.passmanager1.append(PassE_AP_NR_NP(False)) self.passmanager1.append( - PassB_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassB_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ) ) self.passmanager1 += PassC_TP_RA_PA() diff --git a/test/python/transpiler/test_passmanager.py b/test/python/transpiler/test_passmanager.py index 3ed34083ee31..181dace29acf 100644 --- a/test/python/transpiler/test_passmanager.py +++ b/test/python/transpiler/test_passmanager.py @@ -21,7 +21,11 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit.library import U2Gate from qiskit.converters import circuit_to_dag -from qiskit.passmanager.flow_controllers import FlowControllerLinear +from qiskit.passmanager.flow_controllers import ( + FlowControllerLinear, + ConditionalController, + DoWhileController, +) from qiskit.transpiler import PassManager, PropertySet, TransformationPass from qiskit.transpiler.passes import CommutativeCancellation from qiskit.transpiler.passes import Optimize1qGates, Unroller @@ -144,20 +148,20 @@ def condition(_): def make_inner(prefix): inner = PassManager() inner.append(DummyPass(f"{prefix} 1")) - inner.append(DummyPass(f"{prefix} 2"), condition=lambda _: False) - inner.append(DummyPass(f"{prefix} 3"), condition=lambda _: True) - inner.append(DummyPass(f"{prefix} 4"), do_while=repeat(1)) + inner.append(ConditionalController(DummyPass(f"{prefix} 2"), condition=lambda _: False)) + inner.append(ConditionalController(DummyPass(f"{prefix} 3"), condition=lambda _: True)) + inner.append(DoWhileController(DummyPass(f"{prefix} 4"), do_while=repeat(1))) return inner.to_flow_controller() self.assertIsInstance(make_inner("test"), FlowControllerLinear) outer = PassManager() outer.append(make_inner("first")) - outer.append(make_inner("second"), condition=lambda _: False) + outer.append(ConditionalController(make_inner("second"), condition=lambda _: False)) # The intent of this `condition=repeat(1)` is to ensure that the outer condition is only # checked once and not flattened into the inner controllers; an inner pass invalidating the # condition should not affect subsequent passes once the initial condition was met. - outer.append(make_inner("third"), condition=repeat(1)) + outer.append(ConditionalController(make_inner("third"), condition=repeat(1))) calls = [] diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 1e737abdcfc2..190227f99b11 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -1296,13 +1296,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1330,13 +1328,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1364,13 +1360,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1398,14 +1392,10 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) - post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] - ] + post_translation_pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) def test_generate_preset_pass_manager_with_list_coupling_map(self): diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index d58bc47bf57d..b68eca204bba 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -17,6 +17,7 @@ from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.circuit.library import U1Gate, CU1Gate +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -450,8 +451,10 @@ def test_optimize_rz_z(self): pass_manager = PassManager() pass_manager.append( - [RemoveDiagonalGatesBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [RemoveDiagonalGatesBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_remove_reset_in_zero_state.py b/test/python/transpiler/test_remove_reset_in_zero_state.py index 64767d1e07ec..ebcd2087c0ec 100644 --- a/test/python/transpiler/test_remove_reset_in_zero_state.py +++ b/test/python/transpiler/test_remove_reset_in_zero_state.py @@ -15,6 +15,7 @@ import unittest from qiskit import QuantumRegister, QuantumCircuit +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import RemoveResetInZeroState, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -94,8 +95,10 @@ def test_two_resets(self): pass_manager = PassManager() pass_manager.append( - [RemoveResetInZeroState(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [RemoveResetInZeroState(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_staged_passmanager.py b/test/python/transpiler/test_staged_passmanager.py index 677426bad3d6..33bec0f4f913 100644 --- a/test/python/transpiler/test_staged_passmanager.py +++ b/test/python/transpiler/test_staged_passmanager.py @@ -37,7 +37,7 @@ def test_default_stages(self): scheduling=PassManager([Depth()]), ) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Unroller", "Depth"], ) @@ -45,14 +45,14 @@ def test_inplace_edit(self): spm = StagedPassManager(stages=["single_stage"]) spm.single_stage = PassManager([Optimize1qGates(), Depth()]) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Depth"], ) with self.assertWarns(DeprecationWarning): spm.single_stage.append(Unroller(["u"])) spm.single_stage.append(Depth()) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Depth", "Unroller", "Depth"], ) @@ -62,8 +62,9 @@ def test_invalid_stage(self): def test_pre_phase_is_valid_stage(self): spm = StagedPassManager(stages=["init"], pre_init=PassManager([Depth()])) + self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Depth"], ) @@ -110,16 +111,16 @@ def test_repeated_stages(self): spm = StagedPassManager( stages, pre_alpha=pre_alpha, alpha=alpha, post_alpha=post_alpha, omega=omega ) - passes = [ - *pre_alpha.passes(), - *alpha.passes(), - *post_alpha.passes(), - *omega.passes(), - *pre_alpha.passes(), - *alpha.passes(), - *post_alpha.passes(), - ] - self.assertEqual(spm.passes(), passes) + passes = ( + *pre_alpha.to_flow_controller().tasks, + *alpha.to_flow_controller().tasks, + *post_alpha.to_flow_controller().tasks, + *omega.to_flow_controller().tasks, + *pre_alpha.to_flow_controller().tasks, + *alpha.to_flow_controller().tasks, + *post_alpha.to_flow_controller().tasks, + ) + self.assertEqual(spm.to_flow_controller().tasks, passes) def test_edit_stages(self): spm = StagedPassManager() diff --git a/test/python/visualization/references/pass_manager_standard.dot b/test/python/visualization/references/pass_manager_standard.dot index e1c5d3979ca2..bdb2137f33a3 100644 --- a/test/python/visualization/references/pass_manager_standard.dot +++ b/test/python/visualization/references/pass_manager_standard.dot @@ -10,7 +10,7 @@ labeljust=l; subgraph cluster_3 { fontname=helvetica; -label="[1] condition"; +label="[1] ConditionalController"; labeljust=l; 4 [color=red, fontname=helvetica, label=TrivialLayout, shape=rectangle]; 5 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; @@ -58,7 +58,7 @@ labeljust=l; subgraph cluster_17 { fontname=helvetica; -label="[6] do_while"; +label="[6] DoWhileController"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; 19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; diff --git a/test/python/visualization/references/pass_manager_style.dot b/test/python/visualization/references/pass_manager_style.dot index 0c80853d00be..3de498c4ada7 100644 --- a/test/python/visualization/references/pass_manager_style.dot +++ b/test/python/visualization/references/pass_manager_style.dot @@ -10,7 +10,7 @@ labeljust=l; subgraph cluster_3 { fontname=helvetica; -label="[1] condition"; +label="[1] ConditionalController"; labeljust=l; 4 [color=red, fontname=helvetica, label=TrivialLayout, shape=rectangle]; 5 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; @@ -58,7 +58,7 @@ labeljust=l; subgraph cluster_17 { fontname=helvetica; -label="[6] do_while"; +label="[6] DoWhileController"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; 19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; diff --git a/test/python/visualization/test_pass_manager_drawer.py b/test/python/visualization/test_pass_manager_drawer.py index b3456b141406..6e53a895f67d 100644 --- a/test/python/visualization/test_pass_manager_drawer.py +++ b/test/python/visualization/test_pass_manager_drawer.py @@ -18,6 +18,7 @@ from qiskit.transpiler import CouplingMap, Layout from qiskit.transpiler.passmanager import PassManager from qiskit import QuantumRegister +from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController from qiskit.transpiler.passes import GateDirection, Unroller from qiskit.transpiler.passes import CheckMap from qiskit.transpiler.passes import SetLayout @@ -48,13 +49,17 @@ def setUp(self): # Create a pass manager with a variety of passes and flow control structures self.pass_manager = PassManager() self.pass_manager.append(SetLayout(layout)) - self.pass_manager.append(TrivialLayout(coupling_map), condition=lambda x: True) + self.pass_manager.append( + ConditionalController(TrivialLayout(coupling_map), condition=lambda x: True) + ) self.pass_manager.append(FullAncillaAllocation(coupling_map)) self.pass_manager.append(EnlargeWithAncilla()) with self.assertWarns(DeprecationWarning): self.pass_manager.append(Unroller(basis_gates)) self.pass_manager.append(CheckMap(coupling_map)) - self.pass_manager.append(BarrierBeforeFinalMeasurements(), do_while=lambda x: False) + self.pass_manager.append( + DoWhileController(BarrierBeforeFinalMeasurements(), do_while=lambda x: False) + ) self.pass_manager.append(GateDirection(coupling_map)) self.pass_manager.append(RemoveResetInZeroState())