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

Add ElidePermutations pass to optimization level 3 #12111

Merged
merged 57 commits into from
May 2, 2024

Conversation

alexanderivrii
Copy link
Contributor

Summary

This is a follow-up of #9523 that actually adds the ElidePermutations and the accompanying FinalizeLayouts passes to optimization level 3, and removes from there the OptimizeSwapBeforeMeasure as it's really just a special case of ElidePermutations (moreover, OptimizeSwapBeforeMeasure does not update the "final_layout" and breaks unitary equivalence between the original and the transpiled circuits).

Details and comments

Should be rebased on top of #9523 (and in fact, separated from #9523 into a separate PR).

mtreinish and others added 30 commits February 2, 2023 12:30
This commit adds a new transpiler pass ElideSwaps which is a logical
optimization pass designed to run prior to layout or any other physical
embedding steps in the transpiler pipeline. It traverses the DAG in
topological order and when a swap gate is encountered it removes that
gate and instead permutes the logical qubits for any subsequent gates
in the DAG. This will eliminate any swaps in a circuit not caused by
routing. Additionally, this pass is added to the preset pass manager for
optimization level 3, we can consider adding it to other levels too if
we think it makes sense (there is little overhead, and almost 0 if there
are no swaps).

One thing to note is that this pass does not recurse into control flow
blocks at all, it treats them as black box operations. So if any control
flow blocks contain swap gates those will not be optimized away. This
change was made because mapping the permutation outside and inside any
control flow block was larger in scope than what the intent for this
pass was. That type of work is better suited for the routing passes
which already have to reason about this.
The new ElideSwap pass is causing an output permutation just as a
routing pass would. This information needs to be passed through to the
output in case there is no layout or routing run. In those cases the
information about the output permutation caused by the swap elision will
be lost and doing layout aware operations like Operator.from_circuit()
will not be able to reason about the permutation. This commit fixes this
by inserting the original layout and qubit mapping into the property set
along with the final layout. Then the base pass class and pass manager
class are updated to use the original layout as the initial layout if
one isn't set. In cases where we run layout and routing the output
metadata from those passes will superscede these new metadata fields.
This commit fixes 2 issues in the execution of the new ElideSwaps pass
as part of optimization level 3. First we were running it too late in
the process both after high level synthesis (which isn't relavant yet,
but will be soon when this is expanded to elide all permutations not
just swaps) and also after reset diagonal before measurement. The second
issue is that if the user is specifying to run with a manual layout set
we should not run this pass, as it will interfere with the user intent.
There were 2 issues with the permutation tracking done in an earlier
commit. First, it conflicted with the final_layout property set via
routing (or internally by the routing done in the combined sabre
layout). This was breaking conditional checks in the preset pass manager
around embedding. To fix this a different property is used and set as
the output final layout if no final layout is set. The second issue was
the output layout object was not taking into account a set initial
layout which will permute the qubits and cause the output to not be up
to date. This is fixed by updating apply layout to apply the initial
layout to the elision_final_layout in the property set.
This commit generalizes the pass from just considering swap gates to all
permutations (via the PermutationGate class). This enables the pass to
elide additional circuit permutations, not just the special case of a
swap gate. The pass and module are renamed accordingly to
ElidePermutations and elide_permutations respectively.
This commit fixes the recently added handling of the PermutationGate so
that it correctly is mapping the qubits. The previous iteration of this
messed up the mapping logic so it wasn't valid.
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.

If I understand the ordering correctly, the actual meat of this PR is commit 7a90b70. Assuming that's true, this mostly looks fine to me.

Comment on lines 411 to 415
def generate_post_op_passmanager():
"""Generates the post-optimization pass manager."""
out = PassManager()
out.append(FinalizeLayouts())
return out
Copy link
Member

@jakelishman jakelishman May 2, 2024

Choose a reason for hiding this comment

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

Highly unimportant, but just to point out that this can also be spelled return PassManager([FinalizeLayouts()]).

(No need to change it, just commenting in case it's shorter for you in the future.)

Comment on lines 109 to 111

post_optimization = common.generate_post_op_passmanager()

Copy link
Member

Choose a reason for hiding this comment

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

I think we ought to include the layout finalisation in every optimisation level, even if by our presets it won't have any work to do. It just helps catch the case of somebody using (say) an O1 pass manager but injecting ElideSwaps into pre-op, which is a totally valid use of StagedPassManager.

Since we don't have final_permutation and its fixing of the update responsibilities to be local to the modifying pass, I think we need to treat FinalizeLayouts as a required part of the transpiler pipeline at all optimisation levels, or shift the responsibility of FinalizeLayouts into PassManager._passmanager_backend (#9523 (comment)).

Copy link
Member

Choose a reason for hiding this comment

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

Since #9523 moved the responsibility of FinalizeLayouts into PassManager._passmanager_backend, now we don't need the post-op stage at all, rather than needing it everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, this is much-much cleaner than having a separate FinalizeLayouts pass.

@alexanderivrii
Copy link
Contributor Author

We don't need this PR anymore, should I close it?

@jakelishman
Copy link
Member

#9523 didn't add the pass to O3, so that part is still something we'd want to do separately. It's just that I think FinalizeLayouts is no longer needed because it's built-in to our PassManager.

@alexanderivrii
Copy link
Contributor Author

Ok, so the only thing that needs to be done (once ElidePermutations merges) is to replace OptimizeSwapsBeforeMeasure by ElidePermutations for level=3.

mtreinish
mtreinish previously approved these changes May 2, 2024
@mtreinish mtreinish enabled auto-merge May 2, 2024 16:28
Comment on lines +1767 to +1772
# an iswap gate is equivalent to (swap, CZ) up to single-qubit rotations. Normally, the swap gate
# in the circuit would cancel with the swap gate of the (swap, CZ), leaving a single CZ gate that
# can be realized via one ECR gate. However, with the introduction of ElideSwap, the swap gate
# cancellation can not occur anymore, thus requiring two ECR gates for the iswap gate.
self.assertEqual(res.count_ops()["ecr"], 2)
self.assertEqual(Operator(circuit), Operator.from_circuit(res))
Copy link
Member

Choose a reason for hiding this comment

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

I guess this is the edge case of the elide permutations pass. It is a fun edge case and is unlikely to come up in practice I think.

…zation level 3, it does remove the SWAP gate. So we need to consider optimization_level=1 to assert that the extra pass does not remove SWAP gates
Comment on lines 315 to 318
spm = generate_preset_pass_manager(
optimization_level=3, initial_layout=list(range(2, -1, -1)), seed_transpiler=42
optimization_level=1, initial_layout=list(range(2, -1, -1)), seed_transpiler=42
)
spm.layout += ElidePermutations()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was the other new failing test. Now that the ElidePermutations already runs in level=3, it is able to remove those SWAP gates before getting to the extra pass in line 318.

@mtreinish mtreinish added this pull request to the merge queue May 2, 2024
Merged via the queue into Qiskit:main with commit 43c065f May 2, 2024
15 checks passed
@sbrandhsn sbrandhsn added the Changelog: New Feature Include in the "Added" section of the changelog label May 8, 2024
ElePT pushed a commit to ElePT/qiskit that referenced this pull request May 31, 2024
* Add ElideSwaps transpiler pass

This commit adds a new transpiler pass ElideSwaps which is a logical
optimization pass designed to run prior to layout or any other physical
embedding steps in the transpiler pipeline. It traverses the DAG in
topological order and when a swap gate is encountered it removes that
gate and instead permutes the logical qubits for any subsequent gates
in the DAG. This will eliminate any swaps in a circuit not caused by
routing. Additionally, this pass is added to the preset pass manager for
optimization level 3, we can consider adding it to other levels too if
we think it makes sense (there is little overhead, and almost 0 if there
are no swaps).

One thing to note is that this pass does not recurse into control flow
blocks at all, it treats them as black box operations. So if any control
flow blocks contain swap gates those will not be optimized away. This
change was made because mapping the permutation outside and inside any
control flow block was larger in scope than what the intent for this
pass was. That type of work is better suited for the routing passes
which already have to reason about this.

* Update tests with optimization level 3

* Pass final layout from ElideSwap to output

The new ElideSwap pass is causing an output permutation just as a
routing pass would. This information needs to be passed through to the
output in case there is no layout or routing run. In those cases the
information about the output permutation caused by the swap elision will
be lost and doing layout aware operations like Operator.from_circuit()
will not be able to reason about the permutation. This commit fixes this
by inserting the original layout and qubit mapping into the property set
along with the final layout. Then the base pass class and pass manager
class are updated to use the original layout as the initial layout if
one isn't set. In cases where we run layout and routing the output
metadata from those passes will superscede these new metadata fields.

* Move pass in opt level 3 earlier in stage and skip with explicit layout

This commit fixes 2 issues in the execution of the new ElideSwaps pass
as part of optimization level 3. First we were running it too late in
the process both after high level synthesis (which isn't relavant yet,
but will be soon when this is expanded to elide all permutations not
just swaps) and also after reset diagonal before measurement. The second
issue is that if the user is specifying to run with a manual layout set
we should not run this pass, as it will interfere with the user intent.

* Doc and copy paste fixes

* Expand test coverage

* Update permutation tracking

There were 2 issues with the permutation tracking done in an earlier
commit. First, it conflicted with the final_layout property set via
routing (or internally by the routing done in the combined sabre
layout). This was breaking conditional checks in the preset pass manager
around embedding. To fix this a different property is used and set as
the output final layout if no final layout is set. The second issue was
the output layout object was not taking into account a set initial
layout which will permute the qubits and cause the output to not be up
to date. This is fixed by updating apply layout to apply the initial
layout to the elision_final_layout in the property set.

* Generalize pass to support PermutationGate too

This commit generalizes the pass from just considering swap gates to all
permutations (via the PermutationGate class). This enables the pass to
elide additional circuit permutations, not just the special case of a
swap gate. The pass and module are renamed accordingly to
ElidePermutations and elide_permutations respectively.

* Fix permutation handling

This commit fixes the recently added handling of the PermutationGate so
that it correctly is mapping the qubits. The previous iteration of this
messed up the mapping logic so it wasn't valid.

* Fix formatting

* Fix final layout handling for no initial layout

* Improve documentation and log a warning if run post layout

* Fix final layout handling with no ElideSwaps being run

* Fix docs build

* Fix release note

* Fix typo

* Add test for routing and elide permutations

* Apply suggestions from code review

Co-authored-by: Jim Garrison <[email protected]>

* Rename test file to test_elide_permutations.py

* Apply suggestions from code review

Co-authored-by: Kevin Hartman <[email protected]>

* Fix test import after rebase

* fixing failing test cases

this should pass CI after merging Qiskit#12057

* addresses kehas comments - thx

* Adding FinalyzeLayouts pass to pull the virtual circuit permutation from ElidePermutations to the final layout

* formatting

* partial rebase on top of 12057 + tests

* also need test_operator for partial rebase

* removing from transpiler flow for now; reworking elide tests

* also adding permutation gate to the example

* also temporarily reverting test_transpiler.py

* update to release notes

* minor fixes

* adding ElidePermutations and FinalizeLayouts to level 3

* fixing tests

* release notes

* properly merging with main (now that ElidePermutations is merged)

* and also deleting finalize_layouts

* Update releasenotes/notes/add-elide-permutations-to-pipeline-077dad03bd55ab9c.yaml

* updating code comment

* Fixing failing test: now that ElidePermutations pass runs with optimization level 3, it does remove the SWAP gate. So we need to consider optimization_level=1 to assert that the extra pass does not remove SWAP gates

---------

Co-authored-by: Matthew Treinish <[email protected]>
Co-authored-by: Jim Garrison <[email protected]>
Co-authored-by: Kevin Hartman <[email protected]>
Co-authored-by: Sebastian Brandhofer <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: New Feature Include in the "Added" section of the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants