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

Filter/ignore qubits in Target without any operations #9927

Merged
merged 16 commits into from
Apr 18, 2023

Conversation

mtreinish
Copy link
Member

@mtreinish mtreinish commented Apr 6, 2023

Summary

Building off #9840 which adds full path support in all the preset pass
managers for targeting backends with a disconnected coupling graph,
this commit adds support for ignoring qubits that do not support any
operations. When a Target is generated from #9911 with filter_faulty
set to True this will potentially result in qubits being present in
the Target without any supported operations. In these cases the
layout passes in the transpiler might inadvertently use these qubits
only to fail in the basis translator because there are no instructions
available. This commit adds filtering of connected components from the
list of output connected components if the Target does have any
supported instructions on a qubit.

This works by building a copy of the coupling map's internal graph
that removes the nodes which do not have any supported operations.
Then when we compute the connected components of this graph it will
exclude any components of isolated qubits without any operations
supported. A similar change is made to the coupling graph we pass to
rustworkx.vf2_mapping() inside the vf2 layout family of passes.

Details and comments

This PR is based on #9840 (and in turn #9802 which is the parent of #9840). Once #9840 is merged I'll rebase this
PR to isolate just the local changes here. In the meantime you can refer to:

mtreinish/qiskit-core@full-transpile-disjoint-support...mtreinish:qiskit-core:filter-no-op-standalone-qubits

To see just the contents of this PR.

@mtreinish mtreinish added priority: high Changelog: None Do not include in changelog labels Apr 6, 2023
@mtreinish mtreinish added this to the 0.24.0 milestone Apr 6, 2023
@mtreinish mtreinish requested a review from a team as a code owner April 6, 2023 20:57
@qiskit-bot
Copy link
Collaborator

Thank you for opening a new pull request.

Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.

While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.

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

  • @Qiskit/terra-core

@coveralls
Copy link

coveralls commented Apr 6, 2023

Pull Request Test Coverage Report for Build 4736853646

  • 49 of 50 (98.0%) changed or added relevant lines in 11 files are covered.
  • 11 unchanged lines in 5 files lost coverage.
  • Overall coverage decreased (-0.01%) to 85.858%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/transpiler/passes/routing/bip_mapping.py 0 1 0.0%
Files with Coverage Reduction New Missed Lines %
qiskit/circuit/tools/pi_check.py 1 91.23%
qiskit/transpiler/passes/layout/dense_layout.py 1 96.08%
qiskit/transpiler/passes/synthesis/unitary_synthesis.py 1 95.37%
qiskit/pulse/library/waveform.py 3 91.67%
crates/qasm2/src/lex.rs 5 90.63%
Totals Coverage Status
Change from base Build 4735488897: -0.01%
Covered Lines: 70715
Relevant Lines: 82363

💛 - Coveralls

Building off Qiskit#9840 which adds full path support in all the preset pass
managers for targetting backends with a disconnected coupling graph,
this commit adds support for ignoring qubits that do not support any
operations. When a Target is generated from Qiskit#9911 with `filter_faulty`
set to `True` this will potentially result in qubits being present in
the `Target` without any supported operations. In these cases the
layout passes in the transpiler might inadvertently use these qubits
only to fail in the basis translator because there are no instructions
available. This commit adds filtering of connected components from the
list of output connected components if the `Target` does have any
supported instructions on a qubit.

This works by building a copy of the coupling map's internal graph
that removes the nodes which do not have any supported operations.
Then when we compute the connected components of this graph it will
exclude any components of isolated qubits without any operations
supported. A similar change is made to the coupling graph we pass to
rustworkx.vf2_mapping() inside the vf2 layout family of passes.
@mtreinish mtreinish force-pushed the filter-no-op-standalone-qubits branch from f54333b to 2902fec Compare April 17, 2023 12:32
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.

I'm a little concerned by the number of transpiler passes that needed to be touched to make this work because they can't call the new logic in CouplingMap for their use-cases. It makes it feel like the API we're putting in here isn't entirely correct, and the number of changes makes it feel like it's going to be easy for bugs to slip in in the future where people forget to maintain this property.

As a second point: ought we to update all the other CouplingMap methods as well, if we're going with this API?

def connected_components(self) -> List["CouplingMap"]:
def connected_components(self, target=None) -> List["CouplingMap"]:
Copy link
Member

Choose a reason for hiding this comment

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

This feels like a bit of a weird interface considering how we used things. We first create a CouplingMap from a Target, but then we have to pass the target to connected_components again when calling methods on the derived map? It feels like something we perhaps ought to be encoding at construction time, maybe, or alternatively having connected_components to be an associated function that can be called either as a method or directly as a function passing a target in the first case?

Neither of those feel too great to me, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I wasn't happy with this interface really either. My first thought was that the target generated coupling map actually currently annotates the nodes and edges with some of the necessary information. But managing that was tricky and also the information wasn't exactly easy to use that way. I like your suggestion of using an associated method I can adjust the type signature on the disjoint functions to match what the passes are using to take in a target instead of a coupling map and then just generate it on the fly. It's not perfect but it at least avoids the back and forth between the objects.

Copy link
Member Author

Choose a reason for hiding this comment

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

I reworked the filtering to be a function of the Target class and then updated the disjoint utils methods to build the coupling map connected components from either a target or a coupling map in: c6a1e89 I think this keeps the API separation a bit more clear as it's the target which owns all the aspects of which operations are supported on which qubits.

Comment on lines 481 to 488
qargs = target.qargs
graph = self.graph.copy()
for index in graph.node_indices():
for qargs in target.qargs:
if index in qargs:
break
else:
graph.remove_node(index)
Copy link
Member

Choose a reason for hiding this comment

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

This seems a bit confusing (we have both qargs and direct calls to target.qargs) and inefficient - we scan through all qargs in the target for each of the node indices in the happy path (no faulty gates), which feels a bit quadratic. Does something like

has_operations = set(itertools.chain.from_iterable(target.qargs))
for index in graph.node_indices():
    if index not in has_operations:
       graph.remove_node(index)

I'm not 100% certain that that's the same logic, though.

Copy link
Contributor

@jlapeyre jlapeyre Apr 17, 2023

Choose a reason for hiding this comment

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

I'm confused about the purpose of qargs = target.qargsin line 481. The loop variable qargs in line 484 is not introduced in a new scope, so it overwrites the previous definition in case graph.node_indices() is not empty. And otherwise, it is never used.
EDIT: in fact this tox -epy311 -- -n test.python.compiler.test_transpiler passes with line 481 removed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I reworked the logic using your suggestion as part of doing the filtering in the target as part of: c6a1e89

@mtreinish
Copy link
Member Author

I'm a little concerned by the number of transpiler passes that needed to be touched to make this work because they can't call the new logic in CouplingMap for their use-cases. It makes it feel like the API we're putting in here isn't entirely correct, and the number of changes makes it feel like it's going to be easy for bugs to slip in in the future where people forget to maintain this property.

As a second point: ought we to update all the other CouplingMap methods as well, if we're going with this API?

Well for the most part these changes were just giving passing the target to the disjoint utils function because all of those functions were written using the coupling map directly and didn't have the necessary full context from the target to filter the components out of the coupling map (vf2* needs to be handled manually because it operates on a lower level, the raw graph object, than they other layout passes). But, I think you're inline comment about how we're passing the target is making me think we need to do the filtering directly on the target itself either as part of build_coupling_map() with a flag or something. I'll play with it and see if I can come with a better separation of APIs.

@jlapeyre
Copy link
Contributor

jlapeyre commented Apr 17, 2023

You might want to update the method name in the docstring here
https://github.com/Qiskit/qiskit-terra/blob/4d4d292c8391f9400a7a5607e6d03f98a366d387/qiskit/transpiler/coupling.py#L461

as part of this PR instead of relegating to another issue/PR. (This link is to the line in main. Last I checked, you still can't refer to a line not touched by the PR.)

This commit reworks the logic to construct a filtered coupling map as an
optional argument on `Target.build_coupling_map()`. This makes the
filtering a function of the Target object itself, which is where the
context/data about which qubits support operations or not lives. The
previous versions of this PR had a weird mix of responsibilities where
the target would generate a coupling map and then we'd pass the target
to that coupling map to do an additional round of filtering on it.
@mtreinish
Copy link
Member Author

You might want to update the method name in the docstring here

https://github.com/Qiskit/qiskit-terra/blob/4d4d292c8391f9400a7a5607e6d03f98a366d387/qiskit/transpiler/coupling.py#L461

as part of this PR instead of relegating to another issue/PR. (This link is to the line in main. Last I checked, you still can't refer to a line not touched by the PR.)

This PR is no longer touching the coupling.py module after the most recent update. Do you want to just push up a quick PR to update the doc example since you caught the issue?

@jlapeyre
Copy link
Contributor

jlapeyre commented Apr 17, 2023

Do you want to just push up a quick PR to update the doc example since you caught the issue

Yes.

I didn't scroll down to see that you no longer touch coupling.py.

qiskit/transpiler/passes/layout/disjoint_utils.py Outdated Show resolved Hide resolved
qiskit/transpiler/passes/layout/vf2_layout.py Outdated Show resolved Hide resolved
qiskit/transpiler/passes/layout/vf2_post_layout.py Outdated Show resolved Hide resolved
qiskit/transpiler/passes/routing/basic_swap.py Outdated Show resolved Hide resolved
qiskit/transpiler/passes/layout/disjoint_utils.py Outdated Show resolved Hide resolved
Copy link
Contributor

@kevinhartman kevinhartman left a comment

Choose a reason for hiding this comment

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

LGTM!

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 interface looks much cleaner to me as well, thanks for the changes on that.

@kevinhartman kevinhartman added this pull request to the merge queue Apr 18, 2023
Merged via the queue into Qiskit:main with commit e18e4d2 Apr 18, 2023
king-p3nguin pushed a commit to king-p3nguin/qiskit-terra that referenced this pull request May 22, 2023
* Filter/ignore qubits in Target without any operations

Building off Qiskit#9840 which adds full path support in all the preset pass
managers for targetting backends with a disconnected coupling graph,
this commit adds support for ignoring qubits that do not support any
operations. When a Target is generated from Qiskit#9911 with `filter_faulty`
set to `True` this will potentially result in qubits being present in
the `Target` without any supported operations. In these cases the
layout passes in the transpiler might inadvertently use these qubits
only to fail in the basis translator because there are no instructions
available. This commit adds filtering of connected components from the
list of output connected components if the `Target` does have any
supported instructions on a qubit.

This works by building a copy of the coupling map's internal graph
that removes the nodes which do not have any supported operations.
Then when we compute the connected components of this graph it will
exclude any components of isolated qubits without any operations
supported. A similar change is made to the coupling graph we pass to
rustworkx.vf2_mapping() inside the vf2 layout family of passes.

* Expand testing

* Make filtered qubit coupling map a Target.build_coupling_map option

This commit reworks the logic to construct a filtered coupling map as an
optional argument on `Target.build_coupling_map()`. This makes the
filtering a function of the Target object itself, which is where the
context/data about which qubits support operations or not lives. The
previous versions of this PR had a weird mix of responsibilities where
the target would generate a coupling map and then we'd pass the target
to that coupling map to do an additional round of filtering on it.

* Apply suggestions from code review

Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>

* Fix incorrect set construction

* Expand docstring on build_coupling_map argument

* Rework logic in vf2 passes for filtering

* Update argument name in disjoint_utils.py

* Inline second argument for require_layout_isolated_to_component

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Update qiskit/transpiler/passes/layout/vf2_post_layout.py

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Apply suggestions from code review

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Remove unnecessary len()

* Inline second arg for dense_layout too

---------

Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
Co-authored-by: Kevin Hartman <kevin@hart.mn>
@mtreinish mtreinish deleted the filter-no-op-standalone-qubits branch July 12, 2023 18:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog priority: high
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants