-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Embed C API into Python extension #13934
Embed C API into Python extension #13934
Conversation
One or more of the following people are relevant to this code:
|
64df7b3
to
f2aa474
Compare
Pull Request Test Coverage Report for Build 13695147517Warning: 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.
I think building this into the Python-space object is fine, but I don't think we should modify qiskit/__init__.py
, given that I think this way of suggesting the linkage is not our intended long-term solution, and in the short term it's really fragile.
There's so much fiddling you need to do compared to a normal build process to build an executable against this current version, which is fine for our MVP, but I think we need a lot more guide on how to make it work, the caveats of it all, and I think it oughtn't to be visible to Python users.
For posterity: I was able to use this PR to build the test executable in #include <Python.h>
#include <complex.h>
#include <qiskit.h>
#include <stdint.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
// build a 100-qubit empty observable
uint32_t num_qubits = 100;
QkSparseObservable *obs = qk_obs_zero(num_qubits);
// add the term 2 * (X0 Y1 Z2) to the observable
complex double coeff = 2;
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t indices[3] = {0, 1, 2};
QkSparseTerm term = {coeff, 3, bit_terms, indices, num_qubits};
qk_obs_add_term(obs, &term);
// print some properties
printf("num_qubits: %u\n", qk_obs_num_qubits(obs));
printf("num_terms: %lu\n", qk_obs_num_terms(obs));
// free the memory allocated for the observable
qk_obs_free(obs);
return 0;
} Note that I've inserted I built Qiskit just with Then the build flags: # Where the root of my Qiskit checkout is.
QISKITROOT="$HOME/code/qiskit/terra"
# Where the root of my Python dev components are.
PYROOT="$HOME/code/pyenv/versions/3.13.2"
CFLAGS="-I$QISKITROOT/dist/c/include -I$PYROOT/include/python3.13"
LDFLAGS="-Wl,-rpath,$QISKITROOT/qiskit,-rpath,$PYROOT/lib -L$QISKITROOT/qiskit -l:_accelerate.cpython-313-aarch64-linux-gnu.so -L$PYROOT/lib -lpython3.13"
gcc $CFLAGS obs.c -o obs $LDFLAGS I inserted the libraries in to the rpath during linking, but you could equally choose to insert I haven't managed to work out the incantations you need to get the Apple linker to use a library file with an "incorrect" name. |
This commit adds the C extension crate into the Python extension build. This is done by adding the qiskit-cext crate as a dependency for qiskit-pyext. Then the contents of cext are re-exported from pyext. This enables linking against the _accelerate shared library object using the C API. This facilitates writing compiled Python extensions that interface with Qiskit directly via C. For example, building a SparseObservable object in a compiled language via the C API and then returning that to Python for use with the rest of Qiskit. Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
The path is trivial to get if you need it and we shouldn't commit to this in the public api yet.
fc211a8
to
b275693
Compare
crates/cext/cbindgen.toml
Outdated
@@ -25,6 +30,7 @@ prefix_with_name = true | |||
[export] | |||
prefix = "Qk" | |||
renaming_overrides_prefixing = false | |||
exclude = ["PyObject"] |
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 doesn't seem to be working - the generated file still has the prefix.
I'm fine just using renaming_overrides_prefixing = true
and manually adding Qk
to the renames for now, while we figure out a nicer pattern.
Probably to do later (because of additional infrastructure), but I think we ought to consider adding a test of the Python-interfacing method.
Co-authored-by: Jake Lishman <jake@binhbar.com>
We should find a better solution, maybe the Rust interface of cbindgen allows for more options here.
…ish/qiskit-core into hybrid-c-embedded-accelerate-mode
To add to the above: the current version with the manual renaming works (on Mac) and with the up-to-date from qiskit import transpile
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.quantum_info import SparseObservable
import cextension # C Python extension
if __name__ == "__main__":
obs = cextension.build_observable()
print("-- SparseObservable")
print(obs)
print(isinstance(obs, SparseObservable)) # True
# build an evolution circuit
evo = PauliEvolutionGate(obs, time=2)
circuit = QuantumCircuit(obs.num_qubits)
circuit.append(evo, circuit.qubits)
tqc = transpile(circuit, basis_gates=["u", "cx"])
print("-- Evolution circuit")
print(tqc.draw(idle_wires=False)) The thing I had to change from Jake's workflow above it so symlink |
If you're actually building a completely custom C extension for Python, you shouldn't need to do the On Mac (or more strictly, when using the LLVM linker toolchain), I couldn't work out a set of commands to use a library file with a bad name (i.e. other than For |
crates/cext/Cargo.toml
Outdated
@@ -17,10 +17,12 @@ workspace = true | |||
thiserror.workspace = true | |||
num-complex.workspace = true | |||
qiskit-accelerate.workspace = true | |||
pyo3.workspace = true |
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 needs to move to the feature so we only directly depend on pyo3 in pyyhon_binding mode
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.
But implicitly we'd still depend on pyo3 through qiskit-accelerate, until we move the Rust-only code into a separate crate, right?
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.
cext
itself only depends on pyo3
if the Python feature is active. It happens to have transitive dependencies that depend on pyo3
anyway, but that's an implementation detail as far as it's concerned.
but implicitly it still depends on it via qiskit-accelerate
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 everyone. We need to add more documentation on this, but we can follow up after 2.0rc1.
Summary
This commit adds the C extension crate into the Python extension build.
This is done by adding the qiskit-cext crate as a dependency for
qiskit-pyext. Then the contents of cext are re-exported from pyext. This
enables linking against the _accelerate shared library object using the
C API. This facilitates writing compiled Python extensions that
interface with Qiskit directly via C. For example, building a
SparseObservable object in a compiled language via the C API and then
returning that to Python for use with the rest of Qiskit.
Details and comments
This PR is based on top of #13694 and will need to be rebased once that merges. To view the contents of just this PR you can look at the HEAD commit on this PR branch: f2aa474