Skip to content

Commit

Permalink
Nesting FlowControllers to allow nested conditionals in pass manager (#…
Browse files Browse the repository at this point in the history
…6962)

* added reno

* changes

* added suggested changes

* format

* fixed raise

* added tests

* added tests for nested do_while

* Test edit

* Fix nested conditional test

This was meant to test a conditional within a while loop. Before this
commit, the condition being tested never changed during the while loop.
With this commit, the value checked by the conditional is different for
different iterations. And this test confirms that the conditional pass
runs when it is supposed to and does not run when it is not supposed to.

* Remove two tests that don't test this PR

Two tests were included in this PR whose behavior would be unaffected
by the code changes in this PR. So they don't belong in this PR.
I am removing them with this commit.
modified:   test_pass_scheduler.py

Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 14, 2022
1 parent 25e1d20 commit b66030e
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 12 deletions.
9 changes: 7 additions & 2 deletions qiskit/transpiler/passmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,13 @@ def _normalize_passes(
if isinstance(passes, BasePass):
passes = [passes]
for pass_ in passes:
if not isinstance(pass_, BasePass):
raise TranspilerError("%s is not a pass instance" % pass_.__class__)
if isinstance(pass_, FlowController):
# Normalize passes in nested FlowController
PassManager._normalize_passes(pass_.passes)
elif not isinstance(pass_, BasePass):
raise TranspilerError(
"%s is not a BasePass or FlowController instance " % pass_.__class__
)
return passes

def run(
Expand Down
38 changes: 28 additions & 10 deletions qiskit/transpiler/runningpassmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

from qiskit.dagcircuit import DAGCircuit
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.transpiler.basepasses import BasePass
from .propertyset import PropertySet
from .fencedobjs import FencedPropertySet, FencedDAGCircuit
from .exceptions import TranspilerError
Expand Down Expand Up @@ -132,10 +133,10 @@ def run(self, circuit, output_name=None, callback=None):
return circuit

def _do_pass(self, pass_, dag, options):
"""Do a pass and its "requires".
"""Do either a pass and its "requires" or FlowController.
Args:
pass_ (BasePass): Pass to do.
pass_ (BasePass or FlowController): Pass to do.
dag (DAGCircuit): The dag on which the pass is ran.
options (dict): PassManager options.
Returns:
Expand All @@ -144,18 +145,35 @@ def _do_pass(self, pass_, dag, options):
Raises:
TranspilerError: If the pass is not a proper pass instance.
"""
if isinstance(pass_, BasePass):
# First, do the requires of pass_
for required_pass in pass_.requires:
dag = self._do_pass(required_pass, dag, options)

# First, do the requires of pass_
for required_pass in pass_.requires:
dag = self._do_pass(required_pass, dag, options)
# Run the pass itself, if not already run
if pass_ not in self.valid_passes:
dag = self._run_this_pass(pass_, dag)

# Run the pass itself, if not already run
if pass_ not in self.valid_passes:
dag = self._run_this_pass(pass_, dag)
# update the valid_passes property
self._update_valid_passes(pass_)

# update the valid_passes property
self._update_valid_passes(pass_)
# if provided a nested flow controller
elif isinstance(pass_, FlowController):

if isinstance(pass_, ConditionalController) and not isinstance(
pass_.condition, partial
):
pass_.condition = partial(pass_.condition, self.fenced_property_set)

elif isinstance(pass_, DoWhileController) and not isinstance(pass_.do_while, partial):
pass_.do_while = partial(pass_.do_while, self.fenced_property_set)

for _pass in pass_:
self._do_pass(_pass, dag, pass_.options)
else:
raise TranspilerError(
"Expecting type BasePass or FlowController, got %s." % type(pass_)
)
return dag

def _run_this_pass(self, pass_, dag):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

features:
- |
Allows adding nested FlowControllers (like ConditionalController) to PassManagers when adding passes in the append function.
example::
flow_unroll = [ConditionalController(_unroll, condition=_unroll_condition)]
pm.append(_depth_check + _opt + _unroll_check + flow_unroll, do_while=_opt_control)
55 changes: 55 additions & 0 deletions test/python/transpiler/test_pass_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,61 @@ 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
)
]
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"],
)
expected = [
"run analysis pass PassG_calculates_dag_property",
"set property as 8 (from dag.property)",
"run analysis pass PassK_check_fixed_point_property",
"run transformation pass PassA_TP_NR_NP",
"run transformation pass PassF_reduce_dag_property",
"dag property = 6",
"run analysis pass PassG_calculates_dag_property",
"set property as 6 (from dag.property)",
"run analysis pass PassK_check_fixed_point_property",
"run transformation pass PassA_TP_NR_NP",
"run transformation pass PassF_reduce_dag_property",
"dag property = 5",
"run analysis pass PassG_calculates_dag_property",
"set property as 5 (from dag.property)",
"run analysis pass PassK_check_fixed_point_property",
"run transformation pass PassA_TP_NR_NP",
"run transformation pass PassF_reduce_dag_property",
"dag property = 4",
"run analysis pass PassG_calculates_dag_property",
"set property as 4 (from dag.property)",
"run analysis pass PassK_check_fixed_point_property",
"run transformation pass PassF_reduce_dag_property",
"dag property = 3",
"run analysis pass PassG_calculates_dag_property",
"set property as 3 (from dag.property)",
"run analysis pass PassK_check_fixed_point_property",
"run transformation pass PassF_reduce_dag_property",
"dag property = 2",
"run analysis pass PassG_calculates_dag_property",
"set property as 2 (from dag.property)",
"run analysis pass PassK_check_fixed_point_property",
"run transformation pass PassF_reduce_dag_property",
"dag property = 2",
"run analysis pass PassG_calculates_dag_property",
"set property as 2 (from dag.property)",
"run analysis pass PassK_check_fixed_point_property",
"run transformation pass PassF_reduce_dag_property",
"dag property = 2",
]
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."""
Expand Down

0 comments on commit b66030e

Please sign in to comment.