Skip to content

Commit

Permalink
Legacy code removal in pass manager (Qiskit#11448)
Browse files Browse the repository at this point in the history
* Remove flow controller subclass .append

* Update builtin pass managers to avoid append pattern

* Remove running pass manager

* Remove PropertySet and Fenced objects from transpile

* Remove _pass_sets and let PassManager generate pass list on the fly

This is necessary because kwargs is going to be removed from append method.
Current pass list is generated based on the kwargs.

* Remove kwargs from PassManager.append and .replace

This also removes FlowController factory class

* Remove max_iteration keyword from append and replace

* Reno

* minor: fix test and docs

* Remove qiskit.passmanager.flow_controllers.FlowController

* Remove .passes method from PassManager and StagedPassManager

* Update reference image of pass manager drawer test

* minor: fix drawer label generation

* Revert "Update builtin pass managers to avoid append pattern"

This reverts commit 4bcfcd6.

* Minimum update of builtin pass managers
  • Loading branch information
nkanazawa1989 authored and ShellyGarion committed Jan 18, 2024
1 parent ab2e410 commit 68e65f5
Show file tree
Hide file tree
Showing 23 changed files with 497 additions and 1,033 deletions.
2 changes: 0 additions & 2 deletions qiskit/passmanager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ def digit_condition(property_set):
.. autosummary::
:toctree: ../stubs/
FlowController
FlowControllerLinear
ConditionalController
DoWhileController
Expand All @@ -232,7 +231,6 @@ def digit_condition(property_set):

from .passmanager import BasePassManager
from .flow_controllers import (
FlowController,
FlowControllerLinear,
ConditionalController,
DoWhileController,
Expand Down
222 changes: 1 addition & 221 deletions qiskit/passmanager/flow_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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)
13 changes: 1 addition & 12 deletions qiskit/transpiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1231,15 +1231,6 @@
InstructionDurations
Fenced Objects
--------------
.. autosummary::
:toctree: ../stubs/
FencedPropertySet
FencedDAGCircuit
Abstract Passes
---------------
Expand All @@ -1262,22 +1253,20 @@

# 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,
CouplingError,
LayoutError,
CircuitTooWideForTarget,
)
from .fencedobjs import FencedDAGCircuit, FencedPropertySet
from .basepasses import AnalysisPass, TransformationPass
from .coupling import CouplingMap
from .layout import Layout, TranspileLayout
Expand Down
78 changes: 0 additions & 78 deletions qiskit/transpiler/fencedobjs.py

This file was deleted.

Loading

0 comments on commit 68e65f5

Please sign in to comment.