Passmanager
qiskit.passmanager
Overview
The Qiskit pass manager is somewhat inspired by the LLVM compiler, but it is designed to take a Python object as an input instead of plain source code.
The pass manager converts the input Python object into an intermediate representation (IR), and it can be optimized and get lowered with a variety of transformations over multiple passes. The pass manager framework may employ multiple IRs with interleaved conversion passes, depending on the context of the optimization.
Currently there is no actual use/design of multiple IRs in the builtin Qiskit pass managers. The implementation of the passmanager
module is agnostic to actual IR types (i.e. no strict type check is performed), and the pass manager works as long as the IR implements all methods required by subsequent passes. A concrete design for the use of multiple IRs might be provided in the future release.
The passes may consume the hardware constraints that the Qiskit backend may provide. Finally, the IR is converted back to some Python object. Note that the input type and output type are not necessarily the same.
Compilation in the pass manager is a chain of Task
executions that take an IR and output a new IR with some optimization or data analysis. An atomic task is a pass which is a subclass of GenericPass
that implements a run()
method that performs some work on the received IR. A set of passes may form a flow controller, which is a subclass of BaseController
, which can implement arbitrary compilation-state-dependent logic for deciding which pass will get run next. Passes share intermediate data via the PropertySet
object which is a free-form dictionary. A pass can populate the property set dictionary during the task execution. A flow controller can also consume the property set to control the pass execution, but this access must be read-only. The property set is portable and handed over from pass to pass at execution. In addition to the property set, tasks also receive a WorkflowStatus
data structure. This object is initialized when the pass manager is run and handed over to underlying tasks. The status is updated after every pass is run, and contains information about the pipeline state (number of passes run, failure state, and so on) as opposed to the PropertySet
, which contains information about the IR being optimized.
A pass manager is a wrapper of the flow controller, with responsibilities of
- Scheduling optimization tasks,
- Converting an input Python object to a particular Qiskit IR,
- Initializing a property set and workflow status,
- Running scheduled tasks to apply a series of transformations to the IR,
- Converting the IR back to an output Python object.
This indicates that the flow controller itself is type-agnostic, and a developer must implement a subclass of the BasePassManager
to manage the data conversion steps. This veil of ignorance allows us to choose the most efficient data representation for a particular pass manager task, while we can reuse the flow control machinery for different input and output types.
A single flow controller always takes a single IR object, and returns a single IR object. Parallelism for multiple input objects is supported by the BasePassManager
by broadcasting the flow controller via the parallel_map()
function.
Examples
We look into a toy optimization task, namely, preparing a row of numbers and remove a digit if the number is five. Such task might be easily done by converting the input numbers into string. We use the pass manager framework here, putting the efficiency aside for a moment to learn how to build a custom Qiskit compiler.
from qiskit.passmanager import BasePassManager, GenericPass, ConditionalController
class ToyPassManager(BasePassManager):
def _passmanager_frontend(self, input_program: int, **kwargs) -> str:
return str(input_program)
def _passmanager_backend(self, passmanager_ir: str, in_program: int, **kwargs) -> int:
return int(passmanager_ir)
This pass manager inputs and outputs an integer number, while performing the optimization tasks on a string data. Hence, input, IR, output type are integer, string, integer, respectively. The _passmanager_frontend()
method defines the conversion from the input data to IR, and _passmanager_backend()
defines the conversion from the IR to output data. The pass manager backend is also given an in_program
parameter that contains the original input_program
to the front end, for referencing any original metadata of the input program for the final conversion.
Next, we implement a pass that removes a digit when the number is five.
class RemoveFive(GenericPass):
def run(self, passmanager_ir: str):
return passmanager_ir.replace("5", "")
task = RemoveFive()
Finally, we instantiate a pass manager and schedule the task with it. Running the pass manager with random row of numbers returns new numbers that don’t contain five.
pm = ToyPassManager()
pm.append(task)
pm.run([123456789, 45654, 36785554])
Output:
[12346789, 464, 36784]
Now we consider the case of conditional execution. We avoid execution of the “remove five” task when the input number is six digits or less. Such control can be implemented by a flow controller. We start from an analysis pass that provides the flow controller with information about the number of digits.
class CountDigits(GenericPass):
def run(self, passmanager_ir: str):
self.property_set["ndigits"] = len(passmanager_ir)
analysis_task = CountDigits()
Then, we wrap the remove five task with the ConditionalController
that runs the stored tasks only when the condition is met.
def digit_condition(property_set):
# Return True when condition is met.
return property_set["ndigits"] > 6
conditional_task = ConditionalController(
tasks=[RemoveFive()],
condition=digit_condition,
)
As before, we schedule these passes with the pass manager and run.
pm = ToyPassManager()
pm.append(analysis_task)
pm.append(conditional_task)
pm.run([123456789, 45654, 36785554])
Output:
[12346789, 45654, 36784]
The “remove five” task is triggered only for the first and third input values, which have more than six digits.
With the pass manager framework, a developer can flexibly customize the optimization task by combining multiple passes and flow controllers. See details for following class API documentations.
Interface
Base classes
BasePassManager ([tasks, max_iteration]) | Pass manager base class. |
BaseController ([options]) | Base class of controller. |
GenericPass () | Base class of a single pass manager task. |
Flow controllers
FlowControllerLinear ([tasks, options]) | A standard flow controller that runs tasks one after the other. |
ConditionalController ([tasks, condition, ...]) | A flow controller runs the pipeline once if the condition is true, or does nothing if the condition is false. |
DoWhileController ([tasks, do_while, options]) | Run the given tasks in a loop until the do_while condition on the property set becomes False . |
Compilation state
PropertySet | A default dictionary-like object. |
WorkflowStatus ([count, completed_passes, ...]) | Collection of compilation status of workflow, i.e. pass manager run. |
PassManagerState (workflow_status, property_set) | A portable container object that pass manager tasks communicate through generator. |
Exceptions
PassManagerError
exception qiskit.passmanager.PassManagerError(*message)
Pass manager error.
Set the error message.