summaryrefslogtreecommitdiff
path: root/ext/pybind11/include/pybind11/numpy.h
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pybind11/include/pybind11/numpy.h')
-rw-r--r--ext/pybind11/include/pybind11/numpy.h635
1 files changed, 421 insertions, 214 deletions
diff --git a/ext/pybind11/include/pybind11/numpy.h b/ext/pybind11/include/pybind11/numpy.h
index 3227a12eb..55bb81698 100644
--- a/ext/pybind11/include/pybind11/numpy.h
+++ b/ext/pybind11/include/pybind11/numpy.h
@@ -29,12 +29,12 @@
#endif
/* This will be true on all flat address space platforms and allows us to reduce the
- whole npy_intp / size_t / Py_intptr_t business down to just size_t for all size
+ whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
and dimension types (e.g. shape, strides, indexing), instead of inflicting this
upon the library user. */
-static_assert(sizeof(size_t) == sizeof(Py_intptr_t), "size_t != Py_intptr_t");
+static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t");
-NAMESPACE_BEGIN(pybind11)
+NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
class array; // Forward declaration
@@ -129,6 +129,11 @@ struct npy_api {
NPY_STRING_, NPY_UNICODE_, NPY_VOID_
};
+ typedef struct {
+ Py_intptr_t *ptr;
+ int len;
+ } PyArray_Dims;
+
static npy_api& get() {
static npy_api api = lookup();
return api;
@@ -141,11 +146,13 @@ struct npy_api {
return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_);
}
+ unsigned int (*PyArray_GetNDArrayCFeatureVersion_)();
PyObject *(*PyArray_DescrFromType_)(int);
PyObject *(*PyArray_NewFromDescr_)
(PyTypeObject *, PyObject *, int, Py_intptr_t *,
Py_intptr_t *, void *, int, PyObject *);
PyObject *(*PyArray_DescrNewFromType_)(int);
+ int (*PyArray_CopyInto_)(PyObject *, PyObject *);
PyObject *(*PyArray_NewCopy_)(PyObject *, int);
PyTypeObject *PyArray_Type_;
PyTypeObject *PyVoidArrType_Type_;
@@ -158,14 +165,18 @@ struct npy_api {
Py_ssize_t *, PyObject **, PyObject *);
PyObject *(*PyArray_Squeeze_)(PyObject *);
int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
+ PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int);
private:
enum functions {
+ API_PyArray_GetNDArrayCFeatureVersion = 211,
API_PyArray_Type = 2,
API_PyArrayDescr_Type = 3,
API_PyVoidArrType_Type = 39,
API_PyArray_DescrFromType = 45,
API_PyArray_DescrFromScalar = 57,
API_PyArray_FromAny = 69,
+ API_PyArray_Resize = 80,
+ API_PyArray_CopyInto = 82,
API_PyArray_NewCopy = 85,
API_PyArray_NewFromDescr = 94,
API_PyArray_DescrNewFromType = 9,
@@ -186,12 +197,17 @@ private:
#endif
npy_api api;
#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
+ DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
+ if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7)
+ pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0");
DECL_NPY_API(PyArray_Type);
DECL_NPY_API(PyVoidArrType_Type);
DECL_NPY_API(PyArrayDescr_Type);
DECL_NPY_API(PyArray_DescrFromType);
DECL_NPY_API(PyArray_DescrFromScalar);
DECL_NPY_API(PyArray_FromAny);
+ DECL_NPY_API(PyArray_Resize);
+ DECL_NPY_API(PyArray_CopyInto);
DECL_NPY_API(PyArray_NewCopy);
DECL_NPY_API(PyArray_NewFromDescr);
DECL_NPY_API(PyArray_DescrNewFromType);
@@ -230,18 +246,68 @@ template <typename T, size_t N> struct is_std_array<std::array<T, N>> : std::tru
template <typename T> struct is_complex : std::false_type { };
template <typename T> struct is_complex<std::complex<T>> : std::true_type { };
+template <typename T> struct array_info_scalar {
+ typedef T type;
+ static constexpr bool is_array = false;
+ static constexpr bool is_empty = false;
+ static PYBIND11_DESCR extents() { return _(""); }
+ static void append_extents(list& /* shape */) { }
+};
+// Computes underlying type and a comma-separated list of extents for array
+// types (any mix of std::array and built-in arrays). An array of char is
+// treated as scalar because it gets special handling.
+template <typename T> struct array_info : array_info_scalar<T> { };
+template <typename T, size_t N> struct array_info<std::array<T, N>> {
+ using type = typename array_info<T>::type;
+ static constexpr bool is_array = true;
+ static constexpr bool is_empty = (N == 0) || array_info<T>::is_empty;
+ static constexpr size_t extent = N;
+
+ // appends the extents to shape
+ static void append_extents(list& shape) {
+ shape.append(N);
+ array_info<T>::append_extents(shape);
+ }
+
+ template<typename T2 = T, enable_if_t<!array_info<T2>::is_array, int> = 0>
+ static PYBIND11_DESCR extents() {
+ return _<N>();
+ }
+
+ template<typename T2 = T, enable_if_t<array_info<T2>::is_array, int> = 0>
+ static PYBIND11_DESCR extents() {
+ return concat(_<N>(), array_info<T>::extents());
+ }
+};
+// For numpy we have special handling for arrays of characters, so we don't include
+// the size in the array extents.
+template <size_t N> struct array_info<char[N]> : array_info_scalar<char[N]> { };
+template <size_t N> struct array_info<std::array<char, N>> : array_info_scalar<std::array<char, N>> { };
+template <typename T, size_t N> struct array_info<T[N]> : array_info<std::array<T, N>> { };
+template <typename T> using remove_all_extents_t = typename array_info<T>::type;
+
template <typename T> using is_pod_struct = all_of<
- std::is_pod<T>, // since we're accessing directly in memory we need a POD type
+ std::is_standard_layout<T>, // since we're accessing directly in memory we need a standard layout type
+#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI)
+ // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent
+ // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4).
+ std::is_trivially_copyable<T>,
+#else
+ // GCC 4 doesn't implement is_trivially_copyable, so approximate it
+ std::is_trivially_destructible<T>,
+ satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>,
+#endif
satisfies_none_of<T, std::is_reference, std::is_array, is_std_array, std::is_arithmetic, is_complex, std::is_enum>
>;
-template <size_t Dim = 0, typename Strides> size_t byte_offset_unsafe(const Strides &) { return 0; }
-template <size_t Dim = 0, typename Strides, typename... Ix>
-size_t byte_offset_unsafe(const Strides &strides, size_t i, Ix... index) {
+template <ssize_t Dim = 0, typename Strides> ssize_t byte_offset_unsafe(const Strides &) { return 0; }
+template <ssize_t Dim = 0, typename Strides, typename... Ix>
+ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) {
return i * strides[Dim] + byte_offset_unsafe<Dim + 1>(strides, index...);
}
-/** Proxy class providing unsafe, unchecked const access to array data. This is constructed through
+/**
+ * Proxy class providing unsafe, unchecked const access to array data. This is constructed through
* the `unchecked<T, N>()` method of `array` or the `unchecked<N>()` method of `array_t<T>`. `Dims`
* will be -1 for dimensions determined at runtime.
*/
@@ -252,66 +318,68 @@ protected:
const unsigned char *data_;
// 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
- conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>>
- shape_, strides_;
- const size_t dims_;
+ conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>>
+ shape_, strides_;
+ const ssize_t dims_;
friend class pybind11::array;
// Constructor for compile-time dimensions:
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 ssize_t *shape, const ssize_t *strides, enable_if_t<!Dyn, ssize_t>)
: data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
- for (size_t i = 0; i < dims_; i++) {
+ for (size_t i = 0; i < (size_t) dims_; i++) {
shape_[i] = shape[i];
strides_[i] = strides[i];
}
}
// Constructor for runtime dimensions:
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 ssize_t *shape, const ssize_t *strides, enable_if_t<Dyn, ssize_t> dims)
: data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides}, dims_{dims} {}
public:
- /** Unchecked const reference access to data at the given indices. For a compile-time known
+ /**
+ * Unchecked const reference access to data at the given indices. For a compile-time known
* number of dimensions, this requires the correct number of arguments; for run-time
* dimensionality, this is not checked (and so is up to the caller to use safely).
*/
template <typename... Ix> const T &operator()(Ix... index) const {
- static_assert(sizeof...(Ix) == Dims || Dynamic,
+ static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
"Invalid number of indices for unchecked array reference");
- return *reinterpret_cast<const T *>(data_ + byte_offset_unsafe(strides_, size_t(index)...));
+ return *reinterpret_cast<const T *>(data_ + byte_offset_unsafe(strides_, ssize_t(index)...));
}
- /** Unchecked const reference access to data; this operator only participates if the reference
+ /**
+ * Unchecked const reference access to data; this operator only participates if the reference
* is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`.
*/
- template <size_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
- const T &operator[](size_t index) const { return operator()(index); }
+ template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
+ const T &operator[](ssize_t index) const { return operator()(index); }
/// Pointer access to the data at the given indices.
- template <typename... Ix> const T *data(Ix... ix) const { return &operator()(size_t(ix)...); }
+ template <typename... Ix> const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); }
/// Returns the item size, i.e. sizeof(T)
- constexpr static size_t itemsize() { return sizeof(T); }
+ constexpr static ssize_t itemsize() { return sizeof(T); }
/// Returns the shape (i.e. size) of dimension `dim`
- size_t shape(size_t dim) const { return shape_[dim]; }
+ ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; }
/// Returns the number of dimensions of the array
- size_t ndim() const { return dims_; }
+ ssize_t ndim() const { return dims_; }
/// Returns the total number of elements in the referenced array, i.e. the product of the shapes
template <bool Dyn = Dynamic>
- enable_if_t<!Dyn, size_t> size() const {
- return std::accumulate(shape_.begin(), shape_.end(), (size_t) 1, std::multiplies<size_t>());
+ enable_if_t<!Dyn, ssize_t> size() const {
+ return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies<ssize_t>());
}
template <bool Dyn = Dynamic>
- enable_if_t<Dyn, size_t> size() const {
- return std::accumulate(shape_, shape_ + ndim(), (size_t) 1, std::multiplies<size_t>());
+ enable_if_t<Dyn, ssize_t> size() const {
+ return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
}
/// Returns the total number of bytes used by the referenced data. Note that the actual span in
/// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice).
- size_t nbytes() const {
+ ssize_t nbytes() const {
return size() * itemsize();
}
};
@@ -325,26 +393,27 @@ class unchecked_mutable_reference : public unchecked_reference<T, Dims> {
public:
/// Mutable, unchecked access to data at the given indices.
template <typename... Ix> T& operator()(Ix... index) {
- static_assert(sizeof...(Ix) == Dims || Dynamic,
+ static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
"Invalid number of indices for unchecked array reference");
return const_cast<T &>(ConstBase::operator()(index...));
}
- /** Mutable, unchecked access data at the given index; this operator only participates if the
+ /**
+ * Mutable, unchecked access data at the given index; this operator only participates if the
* reference is to a 1-dimensional array (or has runtime dimensions). When present, this is
* exactly equivalent to `obj(index)`.
*/
- template <size_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
- T &operator[](size_t index) { return operator()(index); }
+ template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
+ T &operator[](ssize_t index) { return operator()(index); }
/// Mutable pointer access to the data at the given indices.
- template <typename... Ix> T *mutable_data(Ix... ix) { return &operator()(size_t(ix)...); }
+ template <typename... Ix> T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); }
};
-template <typename T, size_t Dim>
+template <typename T, ssize_t Dim>
struct type_caster<unchecked_reference<T, Dim>> {
static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable");
};
-template <typename T, size_t Dim>
+template <typename T, ssize_t Dim>
struct type_caster<unchecked_mutable_reference<T, Dim>> : type_caster<unchecked_reference<T, Dim>> {};
NAMESPACE_END(detail)
@@ -365,7 +434,7 @@ public:
dtype(const char *format) : dtype(std::string(format)) { }
- dtype(list names, list formats, list offsets, size_t itemsize) {
+ dtype(list names, list formats, list offsets, ssize_t itemsize) {
dict args;
args["names"] = names;
args["formats"] = formats;
@@ -388,8 +457,8 @@ public:
}
/// Size of the data type in bytes.
- size_t itemsize() const {
- return (size_t) detail::array_descriptor_proxy(m_ptr)->elsize;
+ ssize_t itemsize() const {
+ return detail::array_descriptor_proxy(m_ptr)->elsize;
}
/// Returns true for structured data types.
@@ -409,7 +478,7 @@ private:
return reinterpret_borrow<object>(obj);
}
- dtype strip_padding(size_t itemsize) {
+ dtype strip_padding(ssize_t itemsize) {
// Recursively strip all void fields with empty names that are generated for
// padding fields (as of NumPy v1.11).
if (!has_fields())
@@ -453,14 +522,20 @@ public:
forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_
};
- array() : array(0, static_cast<const double *>(nullptr)) {}
+ array() : array({{0}}, static_cast<const double *>(nullptr)) {}
- array(const pybind11::dtype &dt, const std::vector<size_t> &shape,
- const std::vector<size_t> &strides, const void *ptr = nullptr,
- handle base = handle()) {
- auto& api = detail::npy_api::get();
- auto ndim = shape.size();
- if (shape.size() != strides.size())
+ using ShapeContainer = detail::any_container<ssize_t>;
+ using StridesContainer = detail::any_container<ssize_t>;
+
+ // Constructs an array taking shape/strides from arbitrary container types
+ array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides,
+ const void *ptr = nullptr, handle base = handle()) {
+
+ if (strides->empty())
+ *strides = c_strides(*shape, dt.itemsize());
+
+ auto ndim = shape->size();
+ if (ndim != strides->size())
pybind11_fail("NumPy: shape ndim doesn't match strides ndim");
auto descr = dt;
@@ -474,13 +549,12 @@ public:
flags = detail::npy_api::NPY_ARRAY_WRITEABLE_;
}
+ auto &api = detail::npy_api::get();
auto tmp = reinterpret_steal<object>(api.PyArray_NewFromDescr_(
- api.PyArray_Type_, descr.release().ptr(), (int) ndim,
- reinterpret_cast<Py_intptr_t *>(const_cast<size_t*>(shape.data())),
- reinterpret_cast<Py_intptr_t *>(const_cast<size_t*>(strides.data())),
+ api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(),
const_cast<void *>(ptr), flags, nullptr));
if (!tmp)
- pybind11_fail("NumPy: unable to create array!");
+ throw error_already_set();
if (ptr) {
if (base) {
api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr());
@@ -491,27 +565,23 @@ public:
m_ptr = tmp.release().ptr();
}
- array(const pybind11::dtype &dt, const std::vector<size_t> &shape,
- const void *ptr = nullptr, handle base = handle())
- : array(dt, shape, default_strides(shape, dt.itemsize()), ptr, base) { }
+ array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle())
+ : array(dt, std::move(shape), {}, ptr, base) { }
- array(const pybind11::dtype &dt, size_t count, const void *ptr = nullptr,
- handle base = handle())
- : array(dt, std::vector<size_t>{ count }, ptr, base) { }
+ template <typename T, typename = detail::enable_if_t<std::is_integral<T>::value && !std::is_same<bool, T>::value>>
+ array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle())
+ : array(dt, {{count}}, ptr, base) { }
- template<typename T> array(const std::vector<size_t>& shape,
- const std::vector<size_t>& strides,
- const T* ptr, handle base = handle())
- : array(pybind11::dtype::of<T>(), shape, strides, (const void *) ptr, base) { }
+ template <typename T>
+ array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle())
+ : array(pybind11::dtype::of<T>(), std::move(shape), std::move(strides), ptr, base) { }
template <typename T>
- array(const std::vector<size_t> &shape, const T *ptr,
- handle base = handle())
- : array(shape, default_strides(shape, sizeof(T)), ptr, base) { }
+ array(ShapeContainer shape, const T *ptr, handle base = handle())
+ : array(std::move(shape), {}, ptr, base) { }
template <typename T>
- array(size_t count, const T *ptr, handle base = handle())
- : array(std::vector<size_t>{ count }, ptr, base) { }
+ explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { }
explicit array(const buffer_info &info)
: array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { }
@@ -522,23 +592,23 @@ public:
}
/// Total number of elements
- size_t size() const {
- return std::accumulate(shape(), shape() + ndim(), (size_t) 1, std::multiplies<size_t>());
+ ssize_t size() const {
+ return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
}
/// Byte size of a single element
- size_t itemsize() const {
- return (size_t) detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize;
+ ssize_t itemsize() const {
+ return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize;
}
/// Total number of bytes
- size_t nbytes() const {
+ ssize_t nbytes() const {
return size() * itemsize();
}
/// Number of dimensions
- size_t ndim() const {
- return (size_t) detail::array_proxy(m_ptr)->nd;
+ ssize_t ndim() const {
+ return detail::array_proxy(m_ptr)->nd;
}
/// Base object
@@ -547,24 +617,24 @@ public:
}
/// Dimensions of the array
- const size_t* shape() const {
- return reinterpret_cast<const size_t *>(detail::array_proxy(m_ptr)->dimensions);
+ const ssize_t* shape() const {
+ return detail::array_proxy(m_ptr)->dimensions;
}
/// Dimension along a given axis
- size_t shape(size_t dim) const {
+ ssize_t shape(ssize_t dim) const {
if (dim >= ndim())
fail_dim_check(dim, "invalid axis");
return shape()[dim];
}
/// Strides of the array
- const size_t* strides() const {
- return reinterpret_cast<const size_t *>(detail::array_proxy(m_ptr)->strides);
+ const ssize_t* strides() const {
+ return detail::array_proxy(m_ptr)->strides;
}
/// Stride along a given axis
- size_t strides(size_t dim) const {
+ ssize_t strides(ssize_t dim) const {
if (dim >= ndim())
fail_dim_check(dim, "invalid axis");
return strides()[dim];
@@ -601,40 +671,42 @@ public:
/// Byte offset from beginning of the array to a given index (full or partial).
/// May throw if the index would lead to out of bounds access.
- template<typename... Ix> size_t offset_at(Ix... index) const {
- if (sizeof...(index) > ndim())
+ template<typename... Ix> ssize_t offset_at(Ix... index) const {
+ if ((ssize_t) sizeof...(index) > ndim())
fail_dim_check(sizeof...(index), "too many indices for an array");
- return byte_offset(size_t(index)...);
+ return byte_offset(ssize_t(index)...);
}
- size_t offset_at() const { return 0; }
+ ssize_t offset_at() const { return 0; }
/// Item count from beginning of the array to a given index (full or partial).
/// May throw if the index would lead to out of bounds access.
- template<typename... Ix> size_t index_at(Ix... index) const {
+ template<typename... Ix> ssize_t index_at(Ix... index) const {
return offset_at(index...) / itemsize();
}
- /** Returns a proxy object that provides access to the array's data without bounds or
+ /**
+ * Returns a proxy object that provides access to the array's data without bounds or
* dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with
* care: the array must not be destroyed or reshaped for the duration of the returned object,
* and the caller must take care not to access invalid dimensions or dimension indices.
*/
- template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() {
- if (Dims >= 0 && ndim() != (size_t) Dims)
+ template <typename T, ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
+ if (Dims >= 0 && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims));
return detail::unchecked_mutable_reference<T, Dims>(mutable_data(), shape(), strides(), ndim());
}
- /** Returns a proxy object that provides const access to the array's data without bounds or
+ /**
+ * Returns a proxy object that provides const access to the array's data without bounds or
* dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the
* underlying array have the `writable` flag. Use with care: the array must not be destroyed or
* reshaped for the duration of the returned object, and the caller must take care not to access
* invalid dimensions or dimension indices.
*/
- template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const {
- if (Dims >= 0 && ndim() != (size_t) Dims)
+ template <typename T, ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & {
+ if (Dims >= 0 && ndim() != Dims)
throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) +
"; expected " + std::to_string(Dims));
return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
@@ -646,6 +718,21 @@ public:
return reinterpret_steal<array>(api.PyArray_Squeeze_(m_ptr));
}
+ /// Resize array to given shape
+ /// If refcheck is true and more that one reference exist to this array
+ /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change
+ void resize(ShapeContainer new_shape, bool refcheck = true) {
+ detail::npy_api::PyArray_Dims d = {
+ new_shape->data(), int(new_shape->size())
+ };
+ // try to resize, set ordering param to -1 cause it's not used anyway
+ object new_array = reinterpret_steal<object>(
+ detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1)
+ );
+ if (!new_array) throw error_already_set();
+ if (isinstance<array>(new_array)) { *this = std::move(new_array); }
+ }
+
/// Ensure that the argument is a NumPy array
/// In case of an error, nullptr is returned and the Python error is cleared.
static array ensure(handle h, int ExtraFlags = 0) {
@@ -658,14 +745,14 @@ public:
protected:
template<typename, typename> friend struct detail::npy_format_descriptor;
- void fail_dim_check(size_t dim, const std::string& msg) const {
+ void fail_dim_check(ssize_t dim, const std::string& msg) const {
throw index_error(msg + ": " + std::to_string(dim) +
" (ndim = " + std::to_string(ndim()) + ")");
}
- template<typename... Ix> size_t byte_offset(Ix... index) const {
+ template<typename... Ix> ssize_t byte_offset(Ix... index) const {
check_dimensions(index...);
- return detail::byte_offset_unsafe(strides(), size_t(index)...);
+ return detail::byte_offset_unsafe(strides(), ssize_t(index)...);
}
void check_writeable() const {
@@ -673,25 +760,31 @@ protected:
throw std::domain_error("array is not writeable");
}
- static std::vector<size_t> default_strides(const std::vector<size_t>& shape, size_t itemsize) {
+ // Default, C-style strides
+ static std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
auto ndim = shape.size();
- std::vector<size_t> strides(ndim);
- if (ndim) {
- std::fill(strides.begin(), strides.end(), itemsize);
- for (size_t i = 0; i < ndim - 1; i++)
- for (size_t j = 0; j < ndim - 1 - i; j++)
- strides[j] *= shape[ndim - 1 - i];
- }
+ std::vector<ssize_t> strides(ndim, itemsize);
+ for (size_t i = ndim - 1; i > 0; --i)
+ strides[i - 1] = strides[i] * shape[i];
+ return strides;
+ }
+
+ // F-style strides; default when constructing an array_t with `ExtraFlags & f_style`
+ static std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
+ auto ndim = shape.size();
+ std::vector<ssize_t> strides(ndim, itemsize);
+ for (size_t i = 1; i < ndim; ++i)
+ strides[i] = strides[i - 1] * shape[i - 1];
return strides;
}
template<typename... Ix> void check_dimensions(Ix... index) const {
- check_dimensions_impl(size_t(0), shape(), size_t(index)...);
+ check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...);
}
- void check_dimensions_impl(size_t, const size_t*) const { }
+ void check_dimensions_impl(ssize_t, const ssize_t*) const { }
- template<typename... Ix> void check_dimensions_impl(size_t axis, const size_t* shape, size_t i, Ix... index) const {
+ template<typename... Ix> void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const {
if (i >= *shape) {
throw index_error(std::string("index ") + std::to_string(i) +
" is out of bounds for axis " + std::to_string(axis) +
@@ -702,50 +795,58 @@ protected:
/// Create array from any object -- always returns a new reference
static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) {
- if (ptr == nullptr)
+ if (ptr == nullptr) {
+ PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr");
return nullptr;
+ }
return detail::npy_api::get().PyArray_FromAny_(
ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr);
}
};
template <typename T, int ExtraFlags = array::forcecast> class array_t : public array {
+private:
+ struct private_ctor {};
+ // Delegating constructor needed when both moving and accessing in the same constructor
+ array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base)
+ : array(std::move(shape), std::move(strides), ptr, base) {}
public:
+ static_assert(!detail::array_info<T>::is_array, "Array types cannot be used with array_t");
+
using value_type = T;
array_t() : array(0, static_cast<const T *>(nullptr)) {}
- array_t(handle h, borrowed_t) : array(h, borrowed) { }
- array_t(handle h, stolen_t) : array(h, stolen) { }
+ array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { }
+ array_t(handle h, stolen_t) : array(h, stolen_t{}) { }
PYBIND11_DEPRECATED("Use array_t<T>::ensure() instead")
- array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen) {
+ array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) {
if (!m_ptr) PyErr_Clear();
if (!is_borrowed) Py_XDECREF(h.ptr());
}
- array_t(const object &o) : array(raw_array_t(o.ptr()), stolen) {
+ array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
if (!m_ptr) throw error_already_set();
}
explicit array_t(const buffer_info& info) : array(info) { }
- array_t(const std::vector<size_t> &shape,
- const std::vector<size_t> &strides, const T *ptr = nullptr,
- handle base = handle())
- : array(shape, strides, ptr, base) { }
+ array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle())
+ : array(std::move(shape), std::move(strides), ptr, base) { }
- explicit array_t(const std::vector<size_t> &shape, const T *ptr = nullptr,
- handle base = handle())
- : array(shape, ptr, base) { }
+ explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
+ : array_t(private_ctor{}, std::move(shape),
+ ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()),
+ ptr, base) { }
explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle())
- : array(count, ptr, base) { }
+ : array({count}, {}, ptr, base) { }
- constexpr size_t itemsize() const {
+ constexpr ssize_t itemsize() const {
return sizeof(T);
}
- template<typename... Ix> size_t index_at(Ix... index) const {
+ template<typename... Ix> ssize_t index_at(Ix... index) const {
return offset_at(index...) / itemsize();
}
@@ -761,32 +862,34 @@ public:
template<typename... Ix> const T& at(Ix... index) const {
if (sizeof...(index) != ndim())
fail_dim_check(sizeof...(index), "index dimension mismatch");
- return *(static_cast<const T*>(array::data()) + byte_offset(size_t(index)...) / itemsize());
+ return *(static_cast<const T*>(array::data()) + byte_offset(ssize_t(index)...) / itemsize());
}
// Mutable reference to element at a given index
template<typename... Ix> T& mutable_at(Ix... index) {
if (sizeof...(index) != ndim())
fail_dim_check(sizeof...(index), "index dimension mismatch");
- return *(static_cast<T*>(array::mutable_data()) + byte_offset(size_t(index)...) / itemsize());
+ return *(static_cast<T*>(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize());
}
- /** Returns a proxy object that provides access to the array's data without bounds or
+ /**
+ * Returns a proxy object that provides access to the array's data without bounds or
* dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with
* care: the array must not be destroyed or reshaped for the duration of the returned object,
* and the caller must take care not to access invalid dimensions or dimension indices.
*/
- template <ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() {
+ template <ssize_t Dims = -1> detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
return array::mutable_unchecked<T, Dims>();
}
- /** Returns a proxy object that provides const access to the array's data without bounds or
+ /**
+ * Returns a proxy object that provides const access to the array's data without bounds or
* dimensionality checking. Unlike `unchecked()`, this does not require that the underlying
* array have the `writable` flag. Use with care: the array must not be destroyed or reshaped
* for the duration of the returned object, and the caller must take care not to access invalid
* dimensions or dimension indices.
*/
- template <ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const {
+ template <ssize_t Dims = -1> detail::unchecked_reference<T, Dims> unchecked() const & {
return array::unchecked<T, Dims>();
}
@@ -808,8 +911,10 @@ public:
protected:
/// Create array from any object -- always returns a new reference
static PyObject *raw_array_t(PyObject *ptr) {
- if (ptr == nullptr)
+ if (ptr == nullptr) {
+ PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr");
return nullptr;
+ }
return detail::npy_api::get().PyArray_FromAny_(
ptr, dtype::of<T>().release().ptr(), 0, 0,
detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr);
@@ -838,6 +943,15 @@ struct format_descriptor<T, detail::enable_if_t<std::is_enum<T>::value>> {
}
};
+template <typename T>
+struct format_descriptor<T, detail::enable_if_t<detail::array_info<T>::is_array>> {
+ static std::string format() {
+ using detail::_;
+ PYBIND11_DESCR extents = _("(") + detail::array_info<T>::extents() + _(")");
+ return extents.text() + format_descriptor<detail::remove_all_extents_t<T>>::format();
+ }
+};
+
NAMESPACE_BEGIN(detail)
template <typename T, int ExtraFlags>
struct pyobject_caster<array_t<T, ExtraFlags>> {
@@ -906,6 +1020,20 @@ template <size_t N> struct npy_format_descriptor<char[N]> { PYBIND11_DECL_CHAR_F
template <size_t N> struct npy_format_descriptor<std::array<char, N>> { PYBIND11_DECL_CHAR_FMT };
#undef PYBIND11_DECL_CHAR_FMT
+template<typename T> struct npy_format_descriptor<T, enable_if_t<array_info<T>::is_array>> {
+private:
+ using base_descr = npy_format_descriptor<typename array_info<T>::type>;
+public:
+ static_assert(!array_info<T>::is_empty, "Zero-sized arrays are not supported");
+
+ static PYBIND11_DESCR name() { return _("(") + array_info<T>::extents() + _(")") + base_descr::name(); }
+ static pybind11::dtype dtype() {
+ list shape;
+ array_info<T>::append_extents(shape);
+ return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape));
+ }
+};
+
template<typename T> struct npy_format_descriptor<T, enable_if_t<std::is_enum<T>::value>> {
private:
using base_descr = npy_format_descriptor<typename std::underlying_type<T>::type>;
@@ -916,16 +1044,15 @@ public:
struct field_descriptor {
const char *name;
- size_t offset;
- size_t size;
- size_t alignment;
+ ssize_t offset;
+ ssize_t size;
std::string format;
dtype descr;
};
inline PYBIND11_NOINLINE void register_structured_dtype(
const std::initializer_list<field_descriptor>& fields,
- const std::type_info& tinfo, size_t itemsize,
+ const std::type_info& tinfo, ssize_t itemsize,
bool (*direct_converter)(PyObject *, void *&)) {
auto& numpy_internals = get_numpy_internals();
@@ -953,15 +1080,17 @@ inline PYBIND11_NOINLINE void register_structured_dtype(
std::vector<field_descriptor> ordered_fields(fields);
std::sort(ordered_fields.begin(), ordered_fields.end(),
[](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
- size_t offset = 0;
+ ssize_t offset = 0;
std::ostringstream oss;
- oss << "T{";
+ // mark the structure as unaligned with '^', because numpy and C++ don't
+ // always agree about alignment (particularly for complex), and we're
+ // explicitly listing all our padding. This depends on none of the fields
+ // overriding the endianness. Putting the ^ in front of individual fields
+ // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049
+ oss << "^T{";
for (auto& field : ordered_fields) {
if (field.offset > offset)
oss << (field.offset - offset) << 'x';
- // mark unaligned fields with '^' (unaligned native type)
- if (field.offset % field.alignment)
- oss << '^';
oss << field.format << ':' << field.name << ':';
offset = field.offset + field.size;
}
@@ -1020,10 +1149,14 @@ private:
}
};
+#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code)
+# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0)
+# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0)
+#else
+
#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \
::pybind11::detail::field_descriptor { \
Name, offsetof(T, Field), sizeof(decltype(std::declval<T>().Field)), \
- alignof(decltype(std::declval<T>().Field)), \
::pybind11::format_descriptor<decltype(std::declval<T>().Field)>::format(), \
::pybind11::detail::npy_format_descriptor<decltype(std::declval<T>().Field)>::dtype() \
}
@@ -1088,6 +1221,8 @@ private:
::pybind11::detail::npy_format_descriptor<Type>::register_dtype \
({PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
+#endif // __CLION_IDE__
+
template <class T>
using array_iterator = typename std::add_pointer<T>::type;
@@ -1103,13 +1238,13 @@ array_iterator<T> array_end(const buffer_info& buffer) {
class common_iterator {
public:
- using container_type = std::vector<size_t>;
+ using container_type = std::vector<ssize_t>;
using value_type = container_type::value_type;
using size_type = container_type::size_type;
common_iterator() : p_ptr(0), m_strides() {}
- common_iterator(void* ptr, const container_type& strides, const std::vector<size_t>& shape)
+ common_iterator(void* ptr, const container_type& strides, const container_type& shape)
: p_ptr(reinterpret_cast<char*>(ptr)), m_strides(strides.size()) {
m_strides.back() = static_cast<value_type>(strides.back());
for (size_type i = m_strides.size() - 1; i != 0; --i) {
@@ -1134,16 +1269,16 @@ private:
template <size_t N> class multi_array_iterator {
public:
- using container_type = std::vector<size_t>;
+ using container_type = std::vector<ssize_t>;
multi_array_iterator(const std::array<buffer_info, N> &buffers,
- const std::vector<size_t> &shape)
+ const container_type &shape)
: m_shape(shape.size()), m_index(shape.size(), 0),
m_common_iterator() {
// Manual copy to avoid conversion warning if using std::copy
for (size_t i = 0; i < shape.size(); ++i)
- m_shape[i] = static_cast<container_type::value_type>(shape[i]);
+ m_shape[i] = shape[i];
container_type strides(shape.size());
for (size_t i = 0; i < N; ++i)
@@ -1163,8 +1298,8 @@ public:
return *this;
}
- template <size_t K, class T> const T& data() const {
- return *reinterpret_cast<T*>(m_common_iterator[K].data());
+ template <size_t K, class T = void> T* data() const {
+ return reinterpret_cast<T*>(m_common_iterator[K].data());
}
private:
@@ -1172,8 +1307,9 @@ private:
using common_iter = common_iterator;
void init_common_iterator(const buffer_info &buffer,
- const std::vector<size_t> &shape,
- common_iter &iterator, container_type &strides) {
+ const container_type &shape,
+ common_iter &iterator,
+ container_type &strides) {
auto buffer_shape_iter = buffer.shape.rbegin();
auto buffer_strides_iter = buffer.strides.rbegin();
auto shape_iter = shape.rbegin();
@@ -1181,7 +1317,7 @@ private:
while (buffer_shape_iter != buffer.shape.rend()) {
if (*shape_iter == *buffer_shape_iter)
- *strides_iter = static_cast<size_t>(*buffer_strides_iter);
+ *strides_iter = *buffer_strides_iter;
else
*strides_iter = 0;
@@ -1212,13 +1348,13 @@ enum class broadcast_trivial { non_trivial, c_trivial, f_trivial };
// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage
// buffer; returns `non_trivial` otherwise.
template <size_t N>
-broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &ndim, std::vector<size_t> &shape) {
- ndim = std::accumulate(buffers.begin(), buffers.end(), size_t(0), [](size_t res, const buffer_info& buf) {
+broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, ssize_t &ndim, std::vector<ssize_t> &shape) {
+ ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) {
return std::max(res, buf.ndim);
});
shape.clear();
- shape.resize(ndim, 1);
+ shape.resize((size_t) ndim, 1);
// Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or
// the full size).
@@ -1253,7 +1389,7 @@ 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)
if (trivial_broadcast_c) {
- size_t expect_stride = buffers[i].itemsize;
+ ssize_t expect_stride = buffers[i].itemsize;
auto end = buffers[i].shape.crend();
for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin();
trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
@@ -1266,7 +1402,7 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
// Check for Fortran contiguity (if previous inputs were also F contiguous)
if (trivial_broadcast_f) {
- size_t expect_stride = buffers[i].itemsize;
+ ssize_t expect_stride = buffers[i].itemsize;
auto end = buffers[i].shape.cend();
for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin();
trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
@@ -1284,82 +1420,143 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
broadcast_trivial::non_trivial;
}
+template <typename T>
+struct vectorize_arg {
+ static_assert(!std::is_rvalue_reference<T>::value, "Functions with rvalue reference arguments cannot be vectorized");
+ // The wrapped function gets called with this type:
+ using call_type = remove_reference_t<T>;
+ // Is this a vectorized argument?
+ static constexpr bool vectorize =
+ satisfies_any_of<call_type, std::is_arithmetic, is_complex, std::is_pod>::value &&
+ satisfies_none_of<call_type, std::is_pointer, std::is_array, is_std_array, std::is_enum>::value &&
+ (!std::is_reference<T>::value ||
+ (std::is_lvalue_reference<T>::value && std::is_const<call_type>::value));
+ // Accept this type: an array for vectorized types, otherwise the type as-is:
+ using type = conditional_t<vectorize, array_t<remove_cv_t<call_type>, array::forcecast>, T>;
+};
+
template <typename Func, typename Return, typename... Args>
struct vectorize_helper {
- typename std::remove_reference<Func>::type f;
+private:
static constexpr size_t N = sizeof...(Args);
+ static constexpr size_t NVectorized = constexpr_sum(vectorize_arg<Args>::vectorize...);
+ static_assert(NVectorized >= 1,
+ "pybind11::vectorize(...) requires a function with at least one vectorizable argument");
+public:
template <typename T>
- explicit vectorize_helper(T&&f) : f(std::forward<T>(f)) { }
+ explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) { }
- object operator()(array_t<Args, array::forcecast>... args) {
- return run(args..., make_index_sequence<N>());
+ object operator()(typename vectorize_arg<Args>::type... args) {
+ return run(args...,
+ make_index_sequence<N>(),
+ select_indices<vectorize_arg<Args>::vectorize...>(),
+ make_index_sequence<NVectorized>());
}
- template <size_t ... Index> object run(array_t<Args, array::forcecast>&... args, index_sequence<Index...> index) {
- /* Request buffers from all parameters */
- std::array<buffer_info, N> buffers {{ args.request()... }};
+private:
+ remove_reference_t<Func> f;
+
+ template <size_t Index> using param_n_t = typename pack_element<Index, typename vectorize_arg<Args>::call_type...>::type;
+
+ // Runs a vectorized function given arguments tuple and three index sequences:
+ // - Index is the full set of 0 ... (N-1) argument indices;
+ // - VIndex is the subset of argument indices with vectorized parameters, letting us access
+ // vectorized arguments (anything not in this sequence is passed through)
+ // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that
+ // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at
+ // index BIndex in the array).
+ template <size_t... Index, size_t... VIndex, size_t... BIndex> object run(
+ typename vectorize_arg<Args>::type &...args,
+ index_sequence<Index...> i_seq, index_sequence<VIndex...> vi_seq, index_sequence<BIndex...> bi_seq) {
+
+ // Pointers to values the function was called with; the vectorized ones set here will start
+ // out as array_t<T> pointers, but they will be changed them to T pointers before we make
+ // call the wrapped function. Non-vectorized pointers are left as-is.
+ std::array<void *, N> params{{ &args... }};
+
+ // The array of `buffer_info`s of vectorized arguments:
+ std::array<buffer_info, NVectorized> buffers{{ reinterpret_cast<array *>(params[VIndex])->request()... }};
/* Determine dimensions parameters of output array */
- size_t ndim = 0;
- std::vector<size_t> shape(0);
- auto trivial = broadcast(buffers, ndim, shape);
-
- size_t size = 1;
- std::vector<size_t> strides(ndim);
- if (ndim > 0) {
- if (trivial == broadcast_trivial::f_trivial) {
- strides[0] = sizeof(Return);
- for (size_t i = 1; i < ndim; ++i) {
- strides[i] = strides[i - 1] * shape[i - 1];
- size *= shape[i - 1];
- }
- size *= shape[ndim - 1];
- }
- else {
- strides[ndim-1] = sizeof(Return);
- for (size_t i = ndim - 1; i > 0; --i) {
- strides[i - 1] = strides[i] * shape[i];
- size *= shape[i];
- }
- size *= shape[0];
- }
+ ssize_t nd = 0;
+ std::vector<ssize_t> shape(0);
+ auto trivial = broadcast(buffers, nd, shape);
+ size_t ndim = (size_t) nd;
+
+ size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies<size_t>());
+
+ // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e.
+ // not wrapped in an array).
+ if (size == 1 && ndim == 0) {
+ PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr);
+ return cast(f(*reinterpret_cast<param_n_t<Index> *>(params[Index])...));
}
- if (size == 1)
- return cast(f(*reinterpret_cast<Args *>(buffers[Index].ptr)...));
+ array_t<Return> result;
+ if (trivial == broadcast_trivial::f_trivial) result = array_t<Return, array::f_style>(shape);
+ else result = array_t<Return>(shape);
- array_t<Return> result(shape, strides);
- auto buf = result.request();
- auto output = (Return *) buf.ptr;
+ if (size == 0) return result;
/* Call the function */
- if (trivial == broadcast_trivial::non_trivial) {
- apply_broadcast<Index...>(buffers, buf, index);
- } else {
- for (size_t i = 0; i < size; ++i)
- output[i] = f((reinterpret_cast<Args *>(buffers[Index].ptr)[buffers[Index].size == 1 ? 0 : i])...);
- }
+ if (trivial == broadcast_trivial::non_trivial)
+ apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq);
+ else
+ apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq);
return result;
}
- template <size_t... Index>
- void apply_broadcast(const std::array<buffer_info, N> &buffers,
- buffer_info &output, index_sequence<Index...>) {
- using input_iterator = multi_array_iterator<N>;
- using output_iterator = array_iterator<Return>;
+ template <size_t... Index, size_t... VIndex, size_t... BIndex>
+ void apply_trivial(std::array<buffer_info, NVectorized> &buffers,
+ std::array<void *, N> &params,
+ Return *out,
+ size_t size,
+ index_sequence<Index...>, index_sequence<VIndex...>, index_sequence<BIndex...>) {
+
+ // Initialize an array of mutable byte references and sizes with references set to the
+ // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size
+ // (except for singletons, which get an increment of 0).
+ std::array<std::pair<unsigned char *&, const size_t>, NVectorized> vecparams{{
+ std::pair<unsigned char *&, const size_t>(
+ reinterpret_cast<unsigned char *&>(params[VIndex] = buffers[BIndex].ptr),
+ buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t<VIndex>)
+ )...
+ }};
+
+ for (size_t i = 0; i < size; ++i) {
+ out[i] = f(*reinterpret_cast<param_n_t<Index> *>(params[Index])...);
+ for (auto &x : vecparams) x.first += x.second;
+ }
+ }
- input_iterator input_iter(buffers, output.shape);
- output_iterator output_end = array_end<Return>(output);
+ template <size_t... Index, size_t... VIndex, size_t... BIndex>
+ void apply_broadcast(std::array<buffer_info, NVectorized> &buffers,
+ std::array<void *, N> &params,
+ array_t<Return> &output_array,
+ index_sequence<Index...>, index_sequence<VIndex...>, index_sequence<BIndex...>) {
- for (output_iterator iter = array_begin<Return>(output);
- iter != output_end; ++iter, ++input_iter) {
- *iter = f((input_iter.template data<Index, Args>())...);
+ buffer_info output = output_array.request();
+ multi_array_iterator<NVectorized> input_iter(buffers, output.shape);
+
+ for (array_iterator<Return> iter = array_begin<Return>(output), end = array_end<Return>(output);
+ iter != end;
+ ++iter, ++input_iter) {
+ PYBIND11_EXPAND_SIDE_EFFECTS((
+ params[VIndex] = input_iter.template data<BIndex>()
+ ));
+ *iter = f(*reinterpret_cast<param_n_t<Index> *>(std::get<Index>(params))...);
}
}
};
+template <typename Func, typename Return, typename... Args>
+vectorize_helper<Func, Return, Args...>
+vectorize_extractor(const Func &f, Return (*) (Args ...)) {
+ return detail::vectorize_helper<Func, Return, Args...>(f);
+}
+
template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {
static PYBIND11_DESCR name() {
return _("numpy.ndarray[") + npy_format_descriptor<T>::name() + _("]");
@@ -1368,25 +1565,35 @@ template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {
NAMESPACE_END(detail)
-template <typename Func, typename Return, typename... Args>
-detail::vectorize_helper<Func, Return, Args...>
-vectorize(const Func &f, Return (*) (Args ...)) {
- return detail::vectorize_helper<Func, Return, Args...>(f);
-}
-
+// Vanilla pointer vectorizer:
template <typename Return, typename... Args>
-detail::vectorize_helper<Return (*) (Args ...), Return, Args...>
+detail::vectorize_helper<Return (*)(Args...), Return, Args...>
vectorize(Return (*f) (Args ...)) {
- return vectorize<Return (*) (Args ...), Return, Args...>(f, f);
+ return detail::vectorize_helper<Return (*)(Args...), Return, Args...>(f);
}
-template <typename Func, typename FuncType = typename detail::remove_class<decltype(&std::remove_reference<Func>::type::operator())>::type>
+// lambda vectorizer:
+template <typename Func, detail::enable_if_t<detail::is_lambda<Func>::value, int> = 0>
auto vectorize(Func &&f) -> decltype(
- vectorize(std::forward<Func>(f), (FuncType *) nullptr)) {
- return vectorize(std::forward<Func>(f), (FuncType *) nullptr);
+ detail::vectorize_extractor(std::forward<Func>(f), (detail::function_signature_t<Func> *) nullptr)) {
+ return detail::vectorize_extractor(std::forward<Func>(f), (detail::function_signature_t<Func> *) nullptr);
+}
+
+// Vectorize a class method (non-const):
+template <typename Return, typename Class, typename... Args,
+ typename Helper = detail::vectorize_helper<decltype(std::mem_fn(std::declval<Return (Class::*)(Args...)>())), Return, Class *, Args...>>
+Helper vectorize(Return (Class::*f)(Args...)) {
+ return Helper(std::mem_fn(f));
+}
+
+// Vectorize a class method (non-const):
+template <typename Return, typename Class, typename... Args,
+ typename Helper = detail::vectorize_helper<decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const>())), Return, const Class *, Args...>>
+Helper vectorize(Return (Class::*f)(Args...) const) {
+ return Helper(std::mem_fn(f));
}
-NAMESPACE_END(pybind11)
+NAMESPACE_END(PYBIND11_NAMESPACE)
#if defined(_MSC_VER)
#pragma warning(pop)