File indexing completed on 2026-09-16 09:20:23
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #pragma once
0011
0012 #include "pybind11.h"
0013 #include "detail/common.h"
0014 #include "complex.h"
0015 #include "gil_safe_call_once.h"
0016 #include "pytypes.h"
0017
0018 #include <algorithm>
0019 #include <array>
0020 #include <cstdint>
0021 #include <cstdlib>
0022 #include <cstring>
0023 #include <functional>
0024 #include <numeric>
0025 #include <sstream>
0026 #include <string>
0027 #include <type_traits>
0028 #include <typeindex>
0029 #include <utility>
0030 #include <vector>
0031
0032 #ifdef PYBIND11_HAS_SPAN
0033 # include <span>
0034 #endif
0035
0036 #if defined(PYBIND11_NUMPY_1_ONLY)
0037 # error "PYBIND11_NUMPY_1_ONLY is no longer supported (see PR #5595)."
0038 #endif
0039
0040
0041
0042
0043
0044
0045 static_assert(sizeof(::pybind11::ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t");
0046 static_assert(std::is_signed<Py_intptr_t>::value, "Py_intptr_t must be signed");
0047
0048
0049 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0050
0051 PYBIND11_WARNING_DISABLE_MSVC(4127)
0052
0053 class dtype;
0054 class array;
0055
0056 template <typename>
0057 struct numpy_scalar;
0058
0059 PYBIND11_NAMESPACE_BEGIN(detail)
0060
0061 template <>
0062 struct handle_type_name<dtype> {
0063 static constexpr auto name = const_name("numpy.dtype");
0064 };
0065
0066 template <>
0067 struct handle_type_name<array> {
0068 static constexpr auto name = const_name("numpy.ndarray");
0069 };
0070
0071 template <typename type, typename SFINAE = void>
0072 struct npy_format_descriptor;
0073
0074
0075 struct PyArrayDescr1_Proxy {
0076 PyObject_HEAD
0077 PyObject *typeobj;
0078 char kind;
0079 char type;
0080 char byteorder;
0081 char flags;
0082 int type_num;
0083 int elsize;
0084 int alignment;
0085 char *subarray;
0086 PyObject *fields;
0087 PyObject *names;
0088 };
0089
0090 struct PyArrayDescr_Proxy {
0091 PyObject_HEAD
0092 PyObject *typeobj;
0093 char kind;
0094 char type;
0095 char byteorder;
0096 char _former_flags;
0097 int type_num;
0098
0099 };
0100
0101
0102 struct PyArrayDescr2_Proxy {
0103 PyObject_HEAD
0104 PyObject *typeobj;
0105 char kind;
0106 char type;
0107 char byteorder;
0108 char _former_flags;
0109 int type_num;
0110 std::uint64_t flags;
0111 ssize_t elsize;
0112 ssize_t alignment;
0113 PyObject *metadata;
0114 Py_hash_t hash;
0115 void *reserved_null[2];
0116
0117 char *subarray;
0118 PyObject *fields;
0119 PyObject *names;
0120 };
0121
0122 struct PyArray_Proxy {
0123 PyObject_HEAD
0124 char *data;
0125 int nd;
0126 ssize_t *dimensions;
0127 ssize_t *strides;
0128 PyObject *base;
0129 PyObject *descr;
0130 int flags;
0131 };
0132
0133 struct PyVoidScalarObject_Proxy {
0134 PyObject_VAR_HEAD char *obval;
0135 PyArrayDescr_Proxy *descr;
0136 int flags;
0137 PyObject *base;
0138 };
0139
0140 struct numpy_type_info {
0141 PyObject *dtype_ptr;
0142 std::string format_str;
0143 };
0144
0145 struct numpy_internals {
0146 std::unordered_map<std::type_index, numpy_type_info> registered_dtypes;
0147
0148 numpy_type_info *get_type_info(const std::type_info &tinfo, bool throw_if_missing = true) {
0149 auto it = registered_dtypes.find(std::type_index(tinfo));
0150 if (it != registered_dtypes.end()) {
0151 return &(it->second);
0152 }
0153 if (throw_if_missing) {
0154 pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name());
0155 }
0156 return nullptr;
0157 }
0158
0159 template <typename T>
0160 numpy_type_info *get_type_info(bool throw_if_missing = true) {
0161 return get_type_info(typeid(typename std::remove_cv<T>::type), throw_if_missing);
0162 }
0163 };
0164
0165 PYBIND11_NOINLINE void load_numpy_internals(numpy_internals *&ptr) {
0166 ptr = &get_or_create_shared_data<numpy_internals>("_numpy_internals");
0167 }
0168
0169 inline numpy_internals &get_numpy_internals() {
0170 static numpy_internals *ptr = nullptr;
0171 if (!ptr) {
0172 load_numpy_internals(ptr);
0173 }
0174 return *ptr;
0175 }
0176
0177 PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) {
0178 module_ numpy = module_::import("numpy");
0179 str version_string = numpy.attr("__version__");
0180 module_ numpy_lib = module_::import("numpy.lib");
0181 object numpy_version = numpy_lib.attr("NumpyVersion")(version_string);
0182 int major_version = numpy_version.attr("major").cast<int>();
0183
0184
0185
0186 std::string numpy_core_path = major_version >= 2 ? "numpy._core" : "numpy.core";
0187 return module_::import((numpy_core_path + "." + submodule_name).c_str());
0188 }
0189
0190 template <typename T>
0191 struct same_size {
0192 template <typename U>
0193 using as = bool_constant<sizeof(T) == sizeof(U)>;
0194 };
0195
0196 template <typename Concrete>
0197 constexpr int platform_lookup() {
0198 return -1;
0199 }
0200
0201
0202 template <typename Concrete, typename T, typename... Ts, typename... Ints>
0203 constexpr int platform_lookup(int I, Ints... Is) {
0204 return sizeof(Concrete) == sizeof(T) ? I : platform_lookup<Concrete, Ts...>(Is...);
0205 }
0206
0207 struct npy_api {
0208
0209 enum constants {
0210 NPY_ARRAY_C_CONTIGUOUS_ = 0x0001,
0211 NPY_ARRAY_F_CONTIGUOUS_ = 0x0002,
0212 NPY_ARRAY_OWNDATA_ = 0x0004,
0213 NPY_ARRAY_FORCECAST_ = 0x0010,
0214 NPY_ARRAY_ENSUREARRAY_ = 0x0040,
0215 NPY_ARRAY_ALIGNED_ = 0x0100,
0216 NPY_ARRAY_WRITEABLE_ = 0x0400,
0217 NPY_BOOL_ = 0,
0218 NPY_BYTE_,
0219 NPY_UBYTE_,
0220 NPY_SHORT_,
0221 NPY_USHORT_,
0222 NPY_INT_,
0223 NPY_UINT_,
0224 NPY_LONG_,
0225 NPY_ULONG_,
0226 NPY_LONGLONG_,
0227 NPY_ULONGLONG_,
0228 NPY_FLOAT_,
0229 NPY_DOUBLE_,
0230 NPY_LONGDOUBLE_,
0231 NPY_CFLOAT_,
0232 NPY_CDOUBLE_,
0233 NPY_CLONGDOUBLE_,
0234 NPY_OBJECT_ = 17,
0235 NPY_STRING_,
0236 NPY_UNICODE_,
0237 NPY_VOID_,
0238
0239 NPY_INT8_ = NPY_BYTE_,
0240 NPY_UINT8_ = NPY_UBYTE_,
0241 NPY_INT16_ = NPY_SHORT_,
0242 NPY_UINT16_ = NPY_USHORT_,
0243
0244
0245
0246 NPY_INT32_
0247 = platform_lookup<std::int32_t, long, int, short>(NPY_LONG_, NPY_INT_, NPY_SHORT_),
0248 NPY_UINT32_ = platform_lookup<std::uint32_t, unsigned long, unsigned int, unsigned short>(
0249 NPY_ULONG_, NPY_UINT_, NPY_USHORT_),
0250 NPY_INT64_
0251 = platform_lookup<std::int64_t, long, long long, int>(NPY_LONG_, NPY_LONGLONG_, NPY_INT_),
0252 NPY_UINT64_
0253 = platform_lookup<std::uint64_t, unsigned long, unsigned long long, unsigned int>(
0254 NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
0255 NPY_FLOAT32_ = platform_lookup<float, double, float, long double>(
0256 NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0257 NPY_FLOAT64_ = platform_lookup<double, double, float, long double>(
0258 NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0259 NPY_COMPLEX64_
0260 = platform_lookup<std::complex<float>,
0261 std::complex<double>,
0262 std::complex<float>,
0263 std::complex<long double>>(NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0264 NPY_COMPLEX128_
0265 = platform_lookup<std::complex<double>,
0266 std::complex<double>,
0267 std::complex<float>,
0268 std::complex<long double>>(NPY_DOUBLE_, NPY_FLOAT_, NPY_LONGDOUBLE_),
0269 NPY_CHAR_ = std::is_signed<char>::value ? NPY_BYTE_ : NPY_UBYTE_,
0270 };
0271
0272 unsigned int PyArray_RUNTIME_VERSION_;
0273
0274 struct PyArray_Dims {
0275 Py_intptr_t *ptr;
0276 int len;
0277 };
0278
0279 static npy_api &get() {
0280 PYBIND11_CONSTINIT static gil_safe_call_once_and_store<npy_api> storage;
0281 return storage.call_once_and_store_result(lookup).get_stored();
0282 }
0283
0284 bool PyArray_Check_(PyObject *obj) const {
0285 return PyObject_TypeCheck(obj, PyArray_Type_) != 0;
0286 }
0287 bool PyArrayDescr_Check_(PyObject *obj) const {
0288 return PyObject_TypeCheck(obj, PyArrayDescr_Type_) != 0;
0289 }
0290
0291 unsigned int (*PyArray_GetNDArrayCFeatureVersion_)();
0292 PyObject *(*PyArray_DescrFromType_)(int);
0293 PyObject *(*PyArray_TypeObjectFromType_)(int);
0294 PyObject *(*PyArray_NewFromDescr_)(PyTypeObject *,
0295 PyObject *,
0296 int,
0297 Py_intptr_t const *,
0298 Py_intptr_t const *,
0299 void *,
0300 int,
0301 PyObject *);
0302
0303 PyObject *(*PyArray_DescrNewFromType_)(int);
0304 int (*PyArray_CopyInto_)(PyObject *, PyObject *);
0305 PyObject *(*PyArray_NewCopy_)(PyObject *, int);
0306 PyTypeObject *PyArray_Type_;
0307 PyTypeObject *PyVoidArrType_Type_;
0308 PyTypeObject *PyArrayDescr_Type_;
0309 PyObject *(*PyArray_DescrFromScalar_)(PyObject *);
0310 PyObject *(*PyArray_Scalar_)(void *, PyObject *, PyObject *);
0311 void (*PyArray_ScalarAsCtype_)(PyObject *, void *);
0312 PyObject *(*PyArray_FromAny_)(PyObject *, PyObject *, int, int, int, PyObject *);
0313 int (*PyArray_DescrConverter_)(PyObject *, PyObject **);
0314 bool (*PyArray_EquivTypes_)(PyObject *, PyObject *);
0315 PyObject *(*PyArray_Squeeze_)(PyObject *);
0316
0317 int (*PyArray_SetBaseObject_)(PyObject *, PyObject *);
0318 PyObject *(*PyArray_Resize_)(PyObject *, PyArray_Dims *, int, int);
0319 PyObject *(*PyArray_Newshape_)(PyObject *, PyArray_Dims *, int);
0320 PyObject *(*PyArray_View_)(PyObject *, PyObject *, PyObject *);
0321
0322 private:
0323 enum functions {
0324 API_PyArray_GetNDArrayCFeatureVersion = 211,
0325 API_PyArray_Type = 2,
0326 API_PyArrayDescr_Type = 3,
0327 API_PyVoidArrType_Type = 39,
0328 API_PyArray_DescrFromType = 45,
0329 API_PyArray_TypeObjectFromType = 46,
0330 API_PyArray_DescrFromScalar = 57,
0331 API_PyArray_Scalar = 60,
0332 API_PyArray_ScalarAsCtype = 62,
0333 API_PyArray_FromAny = 69,
0334 API_PyArray_Resize = 80,
0335
0336 API_PyArray_CopyInto = 50,
0337 API_PyArray_NewCopy = 85,
0338 API_PyArray_NewFromDescr = 94,
0339 API_PyArray_DescrNewFromType = 96,
0340 API_PyArray_Newshape = 135,
0341 API_PyArray_Squeeze = 136,
0342 API_PyArray_View = 137,
0343 API_PyArray_DescrConverter = 174,
0344 API_PyArray_EquivTypes = 182,
0345 API_PyArray_SetBaseObject = 282
0346 };
0347
0348 static npy_api lookup() {
0349 module_ m = detail::import_numpy_core_submodule("multiarray");
0350 auto c = m.attr("_ARRAY_API");
0351 void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), nullptr);
0352 if (api_ptr == nullptr) {
0353 raise_from(PyExc_SystemError, "FAILURE obtaining numpy _ARRAY_API pointer.");
0354 throw error_already_set();
0355 }
0356 npy_api api;
0357 #define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func];
0358 DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion);
0359 api.PyArray_RUNTIME_VERSION_ = api.PyArray_GetNDArrayCFeatureVersion_();
0360 if (api.PyArray_RUNTIME_VERSION_ < 0x7) {
0361 pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0");
0362 }
0363 DECL_NPY_API(PyArray_Type);
0364 DECL_NPY_API(PyVoidArrType_Type);
0365 DECL_NPY_API(PyArrayDescr_Type);
0366 DECL_NPY_API(PyArray_DescrFromType);
0367 DECL_NPY_API(PyArray_TypeObjectFromType);
0368 DECL_NPY_API(PyArray_DescrFromScalar);
0369 DECL_NPY_API(PyArray_Scalar);
0370 DECL_NPY_API(PyArray_ScalarAsCtype);
0371 DECL_NPY_API(PyArray_FromAny);
0372 DECL_NPY_API(PyArray_Resize);
0373 DECL_NPY_API(PyArray_CopyInto);
0374 DECL_NPY_API(PyArray_NewCopy);
0375 DECL_NPY_API(PyArray_NewFromDescr);
0376 DECL_NPY_API(PyArray_DescrNewFromType);
0377 DECL_NPY_API(PyArray_Newshape);
0378 DECL_NPY_API(PyArray_Squeeze);
0379 DECL_NPY_API(PyArray_View);
0380 DECL_NPY_API(PyArray_DescrConverter);
0381 DECL_NPY_API(PyArray_EquivTypes);
0382 DECL_NPY_API(PyArray_SetBaseObject);
0383
0384 #undef DECL_NPY_API
0385 return api;
0386 }
0387 };
0388
0389 template <typename T>
0390 struct is_complex : std::false_type {};
0391 template <typename T>
0392 struct is_complex<std::complex<T>> : std::true_type {};
0393
0394 template <typename T, typename = void>
0395 struct npy_format_descriptor_name;
0396
0397 template <typename T>
0398 struct npy_format_descriptor_name<T, enable_if_t<std::is_integral<T>::value>> {
0399 static constexpr auto name = const_name<std::is_same<T, bool>::value>(
0400 const_name("numpy.bool"),
0401 const_name<std::is_signed<T>::value>("numpy.int", "numpy.uint")
0402 + const_name<sizeof(T) * 8>());
0403 };
0404
0405 template <typename T>
0406 struct npy_format_descriptor_name<T, enable_if_t<std::is_floating_point<T>::value>> {
0407 static constexpr auto name = const_name < std::is_same<T, float>::value
0408 || std::is_same<T, const float>::value
0409 || std::is_same<T, double>::value
0410 || std::is_same<T, const double>::value
0411 > (const_name("numpy.float") + const_name<sizeof(T) * 8>(),
0412 const_name("numpy.longdouble"));
0413 };
0414
0415 template <typename T>
0416 struct npy_format_descriptor_name<T, enable_if_t<is_complex<T>::value>> {
0417 static constexpr auto name = const_name < std::is_same<typename T::value_type, float>::value
0418 || std::is_same<typename T::value_type, const float>::value
0419 || std::is_same<typename T::value_type, double>::value
0420 || std::is_same<typename T::value_type, const double>::value
0421 > (const_name("numpy.complex")
0422 + const_name<sizeof(typename T::value_type) * 16>(),
0423 const_name("numpy.clongdouble"));
0424 };
0425
0426 template <typename T>
0427 struct numpy_scalar_info {};
0428
0429 #define PYBIND11_NUMPY_SCALAR_IMPL(ctype_, typenum_) \
0430 template <> \
0431 struct numpy_scalar_info<ctype_> { \
0432 static constexpr auto name = npy_format_descriptor_name<ctype_>::name; \
0433 static constexpr int typenum = npy_api::typenum_##_; \
0434 }
0435
0436
0437 PYBIND11_NUMPY_SCALAR_IMPL(bool, NPY_BOOL);
0438
0439
0440 PYBIND11_NUMPY_SCALAR_IMPL(char, NPY_CHAR);
0441 PYBIND11_NUMPY_SCALAR_IMPL(signed char, NPY_BYTE);
0442 PYBIND11_NUMPY_SCALAR_IMPL(unsigned char, NPY_UBYTE);
0443
0444
0445 PYBIND11_NUMPY_SCALAR_IMPL(std::int16_t, NPY_INT16);
0446 PYBIND11_NUMPY_SCALAR_IMPL(std::int32_t, NPY_INT32);
0447 PYBIND11_NUMPY_SCALAR_IMPL(std::int64_t, NPY_INT64);
0448
0449
0450 PYBIND11_NUMPY_SCALAR_IMPL(std::uint16_t, NPY_UINT16);
0451 PYBIND11_NUMPY_SCALAR_IMPL(std::uint32_t, NPY_UINT32);
0452 PYBIND11_NUMPY_SCALAR_IMPL(std::uint64_t, NPY_UINT64);
0453
0454
0455 PYBIND11_NUMPY_SCALAR_IMPL(float, NPY_FLOAT);
0456 PYBIND11_NUMPY_SCALAR_IMPL(double, NPY_DOUBLE);
0457 PYBIND11_NUMPY_SCALAR_IMPL(long double, NPY_LONGDOUBLE);
0458
0459
0460 PYBIND11_NUMPY_SCALAR_IMPL(std::complex<float>, NPY_CFLOAT);
0461 PYBIND11_NUMPY_SCALAR_IMPL(std::complex<double>, NPY_CDOUBLE);
0462 PYBIND11_NUMPY_SCALAR_IMPL(std::complex<long double>, NPY_CLONGDOUBLE);
0463
0464 #undef PYBIND11_NUMPY_SCALAR_IMPL
0465
0466
0467
0468
0469
0470
0471 static constexpr int normalized_dtype_num[npy_api::NPY_VOID_ + 1] = {
0472
0473 npy_api::NPY_BOOL_,
0474
0475 npy_api::NPY_BYTE_,
0476
0477 npy_api::NPY_UBYTE_,
0478
0479 npy_api::NPY_INT16_,
0480
0481 npy_api::NPY_UINT16_,
0482
0483 sizeof(int) == sizeof(std::int16_t) ? npy_api::NPY_INT16_
0484 : sizeof(int) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
0485 : sizeof(int) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
0486 : npy_api::NPY_INT_,
0487
0488 sizeof(unsigned int) == sizeof(std::uint16_t) ? npy_api::NPY_UINT16_
0489 : sizeof(unsigned int) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
0490 : sizeof(unsigned int) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
0491 : npy_api::NPY_UINT_,
0492
0493 sizeof(long) == sizeof(std::int16_t) ? npy_api::NPY_INT16_
0494 : sizeof(long) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
0495 : sizeof(long) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
0496 : npy_api::NPY_LONG_,
0497
0498 sizeof(unsigned long) == sizeof(std::uint16_t) ? npy_api::NPY_UINT16_
0499 : sizeof(unsigned long) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
0500 : sizeof(unsigned long) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
0501 : npy_api::NPY_ULONG_,
0502
0503 sizeof(long long) == sizeof(std::int16_t) ? npy_api::NPY_INT16_
0504 : sizeof(long long) == sizeof(std::int32_t) ? npy_api::NPY_INT32_
0505 : sizeof(long long) == sizeof(std::int64_t) ? npy_api::NPY_INT64_
0506 : npy_api::NPY_LONGLONG_,
0507
0508 sizeof(unsigned long long) == sizeof(std::uint16_t) ? npy_api::NPY_UINT16_
0509 : sizeof(unsigned long long) == sizeof(std::uint32_t) ? npy_api::NPY_UINT32_
0510 : sizeof(unsigned long long) == sizeof(std::uint64_t) ? npy_api::NPY_UINT64_
0511 : npy_api::NPY_ULONGLONG_,
0512
0513 npy_api::NPY_FLOAT_,
0514
0515 npy_api::NPY_DOUBLE_,
0516
0517 npy_api::NPY_LONGDOUBLE_,
0518
0519 npy_api::NPY_CFLOAT_,
0520
0521 npy_api::NPY_CDOUBLE_,
0522
0523 npy_api::NPY_CLONGDOUBLE_,
0524
0525 npy_api::NPY_OBJECT_,
0526
0527 npy_api::NPY_STRING_,
0528
0529 npy_api::NPY_UNICODE_,
0530
0531 npy_api::NPY_VOID_,
0532 };
0533
0534 inline PyArray_Proxy *array_proxy(void *ptr) { return reinterpret_cast<PyArray_Proxy *>(ptr); }
0535
0536 inline const PyArray_Proxy *array_proxy(const void *ptr) {
0537 return reinterpret_cast<const PyArray_Proxy *>(ptr);
0538 }
0539
0540 inline PyArrayDescr_Proxy *array_descriptor_proxy(PyObject *ptr) {
0541 return reinterpret_cast<PyArrayDescr_Proxy *>(ptr);
0542 }
0543
0544 inline const PyArrayDescr_Proxy *array_descriptor_proxy(const PyObject *ptr) {
0545 return reinterpret_cast<const PyArrayDescr_Proxy *>(ptr);
0546 }
0547
0548 inline const PyArrayDescr1_Proxy *array_descriptor1_proxy(const PyObject *ptr) {
0549 return reinterpret_cast<const PyArrayDescr1_Proxy *>(ptr);
0550 }
0551
0552 inline const PyArrayDescr2_Proxy *array_descriptor2_proxy(const PyObject *ptr) {
0553 return reinterpret_cast<const PyArrayDescr2_Proxy *>(ptr);
0554 }
0555
0556 inline bool check_flags(const void *ptr, int flag) {
0557 return (flag == (array_proxy(ptr)->flags & flag));
0558 }
0559
0560 template <typename T>
0561 struct is_std_array : std::false_type {};
0562 template <typename T, size_t N>
0563 struct is_std_array<std::array<T, N>> : std::true_type {};
0564
0565 template <typename T>
0566 struct array_info_scalar {
0567 using type = T;
0568 static constexpr bool is_array = false;
0569 static constexpr bool is_empty = false;
0570 static constexpr auto extents = const_name("");
0571 static void append_extents(list & ) {}
0572 };
0573
0574
0575
0576 template <typename T>
0577 struct array_info : array_info_scalar<T> {};
0578 template <typename T, size_t N>
0579 struct array_info<std::array<T, N>> {
0580 using type = typename array_info<T>::type;
0581 static constexpr bool is_array = true;
0582 static constexpr bool is_empty = (N == 0) || array_info<T>::is_empty;
0583 static constexpr size_t extent = N;
0584
0585
0586 static void append_extents(list &shape) {
0587 shape.append(N);
0588 array_info<T>::append_extents(shape);
0589 }
0590
0591 static constexpr auto extents = const_name<array_info<T>::is_array>(
0592 ::pybind11::detail::concat(const_name<N>(), array_info<T>::extents), const_name<N>());
0593 };
0594
0595
0596 template <size_t N>
0597 struct array_info<char[N]> : array_info_scalar<char[N]> {};
0598 template <size_t N>
0599 struct array_info<std::array<char, N>> : array_info_scalar<std::array<char, N>> {};
0600 template <typename T, size_t N>
0601 struct array_info<T[N]> : array_info<std::array<T, N>> {};
0602 template <typename T>
0603 using remove_all_extents_t = typename array_info<T>::type;
0604
0605 template <typename T>
0606 using is_pod_struct
0607 = all_of<std::is_standard_layout<T>,
0608
0609 #if defined(__GLIBCXX__) \
0610 && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623 \
0611 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
0612
0613
0614 std::is_trivially_destructible<T>,
0615 satisfies_any_of<T, std::has_trivial_copy_constructor, std::has_trivial_copy_assign>,
0616 #else
0617 std::is_trivially_copyable<T>,
0618 #endif
0619 satisfies_none_of<T,
0620 std::is_reference,
0621 std::is_array,
0622 is_std_array,
0623 std::is_arithmetic,
0624 is_complex,
0625 std::is_enum>>;
0626
0627
0628 template <typename T>
0629 using is_pod = all_of<std::is_standard_layout<T>, std::is_trivial<T>>;
0630
0631 template <ssize_t Dim = 0, typename Strides>
0632 ssize_t byte_offset_unsafe(const Strides &) {
0633 return 0;
0634 }
0635 template <ssize_t Dim = 0, typename Strides, typename... Ix>
0636 ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) {
0637 return i * strides[Dim] + byte_offset_unsafe<Dim + 1>(strides, index...);
0638 }
0639
0640
0641
0642
0643
0644
0645 template <typename T, ssize_t Dims>
0646 class unchecked_reference {
0647 protected:
0648 static constexpr bool Dynamic = Dims < 0;
0649 const unsigned char *data_;
0650
0651
0652 conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>> shape_, strides_;
0653 const ssize_t dims_;
0654
0655 friend class pybind11::array;
0656
0657 template <bool Dyn = Dynamic>
0658 unchecked_reference(const void *data,
0659 const ssize_t *shape,
0660 const ssize_t *strides,
0661 enable_if_t<!Dyn, ssize_t>)
0662 : data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
0663 for (size_t i = 0; i < (size_t) dims_; i++) {
0664 shape_[i] = shape[i];
0665 strides_[i] = strides[i];
0666 }
0667 }
0668
0669 template <bool Dyn = Dynamic>
0670 unchecked_reference(const void *data,
0671 const ssize_t *shape,
0672 const ssize_t *strides,
0673 enable_if_t<Dyn, ssize_t> dims)
0674 : data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides},
0675 dims_{dims} {}
0676
0677 public:
0678
0679
0680
0681
0682
0683 template <typename... Ix>
0684 const T &operator()(Ix... index) const {
0685 static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
0686 "Invalid number of indices for unchecked array reference");
0687 return *reinterpret_cast<const T *>(data_
0688 + byte_offset_unsafe(strides_, ssize_t(index)...));
0689 }
0690
0691
0692
0693
0694 template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
0695 const T &operator[](ssize_t index) const {
0696 return operator()(index);
0697 }
0698
0699
0700 template <typename... Ix>
0701 const T *data(Ix... ix) const {
0702 return &operator()(ssize_t(ix)...);
0703 }
0704
0705
0706 constexpr static ssize_t itemsize() { return sizeof(T); }
0707
0708
0709 ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; }
0710
0711
0712 ssize_t ndim() const { return dims_; }
0713
0714
0715
0716 template <bool Dyn = Dynamic>
0717 enable_if_t<!Dyn, ssize_t> size() const {
0718 return std::accumulate(
0719 shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies<ssize_t>());
0720 }
0721 template <bool Dyn = Dynamic>
0722 enable_if_t<Dyn, ssize_t> size() const {
0723 return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
0724 }
0725
0726
0727
0728
0729 ssize_t nbytes() const { return size() * itemsize(); }
0730 };
0731
0732 template <typename T, ssize_t Dims>
0733 class unchecked_mutable_reference : public unchecked_reference<T, Dims> {
0734 friend class pybind11::array;
0735 using ConstBase = unchecked_reference<T, Dims>;
0736 using ConstBase::ConstBase;
0737 using ConstBase::Dynamic;
0738
0739 public:
0740
0741 using ConstBase::operator();
0742 using ConstBase::operator[];
0743
0744
0745 template <typename... Ix>
0746 T &operator()(Ix... index) {
0747 static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic,
0748 "Invalid number of indices for unchecked array reference");
0749 return const_cast<T &>(ConstBase::operator()(index...));
0750 }
0751
0752
0753
0754
0755
0756 template <ssize_t D = Dims, typename = enable_if_t<D == 1 || Dynamic>>
0757 T &operator[](ssize_t index) {
0758 return operator()(index);
0759 }
0760
0761
0762 template <typename... Ix>
0763 T *mutable_data(Ix... ix) {
0764 return &operator()(ssize_t(ix)...);
0765 }
0766 };
0767
0768 template <typename T, ssize_t Dim>
0769 struct type_caster<unchecked_reference<T, Dim>> {
0770 static_assert(Dim == 0 && Dim > 0 ,
0771 "unchecked array proxy object is not castable");
0772 };
0773 template <typename T, ssize_t Dim>
0774 struct type_caster<unchecked_mutable_reference<T, Dim>>
0775 : type_caster<unchecked_reference<T, Dim>> {};
0776
0777 template <typename T>
0778 struct type_caster<numpy_scalar<T>> {
0779 using value_type = T;
0780 using type_info = numpy_scalar_info<T>;
0781
0782 PYBIND11_TYPE_CASTER(numpy_scalar<T>, type_info::name);
0783
0784 static handle &target_type() {
0785 static handle tp = npy_api::get().PyArray_TypeObjectFromType_(type_info::typenum);
0786 return tp;
0787 }
0788
0789 static handle &target_dtype() {
0790 static handle tp = npy_api::get().PyArray_DescrFromType_(type_info::typenum);
0791 return tp;
0792 }
0793
0794 bool load(handle src, bool) {
0795 if (isinstance(src, target_type())) {
0796 npy_api::get().PyArray_ScalarAsCtype_(src.ptr(), &value.value);
0797 return true;
0798 }
0799 return false;
0800 }
0801
0802 static handle cast(numpy_scalar<T> src, return_value_policy, handle) {
0803 return npy_api::get().PyArray_Scalar_(&src.value, target_dtype().ptr(), nullptr);
0804 }
0805 };
0806
0807 PYBIND11_NAMESPACE_END(detail)
0808
0809 template <typename T>
0810 struct numpy_scalar {
0811 using value_type = T;
0812
0813 value_type value;
0814
0815 numpy_scalar() = default;
0816 explicit numpy_scalar(value_type value) : value(value) {}
0817
0818 explicit operator value_type() const { return value; }
0819 numpy_scalar &operator=(value_type value) {
0820 this->value = value;
0821 return *this;
0822 }
0823
0824 friend bool operator==(const numpy_scalar &a, const numpy_scalar &b) {
0825 return a.value == b.value;
0826 }
0827
0828 friend bool operator!=(const numpy_scalar &a, const numpy_scalar &b) { return !(a == b); }
0829 };
0830
0831 template <typename T>
0832 numpy_scalar<T> make_scalar(T value) {
0833 return numpy_scalar<T>(value);
0834 }
0835
0836 class dtype : public object {
0837 public:
0838 PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_)
0839
0840 explicit dtype(const buffer_info &info) {
0841 dtype descr(_dtype_from_pep3118()(pybind11::str(info.format)));
0842
0843 m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
0844 .release()
0845 .ptr();
0846 }
0847
0848 explicit dtype(const pybind11::str &format) : dtype(from_args(format)) {}
0849
0850 explicit dtype(const std::string &format) : dtype(pybind11::str(format)) {}
0851
0852 explicit dtype(const char *format) : dtype(pybind11::str(format)) {}
0853
0854 dtype(list names, list formats, list offsets, ssize_t itemsize) {
0855 dict args;
0856 args["names"] = std::move(names);
0857 args["formats"] = std::move(formats);
0858 args["offsets"] = std::move(offsets);
0859 args["itemsize"] = pybind11::int_(itemsize);
0860 m_ptr = from_args(args).release().ptr();
0861 }
0862
0863
0864
0865 explicit dtype(int typenum)
0866 : object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
0867 if (m_ptr == nullptr) {
0868 throw error_already_set();
0869 }
0870 }
0871
0872
0873 static dtype from_args(const object &args) {
0874 PyObject *ptr = nullptr;
0875 if ((detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) == 0) || !ptr) {
0876 throw error_already_set();
0877 }
0878 return reinterpret_steal<dtype>(ptr);
0879 }
0880
0881
0882 template <typename T>
0883 static dtype of() {
0884 return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::dtype();
0885 }
0886
0887
0888
0889 template <typename T>
0890 static constexpr int num_of() {
0891 return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::value;
0892 }
0893
0894
0895 ssize_t itemsize() const {
0896 if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0897 return detail::array_descriptor1_proxy(m_ptr)->elsize;
0898 }
0899 return detail::array_descriptor2_proxy(m_ptr)->elsize;
0900 }
0901
0902
0903 bool has_fields() const {
0904 if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0905 return detail::array_descriptor1_proxy(m_ptr)->names != nullptr;
0906 }
0907 const auto *proxy = detail::array_descriptor2_proxy(m_ptr);
0908 if (proxy->type_num < 0 || proxy->type_num >= 2056) {
0909 return false;
0910 }
0911 return proxy->names != nullptr;
0912 }
0913
0914
0915
0916 char kind() const { return detail::array_descriptor_proxy(m_ptr)->kind; }
0917
0918
0919
0920 char char_() const {
0921
0922
0923
0924 return detail::array_descriptor_proxy(m_ptr)->type;
0925 }
0926
0927
0928
0929
0930 int num() const {
0931
0932
0933
0934 return detail::array_descriptor_proxy(m_ptr)->type_num;
0935 }
0936
0937
0938
0939
0940 int normalized_num() const {
0941 int value = num();
0942 if (value >= 0 && value <= detail::npy_api::NPY_VOID_) {
0943 return detail::normalized_dtype_num[value];
0944 }
0945 return value;
0946 }
0947
0948
0949 char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
0950
0951
0952 ssize_t alignment() const {
0953 if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0954 return detail::array_descriptor1_proxy(m_ptr)->alignment;
0955 }
0956 return detail::array_descriptor2_proxy(m_ptr)->alignment;
0957 }
0958
0959
0960 std::uint64_t flags() const {
0961 if (detail::npy_api::get().PyArray_RUNTIME_VERSION_ < 0x12) {
0962 return (unsigned char) detail::array_descriptor1_proxy(m_ptr)->flags;
0963 }
0964 return detail::array_descriptor2_proxy(m_ptr)->flags;
0965 }
0966
0967 private:
0968 static object &_dtype_from_pep3118() {
0969 PYBIND11_CONSTINIT static gil_safe_call_once_and_store<object> storage;
0970 return storage
0971 .call_once_and_store_result([]() {
0972 return detail::import_numpy_core_submodule("_internal")
0973 .attr("_dtype_from_pep3118");
0974 })
0975 .get_stored();
0976 }
0977
0978 dtype strip_padding(ssize_t itemsize) {
0979
0980
0981 if (!has_fields()) {
0982 return *this;
0983 }
0984
0985 struct field_descr {
0986 pybind11::str name;
0987 object format;
0988 pybind11::int_ offset;
0989 field_descr(pybind11::str &&name, object &&format, pybind11::int_ &&offset)
0990 : name{std::move(name)}, format{std::move(format)}, offset{std::move(offset)} {};
0991 };
0992 auto field_dict = attr("fields").cast<dict>();
0993 std::vector<field_descr> field_descriptors;
0994 field_descriptors.reserve(field_dict.size());
0995
0996 for (auto field : field_dict.attr("items")()) {
0997 auto spec = field.cast<tuple>();
0998 auto name = spec[0].cast<pybind11::str>();
0999 auto spec_fo = spec[1].cast<tuple>();
1000 auto format = spec_fo[0].cast<dtype>();
1001 auto offset = spec_fo[1].cast<pybind11::int_>();
1002 if ((len(name) == 0u) && format.kind() == 'V') {
1003 continue;
1004 }
1005 field_descriptors.emplace_back(
1006 std::move(name), format.strip_padding(format.itemsize()), std::move(offset));
1007 }
1008
1009 std::sort(field_descriptors.begin(),
1010 field_descriptors.end(),
1011 [](const field_descr &a, const field_descr &b) {
1012 return a.offset.cast<int>() < b.offset.cast<int>();
1013 });
1014
1015 list names, formats, offsets;
1016 for (auto &descr : field_descriptors) {
1017 names.append(std::move(descr.name));
1018 formats.append(std::move(descr.format));
1019 offsets.append(std::move(descr.offset));
1020 }
1021 return dtype(std::move(names), std::move(formats), std::move(offsets), itemsize);
1022 }
1023 };
1024
1025 class array : public buffer {
1026 public:
1027 PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array)
1028
1029 enum {
1030 c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_,
1031 f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_,
1032 forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_
1033 };
1034
1035 array() : array(0, static_cast<const double *>(nullptr)) {}
1036
1037 using ShapeContainer = detail::any_container<ssize_t>;
1038 using StridesContainer = detail::any_container<ssize_t>;
1039
1040
1041 array(const pybind11::dtype &dt,
1042 ShapeContainer shape,
1043 StridesContainer strides,
1044 const void *ptr = nullptr,
1045 handle base = handle()) {
1046
1047 if (strides->empty()) {
1048 *strides = detail::c_strides(*shape, dt.itemsize());
1049 }
1050
1051 auto ndim = shape->size();
1052 if (ndim != strides->size()) {
1053 pybind11_fail("NumPy: shape ndim doesn't match strides ndim");
1054 }
1055 auto descr = dt;
1056
1057 int flags = 0;
1058 if (base && ptr) {
1059 if (isinstance<array>(base)) {
1060
1061 flags = reinterpret_borrow<array>(base).flags()
1062 & ~detail::npy_api::NPY_ARRAY_OWNDATA_;
1063 } else {
1064
1065 flags = detail::npy_api::NPY_ARRAY_WRITEABLE_;
1066 }
1067 }
1068
1069 auto &api = detail::npy_api::get();
1070 auto tmp = reinterpret_steal<object>(api.PyArray_NewFromDescr_(
1071 api.PyArray_Type_,
1072 descr.release().ptr(),
1073 (int) ndim,
1074
1075 reinterpret_cast<Py_intptr_t *>(shape->data()),
1076 reinterpret_cast<Py_intptr_t *>(strides->data()),
1077 const_cast<void *>(ptr),
1078 flags,
1079 nullptr));
1080 if (!tmp) {
1081 throw error_already_set();
1082 }
1083 if (ptr) {
1084 if (base) {
1085 api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr());
1086 } else {
1087 tmp = reinterpret_steal<object>(
1088 api.PyArray_NewCopy_(tmp.ptr(), -1 ));
1089 }
1090 }
1091 m_ptr = tmp.release().ptr();
1092 }
1093
1094 array(const pybind11::dtype &dt,
1095 ShapeContainer shape,
1096 const void *ptr = nullptr,
1097 handle base = handle())
1098 : array(dt, std::move(shape), {}, ptr, base) {}
1099
1100 template <typename T,
1101 typename
1102 = detail::enable_if_t<std::is_integral<T>::value && !std::is_same<bool, T>::value>>
1103 array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle())
1104 : array(dt, {{count}}, ptr, base) {}
1105
1106 template <typename T>
1107 array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle())
1108 : array(pybind11::dtype::of<T>(),
1109 std::move(shape),
1110 std::move(strides),
1111 reinterpret_cast<const void *>(ptr),
1112 base) {}
1113
1114 template <typename T>
1115 array(ShapeContainer shape, const T *ptr, handle base = handle())
1116 : array(std::move(shape), {}, ptr, base) {}
1117
1118 template <typename T>
1119 explicit array(ssize_t count, const T *ptr, handle base = handle())
1120 : array({count}, {}, ptr, base) {}
1121
1122 explicit array(const buffer_info &info, handle base = handle())
1123 : array(pybind11::dtype(info), info.shape, info.strides, info.ptr, base) {}
1124
1125
1126 pybind11::dtype dtype() const {
1127 return reinterpret_borrow<pybind11::dtype>(detail::array_proxy(m_ptr)->descr);
1128 }
1129
1130
1131 ssize_t size() const {
1132 return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies<ssize_t>());
1133 }
1134
1135
1136 ssize_t itemsize() const { return dtype().itemsize(); }
1137
1138
1139 ssize_t nbytes() const { return size() * itemsize(); }
1140
1141
1142 ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; }
1143
1144
1145 object base() const { return reinterpret_borrow<object>(detail::array_proxy(m_ptr)->base); }
1146
1147
1148 const ssize_t *shape() const { return detail::array_proxy(m_ptr)->dimensions; }
1149
1150 #ifdef PYBIND11_HAS_SPAN
1151
1152 std::span<const ssize_t, std::dynamic_extent> shape_span() const {
1153 return std::span(shape(), static_cast<std::size_t>(ndim()));
1154 }
1155 #endif
1156
1157
1158 ssize_t shape(ssize_t dim) const {
1159 if (dim >= ndim()) {
1160 fail_dim_check(dim, "invalid axis");
1161 }
1162 return shape()[dim];
1163 }
1164
1165
1166 const ssize_t *strides() const { return detail::array_proxy(m_ptr)->strides; }
1167
1168 #ifdef PYBIND11_HAS_SPAN
1169
1170 std::span<const ssize_t, std::dynamic_extent> strides_span() const {
1171 return std::span(strides(), static_cast<std::size_t>(ndim()));
1172 }
1173 #endif
1174
1175
1176 ssize_t strides(ssize_t dim) const {
1177 if (dim >= ndim()) {
1178 fail_dim_check(dim, "invalid axis");
1179 }
1180 return strides()[dim];
1181 }
1182
1183
1184 int flags() const { return detail::array_proxy(m_ptr)->flags; }
1185
1186
1187 bool writeable() const {
1188 return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_);
1189 }
1190
1191
1192 bool owndata() const {
1193 return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_);
1194 }
1195
1196
1197
1198 template <typename... Ix>
1199 const void *data(Ix... index) const {
1200 return static_cast<const void *>(detail::array_proxy(m_ptr)->data + offset_at(index...));
1201 }
1202
1203
1204
1205
1206 template <typename... Ix>
1207 void *mutable_data(Ix... index) {
1208 check_writeable();
1209 return static_cast<void *>(detail::array_proxy(m_ptr)->data + offset_at(index...));
1210 }
1211
1212
1213
1214 template <typename... Ix>
1215 ssize_t offset_at(Ix... index) const {
1216 if ((ssize_t) sizeof...(index) > ndim()) {
1217 fail_dim_check(sizeof...(index), "too many indices for an array");
1218 }
1219 return byte_offset(ssize_t(index)...);
1220 }
1221
1222 ssize_t offset_at() const { return 0; }
1223
1224
1225
1226 template <typename... Ix>
1227 ssize_t index_at(Ix... index) const {
1228 return offset_at(index...) / itemsize();
1229 }
1230
1231
1232
1233
1234
1235
1236
1237 template <typename T, ssize_t Dims = -1>
1238 detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
1239 if (Dims >= 0 && ndim() != Dims) {
1240 throw std::domain_error("array has incorrect number of dimensions: "
1241 + std::to_string(ndim()) + "; expected "
1242 + std::to_string(Dims));
1243 }
1244 return detail::unchecked_mutable_reference<T, Dims>(
1245 mutable_data(), shape(), strides(), ndim());
1246 }
1247
1248
1249
1250
1251
1252
1253
1254
1255 template <typename T, ssize_t Dims = -1>
1256 detail::unchecked_reference<T, Dims> unchecked() const & {
1257 if (Dims >= 0 && ndim() != Dims) {
1258 throw std::domain_error("array has incorrect number of dimensions: "
1259 + std::to_string(ndim()) + "; expected "
1260 + std::to_string(Dims));
1261 }
1262 return detail::unchecked_reference<T, Dims>(data(), shape(), strides(), ndim());
1263 }
1264
1265
1266 array squeeze() {
1267 auto &api = detail::npy_api::get();
1268 return reinterpret_steal<array>(api.PyArray_Squeeze_(m_ptr));
1269 }
1270
1271
1272
1273
1274 void resize(ShapeContainer new_shape, bool refcheck = true) {
1275 detail::npy_api::PyArray_Dims d
1276 = {
1277 reinterpret_cast<Py_intptr_t *>(new_shape->data()),
1278 int(new_shape->size())};
1279
1280 auto new_array = reinterpret_steal<object>(
1281 detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1));
1282 if (!new_array) {
1283 throw error_already_set();
1284 }
1285 if (isinstance<array>(new_array)) {
1286 *this = std::move(new_array);
1287 }
1288 }
1289
1290
1291 array reshape(ShapeContainer new_shape) {
1292 detail::npy_api::PyArray_Dims d
1293 = {reinterpret_cast<Py_intptr_t *>(new_shape->data()), int(new_shape->size())};
1294 auto new_array
1295 = reinterpret_steal<array>(detail::npy_api::get().PyArray_Newshape_(m_ptr, &d, 0));
1296 if (!new_array) {
1297 throw error_already_set();
1298 }
1299 return new_array;
1300 }
1301
1302
1303
1304
1305
1306
1307 array view(const std::string &dtype) {
1308 auto &api = detail::npy_api::get();
1309 auto new_view = reinterpret_steal<array>(api.PyArray_View_(
1310 m_ptr, dtype::from_args(pybind11::str(dtype)).release().ptr(), nullptr));
1311 if (!new_view) {
1312 throw error_already_set();
1313 }
1314 return new_view;
1315 }
1316
1317
1318
1319 static array ensure(handle h, int ExtraFlags = 0) {
1320 auto result = reinterpret_steal<array>(raw_array(h.ptr(), ExtraFlags));
1321 if (!result) {
1322 PyErr_Clear();
1323 }
1324 return result;
1325 }
1326
1327 protected:
1328 template <typename, typename>
1329 friend struct detail::npy_format_descriptor;
1330
1331 void fail_dim_check(ssize_t dim, const std::string &msg) const {
1332 throw index_error(msg + ": " + std::to_string(dim) + " (ndim = " + std::to_string(ndim())
1333 + ')');
1334 }
1335
1336 template <typename... Ix>
1337 ssize_t byte_offset(Ix... index) const {
1338 check_dimensions(index...);
1339 return detail::byte_offset_unsafe(strides(), ssize_t(index)...);
1340 }
1341
1342 void check_writeable() const {
1343 if (!writeable()) {
1344 throw std::domain_error("array is not writeable");
1345 }
1346 }
1347
1348 template <typename... Ix>
1349 void check_dimensions(Ix... index) const {
1350 check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...);
1351 }
1352
1353 void check_dimensions_impl(ssize_t, const ssize_t *) const {}
1354
1355 template <typename... Ix>
1356 void check_dimensions_impl(ssize_t axis, const ssize_t *shape, ssize_t i, Ix... index) const {
1357 if (i >= *shape) {
1358 throw index_error(std::string("index ") + std::to_string(i)
1359 + " is out of bounds for axis " + std::to_string(axis)
1360 + " with size " + std::to_string(*shape));
1361 }
1362 check_dimensions_impl(axis + 1, shape + 1, index...);
1363 }
1364
1365
1366 static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) {
1367 if (ptr == nullptr) {
1368 set_error(PyExc_ValueError, "cannot create a pybind11::array from a nullptr");
1369 return nullptr;
1370 }
1371 return detail::npy_api::get().PyArray_FromAny_(
1372 ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr);
1373 }
1374 };
1375
1376 template <typename T, int ExtraFlags = array::forcecast>
1377 class array_t : public array {
1378 private:
1379 struct private_ctor {};
1380
1381 array_t(private_ctor,
1382 ShapeContainer &&shape,
1383 StridesContainer &&strides,
1384 const T *ptr,
1385 handle base)
1386 : array(std::move(shape), std::move(strides), ptr, base) {}
1387
1388 public:
1389 static_assert(!detail::array_info<T>::is_array, "Array types cannot be used with array_t");
1390
1391 using value_type = T;
1392
1393 array_t() : array(0, static_cast<const T *>(nullptr)) {}
1394 array_t(handle h, borrowed_t) : array(h, borrowed_t{}) {}
1395 array_t(handle h, stolen_t) : array(h, stolen_t{}) {}
1396
1397 PYBIND11_DEPRECATED("Use array_t<T>::ensure() instead")
1398 array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) {
1399 if (!m_ptr) {
1400 PyErr_Clear();
1401 }
1402 if (!is_borrowed) {
1403 Py_XDECREF(h.ptr());
1404 }
1405 }
1406
1407
1408 array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) {
1409 if (!m_ptr) {
1410 throw error_already_set();
1411 }
1412 }
1413
1414 explicit array_t(const buffer_info &info, handle base = handle()) : array(info, base) {}
1415
1416 array_t(ShapeContainer shape,
1417 StridesContainer strides,
1418 const T *ptr = nullptr,
1419 handle base = handle())
1420 : array(std::move(shape), std::move(strides), ptr, base) {}
1421
1422 explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
1423 : array_t(private_ctor{},
1424 std::move(shape),
1425 (ExtraFlags & f_style) != 0 ? detail::f_strides(*shape, itemsize())
1426 : detail::c_strides(*shape, itemsize()),
1427 ptr,
1428 base) {}
1429
1430 explicit array_t(ssize_t count, const T *ptr = nullptr, handle base = handle())
1431 : array({count}, {}, ptr, base) {}
1432
1433 constexpr ssize_t itemsize() const { return sizeof(T); }
1434
1435 template <typename... Ix>
1436 ssize_t index_at(Ix... index) const {
1437 return offset_at(index...) / itemsize();
1438 }
1439
1440 template <typename... Ix>
1441 const T *data(Ix... index) const {
1442 return static_cast<const T *>(array::data(index...));
1443 }
1444
1445 template <typename... Ix>
1446 T *mutable_data(Ix... index) {
1447 return static_cast<T *>(array::mutable_data(index...));
1448 }
1449
1450
1451 template <typename... Ix>
1452 const T &at(Ix... index) const {
1453 if ((ssize_t) sizeof...(index) != ndim()) {
1454 fail_dim_check(sizeof...(index), "index dimension mismatch");
1455 }
1456 return *(static_cast<const T *>(array::data())
1457 + byte_offset(ssize_t(index)...) / itemsize());
1458 }
1459
1460
1461 template <typename... Ix>
1462 T &mutable_at(Ix... index) {
1463 if ((ssize_t) sizeof...(index) != ndim()) {
1464 fail_dim_check(sizeof...(index), "index dimension mismatch");
1465 }
1466 return *(static_cast<T *>(array::mutable_data())
1467 + byte_offset(ssize_t(index)...) / itemsize());
1468 }
1469
1470
1471
1472
1473
1474
1475
1476 template <ssize_t Dims = -1>
1477 detail::unchecked_mutable_reference<T, Dims> mutable_unchecked() & {
1478 return array::mutable_unchecked<T, Dims>();
1479 }
1480
1481
1482
1483
1484
1485
1486
1487
1488 template <ssize_t Dims = -1>
1489 detail::unchecked_reference<T, Dims> unchecked() const & {
1490 return array::unchecked<T, Dims>();
1491 }
1492
1493
1494
1495 static array_t ensure(handle h) {
1496 auto result = reinterpret_steal<array_t>(raw_array_t(h.ptr()));
1497 if (!result) {
1498 PyErr_Clear();
1499 }
1500 return result;
1501 }
1502
1503 static bool check_(handle h) {
1504 const auto &api = detail::npy_api::get();
1505 return api.PyArray_Check_(h.ptr())
1506 && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr,
1507 dtype::of<T>().ptr())
1508 && detail::check_flags(h.ptr(), ExtraFlags & (array::c_style | array::f_style));
1509 }
1510
1511 protected:
1512
1513 static PyObject *raw_array_t(PyObject *ptr) {
1514 if (ptr == nullptr) {
1515 set_error(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr");
1516 return nullptr;
1517 }
1518 return detail::npy_api::get().PyArray_FromAny_(ptr,
1519 dtype::of<T>().release().ptr(),
1520 0,
1521 0,
1522 detail::npy_api::NPY_ARRAY_ENSUREARRAY_
1523 | ExtraFlags,
1524 nullptr);
1525 }
1526 };
1527
1528 template <typename T>
1529 struct format_descriptor<T, detail::enable_if_t<detail::is_pod_struct<T>::value>> {
1530 static std::string format() {
1531 return detail::npy_format_descriptor<typename std::remove_cv<T>::type>::format();
1532 }
1533 };
1534
1535 template <size_t N>
1536 struct format_descriptor<char[N]> {
1537 static std::string format() { return std::to_string(N) + 's'; }
1538 };
1539 template <size_t N>
1540 struct format_descriptor<std::array<char, N>> {
1541 static std::string format() { return std::to_string(N) + 's'; }
1542 };
1543
1544 template <typename T>
1545 struct format_descriptor<T, detail::enable_if_t<std::is_enum<T>::value>> {
1546 static std::string format() {
1547 return format_descriptor<
1548 typename std::remove_cv<typename std::underlying_type<T>::type>::type>::format();
1549 }
1550 };
1551
1552 template <typename T>
1553 struct format_descriptor<T, detail::enable_if_t<detail::array_info<T>::is_array>> {
1554 static std::string format() {
1555 using namespace detail;
1556 static constexpr auto extents = const_name("(") + array_info<T>::extents + const_name(")");
1557 return extents.text + format_descriptor<remove_all_extents_t<T>>::format();
1558 }
1559 };
1560
1561 PYBIND11_NAMESPACE_BEGIN(detail)
1562 template <typename T, int ExtraFlags>
1563 struct pyobject_caster<array_t<T, ExtraFlags>> {
1564 using type = array_t<T, ExtraFlags>;
1565
1566 bool load(handle src, bool convert) {
1567 if (!convert && !type::check_(src)) {
1568 return false;
1569 }
1570 value = type::ensure(src);
1571 return static_cast<bool>(value);
1572 }
1573
1574 static handle cast(const handle &src, return_value_policy , handle ) {
1575 return src.inc_ref();
1576 }
1577 PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
1578 };
1579
1580 template <typename T>
1581 struct compare_buffer_info<T, detail::enable_if_t<detail::is_pod_struct<T>::value>> {
1582 static bool compare(const buffer_info &b) {
1583 return npy_api::get().PyArray_EquivTypes_(dtype::of<T>().ptr(), dtype(b).ptr());
1584 }
1585 };
1586
1587 template <typename T>
1588 struct npy_format_descriptor<
1589 T,
1590 enable_if_t<satisfies_any_of<T, std::is_arithmetic, is_complex>::value>>
1591 : npy_format_descriptor_name<T> {
1592 private:
1593
1594 constexpr static const int values[15] = {npy_api::NPY_BOOL_,
1595 npy_api::NPY_BYTE_,
1596 npy_api::NPY_UBYTE_,
1597 npy_api::NPY_INT16_,
1598 npy_api::NPY_UINT16_,
1599 npy_api::NPY_INT32_,
1600 npy_api::NPY_UINT32_,
1601 npy_api::NPY_INT64_,
1602 npy_api::NPY_UINT64_,
1603 npy_api::NPY_FLOAT_,
1604 npy_api::NPY_DOUBLE_,
1605 npy_api::NPY_LONGDOUBLE_,
1606 npy_api::NPY_CFLOAT_,
1607 npy_api::NPY_CDOUBLE_,
1608 npy_api::NPY_CLONGDOUBLE_};
1609
1610 public:
1611 static constexpr int value = values[detail::is_fmt_numeric<T>::index];
1612
1613 static pybind11::dtype dtype() { return pybind11::dtype( value); }
1614 };
1615
1616 template <typename T>
1617 struct npy_format_descriptor<
1618 T,
1619 enable_if_t<is_same_ignoring_cvref<T, PyObject *>::value
1620 || ((std::is_same<T, handle>::value || std::is_same<T, object>::value)
1621 && sizeof(T) == sizeof(PyObject *))>> {
1622 static constexpr auto name = const_name("numpy.object_");
1623
1624 static constexpr int value = npy_api::NPY_OBJECT_;
1625
1626 static pybind11::dtype dtype() { return pybind11::dtype( value); }
1627 };
1628
1629 #define PYBIND11_DECL_CHAR_FMT \
1630 static constexpr auto name = const_name("S") + const_name<N>(); \
1631 static pybind11::dtype dtype() { \
1632 return pybind11::dtype(std::string("S") + std::to_string(N)); \
1633 }
1634 template <size_t N>
1635 struct npy_format_descriptor<char[N]> {
1636 PYBIND11_DECL_CHAR_FMT
1637 };
1638 template <size_t N>
1639 struct npy_format_descriptor<std::array<char, N>> {
1640 PYBIND11_DECL_CHAR_FMT
1641 };
1642 #undef PYBIND11_DECL_CHAR_FMT
1643
1644 template <typename T>
1645 struct npy_format_descriptor<T, enable_if_t<array_info<T>::is_array>> {
1646 private:
1647 using base_descr = npy_format_descriptor<typename array_info<T>::type>;
1648
1649 public:
1650 static_assert(!array_info<T>::is_empty, "Zero-sized arrays are not supported");
1651
1652 static constexpr auto name
1653 = const_name("(") + array_info<T>::extents + const_name(")") + base_descr::name;
1654 static pybind11::dtype dtype() {
1655 list shape;
1656 array_info<T>::append_extents(shape);
1657 return pybind11::dtype::from_args(
1658 pybind11::make_tuple(base_descr::dtype(), std::move(shape)));
1659 }
1660 };
1661
1662 template <typename T>
1663 struct npy_format_descriptor<T, enable_if_t<std::is_enum<T>::value>> {
1664 private:
1665 using base_descr = npy_format_descriptor<typename std::underlying_type<T>::type>;
1666
1667 public:
1668 static constexpr auto name = base_descr::name;
1669 static pybind11::dtype dtype() { return base_descr::dtype(); }
1670 };
1671
1672 struct field_descriptor {
1673 const char *name;
1674 ssize_t offset;
1675 ssize_t size;
1676 std::string format;
1677 dtype descr;
1678 };
1679
1680 PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor> fields,
1681 const std::type_info &tinfo,
1682 ssize_t itemsize,
1683 bool (*direct_converter)(PyObject *, void *&)) {
1684
1685 auto &numpy_internals = get_numpy_internals();
1686 if (numpy_internals.get_type_info(tinfo, false)) {
1687 pybind11_fail("NumPy: dtype is already registered");
1688 }
1689
1690
1691
1692 std::vector<field_descriptor> ordered_fields(std::move(fields));
1693 std::sort(
1694 ordered_fields.begin(),
1695 ordered_fields.end(),
1696 [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; });
1697
1698 list names, formats, offsets;
1699 for (auto &field : ordered_fields) {
1700 if (!field.descr) {
1701 pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ "
1702 + tinfo.name());
1703 }
1704 names.append(pybind11::str(field.name));
1705 formats.append(field.descr);
1706 offsets.append(pybind11::int_(field.offset));
1707 }
1708 auto *dtype_ptr
1709 = pybind11::dtype(std::move(names), std::move(formats), std::move(offsets), itemsize)
1710 .release()
1711 .ptr();
1712
1713
1714
1715
1716
1717
1718
1719
1720 ssize_t offset = 0;
1721 std::ostringstream oss;
1722
1723
1724
1725
1726
1727 oss << "^T{";
1728 for (auto &field : ordered_fields) {
1729 if (field.offset > offset) {
1730 oss << (field.offset - offset) << 'x';
1731 }
1732 oss << field.format << ':' << field.name << ':';
1733 offset = field.offset + field.size;
1734 }
1735 if (itemsize > offset) {
1736 oss << (itemsize - offset) << 'x';
1737 }
1738 oss << '}';
1739 auto format_str = oss.str();
1740
1741
1742 auto &api = npy_api::get();
1743 auto arr = array(buffer_info(nullptr, itemsize, format_str, 1));
1744 if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) {
1745 pybind11_fail("NumPy: invalid buffer descriptor!");
1746 }
1747
1748 auto tindex = std::type_index(tinfo);
1749 numpy_internals.registered_dtypes[tindex] = {dtype_ptr, std::move(format_str)};
1750 with_internals([tindex, &direct_converter](internals &internals) {
1751 internals.direct_conversions[tindex].push_back(direct_converter);
1752 });
1753 }
1754
1755 template <typename T, typename SFINAE>
1756 struct npy_format_descriptor {
1757 static_assert(is_pod_struct<T>::value,
1758 "Attempt to use a non-POD or unimplemented POD type as a numpy dtype");
1759
1760 static constexpr auto name = make_caster<T>::name;
1761
1762 static pybind11::dtype dtype() { return reinterpret_borrow<pybind11::dtype>(dtype_ptr()); }
1763
1764 static std::string format() {
1765 static auto format_str = get_numpy_internals().get_type_info<T>(true)->format_str;
1766 return format_str;
1767 }
1768
1769 static void register_dtype(any_container<field_descriptor> fields) {
1770 register_structured_dtype(std::move(fields),
1771 typeid(typename std::remove_cv<T>::type),
1772 sizeof(T),
1773 &direct_converter);
1774 }
1775
1776 private:
1777 static PyObject *dtype_ptr() {
1778 static PyObject *ptr = get_numpy_internals().get_type_info<T>(true)->dtype_ptr;
1779 return ptr;
1780 }
1781
1782 static bool direct_converter(PyObject *obj, void *&value) {
1783 auto &api = npy_api::get();
1784 if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) {
1785 return false;
1786 }
1787 if (auto descr = reinterpret_steal<object>(api.PyArray_DescrFromScalar_(obj))) {
1788 if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) {
1789 value = ((PyVoidScalarObject_Proxy *) obj)->obval;
1790 return true;
1791 }
1792 }
1793 return false;
1794 }
1795 };
1796
1797 #ifdef __CLION_IDE__
1798 # define PYBIND11_NUMPY_DTYPE(Type, ...) ((void) 0)
1799 # define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void) 0)
1800 #else
1801
1802 # define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \
1803 ::pybind11::detail::field_descriptor { \
1804 Name, offsetof(T, Field), sizeof(decltype(std::declval<T>().Field)), \
1805 ::pybind11::format_descriptor<decltype(std::declval<T>().Field)>::format(), \
1806 ::pybind11::detail::npy_format_descriptor< \
1807 decltype(std::declval<T>().Field)>::dtype() \
1808 }
1809
1810
1811 # define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field)
1812
1813
1814
1815 # define PYBIND11_EVAL0(...) __VA_ARGS__
1816 # define PYBIND11_EVAL1(...) PYBIND11_EVAL0(PYBIND11_EVAL0(PYBIND11_EVAL0(__VA_ARGS__)))
1817 # define PYBIND11_EVAL2(...) PYBIND11_EVAL1(PYBIND11_EVAL1(PYBIND11_EVAL1(__VA_ARGS__)))
1818 # define PYBIND11_EVAL3(...) PYBIND11_EVAL2(PYBIND11_EVAL2(PYBIND11_EVAL2(__VA_ARGS__)))
1819 # define PYBIND11_EVAL4(...) PYBIND11_EVAL3(PYBIND11_EVAL3(PYBIND11_EVAL3(__VA_ARGS__)))
1820 # define PYBIND11_EVAL(...) PYBIND11_EVAL4(PYBIND11_EVAL4(PYBIND11_EVAL4(__VA_ARGS__)))
1821 # define PYBIND11_MAP_END(...)
1822 # define PYBIND11_MAP_OUT
1823 # define PYBIND11_MAP_COMMA ,
1824 # define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END
1825 # define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT
1826 # define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0(test, next, 0)
1827 # define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1(PYBIND11_MAP_GET_END test, next)
1828 # if defined(_MSC_VER) \
1829 && !defined(__clang__)
1830 # define PYBIND11_MAP_LIST_NEXT1(test, next) \
1831 PYBIND11_EVAL0(PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0))
1832 # else
1833 # define PYBIND11_MAP_LIST_NEXT1(test, next) \
1834 PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0)
1835 # endif
1836 # define PYBIND11_MAP_LIST_NEXT(test, next) \
1837 PYBIND11_MAP_LIST_NEXT1(PYBIND11_MAP_GET_END test, next)
1838 # define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \
1839 f(t, x) PYBIND11_MAP_LIST_NEXT(peek, PYBIND11_MAP_LIST1)(f, t, peek, __VA_ARGS__)
1840 # define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \
1841 f(t, x) PYBIND11_MAP_LIST_NEXT(peek, PYBIND11_MAP_LIST0)(f, t, peek, __VA_ARGS__)
1842
1843 # define PYBIND11_MAP_LIST(f, t, ...) \
1844 PYBIND11_EVAL(PYBIND11_MAP_LIST1(f, t, __VA_ARGS__, (), 0))
1845
1846 # define PYBIND11_NUMPY_DTYPE(Type, ...) \
1847 ::pybind11::detail::npy_format_descriptor<Type>::register_dtype( \
1848 ::std::vector<::pybind11::detail::field_descriptor>{ \
1849 PYBIND11_MAP_LIST(PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)})
1850
1851 # if defined(_MSC_VER) && !defined(__clang__)
1852 # define PYBIND11_MAP2_LIST_NEXT1(test, next) \
1853 PYBIND11_EVAL0(PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0))
1854 # else
1855 # define PYBIND11_MAP2_LIST_NEXT1(test, next) \
1856 PYBIND11_MAP_NEXT0(test, PYBIND11_MAP_COMMA next, 0)
1857 # endif
1858 # define PYBIND11_MAP2_LIST_NEXT(test, next) \
1859 PYBIND11_MAP2_LIST_NEXT1(PYBIND11_MAP_GET_END test, next)
1860 # define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \
1861 f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT(peek, PYBIND11_MAP2_LIST1)(f, t, peek, __VA_ARGS__)
1862 # define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \
1863 f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT(peek, PYBIND11_MAP2_LIST0)(f, t, peek, __VA_ARGS__)
1864
1865 # define PYBIND11_MAP2_LIST(f, t, ...) \
1866 PYBIND11_EVAL(PYBIND11_MAP2_LIST1(f, t, __VA_ARGS__, (), 0))
1867
1868 # define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \
1869 ::pybind11::detail::npy_format_descriptor<Type>::register_dtype( \
1870 ::std::vector<::pybind11::detail::field_descriptor>{ \
1871 PYBIND11_MAP2_LIST(PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)})
1872
1873 #endif
1874
1875 class common_iterator {
1876 public:
1877 using container_type = std::vector<ssize_t>;
1878 using value_type = container_type::value_type;
1879 using size_type = container_type::size_type;
1880
1881 common_iterator() = default;
1882
1883 common_iterator(void *ptr, const container_type &strides, const container_type &shape)
1884 : p_ptr(reinterpret_cast<char *>(ptr)), m_strides(strides.size()) {
1885 m_strides.back() = static_cast<value_type>(strides.back());
1886 for (size_type i = m_strides.size() - 1; i != 0; --i) {
1887 size_type j = i - 1;
1888 auto s = static_cast<value_type>(shape[i]);
1889 m_strides[j] = strides[j] + m_strides[i] - strides[i] * s;
1890 }
1891 }
1892
1893 void increment(size_type dim) { p_ptr += m_strides[dim]; }
1894
1895 void *data() const { return p_ptr; }
1896
1897 private:
1898 char *p_ptr{nullptr};
1899 container_type m_strides;
1900 };
1901
1902 template <size_t N>
1903 class multi_array_iterator {
1904 public:
1905 using container_type = std::vector<ssize_t>;
1906
1907 multi_array_iterator(const std::array<buffer_info, N> &buffers, const container_type &shape)
1908 : m_shape(shape.size()), m_index(shape.size(), 0), m_common_iterator() {
1909
1910
1911 for (size_t i = 0; i < shape.size(); ++i) {
1912 m_shape[i] = shape[i];
1913 }
1914
1915 container_type strides(shape.size());
1916 for (size_t i = 0; i < N; ++i) {
1917 init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
1918 }
1919 }
1920
1921 multi_array_iterator &operator++() {
1922 for (size_t j = m_index.size(); j != 0; --j) {
1923 size_t i = j - 1;
1924 if (++m_index[i] != m_shape[i]) {
1925 increment_common_iterator(i);
1926 break;
1927 }
1928 m_index[i] = 0;
1929 }
1930 return *this;
1931 }
1932
1933 template <size_t K, class T = void>
1934 T *data() const {
1935 return reinterpret_cast<T *>(m_common_iterator[K].data());
1936 }
1937
1938 private:
1939 using common_iter = common_iterator;
1940
1941 void init_common_iterator(const buffer_info &buffer,
1942 const container_type &shape,
1943 common_iter &iterator,
1944 container_type &strides) {
1945 auto buffer_shape_iter = buffer.shape.rbegin();
1946 auto buffer_strides_iter = buffer.strides.rbegin();
1947 auto shape_iter = shape.rbegin();
1948 auto strides_iter = strides.rbegin();
1949
1950 while (buffer_shape_iter != buffer.shape.rend()) {
1951 if (*shape_iter == *buffer_shape_iter) {
1952 *strides_iter = *buffer_strides_iter;
1953 } else {
1954 *strides_iter = 0;
1955 }
1956
1957 ++buffer_shape_iter;
1958 ++buffer_strides_iter;
1959 ++shape_iter;
1960 ++strides_iter;
1961 }
1962
1963 std::fill(strides_iter, strides.rend(), 0);
1964 iterator = common_iter(buffer.ptr, strides, shape);
1965 }
1966
1967 void increment_common_iterator(size_t dim) {
1968 for (auto &iter : m_common_iterator) {
1969 iter.increment(dim);
1970 }
1971 }
1972
1973 container_type m_shape;
1974 container_type m_index;
1975 std::array<common_iter, N> m_common_iterator;
1976 };
1977
1978 enum class broadcast_trivial { non_trivial, c_trivial, f_trivial };
1979
1980
1981
1982
1983
1984 template <size_t N>
1985 broadcast_trivial
1986 broadcast(const std::array<buffer_info, N> &buffers, ssize_t &ndim, std::vector<ssize_t> &shape) {
1987 ndim = std::accumulate(
1988 buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) {
1989 return std::max(res, buf.ndim);
1990 });
1991
1992 shape.clear();
1993 shape.resize((size_t) ndim, 1);
1994
1995
1996
1997 for (size_t i = 0; i < N; ++i) {
1998 auto res_iter = shape.rbegin();
1999 auto end = buffers[i].shape.rend();
2000 for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end;
2001 ++shape_iter, ++res_iter) {
2002 const auto &dim_size_in = *shape_iter;
2003 auto &dim_size_out = *res_iter;
2004
2005
2006
2007 if (dim_size_out == 1) {
2008 dim_size_out = dim_size_in;
2009 } else if (dim_size_in != 1 && dim_size_in != dim_size_out) {
2010 pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!");
2011 }
2012 }
2013 }
2014
2015 bool trivial_broadcast_c = true;
2016 bool trivial_broadcast_f = true;
2017 for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) {
2018 if (buffers[i].size == 1) {
2019 continue;
2020 }
2021
2022
2023 if (buffers[i].ndim != ndim) {
2024 return broadcast_trivial::non_trivial;
2025 }
2026
2027
2028 if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) {
2029 return broadcast_trivial::non_trivial;
2030 }
2031
2032
2033 if (trivial_broadcast_c) {
2034 ssize_t expect_stride = buffers[i].itemsize;
2035 auto end = buffers[i].shape.crend();
2036 for (auto shape_iter = buffers[i].shape.crbegin(),
2037 stride_iter = buffers[i].strides.crbegin();
2038 trivial_broadcast_c && shape_iter != end;
2039 ++shape_iter, ++stride_iter) {
2040 if (expect_stride == *stride_iter) {
2041 expect_stride *= *shape_iter;
2042 } else {
2043 trivial_broadcast_c = false;
2044 }
2045 }
2046 }
2047
2048
2049 if (trivial_broadcast_f) {
2050 ssize_t expect_stride = buffers[i].itemsize;
2051 auto end = buffers[i].shape.cend();
2052 for (auto shape_iter = buffers[i].shape.cbegin(),
2053 stride_iter = buffers[i].strides.cbegin();
2054 trivial_broadcast_f && shape_iter != end;
2055 ++shape_iter, ++stride_iter) {
2056 if (expect_stride == *stride_iter) {
2057 expect_stride *= *shape_iter;
2058 } else {
2059 trivial_broadcast_f = false;
2060 }
2061 }
2062 }
2063 }
2064
2065 return trivial_broadcast_c ? broadcast_trivial::c_trivial
2066 : trivial_broadcast_f ? broadcast_trivial::f_trivial
2067 : broadcast_trivial::non_trivial;
2068 }
2069
2070 template <typename T>
2071 struct vectorize_arg {
2072 static_assert(!std::is_rvalue_reference<T>::value,
2073 "Functions with rvalue reference arguments cannot be vectorized");
2074
2075 using call_type = remove_reference_t<T>;
2076
2077 static constexpr bool vectorize
2078 = satisfies_any_of<call_type, std::is_arithmetic, is_complex, is_pod>::value
2079 && satisfies_none_of<call_type,
2080 std::is_pointer,
2081 std::is_array,
2082 is_std_array,
2083 std::is_enum>::value
2084 && (!std::is_reference<T>::value
2085 || (std::is_lvalue_reference<T>::value && std::is_const<call_type>::value));
2086
2087 using type = conditional_t<vectorize, array_t<remove_cv_t<call_type>, array::forcecast>, T>;
2088 };
2089
2090
2091 template <typename Func, typename Return, typename... Args>
2092 struct vectorize_returned_array {
2093 using Type = array_t<Return>;
2094
2095 static Type create(broadcast_trivial trivial, const std::vector<ssize_t> &shape) {
2096 if (trivial == broadcast_trivial::f_trivial) {
2097 return array_t<Return, array::f_style>(shape);
2098 }
2099 return array_t<Return>(shape);
2100 }
2101
2102 static Return *mutable_data(Type &array) { return array.mutable_data(); }
2103
2104 static Return call(Func &f, Args &...args) { return f(args...); }
2105
2106 static void call(Return *out, size_t i, Func &f, Args &...args) { out[i] = f(args...); }
2107 };
2108
2109
2110 template <typename Func, typename... Args>
2111 struct vectorize_returned_array<Func, void, Args...> {
2112 using Type = none;
2113
2114 static Type create(broadcast_trivial, const std::vector<ssize_t> &) { return none(); }
2115
2116 static void *mutable_data(Type &) { return nullptr; }
2117
2118 static detail::void_type call(Func &f, Args &...args) {
2119 f(args...);
2120 return {};
2121 }
2122
2123 static void call(void *, size_t, Func &f, Args &...args) { f(args...); }
2124 };
2125
2126 template <typename Func, typename Return, typename... Args>
2127 struct vectorize_helper {
2128
2129
2130 #ifdef __CUDACC__
2131 public:
2132 #else
2133 private:
2134 #endif
2135
2136 static constexpr size_t N = sizeof...(Args);
2137 static constexpr size_t NVectorized = constexpr_sum(vectorize_arg<Args>::vectorize...);
2138 static_assert(
2139 NVectorized >= 1,
2140 "pybind11::vectorize(...) requires a function with at least one vectorizable argument");
2141
2142 public:
2143 template <typename T,
2144
2145 typename = detail::enable_if_t<
2146 !std::is_same<vectorize_helper, typename std::decay<T>::type>::value>>
2147 explicit vectorize_helper(T &&f) : f(std::forward<T>(f)) {}
2148
2149 object operator()(typename vectorize_arg<Args>::type... args) {
2150 return run(args...,
2151 make_index_sequence<N>(),
2152 select_indices<vectorize_arg<Args>::vectorize...>(),
2153 make_index_sequence<NVectorized>());
2154 }
2155
2156 private:
2157 remove_reference_t<Func> f;
2158
2159
2160
2161 using arg_call_types = std::tuple<typename vectorize_arg<Args>::call_type...>;
2162 template <size_t Index>
2163 using param_n_t = typename std::tuple_element<Index, arg_call_types>::type;
2164
2165 using returned_array = vectorize_returned_array<Func, Return, Args...>;
2166
2167
2168
2169
2170
2171
2172
2173
2174 template <size_t... Index, size_t... VIndex, size_t... BIndex>
2175 object run(typename vectorize_arg<Args>::type &...args,
2176 index_sequence<Index...> i_seq,
2177 index_sequence<VIndex...> vi_seq,
2178 index_sequence<BIndex...> bi_seq) {
2179
2180
2181
2182
2183 std::array<void *, N> params{{reinterpret_cast<void *>(&args)...}};
2184
2185
2186 std::array<buffer_info, NVectorized> buffers{
2187 {reinterpret_cast<array *>(params[VIndex])->request()...}};
2188
2189
2190 ssize_t nd = 0;
2191 std::vector<ssize_t> shape(0);
2192 auto trivial = broadcast(buffers, nd, shape);
2193 auto ndim = (size_t) nd;
2194
2195 size_t size
2196 = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies<size_t>());
2197
2198
2199
2200 if (size == 1 && ndim == 0) {
2201 PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr);
2202 return cast(
2203 returned_array::call(f, *reinterpret_cast<param_n_t<Index> *>(params[Index])...));
2204 }
2205
2206 auto result = returned_array::create(trivial, shape);
2207
2208 PYBIND11_WARNING_PUSH
2209 #ifdef PYBIND11_DETECTED_CLANG_WITH_MISLEADING_CALL_STD_MOVE_EXPLICITLY_WARNING
2210 PYBIND11_WARNING_DISABLE_CLANG("-Wreturn-std-move")
2211 #endif
2212
2213 if (size == 0) {
2214 return result;
2215 }
2216
2217
2218 auto *mutable_data = returned_array::mutable_data(result);
2219 if (trivial == broadcast_trivial::non_trivial) {
2220 apply_broadcast(buffers, params, mutable_data, size, shape, i_seq, vi_seq, bi_seq);
2221 } else {
2222 apply_trivial(buffers, params, mutable_data, size, i_seq, vi_seq, bi_seq);
2223 }
2224
2225 return result;
2226 PYBIND11_WARNING_POP
2227 }
2228
2229 template <size_t... Index, size_t... VIndex, size_t... BIndex>
2230 void apply_trivial(std::array<buffer_info, NVectorized> &buffers,
2231 std::array<void *, N> ¶ms,
2232 Return *out,
2233 size_t size,
2234 index_sequence<Index...>,
2235 index_sequence<VIndex...>,
2236 index_sequence<BIndex...>) {
2237
2238
2239
2240
2241 std::array<std::pair<unsigned char *&, const size_t>, NVectorized> vecparams{
2242 {std::pair<unsigned char *&, const size_t>(
2243 reinterpret_cast<unsigned char *&>(params[VIndex] = buffers[BIndex].ptr),
2244 buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t<VIndex>))...}};
2245
2246 for (size_t i = 0; i < size; ++i) {
2247 returned_array::call(
2248 out, i, f, *reinterpret_cast<param_n_t<Index> *>(params[Index])...);
2249 for (auto &x : vecparams) {
2250 x.first += x.second;
2251 }
2252 }
2253 }
2254
2255 template <size_t... Index, size_t... VIndex, size_t... BIndex>
2256 void apply_broadcast(std::array<buffer_info, NVectorized> &buffers,
2257 std::array<void *, N> ¶ms,
2258 Return *out,
2259 size_t size,
2260 const std::vector<ssize_t> &output_shape,
2261 index_sequence<Index...>,
2262 index_sequence<VIndex...>,
2263 index_sequence<BIndex...>) {
2264
2265 multi_array_iterator<NVectorized> input_iter(buffers, output_shape);
2266
2267 for (size_t i = 0; i < size; ++i, ++input_iter) {
2268 PYBIND11_EXPAND_SIDE_EFFECTS((params[VIndex] = input_iter.template data<BIndex>()));
2269 returned_array::call(
2270 out, i, f, *reinterpret_cast<param_n_t<Index> *>(std::get<Index>(params))...);
2271 }
2272 }
2273 };
2274
2275 template <typename Func, typename Return, typename... Args>
2276 vectorize_helper<Func, Return, Args...> vectorize_extractor(const Func &f, Return (*)(Args...)) {
2277 return detail::vectorize_helper<Func, Return, Args...>(f);
2278 }
2279
2280 template <typename T, int Flags>
2281 struct handle_type_name<array_t<T, Flags>> {
2282 static constexpr auto name
2283 = io_name("typing.Annotated[numpy.typing.ArrayLike, ", "numpy.typing.NDArray[")
2284 + npy_format_descriptor<T>::name + const_name("]");
2285 };
2286
2287 PYBIND11_NAMESPACE_END(detail)
2288
2289
2290 template <typename Return, typename... Args>
2291 detail::vectorize_helper<Return (*)(Args...), Return, Args...> vectorize(Return (*f)(Args...)) {
2292 return detail::vectorize_helper<Return (*)(Args...), Return, Args...>(f);
2293 }
2294
2295
2296 template <typename Func, detail::enable_if_t<detail::is_lambda<Func>::value, int> = 0>
2297 auto vectorize(Func &&f)
2298 -> decltype(detail::vectorize_extractor(std::forward<Func>(f),
2299 (detail::function_signature_t<Func> *) nullptr)) {
2300 return detail::vectorize_extractor(std::forward<Func>(f),
2301 (detail::function_signature_t<Func> *) nullptr);
2302 }
2303
2304
2305 template <typename Return,
2306 typename Class,
2307 typename... Args,
2308 typename Helper = detail::vectorize_helper<
2309 decltype(std::mem_fn(std::declval<Return (Class::*)(Args...)>())),
2310 Return,
2311 Class *,
2312 Args...>>
2313 Helper vectorize(Return (Class::*f)(Args...)) {
2314 return Helper(std::mem_fn(f));
2315 }
2316
2317
2318 template <typename Return,
2319 typename Class,
2320 typename... Args,
2321 typename Helper = detail::vectorize_helper<
2322 decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const>())),
2323 Return,
2324 const Class *,
2325 Args...>>
2326 Helper vectorize(Return (Class::*f)(Args...) const) {
2327 return Helper(std::mem_fn(f));
2328 }
2329
2330 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)