Disallow copy with functions taking PyBind11 arrays

Viewed 97

I have created a function which takes a py::array_t<double> as input.

When calling it from Python, I can pass as input a NumPy array with either float32 or float64 dtype. My issue with this is that if the former dtype is used, it will inevitably incur a "silent" copy of the array in order to coerce it to float64.

I would like to disable all such silent copies (e.g., produce an error if a float32 array is passed).

For the same reason, I would also like to disallow inputs that are not C-contiguous since my function assumes C-contiguity.

Are any of these things possible to do (easily) with PyBind11?

1 Answers

The best that I have found is taking as an argument not

py::array_t<double> &my_object  

but instead

py::buffer  & my_object  

which is more general

Then

py::buffer_info buffer_info  = my_object.request();
if( buffer_info.format[0] != 'd') {
   throw std::invalid_argument("The argument  must be an array of float64");
}

the buffer structure contains ndim, shape, strides, itemsize and more. With that you can check if the array is contiguous, c_style, f_style. The data itself is member void *ptr.

To check contiguity

bool check_c_contiguity(py::buffer_info &info) {
    int ndim = info.ndim;
    size_t stride = 0;
    size_t prod=1;
    for(int i=ndim-1; i>=0; i--) {

      stride =  prod * info.itemsize ;

      prod = prod * info.shape[i];
      
      if ( stride != info.strides[i] ) {
    return false;
      }
    }
    return true;  
  }
  bool check_f_contiguity(py::buffer_info &info) {
    int ndim = info.ndim;
    size_t stride = 0;
    size_t prod=1;
    for(int i=0; i<ndim; i++) {

      stride = prod * info.itemsize;
      prod = prod * info.shape[i];

      if ( stride != info.strides[i] ) {
    return false;
      }
    }
    return true;  
  }
Related