pybind / pybind11

Seamless operability between C++11 and Python

Home Page:https://pybind11.readthedocs.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[BUG]: How to create a class that has access to one or more Numpy arrays?

goofy2k opened this issue · comments

Required prerequisites

What version (or hash if on master) of pybind11 are you using?

2.11.1 (latest)

Problem description

I want to create a C++ class that can use a Numpy array. So I started to read the docs on using object-oriented code and Numpy arrays. I tested the examples in these sections and they behave well.

My own code for a class MC_box_1 is based on the struct Pet example (pybind11 documentation > Object-oriented code) and the modify_array example (pybind11 documentation > Python C++ interface >> Numpy). Both examples behave OK but when I combine them in a class MC_box_1 I get an error that is similar to the one described as in issue #2274 and have no clue how to solve this. I hope that someone can be of help on this.

My next step would be to insert the array already in the constructor.

My code:

Reproducible example code

// C++
// add a Numpy attribute (temp use setter)
class  MC_box_1 {
public:

    MC_box_1(const std::string& name) : name(name) { }
    void setName(const std::string& name_) { name = name_; }
    const std::string& getName() const { return name; }

    // preferably set input_array in constructor, first test with setArray
    void setArray(py::array_t<double> input_array_) {
        input_array = input_array_;
        py::buffer_info buf_info = input_array.request();
        double* ptr = static_cast<double*>(buf_info.ptr);
    }
    std::string name;
    py::array_t<double> input_array;
};
// binding code
    // add a Numpy array, later use it via the constructor
    py::class_<MC_box_1>(m, "MC_box_1")
        .def(py::init<const std::string&>())
        .def("setName", &MC_box_1::setName)
        .def("getName", &MC_box_1::getName)
        .def("setArray", &MC_box_1::setArray)
        //__repr__ method to print the object
        .def("__repr__",                           // Binding lambda functions
            [](const MC_box_1& a) {                // uses a "stateless" lambda closure [] (vs stateful)
                return "<MCrun_pybind_0.MC_box_1 named '" + a.name + " '>";
            });
//Python
    # Object oriented code
    p = MCrun_pybind_0.Pet("Molly")
    print(p)
    print(p.getName())
    p.setName("Charly")
    print(p.getName())                              # behaves OK
    
    mybox_0 = MCrun_pybind_0.MC_box_0("simple box") # based on pybind11 docs, struct Pet example
    print("print(mybox_0)",mybox_0)
    

    # first test Numpy array as such 
    arr = np.array([1.0, 2.0, 3.0, 4.0])
    print("type(arr) ",type(arr))          
    MCrun_pybind_0.modify_array(arr)                # taken from pybind11 docs
    print(arr)                                      # Expected output: [2.0, 4.0, 6.0, 8.0]   OK  
    
    arr_1 = np.array([1.0, 2.0, 3.0, 4.0])
    print("type(arr_1) ",type(arr_1))
    mybox_1 = MCrun_pybind_0.MC_box_1("box including Numpy array") # based on struct Pet (now class) and modify_array examples                    
    print("print(mybox_1)",mybox_1)                 # behaves OK
    
    MCrun_pybind_0.MC_box_1.setArray(arr_1)         # ERROR !

Although I use the same array and calling code as in the modify_array example I get an error on using the setArray method:

TypeError: setArray(): incompatible function arguments. The following argument types are supported:
1. (self: MCrun_pybind_0.MC_box1, arg0: numpy.ndarray[numpy.float64]) -> None

Invoked with: array([1., 2., 3., 4.])


### Is this a regression? Put the last known working version here if it is.

Not a regression

Excuse me: the modify_array example was NOT taken from the pybind 11 documentation. I took it from "Pybind11: Numpy Compatible Arrays" on scicoding.org

In attempts to let the array type match the supported argument type, I tried to change the array dtype at creation time of the array. Without succes.

                    # arr_1 = np.array([1.0, 2.0, 3.0, 4.0], dtype='d')                    
                    # arr_1 = np.array([1.0, 2.0, 3.0, 4.0], np.longdouble) #strange: now the "invoked with" type is exactly what is reported to be the supported type, but still throws an error
                    # arr_1 = np.array([1.0, 2.0, 3.0, 4.0], np.float64)

                    # as alternatives for
                    arr_1 = np.array([1.0, 2.0, 3.0, 4.0])

I first started a Discussion item (#5036) on this with the same title. As I come across new insights in trying to work around this, I add those in the Discussion.

In the end I managed to get things working.
This info helped me.