-
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
Use stable Python C API for building Rust extension #10120
Conversation
This commit tweaks the rust extension code to start using the PyO3 abi3 flag to build binaries that are compatible with all python versions, not just a single release. Previously, we were building against the version specific C API and that resulted in needing abinary file for each supported python version on each supported platform/architecture. By using the abi3 feature flag and marking the wheels as being built with the limited api we can reduce our packaging overhead to just having one wheel file per supported platform/architecture. The only real code change needed here was to update the memory marginalization function. PyO3's abi3 feature is incompatible with returning a big int object from rust (the C API they use for that conversion isn't part of the stable C API). So this commit updates the function to convert to create a python int manually using the PyO3 api where that was being done before. Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
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.
fwiw I'm happy with the code changes in here, but we need another reviewer (and the release note + cibuildwheel checks) because I was involved in writing the patch.
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.
Excellent. We were very sad when we had to stop using abi3 in Pantsbuild because of some IO stuff we were doing. This is a good change.
+1 to setting to abi3-py38
According to the docs for the setuptools-rust RustExtension class: https://setuptools-rust.readthedocs.io/en/latest/reference.html#setuptools_rust.RustExtension The best setting to use for the py_limited_api argument is `"auto"` as this will use the setting in the PyO3 module to determine the correct value to set. This commit updates the setup.py to follow the recommendation in the docs.
Hmm, the test failures (both unit test and neko) look like there is an issue using |
It looks like the problem here is actually a little split between us and PyO3, but fortunately enough of the bug is on our end that we should be able to get a working version. PyO3 has (for some reason) special handling to allow conversion of length-1 arrays to scalars, but the conversion into For example, I made a dummy PyO3 project with a Rust function: use num_complex::Complex64;
fn noop_complex(a: Complex64) -> Complex64 {
a
} I wrapped that into a PyO3 module import numpy as np
from pyo3_test import noop_complex
noop_complex(np.array([1j])) If you don't set the limited API, the return result is
That's the same error in the Neko test. It's not identical to the one in the regular test suite, but I'm guessing/hoping it's related. |
The pauli_expval module in Rust that Statevector and DensityMatrix leverage when computing defines the input type of the phase argument as Complex64. Previously, the quantum info code in the Statevector and DensityMatrix classes were passing in a 1 element ndarray for this parameter. When using the the version specific Python C API in pyo3 it would convert the single element array to a scalar value. However when using abi3 this handling was not done (or was not done correctly) and this caused the tests to fail. This commit updates the quantum info module to pass the phase as a complex value instead of a 1 element numpy array to bypass this behavior change in PyO3 when using abi3. Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Pull Request Test Coverage Report for Build 5179802304
💛 - Coveralls |
"All checks have passed". But I don't see any commits address the conversion of complex problem. Am I missing something? |
I think we'll either need to enforce that all instances in our CI that build Terra from source specify John: the test successes may be because of the above, but also commit 7e5b027 fixes the badly typed values that were incorrectly being passed, in the way I suggested in my comment above. That should sidestep this PyO3 bug. edit: On rereading, I realise that I didn't include the context in the previous comment that Matthew and I had tracked down the issue to badly typed values in the |
Oops, I didn't actually mean to commit |
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'm happy with this, though we ought to have a second in-depth review.
Fwiw, I filed the PyO3 inconsistency we found (via our own bug) as PyO3/pyo3#3164.
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.
LGTM once a release note is added.
Most people won't care about this. Pip will do its thing just fine. But I agree it's still worth documenting, e.g. for people with lockfiles that write down the checksum of every artifact for the package. |
…lt repair command
This reverts commit 8ca24cf.
This should be ready for final review now. I've added the missing release note and also tested that cibuildwheel works and is producing a single binary and testing across all python versions. |
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.
Aside from the release-note comments, I'm happy to merge this at this point.
I left a couple of comments inline for posterity. Another one Matt and I discussed more offline: it's not technically necessary to specify abi3-py38
in the Cargo.toml
files when passing --py-limited-api=cp38
to bdist_wheel
as we do universally in setup.py
(it's inferred by setuptools-rust
), but we chose to do it this way so that the build_rust
command to setup.py
and direct cargo
-based commands will still build in limited API mode, since these are common workflows for our Rust developers. The number of times we'll need to change the flag is very small, and once we're at an MSRV of 1.64, we can inherit one rule from the workspace's Cargo.toml
anyway, reducing the duplication.
repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel} && pipx run abi3audit --strict --report {wheel}" | ||
|
||
[tool.cibuildwheel.macos] | ||
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} && pipx run abi3audit --strict --report {wheel}" | ||
|
||
[tool.cibuildwheel.windows] | ||
repair-wheel-command = "cp {wheel} {dest_dir}/. && pipx run abi3audit --strict --report {wheel}" |
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.
For posterity: the pre-&&
components of the repair commands for Linux and macOS are copied from the cibuildwheel
defaults. Windows has no default command.
Co-authored-by: Jake Lishman <jake@binhbar.com>
I'll leave this unmerged for a little while to see if any of the other people interested in the Rust/Python API layer want to comment on the final form, since I was very involved with this PR. |
Merging now, since it's been a week. |
* Use stable Python C API for building Rust extension This commit tweaks the rust extension code to start using the PyO3 abi3 flag to build binaries that are compatible with all python versions, not just a single release. Previously, we were building against the version specific C API and that resulted in needing abinary file for each supported python version on each supported platform/architecture. By using the abi3 feature flag and marking the wheels as being built with the limited api we can reduce our packaging overhead to just having one wheel file per supported platform/architecture. The only real code change needed here was to update the memory marginalization function. PyO3's abi3 feature is incompatible with returning a big int object from rust (the C API they use for that conversion isn't part of the stable C API). So this commit updates the function to convert to create a python int manually using the PyO3 api where that was being done before. Co-authored-by: Jake Lishman <jake.lishman@ibm.com> * Set minimum version on abi3 flag to Python 3.8 * Fix lint * Use py_limited_api="auto" on RustExtension According to the docs for the setuptools-rust RustExtension class: https://setuptools-rust.readthedocs.io/en/latest/reference.html#setuptools_rust.RustExtension The best setting to use for the py_limited_api argument is `"auto"` as this will use the setting in the PyO3 module to determine the correct value to set. This commit updates the setup.py to follow the recommendation in the docs. * Update handling of phase input to expval rust calls The pauli_expval module in Rust that Statevector and DensityMatrix leverage when computing defines the input type of the phase argument as Complex64. Previously, the quantum info code in the Statevector and DensityMatrix classes were passing in a 1 element ndarray for this parameter. When using the the version specific Python C API in pyo3 it would convert the single element array to a scalar value. However when using abi3 this handling was not done (or was not done correctly) and this caused the tests to fail. This commit updates the quantum info module to pass the phase as a complex value instead of a 1 element numpy array to bypass this behavior change in PyO3 when using abi3. Co-authored-by: Jake Lishman <jake.lishman@ibm.com> * Set py_limited_api explicitly to True * DNM: Test cibuildwheel works with abi3 * Add abi3audit to cibuildwheel repair step * Force setuptools to use abi3 tag * Add wheel to sdist build * Workaround abiaudit3 not moving wheels and windows not having a default repair command * Add source of setup.py hack * Add comment about pending pyo3 abi3 bigint support * Revert "DNM: Test cibuildwheel works with abi3" This reverts commit 8ca24cf. * Add release note * Simplify setting abi3 tag in built wheels * Update releasenotes/notes/use-abi3-4a935e0557d3833b.yaml Co-authored-by: Jake Lishman <jake@binhbar.com> * Update release note * Update releasenotes/notes/use-abi3-4a935e0557d3833b.yaml --------- Co-authored-by: Jake Lishman <jake.lishman@ibm.com> Co-authored-by: Jake Lishman <jake@binhbar.com>
In Qiskit#10120 we moved to using the Python stable C API for the qiskit binaries we build. In that PR we encountered a limitation with PyO3 at the time when using abi3 it was unable to convert a BigUInt into a Python int directly. To workaround this we side stepped the issue by generating a string representation of the integer converting that to python and then having python go from a string to a int. This has some performance penalty and also prevented parallelism because a GIL handle was needed to do the conversion. In PyO3 0.19.1 this limitation was fixed and the library can handle the conversion directly now with abi3 and this commit restores the code that existed in the marginalization module prior to Qiskit#10120.
In #10120 we moved to using the Python stable C API for the qiskit binaries we build. In that PR we encountered a limitation with PyO3 at the time when using abi3 it was unable to convert a BigUInt into a Python int directly. To workaround this we side stepped the issue by generating a string representation of the integer converting that to python and then having python go from a string to a int. This has some performance penalty and also prevented parallelism because a GIL handle was needed to do the conversion. In PyO3 0.19.1 this limitation was fixed and the library can handle the conversion directly now with abi3 and this commit restores the code that existed in the marginalization module prior to #10120.
) In Qiskit#10120 we moved to using the Python stable C API for the qiskit binaries we build. In that PR we encountered a limitation with PyO3 at the time when using abi3 it was unable to convert a BigUInt into a Python int directly. To workaround this we side stepped the issue by generating a string representation of the integer converting that to python and then having python go from a string to a int. This has some performance penalty and also prevented parallelism because a GIL handle was needed to do the conversion. In PyO3 0.19.1 this limitation was fixed and the library can handle the conversion directly now with abi3 and this commit restores the code that existed in the marginalization module prior to Qiskit#10120.
Summary
This commit tweaks the rust extension code to start using the PyO3 abi3 flag to build binaries that are compatible with all python versions, not just a single release. Previously, we were building against the version specific C API and that resulted in needing a binary file for each supported python version on each supported platform/architecture. By using the abi3 feature flag and marking the wheels as being built with the limited api we can reduce our packaging overhead to just having one wheel file per supported platform/architecture.
The only real code change needed here was to update the memory marginalization function. PyO3's abi3 feature is incompatible with returning a big int object from rust (the C API they use for that conversion isn't part of the stable C API). So this commit updates the function to convert to create a python int manually using the PyO3 api where that was being done before.
Details and comments
TODO: