Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix PropertySet re-use in BasePassManager.run (backport #11787) #11863

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions qiskit/passmanager/passmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def __init__(
"""
self._tasks = []
self.max_iteration = max_iteration
# This empty property set never gets used; it gets overridden at the completion of a
# workflow run.
self.property_set = PropertySet()

if tasks:
Expand Down Expand Up @@ -281,14 +283,22 @@ def _run_workflow(
input_program=program,
**kwargs,
)
passmanager_ir, _ = flow_controller.execute(
passmanager_ir, final_state = flow_controller.execute(
passmanager_ir=passmanager_ir,
state=PassManagerState(
workflow_status=initial_status,
property_set=pass_manager.property_set,
property_set=PropertySet(),
),
callback=kwargs.get("callback", None),
)
# The `property_set` has historically been returned as a mutable attribute on `PassManager`
# This makes us non-reentrant (though `PassManager` would be dependent on its internal tasks to
# be re-entrant if that was required), but is consistent with previous interfaces. We're still
# safe to be called in a serial loop, again assuming internal tasks are re-runnable. The
# conversion to the backend language is also allowed to use the property set, so it must be set
# before calling it.
pass_manager.property_set = final_state.property_set

out_program = pass_manager._passmanager_backend(
passmanager_ir=passmanager_ir,
in_program=program,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
:meth:`.BasePassManager.run` will no longer leak the previous :class:`.PropertySet` into new
workflows when called more than once. Previously, the same :class:`.PropertySet` as before
would be used to initialize follow-on runs, which could mean that invalid property information
was being given to tasks. The behavior now matches that of Qiskit 0.44. Fixed `#11784 <https://github.com/Qiskit/qiskit/issues/11784>`__.
21 changes: 21 additions & 0 deletions test/python/passmanager/test_passmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,24 @@ def run(self, passmanager_ir):
with self.assertLogContains(expected):
out = pm.run(data)
self.assertEqual(out, data)

def test_reruns_have_clean_property_set(self):
"""Test that re-using a pass manager produces a clean property set for the state."""

sentinel = object()

class CheckPropertySetClean(GenericPass):
def __init__(self, test_case):
super().__init__()
self.test_case = test_case

def run(self, _):
self.test_case.assertIs(self.property_set["check_property"], None)
self.property_set["check_property"] = sentinel
self.test_case.assertIs(self.property_set["check_property"], sentinel)

pm = ToyPassManager([CheckPropertySetClean(self)])
pm.run(0)
self.assertIs(pm.property_set["check_property"], sentinel)
pm.run(1)
self.assertIs(pm.property_set["check_property"], sentinel)