Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:48

0001 /*
0002     pybind11/numpy.h: Basic NumPy support, vectorize() wrapper
0003 
0004     Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
0005 
0006     All rights reserved. Use of this source code is governed by a
0007     BSD-style license that can be found in the LICENSE file.
0008 */
0009 
0010 #pragma once
0011 
0012 #include "pybind11.h"
0013 #include "complex.h"
0014 
0015 #include <algorithm>
0016 #include <array>
0017 #include <cstdint>
0018 #include <cstdlib>
0019 #include <cstring>
0020 #include <functional>
0021 #include <numeric>
0022 #include <sstream>
0023 #include <string>
0024 #include <type_traits>
0025 #include <typeindex>
0026 #include <utility>
0027 #include <vector>
0028 
0029 /* This will be true on all flat address space platforms and allows us to reduce the
0030    whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size
0031    and dimension types (e.g. shape, strides, indexing), instead of inflicting this
0032    upon the library user. */
0033 static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t");
0034 static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
0035 // We now can reinterpret_cast between py::ssize_t and Py_intptr_t (MSVC + PyPy cares)
0036 
0037 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0038 
0039 PYBIND11_WARNING_DISABLE_MSVC(4127)
0040 
0041 class array; // Forward declaration
0042 
0043 PYBIND11_NAMESPACE_BEGIN(detail)
0044 
0045 template <>
0046 struct handle_type_name<array> {
0047     static constexpr auto name = const_name("numpy.ndarray");
0048 };
0049 
0050 template <typename type, typename SFINAE = void>
0051 struct npy_format_descriptor;
0052 
0053 struct PyArrayDescr_Proxy {
0054     PyObject_HEAD
0055     PyObject *typeobj;
0056     char kind;
0057     char type;
0058     char byteorder;
0059     char flags;
0060     int type_num;
0061     int elsize;
0062     int alignment;
0063     char *subarray;
0064     PyObject *fields;
0065     PyObject *names;
0066 };
0067 
0068 struct PyArray_Proxy {
0069     PyObject_HEAD
0070     char *data;
0071     int nd;
0072     ssize_t *dimensions;
0073     ssize_t *strides;
0074     PyObject *base;
0075     PyObject *descr;
0076     int flags;
0077 };
0078 
0079 struct PyVoidScalarObject_Proxy {
0080     PyObject_VAR_HEAD char *obval;
0081     PyArrayDescr_Proxy *descr;
0082     int flags;
0083     PyObject *base;
0084 };
0085 
0086 struct numpy_type_info {
0087     PyObject *dtype_ptr;
0088     std::string format_str;
0089 };
0090 
0091 struct numpy_internals {
0092     std::unordered_map<std::type_index, numpy_type_info> registered_dtypes;
0093 
0094     numpy_type_info *get_type_info(const std::type_info &tinfo, bool throw_if_missing = true) {
0095         auto it = registered_dtypes.find(std::type_index(tinfo));
0096         if (it != registered_dtypes.end()) {
0097             return &(it->second);
0098         }
0099         if (throw_if_missing) {
0100             pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name());
0101         }
0102         return nullptr;
0103     }
0104 
0105     template <typename T>
0106     numpy_type_info *get_type_info(bool throw_if_missing = true) {
0107         return get_type_info(typeid(typename std::remove_cv<T>::type), throw_if_missing);
0108     }
0109 };
0110 
0111 PYBIND11_NOINLINE void load_numpy_internals(numpy_internals *&ptr) {
0112     ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals");
0113 }
0114 
0115 inline numpy_internals &get_numpy_internals() {
0116     static numpy_internals *ptr = nullptr;
0117     if (!ptr) {
0118         load_numpy_internals(ptr);
0119     }
0120     return *ptr;
0121 }
0122 
0123 template <typename T>
0124 struct same_size {
0125     template <typename U>
0126     using as = bool_constant<sizeof(T) == sizeof(U)>;
0127 };
0128 
0129 template <typename Concrete>
0130 constexpr int platform_lookup() {
0131     return -1;
0132 }
0133 
0134 // Lookup a type according to its size, and return a value corresponding to the NumPy typenum.
0135 template <typename Concrete, typename T, typename... Ts, typename... Ints>
0136 constexpr int platform_lookup(int I, Ints... Is) {
0137     return sizeof(Concrete) == sizeof(T) ? I : platform_lookup<Concrete, Ts...>(Is...);
0138 }
0139 
0140 struct npy_api {
0141     enum constants {
0142         NPY_ARRAY_C_CONTIGUOUS_ = 0x0001,
0143         NPY_ARRAY_F_CONTIGUOUS_ = 0x0002,
0144         NPY_ARRAY_OWNDATA_ = 0x0004,
0145         NPY_ARRAY_FORCECAST_ = 0x0010,
0146         NPY_ARRAY_ENSUREARRAY_ = 0x0040,
0147         NPY_ARRAY_ALIGNED_ = 0x0100,
0148         NPY_ARRAY_WRITEABLE_ = 0x0400,
0149         NPY_BOOL_ = 0,
0150         NPY_BYTE_,
0151         NPY_UBYTE_,
0152         NPY_SHORT_,
0153         NPY_USHORT_,
0154         NPY_INT_,
0155         NPY_UINT_,
0156         NPY_LONG_,
0157         NPY_ULONG_,
0158         NPY_LONGLONG_,
0159         NPY_ULONGLONG_,
0160         NPY_FLOAT_,
0161         NPY_DOUBLE_,
0162         NPY_LONGDOUBLE_,
0163         NPY_CFLOAT_,
0164         NPY_CDOUBLE_,
0165         NPY_CLONGDOUBLE_,
0166         NPY_OBJECT_ = 17,
0167         NPY_STRING_,
0168         NPY_UNICODE_,
0169         NPY_VOID_,
0170         // Platform-dependent normalization
0171         NPY_INT8_ = NPY_BYTE_,
0172         NPY_UINT8_ = NPY_UBYTE_,
0173         NPY_INT16_ = NPY_SHORT_,
0174         NPY_UINT16_ = NPY_USHORT_,
0175         // `npy_common.h` defines the integer aliases. In order, it checks:
0176         // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR
0177         // and assigns the alias to the first matching size, so we should check in this order.
0178         NPY_INT32_
0179         = platform_lookup<std::int32_t, long, int, short>(NPY_LONG_, NPY_INT_, NPY_SHORT_),
0180         NPY_UINT32_ = platform_lookup<std::uint32_t, unsigned long, unsigned int, unsigned short>(
0181             NPY_ULONG_, NPY_UINT_, NPY_USHORT_),
0182         NPY_INT64_
0183         = platform_lookup<std::int64_t, long, long long, int>(NPY_LONG_, NPY_LONGLONG_, NPY_INT_),
0184         NPY_UINT64_
0185         = platform_lookup<std::uint64_t, unsigned long, unsigned long long, unsigned int>(
0186             NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
0187     };
0188 
0189     struct PyArray_Dims {
0190         Py_intptr_t *ptr;
0191         int len;
0192     };
0193 
0194     static npy_api &get() {
0195         static npy_api api = lookup();
0196         return api;
0197     }
0198 
0199     bool PyArray_Check_(PyObject *obj) const {
0200         return PyObject_TypeCheck(obj, PyArray_Type_) != 0;
0201     }
0202     bool PyArrayDescr_Check_(PyObject *obj) const {
0203         return PyObject_TypeCheck(obj, PyArrayDescr_Type_) != 0;
0204     }
0205 
0206     unsigned int (*PyArray_GetNDArrayCFeatureVersion_)();
0207     PyObject *(*PyArray_DescrFromType_)(int);
0208     PyObject *(*PyArray_NewFromDescr_)(PyTypeObject *,
0209                                        PyObject *,
0210                                        int,
0211                                        Py_intptr_t const *,
0212                                        Py_intptr_t const *,
0213                                        void *,
0214                                        int,
0215                                        PyObject *);
0216     // Unused. Not removed because that affects ABI of the class.
0217     PyObject *(*PyArray_DescrNewFromType_)(int);
0218     int (*PyArray_CopyInto_)(PyObject *, PyObject *);
0219     PyObject *(*PyArray_NewCopy_)(PyObject *, int);
0220     PyTypeObject *PyArray_Type_;
0221     PyTypeObject *PyVoidArrType_Type_;
0222     PyTypeObject *PyArrayDescr_Type_;
0223     PyObject *(*PyArray_DescrFromScalar_)(PyObject *);
0224     PyObject *(*PyArray_FromAny_)(PyObject *, PyObject *, int, int, int, PyObject *);
0225     int (*PyArray_DescrConverter_)(PyObject *, PyObject **);
0226     bool (*PyArray_EquivTypes_)(PyObject *, PyObject *);
0227     int (*PyArray_GetArrayParamsFromObject_)(PyObject *,
0228                                              PyObject *,
0229                                              unsigned char,
0230                                              PyObject **,
0231                                              int *,
0232                                              Py_intptr_t *,
0233                                              PyObject **,
0234                                              PyObject *);
0235     PyObject *(*PyArray_Squeeze_)(PyObject *);
0236     // Unused. Not removed because that affects ABI of the class.
0237     int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
0238     PyObject *(*PyArray_Resize_)(PyObject *, PyArray_Dims *, int, int);
0239     PyObject *(*PyArray_Newshape_)(PyObject *, PyArray_Dims *, int);
0240     PyObject *(*PyArray_View_)(PyObject *, PyObject *, PyObject *);
0241 
0242 private:
0243     enum functions {
0244         API_PyArray_GetNDArrayCFeatureVersion = 211,
0245         API_PyArray_Type = 2,
0246         API_PyArrayDescr_Type = 3,
0247         API_PyVoidArrType_Type = 39,
0248         API_PyArray_DescrFromType = 45,
0249         API_PyArray_DescrFromScalar = 57,
0250         API_PyArray_FromAny = 69,
0251         API_PyArray_Resize = 80,
0252         API_PyArray_CopyInto = 82,
0253         API_PyArray_NewCopy = 85,
0254         API_PyArray_NewFromDescr = 94,
0255         API_PyArray_DescrNewFromType = 96,
0256         API_PyArray_Newshape = 135,
0257         API_PyArray_Squeeze = 136,
0258         API_PyArray_View = 137,
0259         API_PyArray_DescrConverter = 174,
0260         API_PyArray_EquivTypes = 182,
0261         API_PyArray_GetArrayParamsFromObject = 278,
0262         API_PyArray_SetBaseObject = 282
0263     };
0264 
0265     static npy_api lookup() {
0266         module_ m = module_::import("numpy.core.multiarray");
0267         auto c = m.attr("_ARRAY_API");
0268         void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
0269         npy_api api;
0270 #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
0271         DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
0272         if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) {
0273             pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0");
0274         }
0275         DECL_NPY_API(PyArray_Type);
0276         DECL_NPY_API(PyVoidArrType_Type);
0277         DECL_NPY_API(PyArrayDescr_Type);
0278         DECL_NPY_API(PyArray_DescrFromType);
0279         DECL_NPY_API(PyArray_DescrFromScalar);
0280         DECL_NPY_API(PyArray_FromAny);
0281         DECL_NPY_API(PyArray_Resize);
0282         DECL_NPY_API(PyArray_CopyInto);
0283         DECL_NPY_API(PyArray_NewCopy);
0284         DECL_NPY_API(PyArray_NewFromDescr);
0285         DECL_NPY_API(PyArray_DescrNewFromType);
0286         DECL_NPY_API(PyArray_Newshape);
0287         DECL_NPY_API(PyArray_Squeeze);
0288         DECL_NPY_API(PyArray_View);
0289         DECL_NPY_API(PyArray_DescrConverter);
0290         DECL_NPY_API(PyArray_EquivTypes);
0291         DECL_NPY_API(PyArray_GetArrayParamsFromObject);
0292         DECL_NPY_API(PyArray_SetBaseObject);
0293 
0294 #undef DECL_NPY_API
0295         return api;
0296     }
0297 };
0298 
0299 inline PyArray_Proxy *array_proxy(void *ptr) { return reinterpret_cast<PyArray_Proxy *>(ptr); }
0300 
0301 inline const PyArray_Proxy *array_proxy(const void *ptr) {
0302     return reinterpret_cast<const PyArray_Proxy *>(ptr);
0303 }
0304 
0305 inline PyArrayDescr_Proxy *array_descriptor_proxy(PyObject *ptr) {
0306     return reinterpret_cast<PyArrayDescr_Proxy *>(ptr);
0307 }
0308 
0309 inline const PyArrayDescr_Proxy *array_descriptor_proxy(const PyObject *ptr) {
0310     return reinterpret_cast<const PyArrayDescr_Proxy *>(ptr);
0311 }
0312 
0313 inline bool check_flags(const void *ptr, int flag) {
0314     return (flag == (array_proxy(ptr)->flags & flag));
0315 }
0316 
0317 template <typename T>
0318 struct is_std_array : std::false_type {};
0319 template <typename T, size_t N>
0320 struct is_std_array<std::array<T, N>> : std::true_type {};
0321 template <typename T>
0322 struct is_complex : std::false_type {};
0323 template <typename T>
0324 struct is_complex<std::complex<T>> : std::true_type {};
0325 
0326 template <typename T>
0327 struct array_info_scalar {
0328     using type = T;
0329     static constexpr bool is_array = false;
0330     static constexpr bool is_empty = false;
0331     static constexpr auto extents = const_name("");
0332     static void append_extents(list & /* shape */) {}
0333 };
0334 // Computes underlying type and a comma-separated list of extents for array
0335 // types (any mix of std::array and built-in arrays). An array of char is
0336 // treated as scalar because it gets special handling.
0337 template <typename T>
0338 struct array_info : array_info_scalar<T> {};
0339 template <typename T, size_t N>
0340 struct array_info<std::array<T, N>> {
0341     using type = typename array_info<T>::type;
0342     static constexpr bool is_array = true;
0343     static constexpr bool is_empty = (N == 0) || array_info<T>::is_empty;
0344     static constexpr size_t extent = N;
0345 
0346     // appends the extents to shape
0347     static void append_extents(list &shape) {
0348         shape.append(N);
0349         array_info<T>::append_extents(shape);
0350     }
0351 
0352     static constexpr auto extents = const_name<array_info<T>::is_array>(
0353         concat(const_name<N>(), array_info<T>::extents), const_name<N>());
0354 };
0355 // For numpy we have special handling for arrays of characters, so we don't include
0356 // the size in the array extents.
0357 template <size_t N>
0358 struct array_info<char[N]> : array_info_scalar<char[N]> {};
0359 template <size_t N>
0360 struct array_info<std::array<char, N>> : array_info_scalar<std::array<char, N>> {};
0361 template <typename T, size_t N>
0362 struct array_info<T[N]> : array_info<std::array<T, N>> {};
0363 template <typename T>
0364 using remove_all_extents_t = typename array_info<T>::type;
0365 
0366 template <typename T>
0367 using is_pod_struct
0368     = all_of<std::is_standard_layout<T>, // since we're accessing directly in memory
0369                                          // we need a standard layout type
0370 #if defined(__GLIBCXX__)                                                                          \
0371     && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623              \
0372         || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
0373              // libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after
0374              // 5) don't implement is_trivially_copyable, so approximate it
0375              std::is_trivially_destructible<T>,
0376              satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>,
0377 #else
0378              std::is_trivially_copyable<T>,
0379 #endif
0380              satisfies_none_of<T,
0381                                std::is_reference,
0382                                std::is_array,
0383                                is_std_array,
0384                                std::is_arithmetic,
0385                                is_complex,
0386                                std::is_enum>>;
0387 
0388 // Replacement for std::is_pod (deprecated in C++20)
0389 template <typename T>
0390 using is_pod = all_of<std::is_standard_layout<T>, std::is_trivial<T>>;
0391 
0392 template <ssize_t Dim = 0, typename Strides>
0393 ssize_t byte_offset_unsafe(const Strides &) {
0394     return 0;
0395 }
0396 template <ssize_t Dim = 0, typename Strides, typename... Ix>
0397 ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) {
0398     return i * strides[Dim] + byte_offset_unsafe<Dim + 1>(strides, index...);
0399 }
0400 
0401 /**
0402  * Proxy class providing unsafe, unchecked const access to array data.  This is constructed through
0403  * the `unchecked<T, N>()` method of `array` or the `unchecked<N>()` method of `array_t<T>`. `Dims`
0404  * will be -1 for dimensions determined at runtime.
0405  */
0406 template <typename T, ssize_t Dims>
0407 class unchecked_reference {
0408 protected:
0409     static constexpr bool Dynamic = Dims < 0;
0410     const unsigned char *data_;
0411     // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
0412     // make large performance gains on big, nested loops, but requires compile-time dimensions
0413     conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>> shape_, strides_;
0414     const ssize_t dims_;
0415 
0416     friend class pybind11::array;
0417     // Constructor for compile-time dimensions:
0418     template <bool Dyn = Dynamic>
0419     unchecked_reference(const void *data,
0420                         const ssize_t *shape,
0421                         const ssize_t *strides,
0422                         enable_if_t<!Dyn, ssize_t>)
0423         : data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
0424         for (size_t i = 0; i < (size_t) dims_; i++) {
0425             shape_[i] = shape[i];
0426             strides_[i] = strides[i];
0427         }
0428     }
0429     // Constructor for runtime dimensions:
0430     template <bool Dyn = Dynamic>
0431     unchecked_reference(const void *data,
0432                         const ssize_t *shape,
0433                         const ssize_t *strides,
0434                         enable_if_t<Dyn, ssize_t> dims)
0435         : data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides},
0436           dims_{dims} {}
0437 
0438 public:
0439     /**
0440      * Unchecked const reference access to data at the given indices.  For a compile-time known
0441      * number of dimensions, this requires the correct number of arguments; for run-time
0442      * dimensionality, this is not checked (and so is up to the caller to use safely).
0443      */
0444     template <typename... Ix>
0445     const T &operator()(Ix... index) const {
0446         static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
0447                       "Invalid number of indices for unchecked array reference");
0448         return *reinterpret_cast<const T *>(data_
0449                                             + byte_offset_unsafe(strides_, ssize_t(index)...));
0450     }
0451     /**
0452      * Unchecked const reference access to data; this operator only participates if the reference
0453      * is to a 1-dimensional array.  When present, this is exactly equivalent to `obj(index)`.
0454      */
0455     template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
0456     const T &operator[](ssize_t index) const {
0457         return operator()(index);
0458     }
0459 
0460     /// Pointer access to the data at the given indices.
0461     template <typename... Ix>
0462     const T *data(Ix... ix) const {
0463         return &operator()(ssize_t(ix)...);
0464     }
0465 
0466     /// Returns the item size, i.e. sizeof(T)
0467     constexpr static ssize_t itemsize() { return sizeof(T); }
0468 
0469     /// Returns the shape (i.e. size) of dimension `dim`
0470     ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; }
0471 
0472     /// Returns the number of dimensions of the array
0473     ssize_t ndim() const { return dims_; }
0474 
0475     /// Returns the total number of elements in the referenced array, i.e. the product of the
0476     /// shapes
0477     template <bool Dyn = Dynamic>
0478     enable_if_t<!Dyn, ssize_t> size() const {
0479         return std::accumulate(
0480             shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies<ssize_t>());
0481     }
0482     template <bool Dyn = Dynamic>
0483     enable_if_t<Dyn, ssize_t> size() const {
0484         return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
0485     }
0486 
0487     /// Returns the total number of bytes used by the referenced data.  Note that the actual span
0488     /// in memory may be larger if the referenced array has non-contiguous strides (e.g. for a
0489     /// slice).
0490     ssize_t nbytes() const { return size() * itemsize(); }
0491 };
0492 
0493 template <typename T, ssize_t Dims>
0494 class unchecked_mutable_reference : public unchecked_reference<T, Dims> {
0495     friend class pybind11::array;
0496     using ConstBase = unchecked_reference<T, Dims>;
0497     using ConstBase::ConstBase;
0498     using ConstBase::Dynamic;
0499 
0500 public:
0501     // Bring in const-qualified versions from base class
0502     using ConstBase::operator();
0503     using ConstBase::operator[];
0504 
0505     /// Mutable, unchecked access to data at the given indices.
0506     template <typename... Ix>
0507     T &operator()(Ix... index) {
0508         static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
0509                       "Invalid number of indices for unchecked array reference");
0510         return const_cast<T &>(ConstBase::operator()(index...));
0511     }
0512     /**
0513      * Mutable, unchecked access data at the given index; this operator only participates if the
0514      * reference is to a 1-dimensional array (or has runtime dimensions).  When present, this is
0515      * exactly equivalent to `obj(index)`.
0516      */
0517     template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
0518     T &operator[](ssize_t index) {
0519         return operator()(index);
0520     }
0521 
0522     /// Mutable pointer access to the data at the given indices.
0523     template <typename... Ix>
0524     T *mutable_data(Ix... ix) {
0525         return &operator()(ssize_t(ix)...);
0526     }
0527 };
0528 
0529 template <typename T, ssize_t Dim>
0530 struct type_caster<unchecked_reference<T, Dim>> {
0531     static_assert(Dim == 0 && Dim > 0 /* always fail */,
0532                   "unchecked array proxy object is not castable");
0533 };
0534 template <typename T, ssize_t Dim>
0535 struct type_caster<unchecked_mutable_reference<T, Dim>>
0536     : type_caster<unchecked_reference<T, Dim>> {};
0537 
0538 PYBIND11_NAMESPACE_END(detail)
0539 
0540 class dtype : public object {
0541 public:
0542     PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_)
0543 
0544     explicit dtype(const buffer_info &info) {
0545         dtype descr(_dtype_from_pep3118()(pybind11::str(info.format)));
0546         // If info.itemsize == 0, use the value calculated from the format string
0547         m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
0548                     .release()
0549                     .ptr();
0550     }
0551 
0552     explicit dtype(const pybind11::str &format) : dtype(from_args(format)) {}
0553 
0554     explicit dtype(const std::string &format) : dtype(pybind11::str(format)) {}
0555 
0556     explicit dtype(const char *format) : dtype(pybind11::str(format)) {}
0557 
0558     dtype(list names, list formats, list offsets, ssize_t itemsize) {
0559         dict args;
0560         args["names"] = std::move(names);
0561         args["formats"] = std::move(formats);
0562         args["offsets"] = std::move(offsets);
0563         args["itemsize"] = pybind11::int_(itemsize);
0564         m_ptr = from_args(args).release().ptr();
0565     }
0566 
0567     explicit dtype(int typenum)
0568         : object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
0569         if (m_ptr == nullptr) {
0570             throw error_already_set();
0571         }
0572     }
0573 
0574     /// This is essentially the same as calling numpy.dtype(args) in Python.
0575     static dtype from_args(const object &args) {
0576         PyObject *ptr = nullptr;
0577         if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr) {
0578             throw error_already_set();
0579         }
0580         return reinterpret_steal<dtype>(ptr);
0581     }
0582 
0583     /// Return dtype associated with a C++ type.
0584     template <typename T>
0585     static dtype of() {
0586         return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::dtype();
0587     }
0588 
0589     /// Size of the data type in bytes.
0590     ssize_t itemsize() const { return detail::array_descriptor_proxy(m_ptr)->elsize; }
0591 
0592     /// Returns true for structured data types.
0593     bool has_fields() const { return detail::array_descriptor_proxy(m_ptr)->names != nullptr; }
0594 
0595     /// Single-character code for dtype's kind.
0596     /// For example, floating point types are 'f' and integral types are 'i'.
0597     char kind() const { return detail::array_descriptor_proxy(m_ptr)->kind; }
0598 
0599     /// Single-character for dtype's type.
0600     /// For example, ``float`` is 'f', ``double`` 'd', ``int`` 'i', and ``long`` 'l'.
0601     char char_() const {
0602         // Note: The signature, `dtype::char_` follows the naming of NumPy's
0603         // public Python API (i.e., ``dtype.char``), rather than its internal
0604         // C API (``PyArray_Descr::type``).
0605         return detail::array_descriptor_proxy(m_ptr)->type;
0606     }
0607 
0608     /// type number of dtype.
0609     int num() const {
0610         // Note: The signature, `dtype::num` follows the naming of NumPy's public
0611         // Python API (i.e., ``dtype.num``), rather than its internal
0612         // C API (``PyArray_Descr::type_num``).
0613         return detail::array_descriptor_proxy(m_ptr)->type_num;
0614     }
0615 
0616     /// Single character for byteorder
0617     char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
0618 
0619     /// Alignment of the data type
0620     int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; }
0621 
0622     /// Flags for the array descriptor
0623     char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; }
0624 
0625 private:
0626     static object _dtype_from_pep3118() {
0627         static PyObject *obj = module_::import("numpy.core._internal")
0628                                    .attr("_dtype_from_pep3118")
0629                                    .cast<object>()
0630                                    .release()
0631                                    .ptr();
0632         return reinterpret_borrow<object>(obj);
0633     }
0634 
0635     dtype strip_padding(ssize_t itemsize) {
0636         // Recursively strip all void fields with empty names that are generated for
0637         // padding fields (as of NumPy v1.11).
0638         if (!has_fields()) {
0639             return *this;
0640         }
0641 
0642         struct field_descr {
0643             pybind11::str name;
0644             object format;
0645             pybind11::int_ offset;
0646             field_descr(pybind11::str &&name, object &&format, pybind11::int_ &&offset)
0647                 : name{std::move(name)}, format{std::move(format)}, offset{std::move(offset)} {};
0648         };
0649         auto field_dict = attr("fields").cast<dict>();
0650         std::vector<field_descr> field_descriptors;
0651         field_descriptors.reserve(field_dict.size());
0652 
0653         for (auto field : field_dict.attr("items")()) {
0654             auto spec = field.cast<tuple>();
0655             auto name = spec[0].cast<pybind11::str>();
0656             auto spec_fo = spec[1].cast<tuple>();
0657             auto format = spec_fo[0].cast<dtype>();
0658             auto offset = spec_fo[1].cast<pybind11::int_>();
0659             if ((len(name) == 0u) && format.kind() == 'V') {
0660                 continue;
0661             }
0662             field_descriptors.emplace_back(
0663                 std::move(name), format.strip_padding(format.itemsize()), std::move(offset));
0664         }
0665 
0666         std::sort(field_descriptors.begin(),
0667                   field_descriptors.end(),
0668                   [](const field_descr &a, const field_descr &b) {
0669                       return a.offset.cast<int>() < b.offset.cast<int>();
0670                   });
0671 
0672         list names, formats, offsets;
0673         for (auto &descr : field_descriptors) {
0674             names.append(std::move(descr.name));
0675             formats.append(std::move(descr.format));
0676             offsets.append(std::move(descr.offset));
0677         }
0678         return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
0679     }
0680 };
0681 
0682 class array : public buffer {
0683 public:
0684     PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array)
0685 
0686     enum {
0687         c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_,
0688         f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_,
0689         forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_
0690     };
0691 
0692     array() : array(0, static_cast<const double *>(nullptr)) {}
0693 
0694     using ShapeContainer = detail::any_container<ssize_t>;
0695     using StridesContainer = detail::any_container<ssize_t>;
0696 
0697     // Constructs an array taking shape/strides from arbitrary container types
0698     array(const pybind11::dtype &dt,
0699           ShapeContainer shape,
0700           StridesContainer strides,
0701           const void *ptr = nullptr,
0702           handle base = handle()) {
0703 
0704         if (strides->empty()) {
0705             *strides = detail::c_strides(*shape, dt.itemsize());
0706         }
0707 
0708         auto ndim = shape->size();
0709         if (ndim != strides->size()) {
0710             pybind11_fail("NumPy: shape ndim doesn't match strides ndim");
0711         }
0712         auto descr = dt;
0713 
0714         int flags = 0;
0715         if (base && ptr) {
0716             if (isinstance<array>(base)) {
0717                 /* Copy flags from base (except ownership bit) */
0718                 flags = reinterpret_borrow<array>(base).flags()
0719                         & ~detail::npy_api::NPY_ARRAY_OWNDATA_;
0720             } else {
0721                 /* Writable by default, easy to downgrade later on if needed */
0722                 flags = detail::npy_api::NPY_ARRAY_WRITEABLE_;
0723             }
0724         }
0725 
0726         auto &api = detail::npy_api::get();
0727         auto tmp = reinterpret_steal<object>(api.PyArray_NewFromDescr_(
0728             api.PyArray_Type_,
0729             descr.release().ptr(),
0730             (int) ndim,
0731             // Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1)
0732             reinterpret_cast<Py_intptr_t *>(shape->data()),
0733             reinterpret_cast<Py_intptr_t *>(strides->data()),
0734             const_cast<void *>(ptr),
0735             flags,
0736             nullptr));
0737         if (!tmp) {
0738             throw error_already_set();
0739         }
0740         if (ptr) {
0741             if (base) {
0742                 api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr());
0743             } else {
0744                 tmp = reinterpret_steal<object>(
0745                     api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */));
0746             }
0747         }
0748         m_ptr = tmp.release().ptr();
0749     }
0750 
0751     array(const pybind11::dtype &dt,
0752           ShapeContainer shape,
0753           const void *ptr = nullptr,
0754           handle base = handle())
0755         : array(dt, std::move(shape), {}, ptr, base) {}
0756 
0757     template <typename T,
0758               typename
0759               = detail::enable_if_t<std::is_integral<T>::value && !std::is_same<bool, T>::value>>
0760     array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle())
0761         : array(dt, {{count}}, ptr, base) {}
0762 
0763     template <typename T>
0764     array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle())
0765         : array(pybind11::dtype::of<T>(), std::move(shape), std::move(strides), ptr, base) {}
0766 
0767     template <typename T>
0768     array(ShapeContainer shape, const T *ptr, handle base = handle())
0769         : array(std::move(shape), {}, ptr, base) {}
0770 
0771     template <typename T>
0772     explicit array(ssize_t count, const T *ptr, handle base = handle())
0773         : array({count}, {}, ptr, base) {}
0774 
0775     explicit array(const buffer_info &info, handle base = handle())
0776         : array(pybind11::dtype(info), info.shape, info.strides, info.ptr, base) {}
0777 
0778     /// Array descriptor (dtype)
0779     pybind11::dtype dtype() const {
0780         return reinterpret_borrow<pybind11::dtype>(detail::array_proxy(m_ptr)->descr);
0781     }
0782 
0783     /// Total number of elements
0784     ssize_t size() const {
0785         return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
0786     }
0787 
0788     /// Byte size of a single element
0789     ssize_t itemsize() const {
0790         return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize;
0791     }
0792 
0793     /// Total number of bytes
0794     ssize_t nbytes() const { return size() * itemsize(); }
0795 
0796     /// Number of dimensions
0797     ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; }
0798 
0799     /// Base object
0800     object base() const { return reinterpret_borrow<object>(detail::array_proxy(m_ptr)->base); }
0801 
0802     /// Dimensions of the array
0803     const ssize_t *shape() const { return detail::array_proxy(m_ptr)->dimensions; }
0804 
0805     /// Dimension along a given axis
0806     ssize_t shape(ssize_t dim) const {
0807         if (dim >= ndim()) {
0808             fail_dim_check(dim, "invalid axis");
0809         }
0810         return shape()[dim];
0811     }
0812 
0813     /// Strides of the array
0814     const ssize_t *strides() const { return detail::array_proxy(m_ptr)->strides; }
0815 
0816     /// Stride along a given axis
0817     ssize_t strides(ssize_t dim) const {
0818         if (dim >= ndim()) {
0819             fail_dim_check(dim, "invalid axis");
0820         }
0821         return strides()[dim];
0822     }
0823 
0824     /// Return the NumPy array flags
0825     int flags() const { return detail::array_proxy(m_ptr)->flags; }
0826 
0827     /// If set, the array is writeable (otherwise the buffer is read-only)
0828     bool writeable() const {
0829         return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_);
0830     }
0831 
0832     /// If set, the array owns the data (will be freed when the array is deleted)
0833     bool owndata() const {
0834         return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_);
0835     }
0836 
0837     /// Pointer to the contained data. If index is not provided, points to the
0838     /// beginning of the buffer. May throw if the index would lead to out of bounds access.
0839     template <typename... Ix>
0840     const void *data(Ix... index) const {
0841         return static_cast<const void *>(detail::array_proxy(m_ptr)->data + offset_at(index...));
0842     }
0843 
0844     /// Mutable pointer to the contained data. If index is not provided, points to the
0845     /// beginning of the buffer. May throw if the index would lead to out of bounds access.
0846     /// May throw if the array is not writeable.
0847     template <typename... Ix>
0848     void *mutable_data(Ix... index) {
0849         check_writeable();
0850         return static_cast<void *>(detail::array_proxy(m_ptr)->data + offset_at(index...));
0851     }
0852 
0853     /// Byte offset from beginning of the array to a given index (full or partial).
0854     /// May throw if the index would lead to out of bounds access.
0855     template <typename... Ix>
0856     ssize_t offset_at(Ix... index) const {
0857         if ((ssize_t) sizeof...(index) > ndim()) {
0858             fail_dim_check(sizeof...(index), "too many indices for an array");
0859         }
0860         return byte_offset(ssize_t(index)...);
0861     }
0862 
0863     ssize_t offset_at() const { return 0; }
0864 
0865     /// Item count from beginning of the array to a given index (full or partial).
0866     /// May throw if the index would lead to out of bounds access.
0867     template <typename... Ix>
0868     ssize_t index_at(Ix... index) const {
0869         return offset_at(index...) / itemsize();
0870     }
0871 
0872     /**
0873      * Returns a proxy object that provides access to the array's data without bounds or
0874      * dimensionality checking.  Will throw if the array is missing the `writeable` flag.  Use with
0875      * care: the array must not be destroyed or reshaped for the duration of the returned object,
0876      * and the caller must take care not to access invalid dimensions or dimension indices.
0877      */
0878     template <typename T, ssize_t Dims = -1>
0879     detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
0880         if (Dims >= 0 && ndim() != Dims) {
0881             throw std::domain_error("array has incorrect number of dimensions: "
0882                                     + std::to_string(ndim()) + "; expected "
0883                                     + std::to_string(Dims));
0884         }
0885         return detail::unchecked_mutable_reference<T, Dims>(
0886             mutable_data(), shape(), strides(), ndim());
0887     }
0888 
0889     /**
0890      * Returns a proxy object that provides const access to the array's data without bounds or
0891      * dimensionality checking.  Unlike `mutable_unchecked()`, this does not require that the
0892      * underlying array have the `writable` flag.  Use with care: the array must not be destroyed
0893      * or reshaped for the duration of the returned object, and the caller must take care not to
0894      * access invalid dimensions or dimension indices.
0895      */
0896     template <typename T, ssize_t Dims = -1>
0897     detail::unchecked_reference<T, Dims> unchecked() const & {
0898         if (Dims >= 0 && ndim() != Dims) {
0899             throw std::domain_error("array has incorrect number of dimensions: "
0900                                     + std::to_string(ndim()) + "; expected "
0901                                     + std::to_string(Dims));
0902         }
0903         return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
0904     }
0905 
0906     /// Return a new view with all of the dimensions of length 1 removed
0907     array squeeze() {
0908         auto &api = detail::npy_api::get();
0909         return reinterpret_steal<array>(api.PyArray_Squeeze_(m_ptr));
0910     }
0911 
0912     /// Resize array to given shape
0913     /// If refcheck is true and more that one reference exist to this array
0914     /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change
0915     void resize(ShapeContainer new_shape, bool refcheck = true) {
0916         detail::npy_api::PyArray_Dims d
0917             = {// Use reinterpret_cast for PyPy on Windows (remove if fixed, checked on 7.3.1)
0918                reinterpret_cast<Py_intptr_t *>(new_shape->data()),
0919                int(new_shape->size())};
0920         // try to resize, set ordering param to -1 cause it's not used anyway
0921         auto new_array = reinterpret_steal<object>(
0922             detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1));
0923         if (!new_array) {
0924             throw error_already_set();
0925         }
0926         if (isinstance<array>(new_array)) {
0927             *this = std::move(new_array);
0928         }
0929     }
0930 
0931     /// Optional `order` parameter omitted, to be added as needed.
0932     array reshape(ShapeContainer new_shape) {
0933         detail::npy_api::PyArray_Dims d
0934             = {reinterpret_cast<Py_intptr_t *>(new_shape->data()), int(new_shape->size())};
0935         auto new_array
0936             = reinterpret_steal<array>(detail::npy_api::get().PyArray_Newshape_(m_ptr, &d, 0));
0937         if (!new_array) {
0938             throw error_already_set();
0939         }
0940         return new_array;
0941     }
0942 
0943     /// Create a view of an array in a different data type.
0944     /// This function may fundamentally reinterpret the data in the array.
0945     /// It is the responsibility of the caller to ensure that this is safe.
0946     /// Only supports the `dtype` argument, the `type` argument is omitted,
0947     /// to be added as needed.
0948     array view(const std::string &dtype) {
0949         auto &api = detail::npy_api::get();
0950         auto new_view = reinterpret_steal<array>(api.PyArray_View_(
0951             m_ptr, dtype::from_args(pybind11::str(dtype)).release().ptr(), nullptr));
0952         if (!new_view) {
0953             throw error_already_set();
0954         }
0955         return new_view;
0956     }
0957 
0958     /// Ensure that the argument is a NumPy array
0959     /// In case of an error, nullptr is returned and the Python error is cleared.
0960     static array ensure(handle h, int ExtraFlags = 0) {
0961         auto result = reinterpret_steal<array>(raw_array(h.ptr(), ExtraFlags));
0962         if (!result) {
0963             PyErr_Clear();
0964         }
0965         return result;
0966     }
0967 
0968 protected:
0969     template <typename, typename>
0970     friend struct detail::npy_format_descriptor;
0971 
0972     void fail_dim_check(ssize_t dim, const std::string &msg) const {
0973         throw index_error(msg + ": " + std::to_string(dim) + " (ndim = " + std::to_string(ndim())
0974                           + ')');
0975     }
0976 
0977     template <typename... Ix>
0978     ssize_t byte_offset(Ix... index) const {
0979         check_dimensions(index...);
0980         return detail::byte_offset_unsafe(strides(), ssize_t(index)...);
0981     }
0982 
0983     void check_writeable() const {
0984         if (!writeable()) {
0985             throw std::domain_error("array is not writeable");
0986         }
0987     }
0988 
0989     template <typename... Ix>
0990     void check_dimensions(Ix... index) const {
0991         check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...);
0992     }
0993 
0994     void check_dimensions_impl(ssize_t, const ssize_t *) const {}
0995 
0996     template <typename... Ix>
0997     void check_dimensions_impl(ssize_t axis, const ssize_t *shape, ssize_t i, Ix... index) const {
0998         if (i >= *shape) {
0999             throw index_error(std::string("index ") + std::to_string(i)
1000                               + " is out of bounds for axis " + std::to_string(axis)
1001                               + " with size " + std::to_string(*shape));
1002         }
1003         check_dimensions_impl(axis + 1, shape + 1, index...);
1004     }
1005 
1006     /// Create array from any object -- always returns a new reference
1007     static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) {
1008         if (ptr == nullptr) {
1009             PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr");
1010             return nullptr;
1011         }
1012         return detail::npy_api::get().PyArray_FromAny_(
1013             ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr);
1014     }
1015 };
1016 
1017 template <typename T, int ExtraFlags = array::forcecast>
1018 class array_t : public array {
1019 private:
1020     struct private_ctor {};
1021     // Delegating constructor needed when both moving and accessing in the same constructor
1022     array_t(private_ctor,
1023             ShapeContainer &&shape,
1024             StridesContainer &&strides,
1025             const T *ptr,
1026             handle base)
1027         : array(std::move(shape), std::move(strides), ptr, base) {}
1028 
1029 public:
1030     static_assert(!detail::array_info<T>::is_array, "Array types cannot be used with array_t");
1031 
1032     using value_type = T;
1033 
1034     array_t() : array(0, static_cast<const T *>(nullptr)) {}
1035     array_t(handle h, borrowed_t) : array(h, borrowed_t{}) {}
1036     array_t(handle h, stolen_t) : array(h, stolen_t{}) {}
1037 
1038     PYBIND11_DEPRECATED("Use array_t<T>::ensure() instead")
1039     array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) {
1040         if (!m_ptr) {
1041             PyErr_Clear();
1042         }
1043         if (!is_borrowed) {
1044             Py_XDECREF(h.ptr());
1045         }
1046     }
1047 
1048     // NOLINTNEXTLINE(google-explicit-constructor)
1049     array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
1050         if (!m_ptr) {
1051             throw error_already_set();
1052         }
1053     }
1054 
1055     explicit array_t(const buffer_info &info, handle base = handle()) : array(info, base) {}
1056 
1057     array_t(ShapeContainer shape,
1058             StridesContainer strides,
1059             const T *ptr = nullptr,
1060             handle base = handle())
1061         : array(std::move(shape), std::move(strides), ptr, base) {}
1062 
1063     explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
1064         : array_t(private_ctor{},
1065                   std::move(shape),
1066                   (ExtraFlags & f_style) != 0 ? detail::f_strides(*shape, itemsize())
1067                                               : detail::c_strides(*shape, itemsize()),
1068                   ptr,
1069                   base) {}
1070 
1071     explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle())
1072         : array({count}, {}, ptr, base) {}
1073 
1074     constexpr ssize_t itemsize() const { return sizeof(T); }
1075 
1076     template <typename... Ix>
1077     ssize_t index_at(Ix... index) const {
1078         return offset_at(index...) / itemsize();
1079     }
1080 
1081     template <typename... Ix>
1082     const T *data(Ix... index) const {
1083         return static_cast<const T *>(array::data(index...));
1084     }
1085 
1086     template <typename... Ix>
1087     T *mutable_data(Ix... index) {
1088         return static_cast<T *>(array::mutable_data(index...));
1089     }
1090 
1091     // Reference to element at a given index
1092     template <typename... Ix>
1093     const T &at(Ix... index) const {
1094         if ((ssize_t) sizeof...(index) != ndim()) {
1095             fail_dim_check(sizeof...(index), "index dimension mismatch");
1096         }
1097         return *(static_cast<const T *>(array::data())
1098                  + byte_offset(ssize_t(index)...) / itemsize());
1099     }
1100 
1101     // Mutable reference to element at a given index
1102     template <typename... Ix>
1103     T &mutable_at(Ix... index) {
1104         if ((ssize_t) sizeof...(index) != ndim()) {
1105             fail_dim_check(sizeof...(index), "index dimension mismatch");
1106         }
1107         return *(static_cast<T *>(array::mutable_data())
1108                  + byte_offset(ssize_t(index)...) / itemsize());
1109     }
1110 
1111     /**
1112      * Returns a proxy object that provides access to the array's data without bounds or
1113      * dimensionality checking.  Will throw if the array is missing the `writeable` flag.  Use with
1114      * care: the array must not be destroyed or reshaped for the duration of the returned object,
1115      * and the caller must take care not to access invalid dimensions or dimension indices.
1116      */
1117     template <ssize_t Dims = -1>
1118     detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
1119         return array::mutable_unchecked<T, Dims>();
1120     }
1121 
1122     /**
1123      * Returns a proxy object that provides const access to the array's data without bounds or
1124      * dimensionality checking.  Unlike `unchecked()`, this does not require that the underlying
1125      * array have the `writable` flag.  Use with care: the array must not be destroyed or reshaped
1126      * for the duration of the returned object, and the caller must take care not to access invalid
1127      * dimensions or dimension indices.
1128      */
1129     template <ssize_t Dims = -1>
1130     detail::unchecked_reference<T, Dims> unchecked() const & {
1131         return array::unchecked<T, Dims>();
1132     }
1133 
1134     /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert
1135     /// it).  In case of an error, nullptr is returned and the Python error is cleared.
1136     static array_t ensure(handle h) {
1137         auto result = reinterpret_steal<array_t>(raw_array_t(h.ptr()));
1138         if (!result) {
1139             PyErr_Clear();
1140         }
1141         return result;
1142     }
1143 
1144     static bool check_(handle h) {
1145         const auto &api = detail::npy_api::get();
1146         return api.PyArray_Check_(h.ptr())
1147                && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr,
1148                                           dtype::of<T>().ptr())
1149                && detail::check_flags(h.ptr(), ExtraFlags & (array::c_style | array::f_style));
1150     }
1151 
1152 protected:
1153     /// Create array from any object -- always returns a new reference
1154     static PyObject *raw_array_t(PyObject *ptr) {
1155         if (ptr == nullptr) {
1156             PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr");
1157             return nullptr;
1158         }
1159         return detail::npy_api::get().PyArray_FromAny_(ptr,
1160                                                        dtype::of<T>().release().ptr(),
1161                                                        0,
1162                                                        0,
1163                                                        detail::npy_api::NPY_ARRAY_ENSUREARRAY_
1164                                                            | ExtraFlags,
1165                                                        nullptr);
1166     }
1167 };
1168 
1169 template <typename T>
1170 struct format_descriptor<T, detail::enable_if_t<detail::is_pod_struct<T>::value>> {
1171     static std::string format() {
1172         return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::format();
1173     }
1174 };
1175 
1176 template <size_t N>
1177 struct format_descriptor<char[N]> {
1178     static std::string format() { return std::to_string(N) + 's'; }
1179 };
1180 template <size_t N>
1181 struct format_descriptor<std::array<char, N>> {
1182     static std::string format() { return std::to_string(N) + 's'; }
1183 };
1184 
1185 template <typename T>
1186 struct format_descriptor<T, detail::enable_if_t<std::is_enum<T>::value>> {
1187     static std::string format() {
1188         return format_descriptor<
1189             typename std::remove_cv<typename std::underlying_type<T>::type>::type>::format();
1190     }
1191 };
1192 
1193 template <typename T>
1194 struct format_descriptor<T, detail::enable_if_t<detail::array_info<T>::is_array>> {
1195     static std::string format() {
1196         using namespace detail;
1197         static constexpr auto extents = const_name("(") + array_info<T>::extents + const_name(")");
1198         return extents.text + format_descriptor<remove_all_extents_t<T>>::format();
1199     }
1200 };
1201 
1202 PYBIND11_NAMESPACE_BEGIN(detail)
1203 template <typename T, int ExtraFlags>
1204 struct pyobject_caster<array_t<T, ExtraFlags>> {
1205     using type = array_t<T, ExtraFlags>;
1206 
1207     bool load(handle src, bool convert) {
1208         if (!convert && !type::check_(src)) {
1209             return false;
1210         }
1211         value = type::ensure(src);
1212         return static_cast<bool>(value);
1213     }
1214 
1215     static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
1216         return src.inc_ref();
1217     }
1218     PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
1219 };
1220 
1221 template <typename T>
1222 struct compare_buffer_info<T, detail::enable_if_t<detail::is_pod_struct<T>::value>> {
1223     static bool compare(const buffer_info &b) {
1224         return npy_api::get().PyArray_EquivTypes_(dtype::of<T>().ptr(), dtype(b).ptr());
1225     }
1226 };
1227 
1228 template <typename T, typename = void>
1229 struct npy_format_descriptor_name;
1230 
1231 template <typename T>
1232 struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
1233     static constexpr auto name = const_name<std::is_same<T, bool>::value>(
1234         const_name("bool"),
1235         const_name<std::is_signed<T>::value>("numpy.int", "numpy.uint")
1236             + const_name<sizeof(T) * 8>());
1237 };
1238 
1239 template <typename T>
1240 struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::value>> {
1241     static constexpr auto name = const_name < std::is_same<T, float>::value
1242                                  || std::is_same<T, const float>::value
1243                                  || std::is_same<T, double>::value
1244                                  || std::is_same<T, const double>::value
1245                                         > (const_name("numpy.float") + const_name<sizeof(T) * 8>(),
1246                                            const_name("numpy.longdouble"));
1247 };
1248 
1249 template <typename T>
1250 struct npy_format_descriptor_name<T, enable_if_t<is_complex<T>::value>> {
1251     static constexpr auto name = const_name < std::is_same<typename T::value_type, float>::value
1252                                  || std::is_same<typename T::value_type, const float>::value
1253                                  || std::is_same<typename T::value_type, double>::value
1254                                  || std::is_same<typename T::value_type, const double>::value
1255                                         > (const_name("numpy.complex")
1256                                                + const_name<sizeof(typename T::value_type) * 16>(),
1257                                            const_name("numpy.longcomplex"));
1258 };
1259 
1260 template <typename T>
1261 struct npy_format_descriptor<
1262     T,
1263     enable_if_t<satisfies_any_of<T, std::is_arithmetic, is_complex>::value>>
1264     : npy_format_descriptor_name<T> {
1265 private:
1266     // NB: the order here must match the one in common.h
1267     constexpr static const int values[15] = {npy_api::NPY_BOOL_,
1268                                              npy_api::NPY_BYTE_,
1269                                              npy_api::NPY_UBYTE_,
1270                                              npy_api::NPY_INT16_,
1271                                              npy_api::NPY_UINT16_,
1272                                              npy_api::NPY_INT32_,
1273                                              npy_api::NPY_UINT32_,
1274                                              npy_api::NPY_INT64_,
1275                                              npy_api::NPY_UINT64_,
1276                                              npy_api::NPY_FLOAT_,
1277                                              npy_api::NPY_DOUBLE_,
1278                                              npy_api::NPY_LONGDOUBLE_,
1279                                              npy_api::NPY_CFLOAT_,
1280                                              npy_api::NPY_CDOUBLE_,
1281                                              npy_api::NPY_CLONGDOUBLE_};
1282 
1283 public:
1284     static constexpr int value = values[detail::is_fmt_numeric<T>::index];
1285 
1286     static pybind11::dtype dtype() {
1287         if (auto *ptr = npy_api::get().PyArray_DescrFromType_(value)) {
1288             return reinterpret_steal<pybind11::dtype>(ptr);
1289         }
1290         pybind11_fail("Unsupported buffer format!");
1291     }
1292 };
1293 
1294 #define PYBIND11_DECL_CHAR_FMT                                                                    \
1295     static constexpr auto name = const_name("S") + const_name<N>();                               \
1296     static pybind11::dtype dtype() {                                                              \
1297         return pybind11::dtype(std::string("S") + std::to_string(N));                             \
1298     }
1299 template <size_t N>
1300 struct npy_format_descriptor<char[N]> {
1301     PYBIND11_DECL_CHAR_FMT
1302 };
1303 template <size_t N>
1304 struct npy_format_descriptor<std::array<char, N>> {
1305     PYBIND11_DECL_CHAR_FMT
1306 };
1307 #undef PYBIND11_DECL_CHAR_FMT
1308 
1309 template <typename T>
1310 struct npy_format_descriptor<T, enable_if_t<array_info<T>::is_array>> {
1311 private:
1312     using base_descr = npy_format_descriptor<typename array_info<T>::type>;
1313 
1314 public:
1315     static_assert(!array_info<T>::is_empty, "Zero-sized arrays are not supported");
1316 
1317     static constexpr auto name
1318         = const_name("(") + array_info<T>::extents + const_name(")") + base_descr::name;
1319     static pybind11::dtype dtype() {
1320         list shape;
1321         array_info<T>::append_extents(shape);
1322         return pybind11::dtype::from_args(
1323             pybind11::make_tuple(base_descr::dtype(), std::move(shape)));
1324     }
1325 };
1326 
1327 template <typename T>
1328 struct npy_format_descriptor<T, enable_if_t<std::is_enum<T>::value>> {
1329 private:
1330     using base_descr = npy_format_descriptor<typename std::underlying_type<T>::type>;
1331 
1332 public:
1333     static constexpr auto name = base_descr::name;
1334     static pybind11::dtype dtype() { return base_descr::dtype(); }
1335 };
1336 
1337 struct field_descriptor {
1338     const char *name;
1339     ssize_t offset;
1340     ssize_t size;
1341     std::string format;
1342     dtype descr;
1343 };
1344 
1345 PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor> fields,
1346                                                  const std::type_info &tinfo,
1347                                                  ssize_t itemsize,
1348                                                  bool (*direct_converter)(PyObject *, void *&)) {
1349 
1350     auto &numpy_internals = get_numpy_internals();
1351     if (numpy_internals.get_type_info(tinfo, false)) {
1352         pybind11_fail("NumPy: dtype is already registered");
1353     }
1354 
1355     // Use ordered fields because order matters as of NumPy 1.14:
1356     // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays
1357     std::vector<field_descriptor> ordered_fields(std::move(fields));
1358     std::sort(
1359         ordered_fields.begin(),
1360         ordered_fields.end(),
1361         [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
1362 
1363     list names, formats, offsets;
1364     for (auto &field : ordered_fields) {
1365         if (!field.descr) {
1366             pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ "
1367                           + tinfo.name());
1368         }
1369         names.append(pybind11::str(field.name));
1370         formats.append(field.descr);
1371         offsets.append(pybind11::int_(field.offset));
1372     }
1373     auto *dtype_ptr
1374         = pybind11::dtype(std::move(names), std::move(formats), std::move(offsets), itemsize)
1375               .release()
1376               .ptr();
1377 
1378     // There is an existing bug in NumPy (as of v1.11): trailing bytes are
1379     // not encoded explicitly into the format string. This will supposedly
1380     // get fixed in v1.12; for further details, see these:
1381     // - https://github.com/numpy/numpy/issues/7797
1382     // - https://github.com/numpy/numpy/pull/7798
1383     // Because of this, we won't use numpy's logic to generate buffer format
1384     // strings and will just do it ourselves.
1385     ssize_t offset = 0;
1386     std::ostringstream oss;
1387     // mark the structure as unaligned with '^', because numpy and C++ don't
1388     // always agree about alignment (particularly for complex), and we're
1389     // explicitly listing all our padding. This depends on none of the fields
1390     // overriding the endianness. Putting the ^ in front of individual fields
1391     // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049
1392     oss << "^T{";
1393     for (auto &field : ordered_fields) {
1394         if (field.offset > offset) {
1395             oss << (field.offset - offset) << 'x';
1396         }
1397         oss << field.format << ':' << field.name << ':';
1398         offset = field.offset + field.size;
1399     }
1400     if (itemsize > offset) {
1401         oss << (itemsize - offset) << 'x';
1402     }
1403     oss << '}';
1404     auto format_str = oss.str();
1405 
1406     // Smoke test: verify that NumPy properly parses our buffer format string
1407     auto &api = npy_api::get();
1408     auto arr = array(buffer_info(nullptr, itemsize, format_str, 1));
1409     if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) {
1410         pybind11_fail("NumPy: invalid buffer descriptor!");
1411     }
1412 
1413     auto tindex = std::type_index(tinfo);
1414     numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
1415     get_internals().direct_conversions[tindex].push_back(direct_converter);
1416 }
1417 
1418 template <typename T, typename SFINAE>
1419 struct npy_format_descriptor {
1420     static_assert(is_pod_struct<T>::value,
1421                   "Attempt to use a non-POD or unimplemented POD type as a numpy dtype");
1422 
1423     static constexpr auto name = make_caster<T>::name;
1424 
1425     static pybind11::dtype dtype() { return reinterpret_borrow<pybind11::dtype>(dtype_ptr()); }
1426 
1427     static std::string format() {
1428         static auto format_str = get_numpy_internals().get_type_info<T>(true)->format_str;
1429         return format_str;
1430     }
1431 
1432     static void register_dtype(any_container<field_descriptor> fields) {
1433         register_structured_dtype(std::move(fields),
1434                                   typeid(typename std::remove_cv<T>::type),
1435                                   sizeof(T),
1436                                   &direct_converter);
1437     }
1438 
1439 private:
1440     static PyObject *dtype_ptr() {
1441         static PyObject *ptr = get_numpy_internals().get_type_info<T>(true)->dtype_ptr;
1442         return ptr;
1443     }
1444 
1445     static bool direct_converter(PyObject *obj, void *&value) {
1446         auto &api = npy_api::get();
1447         if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) {
1448             return false;
1449         }
1450         if (auto descr = reinterpret_steal<object>(api.PyArray_DescrFromScalar_(obj))) {
1451             if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) {
1452                 value = ((PyVoidScalarObject_Proxy *) obj)->obval;
1453                 return true;
1454             }
1455         }
1456         return false;
1457     }
1458 };
1459 
1460 #ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code)
1461 #    define PYBIND11_NUMPY_DTYPE(Type, ...) ((void) 0)
1462 #    define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void) 0)
1463 #else
1464 
1465 #    define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name)                                          \
1466         ::pybind11::detail::field_descriptor {                                                    \
1467             Name, offsetof(T, Field), sizeof(decltype(std::declval<T>().Field)),                  \
1468                 ::pybind11::format_descriptor<decltype(std::declval<T>().Field)>::format(),       \
1469                 ::pybind11::detail::npy_format_descriptor<                                        \
1470                     decltype(std::declval<T>().Field)>::dtype()                                   \
1471         }
1472 
1473 // Extract name, offset and format descriptor for a struct field
1474 #    define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field)
1475 
1476 // The main idea of this macro is borrowed from https://github.com/swansontec/map-macro
1477 // (C) William Swanson, Paul Fultz
1478 #    define PYBIND11_EVAL0(...) __VA_ARGS__
1479 #    define PYBIND11_EVAL1(...) PYBIND11_EVAL0(PYBIND11_EVAL0(PYBIND11_EVAL0(__VA_ARGS__)))
1480 #    define PYBIND11_EVAL2(...) PYBIND11_EVAL1(PYBIND11_EVAL1(PYBIND11_EVAL1(__VA_ARGS__)))
1481 #    define PYBIND11_EVAL3(...) PYBIND11_EVAL2(PYBIND11_EVAL2(PYBIND11_EVAL2(__VA_ARGS__)))
1482 #    define PYBIND11_EVAL4(...) PYBIND11_EVAL3(PYBIND11_EVAL3(PYBIND11_EVAL3(__VA_ARGS__)))
1483 #    define PYBIND11_EVAL(...) PYBIND11_EVAL4(PYBIND11_EVAL4(PYBIND11_EVAL4(__VA_ARGS__)))
1484 #    define PYBIND11_MAP_END(...)
1485 #    define PYBIND11_MAP_OUT
1486 #    define PYBIND11_MAP_COMMA ,
1487 #    define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END
1488 #    define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT
1489 #    define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0(test, next, 0)
1490 #    define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1(PYBIND11_MAP_GET_END test, next)
1491 #    if defined(_MSC_VER)                                                                         \
1492         && !defined(__clang__) // MSVC is not as eager to expand macros, hence this workaround
1493 #        define PYBIND11_MAP_LIST_NEXT1(test, next)                                               \
1494             PYBIND11_EVAL0(PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0))
1495 #    else
1496 #        define PYBIND11_MAP_LIST_NEXT1(test, next)                                               \
1497             PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0)
1498 #    endif
1499 #    define PYBIND11_MAP_LIST_NEXT(test, next)                                                    \
1500         PYBIND11_MAP_LIST_NEXT1(PYBIND11_MAP_GET_END test, next)
1501 #    define PYBIND11_MAP_LIST0(f, t, x, peek, ...)                                                \
1502         f(t, x) PYBIND11_MAP_LIST_NEXT(peek, PYBIND11_MAP_LIST1)(f, t, peek, __VA_ARGS__)
1503 #    define PYBIND11_MAP_LIST1(f, t, x, peek, ...)                                                \
1504         f(t, x) PYBIND11_MAP_LIST_NEXT(peek, PYBIND11_MAP_LIST0)(f, t, peek, __VA_ARGS__)
1505 // PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ...
1506 #    define PYBIND11_MAP_LIST(f, t, ...)                                                          \
1507         PYBIND11_EVAL(PYBIND11_MAP_LIST1(f, t, __VA_ARGS__, (), 0))
1508 
1509 #    define PYBIND11_NUMPY_DTYPE(Type, ...)                                                       \
1510         ::pybind11::detail::npy_format_descriptor<Type>::register_dtype(                          \
1511             ::std::vector<::pybind11::detail::field_descriptor>{                                  \
1512                 PYBIND11_MAP_LIST(PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
1513 
1514 #    if defined(_MSC_VER) && !defined(__clang__)
1515 #        define PYBIND11_MAP2_LIST_NEXT1(test, next)                                              \
1516             PYBIND11_EVAL0(PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0))
1517 #    else
1518 #        define PYBIND11_MAP2_LIST_NEXT1(test, next)                                              \
1519             PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0)
1520 #    endif
1521 #    define PYBIND11_MAP2_LIST_NEXT(test, next)                                                   \
1522         PYBIND11_MAP2_LIST_NEXT1(PYBIND11_MAP_GET_END test, next)
1523 #    define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...)                                          \
1524         f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT(peek, PYBIND11_MAP2_LIST1)(f, t, peek, __VA_ARGS__)
1525 #    define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...)                                          \
1526         f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT(peek, PYBIND11_MAP2_LIST0)(f, t, peek, __VA_ARGS__)
1527 // PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ...
1528 #    define PYBIND11_MAP2_LIST(f, t, ...)                                                         \
1529         PYBIND11_EVAL(PYBIND11_MAP2_LIST1(f, t, __VA_ARGS__, (), 0))
1530 
1531 #    define PYBIND11_NUMPY_DTYPE_EX(Type, ...)                                                    \
1532         ::pybind11::detail::npy_format_descriptor<Type>::register_dtype(                          \
1533             ::std::vector<::pybind11::detail::field_descriptor>{                                  \
1534                 PYBIND11_MAP2_LIST(PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
1535 
1536 #endif // __CLION_IDE__
1537 
1538 class common_iterator {
1539 public:
1540     using container_type = std::vector<ssize_t>;
1541     using value_type = container_type::value_type;
1542     using size_type = container_type::size_type;
1543 
1544     common_iterator() : m_strides() {}
1545 
1546     common_iterator(void *ptr, const container_type &strides, const container_type &shape)
1547         : p_ptr(reinterpret_cast<char *>(ptr)), m_strides(strides.size()) {
1548         m_strides.back() = static_cast<value_type>(strides.back());
1549         for (size_type i = m_strides.size() - 1; i != 0; --i) {
1550             size_type j = i - 1;
1551             auto s = static_cast<value_type>(shape[i]);
1552             m_strides[j] = strides[j] + m_strides[i] - strides[i] * s;
1553         }
1554     }
1555 
1556     void increment(size_type dim) { p_ptr += m_strides[dim]; }
1557 
1558     void *data() const { return p_ptr; }
1559 
1560 private:
1561     char *p_ptr{nullptr};
1562     container_type m_strides;
1563 };
1564 
1565 template <size_t N>
1566 class multi_array_iterator {
1567 public:
1568     using container_type = std::vector<ssize_t>;
1569 
1570     multi_array_iterator(const std::array<buffer_info, N> &buffers, const container_type &shape)
1571         : m_shape(shape.size()), m_index(shape.size(), 0), m_common_iterator() {
1572 
1573         // Manual copy to avoid conversion warning if using std::copy
1574         for (size_t i = 0; i < shape.size(); ++i) {
1575             m_shape[i] = shape[i];
1576         }
1577 
1578         container_type strides(shape.size());
1579         for (size_t i = 0; i < N; ++i) {
1580             init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
1581         }
1582     }
1583 
1584     multi_array_iterator &operator++() {
1585         for (size_t j = m_index.size(); j != 0; --j) {
1586             size_t i = j - 1;
1587             if (++m_index[i] != m_shape[i]) {
1588                 increment_common_iterator(i);
1589                 break;
1590             }
1591             m_index[i] = 0;
1592         }
1593         return *this;
1594     }
1595 
1596     template <size_t K, class T = void>
1597     T *data() const {
1598         return reinterpret_cast<T *>(m_common_iterator[K].data());
1599     }
1600 
1601 private:
1602     using common_iter = common_iterator;
1603 
1604     void init_common_iterator(const buffer_info &buffer,
1605                               const container_type &shape,
1606                               common_iter &iterator,
1607                               container_type &strides) {
1608         auto buffer_shape_iter = buffer.shape.rbegin();
1609         auto buffer_strides_iter = buffer.strides.rbegin();
1610         auto shape_iter = shape.rbegin();
1611         auto strides_iter = strides.rbegin();
1612 
1613         while (buffer_shape_iter != buffer.shape.rend()) {
1614             if (*shape_iter == *buffer_shape_iter) {
1615                 *strides_iter = *buffer_strides_iter;
1616             } else {
1617                 *strides_iter = 0;
1618             }
1619 
1620             ++buffer_shape_iter;
1621             ++buffer_strides_iter;
1622             ++shape_iter;
1623             ++strides_iter;
1624         }
1625 
1626         std::fill(strides_iter, strides.rend(), 0);
1627         iterator = common_iter(buffer.ptr, strides, shape);
1628     }
1629 
1630     void increment_common_iterator(size_t dim) {
1631         for (auto &iter : m_common_iterator) {
1632             iter.increment(dim);
1633         }
1634     }
1635 
1636     container_type m_shape;
1637     container_type m_index;
1638     std::array<common_iter, N> m_common_iterator;
1639 };
1640 
1641 enum class broadcast_trivial { non_trivial, c_trivial, f_trivial };
1642 
1643 // Populates the shape and number of dimensions for the set of buffers.  Returns a
1644 // broadcast_trivial enum value indicating whether the broadcast is "trivial"--that is, has each
1645 // buffer being either a singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous
1646 // (`f_trivial`) storage buffer; returns `non_trivial` otherwise.
1647 template <size_t N>
1648 broadcast_trivial
1649 broadcast(const std::array<buffer_info, N> &buffers, ssize_t &ndim, std::vector<ssize_t> &shape) {
1650     ndim = std::accumulate(
1651         buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) {
1652             return std::max(res, buf.ndim);
1653         });
1654 
1655     shape.clear();
1656     shape.resize((size_t) ndim, 1);
1657 
1658     // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1
1659     // or the full size).
1660     for (size_t i = 0; i < N; ++i) {
1661         auto res_iter = shape.rbegin();
1662         auto end = buffers[i].shape.rend();
1663         for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end;
1664              ++shape_iter, ++res_iter) {
1665             const auto &dim_size_in = *shape_iter;
1666             auto &dim_size_out = *res_iter;
1667 
1668             // Each input dimension can either be 1 or `n`, but `n` values must match across
1669             // buffers
1670             if (dim_size_out == 1) {
1671                 dim_size_out = dim_size_in;
1672             } else if (dim_size_in != 1 && dim_size_in != dim_size_out) {
1673                 pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!");
1674             }
1675         }
1676     }
1677 
1678     bool trivial_broadcast_c = true;
1679     bool trivial_broadcast_f = true;
1680     for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) {
1681         if (buffers[i].size == 1) {
1682             continue;
1683         }
1684 
1685         // Require the same number of dimensions:
1686         if (buffers[i].ndim != ndim) {
1687             return broadcast_trivial::non_trivial;
1688         }
1689 
1690         // Require all dimensions be full-size:
1691         if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) {
1692             return broadcast_trivial::non_trivial;
1693         }
1694 
1695         // Check for C contiguity (but only if previous inputs were also C contiguous)
1696         if (trivial_broadcast_c) {
1697             ssize_t expect_stride = buffers[i].itemsize;
1698             auto end = buffers[i].shape.crend();
1699             for (auto shape_iter = buffers[i].shape.crbegin(),
1700                       stride_iter = buffers[i].strides.crbegin();
1701                  trivial_broadcast_c && shape_iter != end;
1702                  ++shape_iter, ++stride_iter) {
1703                 if (expect_stride == *stride_iter) {
1704                     expect_stride *= *shape_iter;
1705                 } else {
1706                     trivial_broadcast_c = false;
1707                 }
1708             }
1709         }
1710 
1711         // Check for Fortran contiguity (if previous inputs were also F contiguous)
1712         if (trivial_broadcast_f) {
1713             ssize_t expect_stride = buffers[i].itemsize;
1714             auto end = buffers[i].shape.cend();
1715             for (auto shape_iter = buffers[i].shape.cbegin(),
1716                       stride_iter = buffers[i].strides.cbegin();
1717                  trivial_broadcast_f && shape_iter != end;
1718                  ++shape_iter, ++stride_iter) {
1719                 if (expect_stride == *stride_iter) {
1720                     expect_stride *= *shape_iter;
1721                 } else {
1722                     trivial_broadcast_f = false;
1723                 }
1724             }
1725         }
1726     }
1727 
1728     return trivial_broadcast_c   ? broadcast_trivial::c_trivial
1729            : trivial_broadcast_f ? broadcast_trivial::f_trivial
1730                                  : broadcast_trivial::non_trivial;
1731 }
1732 
1733 template <typename T>
1734 struct vectorize_arg {
1735     static_assert(!std::is_rvalue_reference<T>::value,
1736                   "Functions with rvalue reference arguments cannot be vectorized");
1737     // The wrapped function gets called with this type:
1738     using call_type = remove_reference_t<T>;
1739     // Is this a vectorized argument?
1740     static constexpr bool vectorize
1741         = satisfies_any_of<call_type, std::is_arithmetic, is_complex, is_pod>::value
1742           && satisfies_none_of<call_type,
1743                                std::is_pointer,
1744                                std::is_array,
1745                                is_std_array,
1746                                std::is_enum>::value
1747           && (!std::is_reference<T>::value
1748               || (std::is_lvalue_reference<T>::value && std::is_const<call_type>::value));
1749     // Accept this type: an array for vectorized types, otherwise the type as-is:
1750     using type = conditional_t<vectorize, array_t<remove_cv_t<call_type>, array::forcecast>, T>;
1751 };
1752 
1753 // py::vectorize when a return type is present
1754 template <typename Func, typename Return, typename... Args>
1755 struct vectorize_returned_array {
1756     using Type = array_t<Return>;
1757 
1758     static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) {
1759         if (trivial == broadcast_trivial::f_trivial) {
1760             return array_t<Return, array::f_style>(shape);
1761         }
1762         return array_t<Return>(shape);
1763     }
1764 
1765     static Return *mutable_data(Type &array) { return array.mutable_data(); }
1766 
1767     static Return call(Func &f, Args &...args) { return f(args...); }
1768 
1769     static void call(Return *out, size_t i, Func &f, Args &...args) { out[i] = f(args...); }
1770 };
1771 
1772 // py::vectorize when a return type is not present
1773 template <typename Func, typename... Args>
1774 struct vectorize_returned_array<Func, void, Args...> {
1775     using Type = none;
1776 
1777     static Type create(broadcast_trivial, const std::vector<ssize_t> &) { return none(); }
1778 
1779     static void *mutable_data(Type &) { return nullptr; }
1780 
1781     static detail::void_type call(Func &f, Args &...args) {
1782         f(args...);
1783         return {};
1784     }
1785 
1786     static void call(void *, size_t, Func &f, Args &...args) { f(args...); }
1787 };
1788 
1789 template <typename Func, typename Return, typename... Args>
1790 struct vectorize_helper {
1791 
1792 // NVCC for some reason breaks if NVectorized is private
1793 #ifdef __CUDACC__
1794 public:
1795 #else
1796 private:
1797 #endif
1798 
1799     static constexpr size_t N = sizeof...(Args);
1800     static constexpr size_t NVectorized = constexpr_sum(vectorize_arg<Args>::vectorize...);
1801     static_assert(
1802         NVectorized >= 1,
1803         "pybind11::vectorize(...) requires a function with at least one vectorizable argument");
1804 
1805 public:
1806     template <typename T,
1807               // SFINAE to prevent shadowing the copy constructor.
1808               typename = detail::enable_if_t<
1809                   !std::is_same<vectorize_helper, typename std::decay<T>::type>::value>>
1810     explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) {}
1811 
1812     object operator()(typename vectorize_arg<Args>::type... args) {
1813         return run(args...,
1814                    make_index_sequence<N>(),
1815                    select_indices<vectorize_arg<Args>::vectorize...>(),
1816                    make_index_sequence<NVectorized>());
1817     }
1818 
1819 private:
1820     remove_reference_t<Func> f;
1821 
1822     // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling
1823     // with "/permissive-" flag when arg_call_types is manually inlined.
1824     using arg_call_types = std::tuple<typename vectorize_arg<Args>::call_type...>;
1825     template <size_t Index>
1826     using param_n_t = typename std::tuple_element<Index, arg_call_types>::type;
1827 
1828     using returned_array = vectorize_returned_array<Func, Return, Args...>;
1829 
1830     // Runs a vectorized function given arguments tuple and three index sequences:
1831     //     - Index is the full set of 0 ... (N-1) argument indices;
1832     //     - VIndex is the subset of argument indices with vectorized parameters, letting us access
1833     //       vectorized arguments (anything not in this sequence is passed through)
1834     //     - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that
1835     //       we can store vectorized buffer_infos in an array (argument VIndex has its buffer at
1836     //       index BIndex in the array).
1837     template <size_t... Index, size_t... VIndex, size_t... BIndex>
1838     object run(typename vectorize_arg<Args>::type &...args,
1839                index_sequence<Index...> i_seq,
1840                index_sequence<VIndex...> vi_seq,
1841                index_sequence<BIndex...> bi_seq) {
1842 
1843         // Pointers to values the function was called with; the vectorized ones set here will start
1844         // out as array_t<T> pointers, but they will be changed them to T pointers before we make
1845         // call the wrapped function.  Non-vectorized pointers are left as-is.
1846         std::array<void *, N> params{{&args...}};
1847 
1848         // The array of `buffer_info`s of vectorized arguments:
1849         std::array<buffer_info, NVectorized> buffers{
1850             {reinterpret_cast<array *>(params[VIndex])->request()...}};
1851 
1852         /* Determine dimensions parameters of output array */
1853         ssize_t nd = 0;
1854         std::vector<ssize_t> shape(0);
1855         auto trivial = broadcast(buffers, nd, shape);
1856         auto ndim = (size_t) nd;
1857 
1858         size_t size
1859             = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies<size_t>());
1860 
1861         // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e.
1862         // not wrapped in an array).
1863         if (size == 1 && ndim == 0) {
1864             PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr);
1865             return cast(
1866                 returned_array::call(f, *reinterpret_cast<param_n_t<Index> *>(params[Index])...));
1867         }
1868 
1869         auto result = returned_array::create(trivial, shape);
1870 
1871         PYBIND11_WARNING_PUSH
1872 #ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
1873         PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
1874 #endif
1875 
1876         if (size == 0) {
1877             return result;
1878         }
1879 
1880         /* Call the function */
1881         auto *mutable_data = returned_array::mutable_data(result);
1882         if (trivial == broadcast_trivial::non_trivial) {
1883             apply_broadcast(buffers, params, mutable_data, size, shape, i_seq, vi_seq, bi_seq);
1884         } else {
1885             apply_trivial(buffers, params, mutable_data, size, i_seq, vi_seq, bi_seq);
1886         }
1887 
1888         return result;
1889         PYBIND11_WARNING_POP
1890     }
1891 
1892     template <size_t... Index, size_t... VIndex, size_t... BIndex>
1893     void apply_trivial(std::array<buffer_info, NVectorized> &buffers,
1894                        std::array<void *, N> &params,
1895                        Return *out,
1896                        size_t size,
1897                        index_sequence<Index...>,
1898                        index_sequence<VIndex...>,
1899                        index_sequence<BIndex...>) {
1900 
1901         // Initialize an array of mutable byte references and sizes with references set to the
1902         // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size
1903         // (except for singletons, which get an increment of 0).
1904         std::array<std::pair<unsigned char *&, const size_t>, NVectorized> vecparams{
1905             {std::pair<unsigned char *&, const size_t>(
1906                 reinterpret_cast<unsigned char *&>(params[VIndex] = buffers[BIndex].ptr),
1907                 buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t<VIndex>))...}};
1908 
1909         for (size_t i = 0; i < size; ++i) {
1910             returned_array::call(
1911                 out, i, f, *reinterpret_cast<param_n_t<Index> *>(params[Index])...);
1912             for (auto &x : vecparams) {
1913                 x.first += x.second;
1914             }
1915         }
1916     }
1917 
1918     template <size_t... Index, size_t... VIndex, size_t... BIndex>
1919     void apply_broadcast(std::array<buffer_info, NVectorized> &buffers,
1920                          std::array<void *, N> &params,
1921                          Return *out,
1922                          size_t size,
1923                          const std::vector<ssize_t> &output_shape,
1924                          index_sequence<Index...>,
1925                          index_sequence<VIndex...>,
1926                          index_sequence<BIndex...>) {
1927 
1928         multi_array_iterator<NVectorized> input_iter(buffers, output_shape);
1929 
1930         for (size_t i = 0; i < size; ++i, ++input_iter) {
1931             PYBIND11_EXPAND_SIDE_EFFECTS((params[VIndex] = input_iter.template data<BIndex>()));
1932             returned_array::call(
1933                 out, i, f, *reinterpret_cast<param_n_t<Index> *>(std::get<Index>(params))...);
1934         }
1935     }
1936 };
1937 
1938 template <typename Func, typename Return, typename... Args>
1939 vectorize_helper<Func, Return, Args...> vectorize_extractor(const Func &f, Return (*)(Args...)) {
1940     return detail::vectorize_helper<Func, Return, Args...>(f);
1941 }
1942 
1943 template <typename T, int Flags>
1944 struct handle_type_name<array_t<T, Flags>> {
1945     static constexpr auto name
1946         = const_name("numpy.ndarray[") + npy_format_descriptor<T>::name + const_name("]");
1947 };
1948 
1949 PYBIND11_NAMESPACE_END(detail)
1950 
1951 // Vanilla pointer vectorizer:
1952 template <typename Return, typename... Args>
1953 detail::vectorize_helper<Return (*)(Args...), Return, Args...> vectorize(Return (*f)(Args...)) {
1954     return detail::vectorize_helper<Return (*)(Args...), Return, Args...>(f);
1955 }
1956 
1957 // lambda vectorizer:
1958 template <typename Func, detail::enable_if_t<detail::is_lambda<Func>::value, int> = 0>
1959 auto vectorize(Func &&f)
1960     -> decltype(detail::vectorize_extractor(std::forward<Func>(f),
1961                                             (detail::function_signature_t<Func> *) nullptr)) {
1962     return detail::vectorize_extractor(std::forward<Func>(f),
1963                                        (detail::function_signature_t<Func> *) nullptr);
1964 }
1965 
1966 // Vectorize a class method (non-const):
1967 template <typename Return,
1968           typename Class,
1969           typename... Args,
1970           typename Helper = detail::vectorize_helper<
1971               decltype(std::mem_fn(std::declval<Return (Class::*)(Args...)>())),
1972               Return,
1973               Class *,
1974               Args...>>
1975 Helper vectorize(Return (Class::*f)(Args...)) {
1976     return Helper(std::mem_fn(f));
1977 }
1978 
1979 // Vectorize a class method (const):
1980 template <typename Return,
1981           typename Class,
1982           typename... Args,
1983           typename Helper = detail::vectorize_helper<
1984               decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const>())),
1985               Return,
1986               const Class *,
1987               Args...>>
1988 Helper vectorize(Return (Class::*f)(Args...) const) {
1989     return Helper(std::mem_fn(f));
1990 }
1991 
1992 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)