github.com/kaydxh/golang@v0.0.131/pkg/gocv/cgo/third_path/pybind11/tests/test_numpy_array.cpp (about)

     1  /*
     2      tests/test_numpy_array.cpp -- test core array functionality
     3  
     4      Copyright (c) 2016 Ivan Smirnov <i.s.smirnov@gmail.com>
     5  
     6      All rights reserved. Use of this source code is governed by a
     7      BSD-style license that can be found in the LICENSE file.
     8  */
     9  
    10  #include <pybind11/numpy.h>
    11  #include <pybind11/stl.h>
    12  
    13  #include "pybind11_tests.h"
    14  
    15  #include <cstdint>
    16  #include <utility>
    17  
    18  // Size / dtype checks.
    19  struct DtypeCheck {
    20      py::dtype numpy{};
    21      py::dtype pybind11{};
    22  };
    23  
    24  template <typename T>
    25  DtypeCheck get_dtype_check(const char *name) {
    26      py::module_ np = py::module_::import("numpy");
    27      DtypeCheck check{};
    28      check.numpy = np.attr("dtype")(np.attr(name));
    29      check.pybind11 = py::dtype::of<T>();
    30      return check;
    31  }
    32  
    33  std::vector<DtypeCheck> get_concrete_dtype_checks() {
    34      return {// Normalization
    35              get_dtype_check<std::int8_t>("int8"),
    36              get_dtype_check<std::uint8_t>("uint8"),
    37              get_dtype_check<std::int16_t>("int16"),
    38              get_dtype_check<std::uint16_t>("uint16"),
    39              get_dtype_check<std::int32_t>("int32"),
    40              get_dtype_check<std::uint32_t>("uint32"),
    41              get_dtype_check<std::int64_t>("int64"),
    42              get_dtype_check<std::uint64_t>("uint64")};
    43  }
    44  
    45  struct DtypeSizeCheck {
    46      std::string name{};
    47      int size_cpp{};
    48      int size_numpy{};
    49      // For debugging.
    50      py::dtype dtype{};
    51  };
    52  
    53  template <typename T>
    54  DtypeSizeCheck get_dtype_size_check() {
    55      DtypeSizeCheck check{};
    56      check.name = py::type_id<T>();
    57      check.size_cpp = sizeof(T);
    58      check.dtype = py::dtype::of<T>();
    59      check.size_numpy = check.dtype.attr("itemsize").template cast<int>();
    60      return check;
    61  }
    62  
    63  std::vector<DtypeSizeCheck> get_platform_dtype_size_checks() {
    64      return {
    65          get_dtype_size_check<short>(),
    66          get_dtype_size_check<unsigned short>(),
    67          get_dtype_size_check<int>(),
    68          get_dtype_size_check<unsigned int>(),
    69          get_dtype_size_check<long>(),
    70          get_dtype_size_check<unsigned long>(),
    71          get_dtype_size_check<long long>(),
    72          get_dtype_size_check<unsigned long long>(),
    73      };
    74  }
    75  
    76  // Arrays.
    77  using arr = py::array;
    78  using arr_t = py::array_t<uint16_t, 0>;
    79  static_assert(std::is_same<arr_t::value_type, uint16_t>::value, "");
    80  
    81  template <typename... Ix>
    82  arr data(const arr &a, Ix... index) {
    83      return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...));
    84  }
    85  
    86  template <typename... Ix>
    87  arr data_t(const arr_t &a, Ix... index) {
    88      return arr(a.size() - a.index_at(index...), a.data(index...));
    89  }
    90  
    91  template <typename... Ix>
    92  arr &mutate_data(arr &a, Ix... index) {
    93      auto *ptr = (uint8_t *) a.mutable_data(index...);
    94      for (py::ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) {
    95          ptr[i] = (uint8_t) (ptr[i] * 2);
    96      }
    97      return a;
    98  }
    99  
   100  template <typename... Ix>
   101  arr_t &mutate_data_t(arr_t &a, Ix... index) {
   102      auto ptr = a.mutable_data(index...);
   103      for (py::ssize_t i = 0; i < a.size() - a.index_at(index...); i++) {
   104          ptr[i]++;
   105      }
   106      return a;
   107  }
   108  
   109  template <typename... Ix>
   110  py::ssize_t index_at(const arr &a, Ix... idx) {
   111      return a.index_at(idx...);
   112  }
   113  template <typename... Ix>
   114  py::ssize_t index_at_t(const arr_t &a, Ix... idx) {
   115      return a.index_at(idx...);
   116  }
   117  template <typename... Ix>
   118  py::ssize_t offset_at(const arr &a, Ix... idx) {
   119      return a.offset_at(idx...);
   120  }
   121  template <typename... Ix>
   122  py::ssize_t offset_at_t(const arr_t &a, Ix... idx) {
   123      return a.offset_at(idx...);
   124  }
   125  template <typename... Ix>
   126  py::ssize_t at_t(const arr_t &a, Ix... idx) {
   127      return a.at(idx...);
   128  }
   129  template <typename... Ix>
   130  arr_t &mutate_at_t(arr_t &a, Ix... idx) {
   131      a.mutable_at(idx...)++;
   132      return a;
   133  }
   134  
   135  #define def_index_fn(name, type)                                                                  \
   136      sm.def(#name, [](type a) { return name(a); });                                                \
   137      sm.def(#name, [](type a, int i) { return name(a, i); });                                      \
   138      sm.def(#name, [](type a, int i, int j) { return name(a, i, j); });                            \
   139      sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); });
   140  
   141  template <typename T, typename T2>
   142  py::handle auxiliaries(T &&r, T2 &&r2) {
   143      if (r.ndim() != 2) {
   144          throw std::domain_error("error: ndim != 2");
   145      }
   146      py::list l;
   147      l.append(*r.data(0, 0));
   148      l.append(*r2.mutable_data(0, 0));
   149      l.append(r.data(0, 1) == r2.mutable_data(0, 1));
   150      l.append(r.ndim());
   151      l.append(r.itemsize());
   152      l.append(r.shape(0));
   153      l.append(r.shape(1));
   154      l.append(r.size());
   155      l.append(r.nbytes());
   156      return l.release();
   157  }
   158  
   159  // note: declaration at local scope would create a dangling reference!
   160  static int data_i = 42;
   161  
   162  TEST_SUBMODULE(numpy_array, sm) {
   163      try {
   164          py::module_::import("numpy");
   165      } catch (const py::error_already_set &) {
   166          return;
   167      }
   168  
   169      // test_dtypes
   170      py::class_<DtypeCheck>(sm, "DtypeCheck")
   171          .def_readonly("numpy", &DtypeCheck::numpy)
   172          .def_readonly("pybind11", &DtypeCheck::pybind11)
   173          .def("__repr__", [](const DtypeCheck &self) {
   174              return py::str("<DtypeCheck numpy={} pybind11={}>").format(self.numpy, self.pybind11);
   175          });
   176      sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks);
   177  
   178      py::class_<DtypeSizeCheck>(sm, "DtypeSizeCheck")
   179          .def_readonly("name", &DtypeSizeCheck::name)
   180          .def_readonly("size_cpp", &DtypeSizeCheck::size_cpp)
   181          .def_readonly("size_numpy", &DtypeSizeCheck::size_numpy)
   182          .def("__repr__", [](const DtypeSizeCheck &self) {
   183              return py::str("<DtypeSizeCheck name='{}' size_cpp={} size_numpy={} dtype={}>")
   184                  .format(self.name, self.size_cpp, self.size_numpy, self.dtype);
   185          });
   186      sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks);
   187  
   188      // test_array_attributes
   189      sm.def("ndim", [](const arr &a) { return a.ndim(); });
   190      sm.def("shape", [](const arr &a) { return arr(a.ndim(), a.shape()); });
   191      sm.def("shape", [](const arr &a, py::ssize_t dim) { return a.shape(dim); });
   192      sm.def("strides", [](const arr &a) { return arr(a.ndim(), a.strides()); });
   193      sm.def("strides", [](const arr &a, py::ssize_t dim) { return a.strides(dim); });
   194      sm.def("writeable", [](const arr &a) { return a.writeable(); });
   195      sm.def("size", [](const arr &a) { return a.size(); });
   196      sm.def("itemsize", [](const arr &a) { return a.itemsize(); });
   197      sm.def("nbytes", [](const arr &a) { return a.nbytes(); });
   198      sm.def("owndata", [](const arr &a) { return a.owndata(); });
   199  
   200      // test_index_offset
   201      def_index_fn(index_at, const arr &);
   202      def_index_fn(index_at_t, const arr_t &);
   203      def_index_fn(offset_at, const arr &);
   204      def_index_fn(offset_at_t, const arr_t &);
   205      // test_data
   206      def_index_fn(data, const arr &);
   207      def_index_fn(data_t, const arr_t &);
   208      // test_mutate_data, test_mutate_readonly
   209      def_index_fn(mutate_data, arr &);
   210      def_index_fn(mutate_data_t, arr_t &);
   211      def_index_fn(at_t, const arr_t &);
   212      def_index_fn(mutate_at_t, arr_t &);
   213  
   214      // test_make_c_f_array
   215      sm.def("make_f_array", [] { return py::array_t<float>({2, 2}, {4, 8}); });
   216      sm.def("make_c_array", [] { return py::array_t<float>({2, 2}, {8, 4}); });
   217  
   218      // test_empty_shaped_array
   219      sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); });
   220      // test numpy scalars (empty shape, ndim==0)
   221      sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); });
   222  
   223      // test_wrap
   224      sm.def("wrap", [](const py::array &a) {
   225          return py::array(a.dtype(),
   226                           {a.shape(), a.shape() + a.ndim()},
   227                           {a.strides(), a.strides() + a.ndim()},
   228                           a.data(),
   229                           a);
   230      });
   231  
   232      // test_numpy_view
   233      struct ArrayClass {
   234          int data[2] = {1, 2};
   235          ArrayClass() { py::print("ArrayClass()"); }
   236          ~ArrayClass() { py::print("~ArrayClass()"); }
   237      };
   238      py::class_<ArrayClass>(sm, "ArrayClass")
   239          .def(py::init<>())
   240          .def("numpy_view", [](py::object &obj) {
   241              py::print("ArrayClass::numpy_view()");
   242              auto &a = obj.cast<ArrayClass &>();
   243              return py::array_t<int>({2}, {4}, a.data, obj);
   244          });
   245  
   246      // test_cast_numpy_int64_to_uint64
   247      sm.def("function_taking_uint64", [](uint64_t) {});
   248  
   249      // test_isinstance
   250      sm.def("isinstance_untyped", [](py::object yes, py::object no) {
   251          return py::isinstance<py::array>(std::move(yes))
   252                 && !py::isinstance<py::array>(std::move(no));
   253      });
   254      sm.def("isinstance_typed", [](const py::object &o) {
   255          return py::isinstance<py::array_t<double>>(o) && !py::isinstance<py::array_t<int>>(o);
   256      });
   257  
   258      // test_constructors
   259      sm.def("default_constructors", []() {
   260          return py::dict("array"_a = py::array(),
   261                          "array_t<int32>"_a = py::array_t<std::int32_t>(),
   262                          "array_t<double>"_a = py::array_t<double>());
   263      });
   264      sm.def("converting_constructors", [](const py::object &o) {
   265          return py::dict("array"_a = py::array(o),
   266                          "array_t<int32>"_a = py::array_t<std::int32_t>(o),
   267                          "array_t<double>"_a = py::array_t<double>(o));
   268      });
   269  
   270      // test_overload_resolution
   271      sm.def("overloaded", [](const py::array_t<double> &) { return "double"; });
   272      sm.def("overloaded", [](const py::array_t<float> &) { return "float"; });
   273      sm.def("overloaded", [](const py::array_t<int> &) { return "int"; });
   274      sm.def("overloaded", [](const py::array_t<unsigned short> &) { return "unsigned short"; });
   275      sm.def("overloaded", [](const py::array_t<long long> &) { return "long long"; });
   276      sm.def("overloaded",
   277             [](const py::array_t<std::complex<double>> &) { return "double complex"; });
   278      sm.def("overloaded", [](const py::array_t<std::complex<float>> &) { return "float complex"; });
   279  
   280      sm.def("overloaded2",
   281             [](const py::array_t<std::complex<double>> &) { return "double complex"; });
   282      sm.def("overloaded2", [](const py::array_t<double> &) { return "double"; });
   283      sm.def("overloaded2",
   284             [](const py::array_t<std::complex<float>> &) { return "float complex"; });
   285      sm.def("overloaded2", [](const py::array_t<float> &) { return "float"; });
   286  
   287      // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
   288  
   289      // Only accept the exact types:
   290      sm.def(
   291          "overloaded3", [](const py::array_t<int> &) { return "int"; }, py::arg{}.noconvert());
   292      sm.def(
   293          "overloaded3",
   294          [](const py::array_t<double> &) { return "double"; },
   295          py::arg{}.noconvert());
   296  
   297      // Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but
   298      // rather that float gets converted via the safe (conversion to double) overload:
   299      sm.def("overloaded4", [](const py::array_t<long long, 0> &) { return "long long"; });
   300      sm.def("overloaded4", [](const py::array_t<double, 0> &) { return "double"; });
   301  
   302      // But we do allow conversion to int if forcecast is enabled (but only if no overload matches
   303      // without conversion)
   304      sm.def("overloaded5", [](const py::array_t<unsigned int> &) { return "unsigned int"; });
   305      sm.def("overloaded5", [](const py::array_t<double> &) { return "double"; });
   306  
   307      // test_greedy_string_overload
   308      // Issue 685: ndarray shouldn't go to std::string overload
   309      sm.def("issue685", [](const std::string &) { return "string"; });
   310      sm.def("issue685", [](const py::array &) { return "array"; });
   311      sm.def("issue685", [](const py::object &) { return "other"; });
   312  
   313      // test_array_unchecked_fixed_dims
   314      sm.def(
   315          "proxy_add2",
   316          [](py::array_t<double> a, double v) {
   317              auto r = a.mutable_unchecked<2>();
   318              for (py::ssize_t i = 0; i < r.shape(0); i++) {
   319                  for (py::ssize_t j = 0; j < r.shape(1); j++) {
   320                      r(i, j) += v;
   321                  }
   322              }
   323          },
   324          py::arg{}.noconvert(),
   325          py::arg());
   326  
   327      sm.def("proxy_init3", [](double start) {
   328          py::array_t<double, py::array::c_style> a({3, 3, 3});
   329          auto r = a.mutable_unchecked<3>();
   330          for (py::ssize_t i = 0; i < r.shape(0); i++) {
   331              for (py::ssize_t j = 0; j < r.shape(1); j++) {
   332                  for (py::ssize_t k = 0; k < r.shape(2); k++) {
   333                      r(i, j, k) = start++;
   334                  }
   335              }
   336          }
   337          return a;
   338      });
   339      sm.def("proxy_init3F", [](double start) {
   340          py::array_t<double, py::array::f_style> a({3, 3, 3});
   341          auto r = a.mutable_unchecked<3>();
   342          for (py::ssize_t k = 0; k < r.shape(2); k++) {
   343              for (py::ssize_t j = 0; j < r.shape(1); j++) {
   344                  for (py::ssize_t i = 0; i < r.shape(0); i++) {
   345                      r(i, j, k) = start++;
   346                  }
   347              }
   348          }
   349          return a;
   350      });
   351      sm.def("proxy_squared_L2_norm", [](const py::array_t<double> &a) {
   352          auto r = a.unchecked<1>();
   353          double sumsq = 0;
   354          for (py::ssize_t i = 0; i < r.shape(0); i++) {
   355              sumsq += r[i] * r(i); // Either notation works for a 1D array
   356          }
   357          return sumsq;
   358      });
   359  
   360      sm.def("proxy_auxiliaries2", [](py::array_t<double> a) {
   361          auto r = a.unchecked<2>();
   362          auto r2 = a.mutable_unchecked<2>();
   363          return auxiliaries(r, r2);
   364      });
   365  
   366      sm.def("proxy_auxiliaries1_const_ref", [](py::array_t<double> a) {
   367          const auto &r = a.unchecked<1>();
   368          const auto &r2 = a.mutable_unchecked<1>();
   369          return r(0) == r2(0) && r[0] == r2[0];
   370      });
   371  
   372      sm.def("proxy_auxiliaries2_const_ref", [](py::array_t<double> a) {
   373          const auto &r = a.unchecked<2>();
   374          const auto &r2 = a.mutable_unchecked<2>();
   375          return r(0, 0) == r2(0, 0);
   376      });
   377  
   378      // test_array_unchecked_dyn_dims
   379      // Same as the above, but without a compile-time dimensions specification:
   380      sm.def(
   381          "proxy_add2_dyn",
   382          [](py::array_t<double> a, double v) {
   383              auto r = a.mutable_unchecked();
   384              if (r.ndim() != 2) {
   385                  throw std::domain_error("error: ndim != 2");
   386              }
   387              for (py::ssize_t i = 0; i < r.shape(0); i++) {
   388                  for (py::ssize_t j = 0; j < r.shape(1); j++) {
   389                      r(i, j) += v;
   390                  }
   391              }
   392          },
   393          py::arg{}.noconvert(),
   394          py::arg());
   395      sm.def("proxy_init3_dyn", [](double start) {
   396          py::array_t<double, py::array::c_style> a({3, 3, 3});
   397          auto r = a.mutable_unchecked();
   398          if (r.ndim() != 3) {
   399              throw std::domain_error("error: ndim != 3");
   400          }
   401          for (py::ssize_t i = 0; i < r.shape(0); i++) {
   402              for (py::ssize_t j = 0; j < r.shape(1); j++) {
   403                  for (py::ssize_t k = 0; k < r.shape(2); k++) {
   404                      r(i, j, k) = start++;
   405                  }
   406              }
   407          }
   408          return a;
   409      });
   410      sm.def("proxy_auxiliaries2_dyn", [](py::array_t<double> a) {
   411          return auxiliaries(a.unchecked(), a.mutable_unchecked());
   412      });
   413  
   414      sm.def("array_auxiliaries2", [](py::array_t<double> a) { return auxiliaries(a, a); });
   415  
   416      // test_array_failures
   417      // Issue #785: Uninformative "Unknown internal error" exception when constructing array from
   418      // empty object:
   419      sm.def("array_fail_test", []() { return py::array(py::object()); });
   420      sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); });
   421      // Make sure the error from numpy is being passed through:
   422      sm.def("array_fail_test_negative_size", []() {
   423          int c = 0;
   424          return py::array(-1, &c);
   425      });
   426  
   427      // test_initializer_list
   428      // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
   429      sm.def("array_initializer_list1", []() { return py::array_t<float>(1); });
   430      // { 1 } also works for the above, but clang warns about it
   431      sm.def("array_initializer_list2", []() { return py::array_t<float>({1, 2}); });
   432      sm.def("array_initializer_list3", []() { return py::array_t<float>({1, 2, 3}); });
   433      sm.def("array_initializer_list4", []() { return py::array_t<float>({1, 2, 3, 4}); });
   434  
   435      // test_array_resize
   436      // reshape array to 2D without changing size
   437      sm.def("array_reshape2", [](py::array_t<double> a) {
   438          const auto dim_sz = (py::ssize_t) std::sqrt(a.size());
   439          if (dim_sz * dim_sz != a.size()) {
   440              throw std::domain_error(
   441                  "array_reshape2: input array total size is not a squared integer");
   442          }
   443          a.resize({dim_sz, dim_sz});
   444      });
   445  
   446      // resize to 3D array with each dimension = N
   447      sm.def("array_resize3", [](py::array_t<double> a, size_t N, bool refcheck) {
   448          a.resize({N, N, N}, refcheck);
   449      });
   450  
   451      // test_array_create_and_resize
   452      // return 2D array with Nrows = Ncols = N
   453      sm.def("create_and_resize", [](size_t N) {
   454          py::array_t<double> a;
   455          a.resize({N, N});
   456          std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.);
   457          return a;
   458      });
   459  
   460      sm.def("array_view",
   461             [](py::array_t<uint8_t> a, const std::string &dtype) { return a.view(dtype); });
   462  
   463      sm.def("reshape_initializer_list", [](py::array_t<int> a, size_t N, size_t M, size_t O) {
   464          return a.reshape({N, M, O});
   465      });
   466      sm.def("reshape_tuple", [](py::array_t<int> a, const std::vector<int> &new_shape) {
   467          return a.reshape(new_shape);
   468      });
   469  
   470      sm.def("index_using_ellipsis",
   471             [](const py::array &a) { return a[py::make_tuple(0, py::ellipsis(), 0)]; });
   472  
   473      // test_argument_conversions
   474      sm.def(
   475          "accept_double", [](const py::array_t<double, 0> &) {}, py::arg("a"));
   476      sm.def(
   477          "accept_double_forcecast",
   478          [](const py::array_t<double, py::array::forcecast> &) {},
   479          py::arg("a"));
   480      sm.def(
   481          "accept_double_c_style",
   482          [](const py::array_t<double, py::array::c_style> &) {},
   483          py::arg("a"));
   484      sm.def(
   485          "accept_double_c_style_forcecast",
   486          [](const py::array_t<double, py::array::forcecast | py::array::c_style> &) {},
   487          py::arg("a"));
   488      sm.def(
   489          "accept_double_f_style",
   490          [](const py::array_t<double, py::array::f_style> &) {},
   491          py::arg("a"));
   492      sm.def(
   493          "accept_double_f_style_forcecast",
   494          [](const py::array_t<double, py::array::forcecast | py::array::f_style> &) {},
   495          py::arg("a"));
   496      sm.def(
   497          "accept_double_noconvert", [](const py::array_t<double, 0> &) {}, "a"_a.noconvert());
   498      sm.def(
   499          "accept_double_forcecast_noconvert",
   500          [](const py::array_t<double, py::array::forcecast> &) {},
   501          "a"_a.noconvert());
   502      sm.def(
   503          "accept_double_c_style_noconvert",
   504          [](const py::array_t<double, py::array::c_style> &) {},
   505          "a"_a.noconvert());
   506      sm.def(
   507          "accept_double_c_style_forcecast_noconvert",
   508          [](const py::array_t<double, py::array::forcecast | py::array::c_style> &) {},
   509          "a"_a.noconvert());
   510      sm.def(
   511          "accept_double_f_style_noconvert",
   512          [](const py::array_t<double, py::array::f_style> &) {},
   513          "a"_a.noconvert());
   514      sm.def(
   515          "accept_double_f_style_forcecast_noconvert",
   516          [](const py::array_t<double, py::array::forcecast | py::array::f_style> &) {},
   517          "a"_a.noconvert());
   518  
   519      // Check that types returns correct npy format descriptor
   520      sm.def("test_fmt_desc_float", [](const py::array_t<float> &) {});
   521      sm.def("test_fmt_desc_double", [](const py::array_t<double> &) {});
   522      sm.def("test_fmt_desc_const_float", [](const py::array_t<const float> &) {});
   523      sm.def("test_fmt_desc_const_double", [](const py::array_t<const double> &) {});
   524  
   525      sm.def("round_trip_float", [](double d) { return d; });
   526  }