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 split barriers leaking during disjoint layout processing #11655

Merged
merged 5 commits into from
Jan 30, 2024

Conversation

mtreinish
Copy link
Member

Summary

This commit fixes an issue in the disjoint layout processing caused by a shared reference from the input dag to a layout pass to the internal transformations the layout processing performs. As part of the disjoint layout processing the input dag needs to be split into smaller dags for each connected component in the circuit. To enable this any barriers present in the circuit need to be split up prior to analyzing the connected components in the dag. A multiqubit barrier doesn't represent a real connectivity requirement so they need to be split prior to analysis to ensure they don't factor into the connected component computation. To faciliate not losing the semantic meaning of the barrier for the layout analysis of each connected component, the barrier split is done by first taking an n-qubit barrier and converting it into n parallel 1q barriers with a uuid label applied to it. Then after we split the circuit into the separate connected components the uuid is used to identify any components of the same barrier and combine them together.

There were two issues with this approach the first is that the splitting of the barriers was done on the input dag without copying first. This causes the input dag to be modified and because in most cases it's a shared instance which would leak out the barrier splitting if the input dag was returned verbatim from the pass, which in most cases it would be. This issue is fixed by ensuring that we re-combine any split barriers after we've split the dag into it's connected components. The second issue is the uuid label assignment would overwrite any existing labels on barriers. While setting labels on barriers is uncommon for users to do (but still completely valid) this is causing a bigger issues since #10323 because the transpiler is assigning labels to barriers it creates internally so they can be removed before the circuit is returned. This is also fixed in this commit by appending a uuid to the existing label instead of overwriting it, so we're able to restore the original label when we recombine barriers.

Details and comments

Fixes #11649

This commit fixes an issue in the disjoint layout processing caused by a
shared reference from the input dag to a layout pass to the internal
transformations the layout processing performs. As part of the disjoint
layout processing the input dag needs to be split into smaller dags
for each connected component in the circuit. To enable this any barriers
present in the circuit need to be split up prior to analyzing the
connected components in the dag. A multiqubit barrier doesn't represent
a real connectivity requirement so they need to be split prior to
analysis to ensure they don't factor into the connected component
computation. To faciliate not losing the semantic meaning of the barrier
for the layout analysis of each connected component, the barrier split is
done by first taking an n-qubit barrier and converting it into n parallel
1q barriers with a uuid label applied to it. Then after we split the
circuit into the separate connected components the uuid is used to
identify any components of the same barrier and combine them together.

There were two issues with this approach the first is that the splitting
of the barriers was done on the input dag without copying first. This
causes the input dag to be modified and because in most cases it's a
shared instance which would leak out the barrier splitting if the input
dag was returned verbatim from the pass, which in most cases it would
be. This issue is fixed by ensuring that we re-combine any split
barriers after we've split the dag into it's connected components.
The second issue is the uuid label assignment would overwrite any
existing labels on barriers. While setting labels on barriers is
uncommon for users to do (but still completely valid) this is causing a
bigger issues since Qiskit#10323 because the transpiler is assigning labels to
barriers it creates internally so they can be removed before the circuit
is returned. This is also fixed in this commit by appending a uuid to
the existing label instead of overwriting it, so we're able to restore
the original label when we recombine barriers.

Fixes Qiskit#11649
@mtreinish mtreinish added this to the 1.0.0 milestone Jan 26, 2024
@mtreinish mtreinish requested a review from a team as a code owner January 26, 2024 21:58
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

  • @Qiskit/terra-core

@mtreinish mtreinish added the Changelog: Bugfix Include in the "Fixed" section of the changelog label Jan 26, 2024
@coveralls
Copy link

coveralls commented Jan 26, 2024

Pull Request Test Coverage Report for Build 7702562231

  • -1 of 13 (92.31%) changed or added relevant lines in 1 file are covered.
  • 19 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.02%) to 89.479%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/transpiler/passes/layout/disjoint_utils.py 12 13 92.31%
Files with Coverage Reduction New Missed Lines %
qiskit/transpiler/passes/layout/disjoint_utils.py 1 96.83%
crates/qasm2/src/lex.rs 6 91.44%
crates/qasm2/src/parse.rs 12 96.23%
Totals Coverage Status
Change from base Build 7702053542: -0.02%
Covered Lines: 59404
Relevant Lines: 66389

💛 - Coveralls

@mtreinish mtreinish added Changelog: None Do not include in changelog and removed Changelog: Bugfix Include in the "Fixed" section of the changelog labels Jan 26, 2024
@mtreinish
Copy link
Member Author

I'm on the fence of whether this should be in the changelog and release note or not. The specific bug of leaking UUID labels is only on main for 1.0.0. On 0.45.x we still leaked the split barriers into the output, but the labels were stripped, while weird it was valid and wouldn't cause any issues. The barriers generated during transpile was expected in qiskit < 1.0.0 because we hadn't introduced #10323 yet.

Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This generally looks good to me, thanks, just a comment on the particulars of the name-mangling for inter-function communication.

I think we ought to have a release note about fixing the incorrect barriers - I think we could have been affecting our own subsequent peephole optimisations like OptimizeSwapBeforeMeasure or whatever the one that does diagonal gates is, and a user might have had their own too.

Comment on lines 110 to 113
if node.op.label:
barrier_uuid = f"{node.op.label}_uuid={uuid.uuid4()}"
else:
barrier_uuid = uuid.uuid4()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we unconditionally string-ify the UUID here? The first branch does, the second doesn't, and it's technically breaking the typing assumptions of Instruction.label - it would be valid for a pass to examine the labels and assume that they should be str | None.

As a follow-up: perhaps instead of the name mangling, we could return a {new_label: old_label} map from this function, which we then pass into combine_barriers so it can restore them? That has a side benefit of letting it be sure it only targets Barriers that were split by this operation, without needing the "uuid=" in label or isinstance(label, UUID) heuristic to guess.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I was using the UUID type here to differentiate between "" and None. But I can add a variant to the prefix to handle that.

As for the follow-up about the return the issue is that combine_barriers isn't always called consistently through every call path. There are several paths where we still have split barriers that get processed in the layout pass and those could manually call combine_barriers (sabre does for example). We'd have to update all of those to work with this return type, but it's more of a risk regarding out of tree layout passes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's multiple places that call these utilities, I think it's even more important that we have the re-combiner target specifically the desired barriers - it could be possible for a pass dependent or one of the places where we use an "inner" pass manager within a single pass to call the barrier splitter and recombiner while there are split barriers from the outer pass, which could desync the outer logic.

If backwards compatibility is a concern, we can leave this PR's logic in place in there's no dictionary given to the combine_barriers, but document that the argument always should be passed. If you want to do that in a follow-up, though, I'm ok with it.

In fancier language, I think it's a liability for us to have non-reentrant logic in a utility function here, when we have in-tree places that call pass managers recursively.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, lets do it in a follow-up I'll open a tracking issue from your comments here,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - the _uuid= magic sentinel feels a little iffy, but we can squash that later anyway.

@jakelishman jakelishman added this pull request to the merge queue Jan 30, 2024
@jakelishman jakelishman added Changelog: Bugfix Include in the "Fixed" section of the changelog and removed Changelog: None Do not include in changelog labels Jan 30, 2024
Merged via the queue into Qiskit:main with commit 9c58064 Jan 30, 2024
12 checks passed
@mtreinish mtreinish deleted the fix-barrier-split-leak branch January 30, 2024 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: Bugfix Include in the "Fixed" section of the changelog priority: high
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Disjoint handling in transpiler fails to rejoin barriers
5 participants