Package: pybind11-cuda-array-interface
Pybind11 - CUDA Array InterfaceOverviewpybind11_cuda_array_interfaceis a plugin for pybind11 that facilitates effortless exchange of arrays between Python and C++ environments. It specifically targets objects or arrays that implement the__cuda_array_interface__, ensuring smooth interaction with GPU data structures.FeaturesAutomatic Conversion:No need for manual data type conversions. The plugin handles it seamlessly.Lifecycle management:Specifically designed to work with the__cuda_array_interface__, by defining acuda_array_t<T>type for the C++ side managing the reference count intrinsically.Header-only:Just include the header and you're set. No heavy installations or configurations required.Usage and making bindings#include"pybind11_cuda_array_interface/pybind11_cuda_array_interface.hpp"namespacepy=pybind11;template<typenameT>cai::cuda_array_t<T>receive_and_return_cuda_array_interface(cai::cuda_array_t<T>s_cai){returns_cai;}template<typenameT>cai::cuda_array_t<T>return_cuda_array_interface(std::vector<size_t>shape={3,4,5},boolreadonly=false,intversion=3){cai::cuda_array_t<T>s_cai(shape,readonly,version);returns_cai;}Bindings:PYBIND11_MODULE(pycai,module){module.doc()="Module for __cuda_array_interface__";caiexcp::register_custom_cuda_array_interface_exceptions(module);module.def("receive_and_return_cuda_array_interface_float",&receive_and_return_cuda_array_interface<float>,"Function accepts a cuda_array_t and immediately just sends it back",py::arg("s"));module.def("return_cuda_array_interface",&return_cuda_array_interface<float>,"Constructs a cuda_array_t in C++ and returns it to Python",py::arg_v("shape",std::vector<size_t>({3,4,5}),"std::vector<size_t>({3, 4, 5})"),py::arg("readonly")=false,py::arg("version")=3);}From python you can now use these functions to send and receive arrays implementing the__cuda_array_interface__defined by Numba.Features and thecuda_array_t<T>classCustom exceptions:The plugin defines a number of internal exceptions and a utility function is provided for exporting them to Python viacaiexcp::register_custom_cuda_array_interface_exceptions(module);. For a complete list please refer to the source codehere.cuda_array_t<T>:The following public utilities are provided bycuda_array_t<T>:cuda_array_t(std::vector<size_t>shape,constboolreadonly=false,constintversion=3):shape(std::move(shape)),readonly(readonly),version(version), whereshape,readonlyandversioncorrespond to their counterparts of the__cuda_array_interface__.conststd::vector<size_t>&get_shape()const, get method to return the shape of the array stored as anstd::vector.py::dtypeget_dtype()const, get method to return thedtypecorresponding to the provided typeT.boolis_readonly()const, get method to  return the boolean state of thereadonlyvariable of__cuda_array_interface__.intget_version()const, get method to return the version of__cuda_array_interface__.size_tsize_of_shape()const, get method to return the total number of elements in the pointed to cuda_array.T*get_compatible_typed_pointer()constT*get_compatible_typed_pointer()const, get method to return a raw pointer of typeTasconstornon-constdepending on the value ofreadonly.Return type:The__cuda_array_interface__is represented by thecuda_array_t<T>class in C++, but the integrity of the Python array implementing the__cuda_array_interface__is respected when said object is returned to Python from C++. I.e. objects sent from Python to C++ and at a later stage returned to Python as acuda_array_t<T>, will still return the actual Python object sent in the first place. However, should acuda_array_t<T>instance be defined or originating from C++ it will be returned to Python as atypes.SimpleNamespaceimplementing the__cuda_array_interface__and apy::capsuleobject to handle the ownership transfer.Using aCuPyarray the following wrapper class might prove useful:fromtypingimportAny,ProtocolimportcupyascpclassArrayCapsuleWrapperProtocol(Protocol):def__init__(self,array:cp.ndarray,capsule:Any)->None:...@propertydefarray(self)->cp.ndarray:...@propertydefcapsule(self)->Any:# expected to be py::capsule from pybind11...defcupy_asarray_with_capsule(obj:Any)->ArrayCapsuleWrapperProtocol:# Check if the object has both __cuda_array_interface__ and _capsule attributesifnot(hasattr(obj,"__cuda_array_interface__")andhasattr(obj,"_capsule")):raiseValueError("Input object must have both __cuda_array_interface__ and _capsule attributes.")# Convert the object to a CuPy arrayarray=cp.asarray(obj)classArrayCapsuleWrapper:def__init__(self,array:cp.ndarray,capsule:Any)->None:self._array=arrayself._capsule=capsule@propertydefarray(self)->cp.ndarray:returnself._array@propertydefcapsule(self)->Any:# expected to be py::capsule from pybind11returnself._capsule# Create a wrapper to hold the array and capsulewrapper=ArrayCapsuleWrapper(array,obj._capsule)returnwrapperInstallationsDepending on your preferred choice the package can be installed in several ways given that three main dependencies are metpybind11>=2.11.1Python>=3.8CUDA Toolkit>=10.2Install from PyPIpipinstallpybind11-cuda-array-interfacewhich will install the header files such thatCMakecan find them.Install from sourcegitclonegit@github.com:mikaeltw/pybind11_cuda_array_interface.gitorgitclonehttps://github.com/mikaeltw/pybind11_cuda_array_interface.gitThenpython-mpipinstall.or (Provided that you have CMake >=3.18 installed)mkdirbuildcdbuild
cmake..
makeinstallInstall by copying headersYou can also copy the headers manually frominclude/pybind11_cuda_array_interface/*.Linting and testsPlease note that the tests require a functional NVIDIA GPU of at least the Pascal generation.Install dependencies and compile tests:pythontests/install_cupy.py
pythonlinting/install_linters.pyBUILD_GTESTS=ONBUILD_PYTESTS=ONpython-mpipinstall.[test]Run tests:python-mpytestandtests/gtest/run_gtest_caiRun Python linting:pythonlinting/check_python_linting.pyClang-tidy & Clang-format:No utility command provided but implemented in.github/workflows/linting.yaml.ContributingIssues and PRs are welcomed. If opening a PR, please do so from a fork of the repository.LicenseThis project is licensed under the BSD-3-Clause License. See theLICENSEfile for details.