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

Expose internal rust interface to DenseLayout #12104

Merged
merged 4 commits into from
May 22, 2024

Conversation

mtreinish
Copy link
Member

Summary

This commit makes a small change to the rust code for DenseLayout that enables calling it more easily from rust. The primary obstacle was the pyfunction used PyReadonlyArray2 inputs which precludes calling it with rust constructed Array2Views. This adds a new inner public function which takes the array view directly and then the pyfunction's only job is to convert the inputs and outputs to Python. The python side of the function is still building a sparse matrix and then runs reverse Cuthill–McKee to get a permutation of the densest subgraph so any rust consumers will want to keep that in mind (and maybe use sprs to do the same).

At the same time it corrects an oversight in the original implementation where the returned numpy arrays of the densest subgraph are copied instead of being returned as references. This should slightly improve performance by eliminating 3 array copies that weren't needed.

Details and comments

This commit makes a small change to the rust code for DenseLayout that
enables calling it more easily from rust. The primary obstacle was the
pyfunction used PyReadonlyArray2<f64> inputs which precludes calling it
with rust constructed Array2Views<f64>. This adds a new inner public
function which takes the array view directly and then the pyfunction's
only job is to convert the inputs and outputs to Python. The python side
of the function is still building a sparse matrix and then runs reverse
Cuthill–McKee to get a permutation of the densest subgraph so any rust
consumers will want to keep that in mind (and maybe use sprs to do the
same).

At the same time it corrects an oversight in the original implementation
where the returned numpy arrays of the densest subgraph are copied
instead of being returned as references. This should slightly improve
performance by eliminating 3 array copies that weren't needed.
@mtreinish mtreinish added priority: low Changelog: None Do not include in changelog Rust This PR or issue is related to Rust code in the repository labels Mar 29, 2024
@mtreinish mtreinish requested a review from a team as a code owner March 29, 2024 19:24
@qiskit-bot
Copy link
Collaborator

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

@coveralls
Copy link

coveralls commented Mar 29, 2024

Pull Request Test Coverage Report for Build 9194248015

Warning: 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

  • 27 of 27 (100.0%) changed or added relevant lines in 1 file are covered.
  • 11 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.008%) to 89.601%

Files with Coverage Reduction New Missed Lines %
crates/qasm2/src/expr.rs 1 94.02%
crates/qasm2/src/lex.rs 4 92.11%
crates/qasm2/src/parse.rs 6 97.15%
Totals Coverage Status
Change from base Build 9190643360: -0.008%
Covered Lines: 62312
Relevant Lines: 69544

💛 - Coveralls

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 looks fine to me. Do either of these functions actually need to return PyResult? They both appear to be infallible.

Let's do this after #12121 merges, though, since there's a couple of tweaks that'll be needed.

@mtreinish
Copy link
Member Author

No, the PyResult wasn't needed. Removed it in 33493a0 and also updated it to use the bounds api.

mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request May 22, 2024
Building on the work done in Qiskit#10829, Qiskit#10721, and Qiskit#12104 this commit adds
a new trial to all runs of `SabreLayout` that runs the dense layout
pass. In general the sabre layout algorithm starts from a random layout
and then runs a routing algorithm to permute that layout virtually where
swaps would be inserted to select a layout that would result in fewer
swaps. As was discussed in Qiskit#10721 and Qiskit#10829 that random starting point
is often not ideal especially for larger targets where the distance
between qubits can be quite far. Especially when the circuit qubit count
is low relative to the target's qubit count this can result it poor
layouts as the distance between the qubits is too large. In qiskit we
have an existing pass, `DenseLayout`, which tries to find the most
densely connected n qubit subgraph of a connectivity graph. This
algorithm necessarily will select a starting layout where the qubits are
near each other and for those large backends where the random starting
layout doesn't work well this can improve the output quality.

As the overhead of `DenseLayout` is quite low and the core algorithm is
written in rust already this commit adds a default trial that uses
DenseLayout as a starting point on top of the random trials (and any
input starting points). For example if the user specifies to run
SabreLayout with 20 layout trials this will run 20 random trials and
one trial with `DenseLayout` as the starting point. This is all done
directly in the sabre layout rust code for efficiency. The other
difference between the standalone `DenseLayout` pass is that in the
standalone pass a sparse matrix is built and a reverse Cuthill-McKee
permutation is run on the densest subgraph qubits to pick the final
layout. This permutation is skipped because in the case of Sabre's
use of dense layout we're relying on the sabre algorithm to perform
the permutation.

Depends on: Qiskit#12104
Copy link
Contributor

@henryzou50 henryzou50 left a comment

Choose a reason for hiding this comment

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

LGTM. The changes improve the Rust interface and optimize performance by reducing unnecessary array copies. Great work!

@henryzou50 henryzou50 enabled auto-merge May 22, 2024 15:54
@henryzou50 henryzou50 added this pull request to the merge queue May 22, 2024
Merged via the queue into Qiskit:main with commit f08c579 May 22, 2024
15 checks passed
@mtreinish mtreinish deleted the dense-layout-rust-interface branch May 22, 2024 18:12
mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request May 22, 2024
Building on the work done in Qiskit#10829, Qiskit#10721, and Qiskit#12104 this commit adds
a new trial to all runs of `SabreLayout` that runs the dense layout
pass. In general the sabre layout algorithm starts from a random layout
and then runs a routing algorithm to permute that layout virtually where
swaps would be inserted to select a layout that would result in fewer
swaps. As was discussed in Qiskit#10721 and Qiskit#10829 that random starting point
is often not ideal especially for larger targets where the distance
between qubits can be quite far. Especially when the circuit qubit count
is low relative to the target's qubit count this can result it poor
layouts as the distance between the qubits is too large. In qiskit we
have an existing pass, `DenseLayout`, which tries to find the most
densely connected n qubit subgraph of a connectivity graph. This
algorithm necessarily will select a starting layout where the qubits are
near each other and for those large backends where the random starting
layout doesn't work well this can improve the output quality.

As the overhead of `DenseLayout` is quite low and the core algorithm is
written in rust already this commit adds a default trial that uses
DenseLayout as a starting point on top of the random trials (and any
input starting points). For example if the user specifies to run
SabreLayout with 20 layout trials this will run 20 random trials and
one trial with `DenseLayout` as the starting point. This is all done
directly in the sabre layout rust code for efficiency. The other
difference between the standalone `DenseLayout` pass is that in the
standalone pass a sparse matrix is built and a reverse Cuthill-McKee
permutation is run on the densest subgraph qubits to pick the final
layout. This permutation is skipped because in the case of Sabre's
use of dense layout we're relying on the sabre algorithm to perform
the permutation.

Depends on: Qiskit#12104
github-merge-queue bot pushed a commit that referenced this pull request May 22, 2024
Building on the work done in #10829, #10721, and #12104 this commit adds
a new trial to all runs of `SabreLayout` that runs the dense layout
pass. In general the sabre layout algorithm starts from a random layout
and then runs a routing algorithm to permute that layout virtually where
swaps would be inserted to select a layout that would result in fewer
swaps. As was discussed in #10721 and #10829 that random starting point
is often not ideal especially for larger targets where the distance
between qubits can be quite far. Especially when the circuit qubit count
is low relative to the target's qubit count this can result it poor
layouts as the distance between the qubits is too large. In qiskit we
have an existing pass, `DenseLayout`, which tries to find the most
densely connected n qubit subgraph of a connectivity graph. This
algorithm necessarily will select a starting layout where the qubits are
near each other and for those large backends where the random starting
layout doesn't work well this can improve the output quality.

As the overhead of `DenseLayout` is quite low and the core algorithm is
written in rust already this commit adds a default trial that uses
DenseLayout as a starting point on top of the random trials (and any
input starting points). For example if the user specifies to run
SabreLayout with 20 layout trials this will run 20 random trials and
one trial with `DenseLayout` as the starting point. This is all done
directly in the sabre layout rust code for efficiency. The other
difference between the standalone `DenseLayout` pass is that in the
standalone pass a sparse matrix is built and a reverse Cuthill-McKee
permutation is run on the densest subgraph qubits to pick the final
layout. This permutation is skipped because in the case of Sabre's
use of dense layout we're relying on the sabre algorithm to perform
the permutation.

Depends on: #12104
ElePT pushed a commit to ElePT/qiskit that referenced this pull request May 31, 2024
* Expose internal rust interface to DenseLayout

This commit makes a small change to the rust code for DenseLayout that
enables calling it more easily from rust. The primary obstacle was the
pyfunction used PyReadonlyArray2<f64> inputs which precludes calling it
with rust constructed Array2Views<f64>. This adds a new inner public
function which takes the array view directly and then the pyfunction's
only job is to convert the inputs and outputs to Python. The python side
of the function is still building a sparse matrix and then runs reverse
Cuthill–McKee to get a permutation of the densest subgraph so any rust
consumers will want to keep that in mind (and maybe use sprs to do the
same).

At the same time it corrects an oversight in the original implementation
where the returned numpy arrays of the densest subgraph are copied
instead of being returned as references. This should slightly improve
performance by eliminating 3 array copies that weren't needed.

* Remove PyResult

---------

Co-authored-by: Henry Zou <[email protected]>
ElePT pushed a commit to ElePT/qiskit that referenced this pull request May 31, 2024
Building on the work done in Qiskit#10829, Qiskit#10721, and Qiskit#12104 this commit adds
a new trial to all runs of `SabreLayout` that runs the dense layout
pass. In general the sabre layout algorithm starts from a random layout
and then runs a routing algorithm to permute that layout virtually where
swaps would be inserted to select a layout that would result in fewer
swaps. As was discussed in Qiskit#10721 and Qiskit#10829 that random starting point
is often not ideal especially for larger targets where the distance
between qubits can be quite far. Especially when the circuit qubit count
is low relative to the target's qubit count this can result it poor
layouts as the distance between the qubits is too large. In qiskit we
have an existing pass, `DenseLayout`, which tries to find the most
densely connected n qubit subgraph of a connectivity graph. This
algorithm necessarily will select a starting layout where the qubits are
near each other and for those large backends where the random starting
layout doesn't work well this can improve the output quality.

As the overhead of `DenseLayout` is quite low and the core algorithm is
written in rust already this commit adds a default trial that uses
DenseLayout as a starting point on top of the random trials (and any
input starting points). For example if the user specifies to run
SabreLayout with 20 layout trials this will run 20 random trials and
one trial with `DenseLayout` as the starting point. This is all done
directly in the sabre layout rust code for efficiency. The other
difference between the standalone `DenseLayout` pass is that in the
standalone pass a sparse matrix is built and a reverse Cuthill-McKee
permutation is run on the densest subgraph qubits to pick the final
layout. This permutation is skipped because in the case of Sabre's
use of dense layout we're relying on the sabre algorithm to perform
the permutation.

Depends on: Qiskit#12104
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: low 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.

5 participants