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

Fully port CheckMap to Rust #13030

Merged
merged 5 commits into from
Sep 6, 2024
Merged

Fully port CheckMap to Rust #13030

merged 5 commits into from
Sep 6, 2024

Conversation

mtreinish
Copy link
Member

@mtreinish mtreinish commented Aug 24, 2024

Summary

This commit migrates the entirety of the CheckMap analysis pass to Rust.
The pass operates solely in the rust domain and returns an
Option<(String, [u32; 2])> to Python which is used to set the two
property set fields appropriately. All the analysis of the dag is done
in Rust. There is still Python interaction required though because
control flow operations are only defined in Python. However the
interaction is minimal and only to get the circuits for control flow
blocks and converting them into DAGs (at least until #13001 is complete).

Details and comments

This commit is based on top of #12959 and will need to be rebased after
that merges. In the meantime you can see the contents of this PR at:
41fe453
Rebased

Closes #12251
Part of #12208

@mtreinish mtreinish added performance Rust This PR or issue is related to Rust code in the repository mod: transpiler Issues and PRs related to Transpiler labels Aug 24, 2024
@mtreinish mtreinish added this to the 1.3 beta milestone Aug 24, 2024
@mtreinish mtreinish requested a review from a team as a code owner August 24, 2024 16:27
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core
  • @kevinhartman
  • @mtreinish

@mtreinish
Copy link
Member Author

I was inspired to quickly do this transpiler pass because asv showed a runtime regression in this pass after #12550 merged. Comparing this PR against main (with that regression) shows a huge speedup:

Benchmarks that have improved:

| Change   | Before [d3040a0b] <main>            | After [41fe4539]    |   Ratio | Benchmark (Parameter)                                        |
|----------|-------------------------------------|---------------------|---------|--------------------------------------------------------------|
| -        | 1.81±0.01ms                         | 32.7±0.1μs          |    0.02 | mapping_passes.PassBenchmarks.time_check_map(5, 1024)        |
| -        | 4.41±0.2ms                          | 101±5μs             |    0.02 | mapping_passes.RoutedPassBenchmarks.time_check_map(5, 1024)  |
| -        | 5.38±0.08ms                         | 33.6±0.7μs          |    0.01 | mapping_passes.PassBenchmarks.time_check_map(14, 1024)       |
| -        | 20.9±0.2ms                          | 289±10μs            |    0.01 | mapping_passes.RoutedPassBenchmarks.time_check_map(14, 1024) |
| -        | 38.6±0.6ms                          | 459±8μs             |    0.01 | mapping_passes.RoutedPassBenchmarks.time_check_map(20, 1024) |
| -        | 8.21±0.3ms                          | 32.9±0.05μs         |    0    | mapping_passes.PassBenchmarks.time_check_map(20, 1024)       |

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE INCREASED.

(I'm not sure I've ever seen asv report a ratio of 0 before)

@mtreinish
Copy link
Member Author

Comparing against 1.2.0 without the regression caused by #12550:

Benchmarks that have improved:

| Change   | Before [7e2e835e] <1.2.0^0>   | After [41fe4539]    |   Ratio | Benchmark (Parameter)                                        |
|----------|-------------------------------|---------------------|---------|--------------------------------------------------------------|
| -        | 341±0.6μs                     | 32.7±0.1μs          |    0.1  | mapping_passes.PassBenchmarks.time_check_map(5, 1024)        |
| -        | 866±5μs                       | 33.0±0.3μs          |    0.04 | mapping_passes.PassBenchmarks.time_check_map(14, 1024)       |
| -        | 2.33±0.01ms                   | 99.3±4μs            |    0.04 | mapping_passes.RoutedPassBenchmarks.time_check_map(5, 1024)  |
| -        | 1.24±0.01ms                   | 33.1±0.2μs          |    0.03 | mapping_passes.PassBenchmarks.time_check_map(20, 1024)       |
| -        | 12.0±0.1ms                    | 281±5μs             |    0.02 | mapping_passes.RoutedPassBenchmarks.time_check_map(14, 1024) |
| -        | 20.2±0.4ms                    | 454±7μs             |    0.02 | mapping_passes.RoutedPassBenchmarks.time_check_map(20, 1024) |

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE INCREASED.

@coveralls
Copy link

coveralls commented Aug 24, 2024

Pull Request Test Coverage Report for Build 10744272901

Details

  • 74 of 76 (97.37%) changed or added relevant lines in 4 files are covered.
  • 4 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.03%) to 89.158%

Changes Missing Coverage Covered Lines Changed/Added Lines %
crates/accelerate/src/check_map.rs 65 67 97.01%
Files with Coverage Reduction New Missed Lines %
crates/qasm2/src/lex.rs 4 92.48%
Totals Coverage Status
Change from base Build 10742451788: 0.03%
Covered Lines: 72795
Relevant Lines: 81647

💛 - Coveralls

This commit migrates the entirety of the CheckMap analysis pass to Rust.
The pass operates solely in the rust domain and returns an
`Option<(String, [u32; 2])>` to Python which is used to set the two
property set fields appropriately. All the analysis of the dag is done
in Rust. There is still Python interaction required though because
control flow operations are only defined in Python. However the
interaction is minimal and only to get the circuits for control flow
blocks and converting them into DAGs (at least until Qiskit#13001 is complete).

This commit is based on top of Qiskit#12959 and will need to be rebased after
that merges.

Closes Qiskit#12251
Part of Qiskit#12208
This commit switches to using a Vec<Qubit> for the internal wire_map
used to map control flow qubits. A HashMap was originally used because
in Python a dictionary is used. However, in the rust domain the inner
qubits are contiguous integers starting from 0 so a Vec can be used for
better performance in the case we have control flow.
Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

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

This looks great, thank you for working on this. I just have a couple of in-line comments that I'm a bit iffy on. Other than that, this is about ready to merge. 🚀 (Once you fix your merge conflicts 🐱)

crates/accelerate/src/check_map.rs Outdated Show resolved Hide resolved
crates/accelerate/src/check_map.rs Show resolved Hide resolved
mtreinish and others added 2 commits September 4, 2024 12:55
Co-authored-by: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com>
pub fn check_map(
py: Python,
dag: &DAGCircuit,
edge_set: HashSet<[u32; 2]>,
Copy link
Member Author

Choose a reason for hiding this comment

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

Besides dropping circuit_to_dag the other potential performance optimization here is building the hash set during init as a pyclass so we avoid converting from a pyset at call time. We can probably do this in a follow up because realistically this probably won't be noticeable except for the cases when there are a lot of edges in the connectivity graph and minimal gates in the circuit (also CheckMap is only typically called once during a transpile, for determining whether to run SabreSwap or not).

None => edge_set.contains(&[qubits[0].into(), qubits[1].into()]),
}
};
for node in dag.op_nodes(false) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not for this PR, but I wonder if eventually we should add methods like DAGCircuit::op_node_references and have that return an iterator of some node reference type which holds the NodeId and a &PackedInstruction so that users don't need to unwrap. Sort of like what petgraph has for their IntoNodeReferences trait.

Copy link
Contributor

Choose a reason for hiding this comment

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

I like the sound of that. Currently a lot of the rust DAGCircuit API is limited, and everyone seems to be adding stuff in their transpiler passes PRs to save time. We could discuss this further in its own issue if you're interested in working on it :)

Copy link
Contributor

@raynelfss raynelfss left a comment

Choose a reason for hiding this comment

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

LGTM! I still think we could wait for #13036 to merge but it seems like you have plans to drop having to perform that conversion, I can add it myself afterwards if anything.

@raynelfss raynelfss added this pull request to the merge queue Sep 6, 2024
Merged via the queue into Qiskit:main with commit 41dc418 Sep 6, 2024
15 checks passed
@mtreinish mtreinish deleted the rust-check-map branch September 6, 2024 21:47
@raynelfss raynelfss added the Changelog: None Do not include in changelog label Nov 7, 2024
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 mod: transpiler Issues and PRs related to Transpiler performance Rust This PR or issue is related to Rust code in the repository
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Port CheckMap to Rust
5 participants