-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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 missing layout in Commuting2qGateRouter
#12137
Conversation
One or more of the the following people are requested to review this:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching this. I certainly agree that as a routing pass, this pass should be setting the final_layout
attribute. Since it's not a layout pass, though, I'm not sure I understand why it might want to set layout
or original_qubit_indices
.
current_layout = Layout.generate_trivial_layout(*dag.qregs.values()) | ||
|
||
self.property_set["layout"] = current_layout.copy() | ||
self.property_set["original_qubit_indices"] = { | ||
bit: index for index, bit in enumerate(dag.qubits) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pass seems to rely on the input circuit being physical already, which would imply that self.property_set["layout"]
likely is already set, and this then overwrites it.
Is there a reason that this routing pass would want to be overwriting the layout and original qubit indices?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I get your point, there was a reason but I think it isn't a good one. I had originally tried to only set "final_layout"
, then realized that in practice we mostly use the pass in isolation (when we transpile qaoa we use it like a pre-layout routing of a sub-circuit that we then use to build the full circuit), and if "layout" wasn't set, the output circuit wouldn't include a layout at all. But if we want to respect the "routing" nature of the pass, I think that we can just guide users to apply a trivial layout to the sub-circuit before running this pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that your proposal in #9523 (comment) might help in the qaoa use case I mentioned? at least it might avoid requiring a layout if we can just store the permutation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in practice we mostly use the pass in isolation
This is kind of where the problem is - if you want to use it in isolation, it should be on a circuit that was already physical, so either you need to have pretended that it was (in which case you should already know how to handle the missing layout), or the pass should be running in a pass manager that makes the circuit physical via a embed
pipeline. Fwiw, I think TranspileLayout
should handle the case of the missing layout
fine already when using routing_permutation
, which setting final_layout
should automatically make available.
If we make this unilaterally set layout
(to the trivial layout, no less) it becomes impossible to use correctly in a full pipeline, which I think might be a larger barrier to entry for general use.
# We use the same scenario as the QAOA test above | ||
mixer = QuantumCircuit(4) | ||
for idx in range(4): | ||
mixer.ry(-idx, idx) | ||
|
||
op = SparsePauliOp.from_list([("IZZI", 1), ("ZIIZ", 2), ("ZIZI", 3)]) | ||
circ = QAOAAnsatz(op, reps=2, mixer_operator=mixer) | ||
|
||
expected_initial_layout = Layout.generate_trivial_layout(*circ.qregs) | ||
expected_final_index_layout = [3, 1, 2, 0] | ||
expected_final_layout = Layout.from_intlist(expected_final_index_layout, *circ.qregs) | ||
|
||
swapped = self.pm_.run(circ.decompose()) | ||
|
||
self.assertEqual(swapped.layout.initial_layout, expected_initial_layout) | ||
self.assertEqual(swapped.layout.final_layout, expected_final_layout) | ||
self.assertEqual(swapped.layout.final_index_layout(), expected_final_index_layout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no layout pass in your PassManager
here, so I wouldn't expect initial_layout
to be set by anything in the pass manager.
fwiw, you don't need to test TranspileLayout.initial_layout
or TranspileLayout.final_layout
- we recommend not using either of those legacy attributes, but only using the methods on TranspileLayout
following #10835. In this case, since the swap-strategy thing is just a router, the only method I'd really expect to return a non-None
value is TranspileLayout.routing_permutation
.
Having some mechanism to store the permutation induced by this pass would be really nice. Before, this was left-up to the user e.g. https://github.com/eggerdj/large_scale_qaoa/blob/21a2d929dabb72887c855767514e566b147b9b79/large_scale_qaoa/qaoa.py#L233-L246 |
Dan: yeah, this pass is a router, so it's a mistake that it wasn't already setting |
Pull Request Test Coverage Report for Build 8988842085Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems mostly ok to me, but given that the docstring of the class suggests that this doesn't run on physical qubits, I'm thinking it's more inline with what the star-to-line pre-routing does (#11387), and if so, perhaps this pass setting virtual_permutation_layout
is more correct?
I don't fully understand the pass, though, so let me just pose this as a question: is this pass supposed to route gates onto physical qubits, or is it supposed to affect the virtual interaction graph, such that a later pass can finalise the routing to physical qubits in the general case?
If the former, it's fine as it is. If the latter (and I think it might be), it probably wants switching to set the pass manager property virtual_permutation_layout
. PassManager.run
should automatically and correctly handle virtual_permutation_layout
in the presence and absence of final_layout
, so no worries there, but we also ought to add a test that running a full layout+routing pipeline after this pass handles those circuits correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks about right to me, and the test seems good (though I can't immediately see off the top of my head that the expected permutations should be what they're set at, but that's fine).
* Add layout to property set, add test * Apply comments from code review * Add virtual permutation instead of final layout. Test (cherry picked from commit b80885d)
* Add layout to property set, add test * Apply comments from code review * Add virtual permutation instead of final layout. Test (cherry picked from commit b80885d) Co-authored-by: Elena Peña Tapia <[email protected]>
* Add layout to property set, add test * Apply comments from code review * Add virtual permutation instead of final layout. Test
Summary
This PR fixes an oversight in the
Commuting2qGateRouter
pass where the qreg permutations weren't tracked in the property set, the output circuitlayout
would be missing, and uses would have to keep track of the mapping from cregs to qregs manually.Details and comments
Found while working on a PR that used this pass in an independent repository: qiskit-community/qopt-best-practices#30.