qiskit/crates
Raynel Sanchez 5db984ae0c
Initial: Expose operation conversion methods to other crates. (#12698)
- Expose `operation_type_to_py`, `operation_type_and_data_to_py`, `convert_py_to_operation_type`, methods to other rust crates.
- Expose `OperationTypeConstruct` struct to other crates.
2024-07-01 19:01:12 +00:00
..
accelerate Encapsulate Python sequence-like indexers (#12669) 2024-07-01 12:59:21 +00:00
circuit Initial: Expose operation conversion methods to other crates. (#12698) 2024-07-01 19:01:12 +00:00
pyext Follow up on #12327: circuit construction in Rust (#12605) 2024-06-24 08:02:31 +00:00
qasm2 Spellcheck Done [Unitary Hack 2024] (#12501) 2024-06-19 15:50:03 +00:00
qasm3 Spellcheck Done [Unitary Hack 2024] (#12501) 2024-06-19 15:50:03 +00:00
README.md Spellcheck Done [Unitary Hack 2024] (#12501) 2024-06-19 15:50:03 +00:00

README.md

Crate structure

All the crates in here are called qiskit-*, and are stored in directories that omit the qiskit-.

This crate structure currently serves the purpose of building only a single Python extension module, which still separates out some of the Rust code into separate logical chunks. The intention is that (much) longer term, we might be wanting to expose more of Qiskit functionality directly for other languages to interact with without going through Python.

  • qiskit-pyext is the only crate that actually builds a Python C extension library. This is kind of like the parent crate of all the others, from an FFI perspective; others define pyclasses and pyfunctions and the like, but it's qiskit-pyext that builds the C extension. Our C extension is built as qiskit._accelerate in Python space.
  • qiskit-accelerate is a catch-all crate for one-off accelerators. If what you're working on is small and largely self-contained, you probably just want to put it in here, then bind it to the C extension module in qiskit-pyext.
  • qiskit-circuit is a base crate defining the Rust-space circuit objects. This is one of the lowest points of the stack; everything that builds or works with circuits from Rust space depends on this.
  • qiskit-qasm2 is the OpenQASM 2 parser. This depends on qiskit-circuit, but is otherwise pretty standalone, and it's unlikely that other things will need to interact with it.
  • qiskit-qasm3 is the Qiskit-specific side of the OpenQASM 3 importer. The actual parser lives at https://github.com/Qiskit/openqasm3_parser, and is its own set of Rust-only crates.

We use a structure with several crates in it for a couple of reasons:

  • logical separation of code
  • faster incremental compile times

When we're doing Rust/Python interaction, though, we have to be careful. Pure-Rust FFI with itself over dynamic-library boundaries (like a Python C extension) isn't very natural, since Rust heavily prefers static linking. If we had more than one Python C extension, it would be very hard to interact between the code in them. This would be a particular problem for defining the circuit object and using it in other places, which is something we absolutely need to do.

Developer notes

Beware of initialization order

The Qiskit C extension qiskit._accelerate needs to be initialized in a single go. It is the lowest part of the Python package stack, so it cannot rely on importing other parts of the Python library at initialization time (except for exceptions through PyO3's import_exception! mechanism). This is because, unlike pure-Python modules, the initialization of _accelerate cannot be done partially, and many components of Qiskit import their accelerators from _accelerate.

In general, this should not be too onerous a requirement, but if you violate it, you might see Rust panics on import, and PyO3 should wrap that up into an exception. You might be able to track down the Rust source of the import cycle by running the import with the environment variable RUST_BACKTRACE=full.

Tests

Most of our functionality is tested through the Python-space tests of the qiskit Python package, since the Rust implementations are (to begin with) just private implementation details. However, where it's more useful, Rust crates can also include Rust-space tests within themselves.

Each of the Rust crates disables doctest because our documentation tends to be written in Sphinx's rST rather than Markdown, so rustdoc has a habit of thinking various random bits of syntax are codeblocks. We can revisit that if we start writing crates that we intend to publish.

Running the tests

You need to make sure that the tests can build in standalone mode, i.e. that they link in libpython. By default, we activate PyO3's extension-module feature in crates/pyext, so you have to run with the tests with that disabled:

cargo test --no-default-features

On Linux, you might find that the dynamic linker still can't find libpython. If so, you can extend the environment variable LD_LIBRARY_PATH to include it:

export LD_LIBRARY_PATH="$(python -c 'import sys; print(sys.base_prefix)')/lib:$LD_LIBRARY_PATH"