Visitar URL original
Add support for nanobind? · Issue #284 · xtensor-stack/xtensor-python · GitHub
Skip to content

Add support for nanobind? #284

Description

@benbovy

After a quick inspection of xtensor-python's internals, it looks like it shouldn't be too hard supporting both pybind11 and nanobind via some sort of minimal compatibility layer? Both have a pretty similar API for the basic features, and unless I'm missing something xtensor-python doesn't seem to depend much on pybind11's numpy and/or other advanced features?

Nanobind is more performant than pybind11 and also offers more control via its low-level interface, which would make things much easier for my use case (*). However, nanobind's numpy support is currently limited, e.g., it doesn't provide a vectorize helper (not sure it will anytime soon?) which is something that I also need. Xtensor-python would nicely fill this gap I guess.

(*) More context: I'm working on a Python/Numpy library (https://github.com/benbovy/s2shapely) providing bindings for the s2geometry / s2geography libraries, via vectorized functions (ufuncs) operating on Geography objects (C++ wrapped classes) referenced in numpy arrays with the numpy.object dtype.

Activity

  1. tdegeus commented on Mar 2, 2023

    @tdegeus
    Member

    I believe that currently this is the blocker : xtensor-stack/xtensor#2366 . It would be amazing if we could get that out of the way. Any help is greatly appreciated!

  2. tdegeus commented on Mar 24, 2023

    @tdegeus
    Member

    @benbovy I've been using C++17 fine. I would be very much open to support this. Have you had time to experiment?

  3. benbovy commented on Mar 24, 2023

    @benbovy
    Author

    @tdegeus good to know C++17 works well. I haven't had the time yet to experiment with this, unfortunately.

  4. tdegeus commented on Mar 24, 2023

    @tdegeus
    Member

    Understood. Open to a contribution if you are up to it (I guess that ideas could be borrowed from https://github.com/wjakob/nanobind/blob/master/include/nanobind/eigen/dense.h ). Otherwise it will come when one of us finds the time or sufficient urgency

  5. tdegeus commented on Mar 30, 2023

    @tdegeus
    Member

    Just for the record: I did some experimenting with nanobind's ndarray which should be very similar to what we want, and things like allocation and function calls are indeed way faster (at least a factor two in what I experimented with), and compilation much much faster. I hope to get some time soon to work on this (and I hope even more that someone beats me to it ;))!

  6. Roy-Kid commented on Jun 19, 2024

    @Roy-Kid
    Contributor

    Some off-topic discussion: Is it possible to provide ctypes support? Since numpy has ctypeslib, I believe it should be very easy to provide a wrapper? And according to many benchmarks, ctypes performs better than pybind: taichi-dev/taichi#4830

  7. SebastianThiede commented on Jul 22, 2024

    @SebastianThiede

    Just for the record: I did some experimenting with nanobind's ndarray which should be very similar to what we want, and things like allocation and function calls are indeed way faster (at least a factor two in what I experimented with), and compilation much much faster. I hope to get some time soon to work on this (and I hope even more that someone beats me to it ;))!

    I did a bit on this for my work. Didn't test for xarray, only xtensor and xtensor_fixed. Maybe a starting point for you?
    SebastianThiede/nanobind_xtensor

    Some off-topic discussion: Is it possible to provide ctypes support? Since numpy has ctypeslib, I believe it should be very easy to provide a wrapper? And according to many benchmarks, ctypes performs better than pybind: taichi-dev/taichi#4830

    Btw. Nanobind can also be very fast compared to pybind11.

  8. Roy-Kid commented on Jul 22, 2024

    @Roy-Kid
    Contributor

    Just for the record: I did some experimenting with nanobind's ndarray which should be very similar to what we want, and things like allocation and function calls are indeed way faster (at least a factor two in what I experimented with), and compilation much much faster. I hope to get some time soon to work on this (and I hope even more that someone beats me to it ;))!

    I did a bit on this for my work. Didn't test for xarray, only xtensor and xtensor_fixed. Maybe a starting point for you? SebastianThiede/nanobind_xtensor

    Some off-topic discussion: Is it possible to provide ctypes support? Since numpy has ctypeslib, I believe it should be very easy to provide a wrapper? And according to many benchmarks, ctypes performs better than pybind: taichi-dev/taichi#4830

    Btw. Nanobind can also be very fast compared to pybind11.

    https://github.com/yanto77/cpp-python-binding-benchmark This is another benchmark I found recently

  9. xerpi commented on Dec 5, 2024

    @xerpi

    Any updates on this?
    Currently, I'm using https://github.com/SebastianThiede/nanobind_xtensor, but native nanobind support would be greatly appreciated!

  10. Roy-Kid commented on Apr 29, 2025

    @Roy-Kid
    Contributor

    Hi, is anyone still working on this?
    If not, I'd like to give it a try!
    I can start by mimicking the pybind11 implementation approach.

  11. peter-urban commented on Sep 22, 2025

    @peter-urban
    Contributor

    @Roy-Kid I am not associated with this project, but I'd greatly appreciate if you'd give it a try :-)
    Nanobind support would be awesome!

  12. peter-urban commented on Oct 8, 2025

    @peter-urban
    Contributor

    I vibe-coded a pytensor class (no pyarray yet) that enables xtensor-based in-place operations on NumPy arrays:
    https://github.com/peter-urban/xtensor_nanobind) Requires C++20 (tested with GCC, Clang, clang-cl; no MSVC yet).

    #include "pytensor_nanobind.hpp"
    ...
    m.def("double_inplace", [](xt::nanobind::pytensor<double, 2>& arr) {
        arr *= 2.0;
    });

    It’s still a bit of an AI-generated mess (possibly buggy and possibly incomplete), but works for my use cases at similar speed to the original pytensor.

    Anyways, it is a proof of concept that might already fit your project.

    I’d be willing to make it more useful or integrate it here, but I’m unsure how — the sollution is quite different from the original and e.g. uses xtensor_adaptor as base and does not deriv from nb::object.
    My attempts to bring it closer to the original structure ended in segfaults that i could not resolve. Maybe someone who is more familiar with xtensor/pybind/nanobind internals could achieve this though.

    Anybody here interested in giving some lead/guidance on how to proceed from here?

  13. JohanMabille commented on Oct 8, 2025

    @JohanMabille
    Member

    @peter-urban thanks for working on this. I think it would be easier to start from the existing code and adapt it to nanobind rather than starting from scratch (that would probably make the review process easier). xtensor_adator is not meant to be a base class (I don't remember why we did not make it final); the issue is that it ill be passed as a reference to one of its CRTP base class at smoe point, and downcasting it will give an xtenosr_adaptor, and not your inheriting class. That breaks the assignment mechanism.

    If you don't need to inherit from py::object, you can keep the inheritance from pycontainer, and remove the inheritance form py::object in this class.

    I'm not familiar to nanobind at all, so I don't know what would be required to get it work. But the changes should at least pass the test suite of xtensor-python.

  14. peter-urban commented on Oct 8, 2025

    @peter-urban
    Contributor

    @JohanMabille thx for the response and the info on xtensor_adaptor. I'll try to rebase on xcontainer and xcontainer_semantics in the future.

    About deriving from pycontainer: The pycontainer class does not only derive from py::object. It seems that it is, for a large part, written to implement pybind11 specifc numpy handling. While the nanobind interface is made to be similar to pybind overall, specifically the numpy array handling is different.

    Similarily, also pytensor includes pybind11 specific things.

    namespace pybind11
    {
        namespace detail
        {
    #ifdef PYBIND11_DESCR // The macro is removed from pybind11 since 2.3
            template <class T, std::size_t N, xt::layout_type L>
            struct handle_type_name<xt::pytensor<T, N, L>>
            {
                static PYBIND11_DESCR name()
                {
                    return _("numpy.ndarray[") + npy_format_descriptor<T>::name() + _("]");
    

    This tight integration makes it difficult to derive from- or adapt these classes to nanobind. So I guess it would be necessary to untie this a bit.

    About using the existing code and adapting it to nanobind: This was my first approach but I simply did not manage to create a working solution. (My attempts ended in segfaults on module import that i could not resolve). I think adapting this somewhat working solution step by step to fit within the xtensor_python framework is easier then trying to create a working solution within the xtensor_python interface directly. (happy to be disproven if somebody wants to do this)

    I guess a good first step would be to split my file into the same structure (e.g. pycontainer pytensor) and try to mimic/copy as much code from xtensor_python as possible. When this is done it will be easier to understand what needs to be done to merge the approaches.

  15. JohanMabille commented on Oct 13, 2025

    @JohanMabille
    Member

    I guess a good first step would be to split my file into the same structure (e.g. pycontainer pytensor) and try to mimic/copy as much code from xtensor_python as possible.

    Yes, that is what I meant in my first answer. If the handling of numpy array is very different in nanobind, writing a new pycontainer-like file from scratch (rather than trying to modify the existing one) is totally acceptable, but trying to keep a similar structure / architecture would definitely help to review the changes.

    Also, even if the changes must pass the test-suite of xtensor-python in the end, do not hesitate to open an early preview PR so that it is easier to us to comment and follow your work on this. And again, thanks a lot for working on this ;)

  16. peter-urban commented on Feb 26, 2026

    @peter-urban
    Contributor

    I prototAIped a proof of concept for a nanobind addition that works well for my project (tests are passing, performance is comparable as far as I can see it).
    https://github.com/peter-urban/xtensor-python/tree/xtensor-nanobind

    The idea is convert this into digestible commits over the coming weeks or months and clean the code up while doing this.
    The reason is that my solution currently requires significant changes to the project structure that need correction or adoption step by step.

    My first question is the main mode of integration/separation.
    Pybind11 and nanobind integrate quite deeply in the main classes (e.g. pycontainer, pytensor, pyarray).
    My implementation separates the pybind11 and the nanobind implementations in different subfolders with seperate namespaces. (xt::pybind11::pytensor and xt::nanobind::pytensor).

    Shared implementations/interface definitions (e.g. for slicing) would then go into the 'detail' folder/namespace.
    As a note on this: The nanobind implementation is currently mostly independend on purpose. This simplifies moving around files and namespaces. Once the final structure is clear and implemented we can work towards more code sharing between the implementations.

    @JohanMabille It would be nice to get some comments on the general approach and the suggested namespace structure. If you are happy, I could work on a pull request that moves the current implementation into a separate pybind11 namespace. For backwards compatibility we could probably create an alias xt::pytensor = xt::pybind11::pytensor

  17. keltecc commented on Mar 19, 2026

    @keltecc

    hey @peter-urban, I'm currently working on xtensor wrapper based on nanobind (as a course project at university), I'm trying to use more idiomatic and nanobind-specific approach than just translating the xtensor-python code. I will release the binding code in a several weeks

  18. peter-urban commented on Mar 19, 2026

    @peter-urban
    Contributor

    @keltecc cool! Thanks for letting me know. Some questions:

    • Will your bindings become part of xtensor-python or do you plan it as seperate project?
    • My current AI version uses nanobinds DLPack interface. Because of this, does not support arrays with python objects (only numeric dtypes). Is this something you are planning to work around?

    Anyways happy to hear more about your work, let me know if you could do with some help testing performance etc.

  19. keltecc commented on Mar 20, 2026

    @keltecc

    Will your bindings become part of xtensor-python or do you plan it as seperate project?

    I plan to release it as a part of nanobind project, the same way as Eigen bindings (include/nanobind/eigen). Hope that nanobind maintainer will accept the PR.

    My current AI version uses nanobinds DLPack interface. Because of this, does not support arrays with python objects (only numeric dtypes). Is this something you are planning to work around?

    Currently I'm working on the support of primitive types (ints, floats, etc) that are natively supported by nanobind. It seems that DLPack supports several other types, for example std::complex. I will also try to add a support for general python objects, but I didn't explored that direction yet.

    Anyways happy to hear more about your work, let me know if you could do with some help testing performance etc.

    Thank you! I've planned to do some performance testing after the development stage, will compare the implementation with the existing xtensor-python bindings.

  20. Roy-Kid commented on Apr 4, 2026

    @Roy-Kid
    Contributor

    Hi, I asked this question several month ago. I think best way is to hold it under xtensor org instead of part of nanobind. wjakob/nanobind#1046. If @keltecc can publish the code we can work together. @JohanMabille How do you think?

  21. keltecc commented on Apr 15, 2026

    @keltecc

    @peter-urban @Roy-Kid I've published the casters

    wjakob/nanobind#1328

    I will try to make a PR in nanobind, hope wjakob will accept it. now xtensor casters have ~330 lines without tests, for example the existing Eigen casters have ~745 lines (and xtensor-python has ~2032 lines), so the new codebase is relatively small

    there's also a some benchmark repository that measures the implementation

    https://github.com/keltecc/xtensor-nanobind-benchmark

  22. peter-urban commented on Apr 15, 2026

    @peter-urban
    Contributor

    @keltecc, nice work! I created a benchmark against my ai implementation.
    On my machine your implementation is ~5% faster than mine (and of cause much cleaner).
    Whether your PR is accepted or whether it becomes it's own repo, you can be sure I'll switch to your version eventually :-)

  23. JohanMabille commented on Apr 17, 2026

    @JohanMabille
    Member

    Hi there,
    Apologies for the very late reply. It seems that Wenzel changed his mind and would be happy to support these bindings in nanobind itself. However, if the C++20 requirements becomes a blocker for future versions of xtensor, I'm happy to create a new repo and have them uder the xtensor org.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions