mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +00:00
Python buffer objects can have negative strides.
This commit is contained in:
parent
2b941b38b4
commit
d400f60c96
@ -41,8 +41,8 @@ completely avoid copy operations with Python expressions like
|
|||||||
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
|
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
|
||||||
2, /* Number of dimensions */
|
2, /* Number of dimensions */
|
||||||
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
||||||
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
|
{ (ssize_t)( sizeof(float) * m.rows() ),/* Strides (in bytes) for each index */
|
||||||
sizeof(float) }
|
(ssize_t)( sizeof(float) ) }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ specification.
|
|||||||
std::string format;
|
std::string format;
|
||||||
int ndim;
|
int ndim;
|
||||||
std::vector<size_t> shape;
|
std::vector<size_t> shape;
|
||||||
std::vector<size_t> strides;
|
std::vector<ssize_t> strides;
|
||||||
};
|
};
|
||||||
|
|
||||||
To create a C++ function that can take a Python buffer object as an argument,
|
To create a C++ function that can take a Python buffer object as an argument,
|
||||||
@ -121,8 +121,8 @@ as follows:
|
|||||||
{ (size_t) m.rows(),
|
{ (size_t) m.rows(),
|
||||||
(size_t) m.cols() },
|
(size_t) m.cols() },
|
||||||
/* Strides (in bytes) for each index */
|
/* Strides (in bytes) for each index */
|
||||||
{ sizeof(Scalar) * (rowMajor ? m.cols() : 1),
|
{ (ssize_t)( sizeof(Scalar) * (rowMajor ? m.cols() : 1) ),
|
||||||
sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
|
(ssize_t)( sizeof(Scalar) * (rowMajor ? 1 : m.rows()) ) }
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -265,14 +265,14 @@ protected:
|
|||||||
const unsigned char *data_;
|
const unsigned char *data_;
|
||||||
// Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
|
// Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
|
||||||
// make large performance gains on big, nested loops, but requires compile-time dimensions
|
// make large performance gains on big, nested loops, but requires compile-time dimensions
|
||||||
conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>>
|
conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>> shape_;
|
||||||
shape_, strides_;
|
conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>> strides_;
|
||||||
const size_t dims_;
|
const size_t dims_;
|
||||||
|
|
||||||
friend class pybind11::array;
|
friend class pybind11::array;
|
||||||
// Constructor for compile-time dimensions:
|
// Constructor for compile-time dimensions:
|
||||||
template <bool Dyn = Dynamic>
|
template <bool Dyn = Dynamic>
|
||||||
unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t<!Dyn, size_t>)
|
unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<!Dyn, size_t>)
|
||||||
: data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
|
: data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
|
||||||
for (size_t i = 0; i < dims_; i++) {
|
for (size_t i = 0; i < dims_; i++) {
|
||||||
shape_[i] = shape[i];
|
shape_[i] = shape[i];
|
||||||
@ -281,7 +281,7 @@ protected:
|
|||||||
}
|
}
|
||||||
// Constructor for runtime dimensions:
|
// Constructor for runtime dimensions:
|
||||||
template <bool Dyn = Dynamic>
|
template <bool Dyn = Dynamic>
|
||||||
unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t<Dyn, size_t> dims)
|
unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<Dyn, size_t> dims)
|
||||||
: data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides}, dims_{dims} {}
|
: data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides}, dims_{dims} {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -573,12 +573,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Strides of the array
|
/// Strides of the array
|
||||||
const size_t* strides() const {
|
const ssize_t* strides() const {
|
||||||
return reinterpret_cast<const size_t *>(detail::array_proxy(m_ptr)->strides);
|
return reinterpret_cast<const ssize_t *>(detail::array_proxy(m_ptr)->strides);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stride along a given axis
|
/// Stride along a given axis
|
||||||
size_t strides(size_t dim) const {
|
ssize_t strides(size_t dim) const {
|
||||||
if (dim >= ndim())
|
if (dim >= ndim())
|
||||||
fail_dim_check(dim, "invalid axis");
|
fail_dim_check(dim, "invalid axis");
|
||||||
return strides()[dim];
|
return strides()[dim];
|
||||||
@ -702,9 +702,9 @@ protected:
|
|||||||
throw std::domain_error("array is not writeable");
|
throw std::domain_error("array is not writeable");
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<Py_intptr_t> default_strides(const std::vector<Py_intptr_t>& shape, size_t itemsize) {
|
static std::vector<ssize_t> default_strides(const std::vector<size_t>& shape, size_t itemsize) {
|
||||||
auto ndim = shape.size();
|
auto ndim = shape.size();
|
||||||
std::vector<Py_intptr_t> strides(ndim);
|
std::vector<ssize_t> strides(ndim);
|
||||||
if (ndim) {
|
if (ndim) {
|
||||||
std::fill(strides.begin(), strides.end(), itemsize);
|
std::fill(strides.begin(), strides.end(), itemsize);
|
||||||
for (size_t i = 0; i < ndim - 1; i++)
|
for (size_t i = 0; i < ndim - 1; i++)
|
||||||
@ -1133,7 +1133,7 @@ array_iterator<T> array_end(const buffer_info& buffer) {
|
|||||||
|
|
||||||
class common_iterator {
|
class common_iterator {
|
||||||
public:
|
public:
|
||||||
using container_type = std::vector<size_t>;
|
using container_type = std::vector<ssize_t>;
|
||||||
using value_type = container_type::value_type;
|
using value_type = container_type::value_type;
|
||||||
using size_type = container_type::size_type;
|
using size_type = container_type::size_type;
|
||||||
|
|
||||||
@ -1175,7 +1175,7 @@ public:
|
|||||||
for (size_t i = 0; i < shape.size(); ++i)
|
for (size_t i = 0; i < shape.size(); ++i)
|
||||||
m_shape[i] = static_cast<container_type::value_type>(shape[i]);
|
m_shape[i] = static_cast<container_type::value_type>(shape[i]);
|
||||||
|
|
||||||
container_type strides(shape.size());
|
std::vector<ssize_t> strides(shape.size());
|
||||||
for (size_t i = 0; i < N; ++i)
|
for (size_t i = 0; i < N; ++i)
|
||||||
init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
|
init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
|
||||||
}
|
}
|
||||||
@ -1203,7 +1203,7 @@ private:
|
|||||||
|
|
||||||
void init_common_iterator(const buffer_info &buffer,
|
void init_common_iterator(const buffer_info &buffer,
|
||||||
const std::vector<size_t> &shape,
|
const std::vector<size_t> &shape,
|
||||||
common_iter &iterator, container_type &strides) {
|
common_iter &iterator, std::vector<ssize_t> &strides) {
|
||||||
auto buffer_shape_iter = buffer.shape.rbegin();
|
auto buffer_shape_iter = buffer.shape.rbegin();
|
||||||
auto buffer_strides_iter = buffer.strides.rbegin();
|
auto buffer_strides_iter = buffer.strides.rbegin();
|
||||||
auto shape_iter = shape.rbegin();
|
auto shape_iter = shape.rbegin();
|
||||||
@ -1211,7 +1211,7 @@ private:
|
|||||||
|
|
||||||
while (buffer_shape_iter != buffer.shape.rend()) {
|
while (buffer_shape_iter != buffer.shape.rend()) {
|
||||||
if (*shape_iter == *buffer_shape_iter)
|
if (*shape_iter == *buffer_shape_iter)
|
||||||
*strides_iter = static_cast<size_t>(*buffer_strides_iter);
|
*strides_iter = *buffer_strides_iter;
|
||||||
else
|
else
|
||||||
*strides_iter = 0;
|
*strides_iter = 0;
|
||||||
|
|
||||||
@ -1283,10 +1283,11 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
|
|||||||
|
|
||||||
// Check for C contiguity (but only if previous inputs were also C contiguous)
|
// Check for C contiguity (but only if previous inputs were also C contiguous)
|
||||||
if (trivial_broadcast_c) {
|
if (trivial_broadcast_c) {
|
||||||
size_t expect_stride = buffers[i].itemsize;
|
ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize);
|
||||||
auto end = buffers[i].shape.crend();
|
auto end = buffers[i].shape.crend();
|
||||||
for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin();
|
auto shape_iter = buffers[i].shape.crbegin();
|
||||||
trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
|
auto stride_iter = buffers[i].strides.crbegin();
|
||||||
|
for (; trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
|
||||||
if (expect_stride == *stride_iter)
|
if (expect_stride == *stride_iter)
|
||||||
expect_stride *= *shape_iter;
|
expect_stride *= *shape_iter;
|
||||||
else
|
else
|
||||||
@ -1296,10 +1297,11 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
|
|||||||
|
|
||||||
// Check for Fortran contiguity (if previous inputs were also F contiguous)
|
// Check for Fortran contiguity (if previous inputs were also F contiguous)
|
||||||
if (trivial_broadcast_f) {
|
if (trivial_broadcast_f) {
|
||||||
size_t expect_stride = buffers[i].itemsize;
|
ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize);
|
||||||
auto end = buffers[i].shape.cend();
|
auto end = buffers[i].shape.cend();
|
||||||
for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin();
|
auto shape_iter = buffers[i].shape.cbegin();
|
||||||
trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
|
auto stride_iter = buffers[i].strides.cbegin();
|
||||||
|
for (; trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
|
||||||
if (expect_stride == *stride_iter)
|
if (expect_stride == *stride_iter)
|
||||||
expect_stride *= *shape_iter;
|
expect_stride *= *shape_iter;
|
||||||
else
|
else
|
||||||
@ -1336,20 +1338,20 @@ struct vectorize_helper {
|
|||||||
auto trivial = broadcast(buffers, ndim, shape);
|
auto trivial = broadcast(buffers, ndim, shape);
|
||||||
|
|
||||||
size_t size = 1;
|
size_t size = 1;
|
||||||
std::vector<size_t> strides(ndim);
|
std::vector<ssize_t> strides(ndim);
|
||||||
if (ndim > 0) {
|
if (ndim > 0) {
|
||||||
if (trivial == broadcast_trivial::f_trivial) {
|
if (trivial == broadcast_trivial::f_trivial) {
|
||||||
strides[0] = sizeof(Return);
|
strides[0] = static_cast<ssize_t>(sizeof(Return));
|
||||||
for (size_t i = 1; i < ndim; ++i) {
|
for (size_t i = 1; i < ndim; ++i) {
|
||||||
strides[i] = strides[i - 1] * shape[i - 1];
|
strides[i] = strides[i - 1] * static_cast<ssize_t>(shape[i - 1]);
|
||||||
size *= shape[i - 1];
|
size *= shape[i - 1];
|
||||||
}
|
}
|
||||||
size *= shape[ndim - 1];
|
size *= shape[ndim - 1];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
strides[ndim-1] = sizeof(Return);
|
strides[ndim-1] = static_cast<ssize_t>(sizeof(Return));
|
||||||
for (size_t i = ndim - 1; i > 0; --i) {
|
for (size_t i = ndim - 1; i > 0; --i) {
|
||||||
strides[i - 1] = strides[i] * shape[i];
|
strides[i - 1] = strides[i] * static_cast<ssize_t>(shape[i]);
|
||||||
size *= shape[i];
|
size *= shape[i];
|
||||||
}
|
}
|
||||||
size *= shape[0];
|
size *= shape[0];
|
||||||
|
@ -109,8 +109,8 @@ test_initializer buffers([](py::module &m) {
|
|||||||
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
|
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
|
||||||
2, /* Number of dimensions */
|
2, /* Number of dimensions */
|
||||||
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
{ m.rows(), m.cols() }, /* Buffer dimensions */
|
||||||
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
|
{ static_cast<ssize_t>(sizeof(float) * m.rows()), /* Strides (in bytes) for each index */
|
||||||
sizeof(float) }
|
static_cast<ssize_t>(sizeof(float)) }
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <pybind11/stl.h>
|
#include <pybind11/stl.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using arr = py::array;
|
using arr = py::array;
|
||||||
using arr_t = py::array_t<uint16_t, 0>;
|
using arr_t = py::array_t<uint16_t, 0>;
|
||||||
|
@ -203,6 +203,10 @@ def test_wrap():
|
|||||||
a2 = wrap(a1d)
|
a2 = wrap(a1d)
|
||||||
assert_references(a1d, a2, a1)
|
assert_references(a1d, a2, a1)
|
||||||
|
|
||||||
|
a1m = a1[::-1, ::-1, ::-1]
|
||||||
|
a2 = wrap(a1m)
|
||||||
|
assert_references(a1m, a2, a1)
|
||||||
|
|
||||||
|
|
||||||
def test_numpy_view(capture):
|
def test_numpy_view(capture):
|
||||||
from pybind11_tests.array import ArrayClass
|
from pybind11_tests.array import ArrayClass
|
||||||
|
@ -226,7 +226,7 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
|
|||||||
|
|
||||||
std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
|
std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
|
||||||
std::vector<size_t> shape { 3, 2 };
|
std::vector<size_t> shape { 3, 2 };
|
||||||
std::vector<size_t> strides { 8, 4 };
|
std::vector<ssize_t> strides { 8, 4 };
|
||||||
|
|
||||||
auto ptr = data.data();
|
auto ptr = data.data();
|
||||||
auto vptr = (void *) ptr;
|
auto vptr = (void *) ptr;
|
||||||
|
Loading…
Reference in New Issue
Block a user