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

Replace pybind11 with nanobind in Catalyst Runtime #1293

Open
wants to merge 39 commits into
base: main
Choose a base branch
from

Conversation

joeycarter
Copy link
Contributor

@joeycarter joeycarter commented Nov 7, 2024

Context:

The Catalyst runtime makes use of Pybind11 in two places:

This PR is part of a larger effort to replace all pybind11 code with nanobind.

Description of the Change:

This change replaces all the pybind11 code in runtime/lib/registry/Registry.cpp and in runtime/lib/backend/openqasm/openqasm_python_module.cpp with the equivalent nanobind objects and operations.

Benefits:

See Epic 68472 for a list of nanobind's benefits.


[sc-72877]

@joeycarter
Copy link
Contributor Author

Still TODO: Find a way to remove calls to pybind11::initialize_interpreter in runtime/lib/backend/openqasm/OpenQasmRunner.hpp, e.g.:

#ifdef INITIALIZE_PYTHON
    if (!Py_IsInitialized()) {
        pybind11::initialize_interpreter();
    }
#endif

Nanobind has no plans to port the equivalent feature from pybind11. See https://nanobind.readthedocs.io/en/latest/porting.html#removed-features:

  • Multi-intepreter, Embedding: The ability to embed Python in an executable or run several independent Python interpreters in the same process is unsupported. Nanobind caters to bindings only. Multi-interpreter support would require TLS lookups for nanobind data structures, which is undesirable.

@erick-xanadu
Copy link
Contributor

erick-xanadu commented Nov 8, 2024

@joeycarter, this is technically not a sub-interpreter. This initialization is done conditionally based on whether we are running tests from the runtime. When we are running tests from the runtime, there is no python process unless we initialize it. However, when we are running Catalyst from python, we don't need to initialize python again since it is already initialized. I hope this context helps.

EDIT: Ah, I see that this is equivalent to Embedding. Yes, we will need a way to test it. Maybe we should test the runtime from python?

@joeycarter
Copy link
Contributor Author

EDIT: Ah, I see that this is equivalent to Embedding. Yes, we will need a way to test it. Maybe we should test the runtime from python?

@erick-xanadu thanks for this. I'm wondering now if we only need to use pybind11 for the sake of testing, then maybe this is fine. Converting the tests from C++ to Python could take some time. The big selling point of nanobind for us is its ability to use the Stable ABI to allow us to ship one wheel across minor Python versions. If I understand correctly, the INITIALIZE_PYTHON macro is only defined for the tests, meaning that the wheels we ship would not contain the pybind11 code that initializes the interpreter.

Maybe one thing we could do is move the

#ifdef INITIALIZE_PYTHON
    if (!Py_IsInitialized()) {
        pybind11::initialize_interpreter();
    }
#endif

snippets into the tests themselves to make it explicit that we only use pybind11 to embed a Python interpreter for testing. What do you think?

@joeycarter joeycarter added the author:build-wheels Run the wheel building workflows on this Pull Request label Nov 12, 2024
@joeycarter joeycarter marked this pull request as ready for review November 12, 2024 19:07
@joeycarter
Copy link
Contributor Author

The macOS (arm64) "Build Wheels" actions are failing for Python 3.11 and 3.12 because there are multiple versions installed on the runner images:

3.10 - 3.12 is already available under /Library/Frameworks/Python.framework/Versions/ [the "system versions"], but homebrew also provides 3.11 and 3.12

It's pip installing nanobind with the system versions, but then CMake is running find_package(python) and it's finding the homebrew Python version. It then uses the homebrew python version to find the installed version of nanobind, which is why it fails.

TODO: Find a way to tell CMake to find the system python installation for the given version, e.g. by specifying a root path to search in.

@joeycarter
Copy link
Contributor Author

TODO: Find a way to tell CMake to find the system python installation for the given version, e.g. by specifying a root path to search in.

Done in fe3dbf8.

@joeycarter joeycarter requested review from dime10 and erick-xanadu and removed request for dime10 November 14, 2024 15:31
Copy link
Contributor

@dime10 dime10 left a comment

Choose a reason for hiding this comment

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

Nice work! 💯

Makefile Show resolved Hide resolved
frontend/test/pytest/test_callback.py Show resolved Hide resolved
runtime/CMakeLists.txt Show resolved Hide resolved
runtime/Makefile Outdated Show resolved Hide resolved
}
}

// TODO: These operations are not yet functional
Copy link
Contributor

Choose a reason for hiding this comment

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

Still working on this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not so familiar with system, but the Python functions that are called for the Expval and Var wrappers, py_expval and py_var, defined in runtime/lib/backend/openqasm/openqasm_python_module.cpp, look off to me. They both return the values attribute of the circuit that was run. However, when I run this example locally, values is an empty list. Both the wrappers expect a result of type double, so it throws a bad_cast exception.

It's not clear to me what the correct way to extract the expval and var for this circuit is; do you happen to know?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure either. @maliasadi ?

Comment on lines 40 to 43
# Use a consistant suffix ".so" rather than, e.g. ".abi3.so" (when using the Stable ABI) or
# ".cpython-3xx-darwin.so". This ensures we can locate it when calling `dlopen(LIBREGISTRY)` in
# runtime/lib/capi/RuntimeCAPI.cpp.
set_target_properties(catalyst_callback_registry PROPERTIES SUFFIX ".so")
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah nice, I think this answers my previous question then!

Unrelated to this PR, but at some point I actually tried normalizing all our library names to .so so we would never have to worry about different extensions on different platforms (which is a common source of bugs during development). Currently, on linux we use .so everywhere, and on mac we have a mixture of .so and .dylib. @erick-xanadu What would you think of this?

Copy link
Contributor Author

@joeycarter joeycarter Nov 14, 2024

Choose a reason for hiding this comment

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

The comment I left here might not be strictly correct that it ensures we can locate the file, but it certainly simplifies the process if all our libraries use the same suffix (assuming that doesn't introduce other issues; it seems to be fine, though).

Edit: updated the comment in e528fae

Copy link

codecov bot commented Nov 15, 2024

Codecov Report

Attention: Patch coverage is 0% with 21 lines in your changes missing coverage. Please review.

Project coverage is 81.35%. Comparing base (4925d37) to head (55ade8e).

Files with missing lines Patch % Lines
runtime/tests/Test_OpenQasmDevice.cpp 0.00% 21 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            main    #1293       +/-   ##
==========================================
+ Coverage   0.37%   81.35%   +80.98%     
==========================================
  Files         17       74       +57     
  Lines       1346     8180     +6834     
  Branches      73      858      +785     
==========================================
+ Hits           5     6655     +6650     
- Misses      1341     1468      +127     
- Partials       0       57       +57     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author:build-wheels Run the wheel building workflows on this Pull Request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants