From 91b3d681ad96951bd51a719897b89487c3e99d37 Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Mon, 29 Aug 2016 02:41:05 +0100 Subject: Expose some dtype/array attributes via NumPy C API --- tests/test_numpy_array.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/test_numpy_array.cpp (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp new file mode 100644 index 0000000..ed118a0 --- /dev/null +++ b/tests/test_numpy_array.cpp @@ -0,0 +1,45 @@ +/* + tests/test_numpy_array.cpp -- test core array functionality + + Copyright (c) 2016 Ivan Smirnov + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#include "pybind11_tests.h" +#include +#include + +test_initializer numpy_array([](py::module &m) { + m.def("get_arr_ndim", [](const py::array& arr) { + return arr.ndim(); + }); + m.def("get_arr_shape", [](const py::array& arr) { + return std::vector(arr.shape(), arr.shape() + arr.ndim()); + }); + m.def("get_arr_shape", [](const py::array& arr, size_t dim) { + return arr.shape(dim); + }); + m.def("get_arr_strides", [](const py::array& arr) { + return std::vector(arr.strides(), arr.strides() + arr.ndim()); + }); + m.def("get_arr_strides", [](const py::array& arr, size_t dim) { + return arr.strides(dim); + }); + m.def("get_arr_writeable", [](const py::array& arr) { + return arr.writeable(); + }); + m.def("get_arr_size", [](const py::array& arr) { + return arr.size(); + }); + m.def("get_arr_itemsize", [](const py::array& arr) { + return arr.itemsize(); + }); + m.def("get_arr_nbytes", [](const py::array& arr) { + return arr.nbytes(); + }); + m.def("get_arr_owndata", [](const py::array& arr) { + return arr.owndata(); + }); +}); -- cgit v1.2.3 From aca6bcaea5e223752b210f991076d42202be61bf Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Thu, 8 Sep 2016 23:03:35 +0100 Subject: Add tests for array data access /index methods --- tests/test_numpy_array.cpp | 109 ++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 30 deletions(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index ed118a0..0614f57 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -8,38 +8,87 @@ */ #include "pybind11_tests.h" + #include #include +#include +#include + +using arr = py::array; +using arr_t = py::array_t; + +template arr data(const arr& a, Ix&&... index) { + return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...)); +} + +template arr data_t(const arr_t& a, Ix&&... index) { + return arr(a.size() - a.index_at(index...), a.data(index...)); +} + +arr& mutate_data(arr& a) { + auto ptr = (uint8_t *) a.mutable_data(); + for (size_t i = 0; i < a.nbytes(); i++) + ptr[i] = (uint8_t) (ptr[i] * 2); + return a; +} + +arr_t& mutate_data_t(arr_t& a) { + auto ptr = a.mutable_data(); + for (size_t i = 0; i < a.size(); i++) + ptr[i]++; + return a; +} + +template arr& mutate_data(arr& a, Ix&&... index) { + auto ptr = (uint8_t *) a.mutable_data(index...); + for (size_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) + ptr[i] = (uint8_t) (ptr[i] * 2); + return a; +} + +template arr_t& mutate_data_t(arr_t& a, Ix&&... index) { + auto ptr = a.mutable_data(index...); + for (size_t i = 0; i < a.size() - a.index_at(index...); i++) + ptr[i]++; + return a; +} + +template size_t index_at(const arr& a, Ix&&... idx) { return a.index_at(idx...); } +template size_t index_at_t(const arr_t& a, Ix&&... idx) { return a.index_at(idx...); } +template size_t offset_at(const arr& a, Ix&&... idx) { return a.offset_at(idx...); } +template size_t offset_at_t(const arr_t& a, Ix&&... idx) { return a.offset_at(idx...); } +template size_t at_t(const arr_t& a, Ix&&... idx) { return a.at(idx...); } +template arr_t& mutate_at_t(arr_t& a, Ix&&... idx) { a.mutable_at(idx...)++; return a; } + +#define def_index_fn(name, type) \ + sm.def(#name, [](type a) { return name(a); }); \ + sm.def(#name, [](type a, int i) { return name(a, i); }); \ + sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \ + sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); }); + test_initializer numpy_array([](py::module &m) { - m.def("get_arr_ndim", [](const py::array& arr) { - return arr.ndim(); - }); - m.def("get_arr_shape", [](const py::array& arr) { - return std::vector(arr.shape(), arr.shape() + arr.ndim()); - }); - m.def("get_arr_shape", [](const py::array& arr, size_t dim) { - return arr.shape(dim); - }); - m.def("get_arr_strides", [](const py::array& arr) { - return std::vector(arr.strides(), arr.strides() + arr.ndim()); - }); - m.def("get_arr_strides", [](const py::array& arr, size_t dim) { - return arr.strides(dim); - }); - m.def("get_arr_writeable", [](const py::array& arr) { - return arr.writeable(); - }); - m.def("get_arr_size", [](const py::array& arr) { - return arr.size(); - }); - m.def("get_arr_itemsize", [](const py::array& arr) { - return arr.itemsize(); - }); - m.def("get_arr_nbytes", [](const py::array& arr) { - return arr.nbytes(); - }); - m.def("get_arr_owndata", [](const py::array& arr) { - return arr.owndata(); - }); + auto sm = m.def_submodule("array"); + + sm.def("ndim", [](const arr& a) { return a.ndim(); }); + sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); }); + sm.def("shape", [](const arr& a, size_t dim) { return a.shape(dim); }); + sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); }); + sm.def("strides", [](const arr& a, size_t dim) { return a.strides(dim); }); + sm.def("writeable", [](const arr& a) { return a.writeable(); }); + sm.def("size", [](const arr& a) { return a.size(); }); + sm.def("itemsize", [](const arr& a) { return a.itemsize(); }); + sm.def("nbytes", [](const arr& a) { return a.nbytes(); }); + sm.def("owndata", [](const arr& a) { return a.owndata(); }); + + def_index_fn(data, const arr&); + def_index_fn(data_t, const arr_t&); + def_index_fn(index_at, const arr&); + def_index_fn(index_at_t, const arr_t&); + def_index_fn(offset_at, const arr&); + def_index_fn(offset_at_t, const arr_t&); + def_index_fn(mutate_data, arr&); + def_index_fn(mutate_data_t, arr_t&); + def_index_fn(at_t, const arr_t&); + def_index_fn(mutate_at_t, arr_t&); }); -- cgit v1.2.3 From 43f6aa6846c776ec092ccb1b03086978a4875039 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 12 Oct 2016 23:34:06 +0200 Subject: added numpy test (minor): check that 'strides' is respected even when creating new arrays - This actually works with no changes, I just wasn't 100% convinced and decided to write a test to see if it's true. --- tests/test_numpy_array.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 0614f57..37a8983 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -91,4 +91,12 @@ test_initializer numpy_array([](py::module &m) { def_index_fn(mutate_data_t, arr_t&); def_index_fn(at_t, const arr_t&); def_index_fn(mutate_at_t, arr_t&); + + sm.def("make_f_array", [] { + return py::array_t({ 2, 2 }, { 4, 8 }); + }); + + sm.def("make_c_array", [] { + return py::array_t({ 2, 2 }, { 8, 4 }); + }); }); -- cgit v1.2.3 From 369e9b39377dfab610328a1f8de7d83273b82f55 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 13 Oct 2016 00:57:42 +0200 Subject: Permit creation of NumPy arrays with a "base" object that owns the data This patch adds an extra base handle parameter to most ``py::array`` and ``py::array_t<>`` constructors. If specified along with a pointer to data, the base object will be registered within NumPy, which increases the base's reference count. This feature is useful to create shallow copies of C++ or Python arrays while ensuring that the owners of the underlying can't be garbage collected while referenced by NumPy. The commit also adds a simple test function involving a ``wrap()`` function that creates shallow copies of various N-D arrays. --- tests/test_numpy_array.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 37a8983..a6bf50d 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -99,4 +99,14 @@ test_initializer numpy_array([](py::module &m) { sm.def("make_c_array", [] { return py::array_t({ 2, 2 }, { 8, 4 }); }); + + sm.def("wrap", [](py::array a) { + return py::array( + a.dtype(), + std::vector(a.shape(), a.shape() + a.ndim()), + std::vector(a.strides(), a.strides() + a.ndim()), + a.data(), + a + ); + }); }); -- cgit v1.2.3 From fac7c0945801bdc37234a9559bfe86ac2f862059 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 13 Oct 2016 10:37:52 +0200 Subject: NumPy "base" feature: integrated feedback by @aldanor --- tests/test_numpy_array.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index a6bf50d..ec4ddac 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -109,4 +109,19 @@ test_initializer numpy_array([](py::module &m) { a ); }); + + struct ArrayClass { + int data[2] = { 1, 2 }; + ArrayClass() { py::print("ArrayClass()"); } + ~ArrayClass() { py::print("~ArrayClass()"); } + }; + + py::class_(sm, "ArrayClass") + .def(py::init<>()) + .def("numpy_view", [](py::object &obj) { + py::print("ArrayClass::numpy_view()"); + ArrayClass &a = obj.cast(); + return py::array_t({2}, {4}, a.data, obj); + } + ); }); -- cgit v1.2.3 From 496feacfd031d185c63252a7040930db717afc6c Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 28 Oct 2016 00:37:07 +0200 Subject: pybind11: implicitly convert NumPy integer scalars The current integer caster was unnecessarily strict and rejected various kinds of NumPy integer types when calling C++ functions expecting normal integers. This relaxes the current behavior. --- tests/test_numpy_array.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index ec4ddac..d422325 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -124,4 +124,6 @@ test_initializer numpy_array([](py::module &m) { return py::array_t({2}, {4}, a.data, obj); } ); + + sm.def("function_taking_uint64", [](uint64_t){ }); }); -- cgit v1.2.3 From 030d10e826b87f8cdf0816aa36b9a515fb7d064d Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 28 Oct 2016 01:23:42 +0200 Subject: minor style fix --- tests/test_numpy_array.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index d422325..f8be722 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -125,5 +125,5 @@ test_initializer numpy_array([](py::module &m) { } ); - sm.def("function_taking_uint64", [](uint64_t){ }); + sm.def("function_taking_uint64", [](uint64_t) { }); }); -- cgit v1.2.3 From 5027c4f95b65dbf76c3f72ce0fcda89716fb5f62 Mon Sep 17 00:00:00 2001 From: Sylvain Corlay Date: Wed, 16 Nov 2016 08:53:37 -0800 Subject: Switch NumPy variadic indexing to per-value arguments (#500) * Also added unsafe version without checks --- tests/test_numpy_array.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index f8be722..df6377e 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -18,11 +18,11 @@ using arr = py::array; using arr_t = py::array_t; -template arr data(const arr& a, Ix&&... index) { +template arr data(const arr& a, Ix... index) { return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...)); } -template arr data_t(const arr_t& a, Ix&&... index) { +template arr data_t(const arr_t& a, Ix... index) { return arr(a.size() - a.index_at(index...), a.data(index...)); } @@ -40,26 +40,26 @@ arr_t& mutate_data_t(arr_t& a) { return a; } -template arr& mutate_data(arr& a, Ix&&... index) { +template arr& mutate_data(arr& a, Ix... index) { auto ptr = (uint8_t *) a.mutable_data(index...); for (size_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) ptr[i] = (uint8_t) (ptr[i] * 2); return a; } -template arr_t& mutate_data_t(arr_t& a, Ix&&... index) { +template arr_t& mutate_data_t(arr_t& a, Ix... index) { auto ptr = a.mutable_data(index...); for (size_t i = 0; i < a.size() - a.index_at(index...); i++) ptr[i]++; return a; } -template size_t index_at(const arr& a, Ix&&... idx) { return a.index_at(idx...); } -template size_t index_at_t(const arr_t& a, Ix&&... idx) { return a.index_at(idx...); } -template size_t offset_at(const arr& a, Ix&&... idx) { return a.offset_at(idx...); } -template size_t offset_at_t(const arr_t& a, Ix&&... idx) { return a.offset_at(idx...); } -template size_t at_t(const arr_t& a, Ix&&... idx) { return a.at(idx...); } -template arr_t& mutate_at_t(arr_t& a, Ix&&... idx) { a.mutable_at(idx...)++; return a; } +template size_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } +template size_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } +template size_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } +template size_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } +template size_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } +template arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } #define def_index_fn(name, type) \ sm.def(#name, [](type a) { return name(a); }); \ -- cgit v1.2.3 From 4de271027d9e8b1daed2ba111c495cae23c970ac Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Wed, 16 Nov 2016 01:35:22 +0100 Subject: Improve consistency of array and array_t with regard to other pytypes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * `array_t(const object &)` now throws on error * `array_t::ensure()` is intended for casters —- old constructor is deprecated * `array` and `array_t` get default constructors (empty array) * `array` gets a converting constructor * `py::isinstance>()` checks the type (but not flags) There is only one special thing which must remain: `array_t` gets its own `type_caster` specialization which uses `ensure` instead of a simple check. --- tests/test_numpy_array.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index df6377e..14c4c29 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -126,4 +126,28 @@ test_initializer numpy_array([](py::module &m) { ); sm.def("function_taking_uint64", [](uint64_t) { }); + + sm.def("isinstance_untyped", [](py::object yes, py::object no) { + return py::isinstance(yes) && !py::isinstance(no); + }); + + sm.def("isinstance_typed", [](py::object o) { + return py::isinstance>(o) && !py::isinstance>(o); + }); + + sm.def("default_constructors", []() { + return py::dict( + "array"_a=py::array(), + "array_t"_a=py::array_t(), + "array_t"_a=py::array_t() + ); + }); + + sm.def("converting_constructors", [](py::object o) { + return py::dict( + "array"_a=py::array(o), + "array_t"_a=py::array_t(o), + "array_t"_a=py::array_t(o) + ); + }); }); -- cgit v1.2.3 From ee2e5a5086d02eda96e6cfcf8e15d8fbcd2af57c Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 24 Feb 2017 05:33:31 -0500 Subject: Make string conversion stricter (#695) * Make string conversion stricter The string conversion logic added in PR #624 for all std::basic_strings was derived from the old std::wstring logic, but that was underused and turns out to have had a bug in accepting almost anything convertible to unicode, while the previous std::string logic was much stricter. This restores the previous std::string logic by only allowing actual unicode or string types. Fixes #685. * Added missing 'requires numpy' decorator (I forgot that the change to a global decorator here is in the not-yet-merged Eigen PR) --- tests/test_numpy_array.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 14c4c29..23da916 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -150,4 +150,9 @@ test_initializer numpy_array([](py::module &m) { "array_t"_a=py::array_t(o) ); }); + + // Issue 685: ndarray shouldn't go to std::string overload + sm.def("issue685", [](std::string) { return "string"; }); + sm.def("issue685", [](py::array) { return "array"; }); + sm.def("issue685", [](py::object) { return "other"; }); }); -- cgit v1.2.3 From c44fe6fda5c7552373942c93847cf9881ac819c1 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sun, 26 Feb 2017 18:03:00 -0500 Subject: array_t overload resolution support This makes array_t respect overload resolution and noconvert by failing to load when `convert = false` if the src isn't already an array of the correct type. --- tests/test_numpy_array.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 23da916..58a2052 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -151,6 +151,34 @@ test_initializer numpy_array([](py::module &m) { ); }); + // Overload resolution tests: + sm.def("overloaded", [](py::array_t) { return "double"; }); + sm.def("overloaded", [](py::array_t) { return "float"; }); + sm.def("overloaded", [](py::array_t) { return "int"; }); + sm.def("overloaded", [](py::array_t) { return "unsigned short"; }); + sm.def("overloaded", [](py::array_t) { return "long long"; }); + sm.def("overloaded", [](py::array_t>) { return "double complex"; }); + sm.def("overloaded", [](py::array_t>) { return "float complex"; }); + + sm.def("overloaded2", [](py::array_t>) { return "double complex"; }); + sm.def("overloaded2", [](py::array_t) { return "double"; }); + sm.def("overloaded2", [](py::array_t>) { return "float complex"; }); + sm.def("overloaded2", [](py::array_t) { return "float"; }); + + // Only accept the exact types: + sm.def("overloaded3", [](py::array_t) { return "int"; }, py::arg().noconvert()); + sm.def("overloaded3", [](py::array_t) { return "double"; }, py::arg().noconvert()); + + // Make sure we don't do unsafe coercion (e.g. float to int) when not using forcecast, but + // rather that float gets converted via the safe (conversion to double) overload: + sm.def("overloaded4", [](py::array_t) { return "long long"; }); + sm.def("overloaded4", [](py::array_t) { return "double"; }); + + // But we do allow conversion to int if forcecast is enabled (but only if no overload matches + // without conversion) + sm.def("overloaded5", [](py::array_t) { return "unsigned int"; }); + sm.def("overloaded5", [](py::array_t) { return "double"; }); + // Issue 685: ndarray shouldn't go to std::string overload sm.def("issue685", [](std::string) { return "string"; }); sm.def("issue685", [](py::array) { return "array"; }); -- cgit v1.2.3 From 16afbcef4633b2c038cef26f7aaa0b4e567eed20 Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Mon, 13 Mar 2017 19:17:18 +0100 Subject: Improve py::array_t scalar type information (#724) * Add value_type member alias to py::array_t (resolve #632) * Use numpy scalar name in py::array_t function signatures (e.g. float32/64 instead of just float) --- tests/test_numpy_array.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 58a2052..8899644 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -17,6 +17,7 @@ using arr = py::array; using arr_t = py::array_t; +static_assert(std::is_same::value, ""); template arr data(const arr& a, Ix... index) { return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...)); -- cgit v1.2.3 From 423a49b8be20c0d4447d5d668232d2ea779159e5 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sun, 19 Mar 2017 01:14:23 -0300 Subject: array: add unchecked access via proxy object This adds bounds-unchecked access to arrays through a `a.unchecked()` method. (For `array_t`, the `Type` template parameter is omitted). The mutable version (which requires the array have the `writeable` flag) is available as `a.mutable_unchecked<...>()`. Specifying the Dimensions as a template parameter allows storage of an std::array; having the strides and sizes stored that way (as opposed to storing a copy of the array's strides/shape pointers) allows the compiler to make significant optimizations of the shape() method that it can't make with a pointer; testing with nested loops of the form: for (size_t i0 = 0; i0 < r.shape(0); i0++) for (size_t i1 = 0; i1 < r.shape(1); i1++) ... r(i0, i1, ...) += 1; over a 10 million element array gives around a 25% speedup (versus using a pointer) for the 1D case, 33% for 2D, and runs more than twice as fast with a 5D array. --- tests/test_numpy_array.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 8899644..461c9c0 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -184,4 +184,36 @@ test_initializer numpy_array([](py::module &m) { sm.def("issue685", [](std::string) { return "string"; }); sm.def("issue685", [](py::array) { return "array"; }); sm.def("issue685", [](py::object) { return "other"; }); + + sm.def("proxy_add2", [](py::array_t a, double v) { + auto r = a.mutable_unchecked<2>(); + for (size_t i = 0; i < r.shape(0); i++) + for (size_t j = 0; j < r.shape(1); j++) + r(i, j) += v; + }, py::arg().noconvert(), py::arg()); + sm.def("proxy_init3", [](double start) { + py::array_t a({ 3, 3, 3 }); + auto r = a.mutable_unchecked<3>(); + for (size_t i = 0; i < r.shape(0); i++) + for (size_t j = 0; j < r.shape(1); j++) + for (size_t k = 0; k < r.shape(2); k++) + r(i, j, k) = start++; + return a; + }); + sm.def("proxy_init3F", [](double start) { + py::array_t a({ 3, 3, 3 }); + auto r = a.mutable_unchecked<3>(); + for (size_t k = 0; k < r.shape(2); k++) + for (size_t j = 0; j < r.shape(1); j++) + for (size_t i = 0; i < r.shape(0); i++) + r(i, j, k) = start++; + return a; + }); + sm.def("proxy_squared_L2_norm", [](py::array_t a) { + auto r = a.unchecked<1>(); + double sumsq = 0; + for (size_t i = 0; i < r.shape(0); i++) + sumsq += r[i] * r(i); // Either notation works for a 1D array + return sumsq; + }); }); -- cgit v1.2.3 From 773339f131488819c878ae1a1d5bfbefec629af6 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Mon, 20 Mar 2017 17:48:38 -0300 Subject: array-unchecked: add runtime dimension support and array-compatible methods The extends the previous unchecked support with the ability to determine the dimensions at runtime. This incurs a small performance hit when used (versus the compile-time fixed alternative), but is still considerably faster than the full checks on every call that happen with `.at()`/`.mutable_at()`. --- tests/test_numpy_array.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 461c9c0..cd64872 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -68,6 +68,21 @@ template arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at( sm.def(#name, [](type a, int i, int j) { return name(a, i, j); }); \ sm.def(#name, [](type a, int i, int j, int k) { return name(a, i, j, k); }); +template py::handle auxiliaries(T &&r, T2 &&r2) { + if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); + py::list l; + l.append(*r.data(0, 0)); + l.append(*r2.mutable_data(0, 0)); + l.append(r.data(0, 1) == r2.mutable_data(0, 1)); + l.append(r.ndim()); + l.append(r.itemsize()); + l.append(r.shape(0)); + l.append(r.shape(1)); + l.append(r.size()); + l.append(r.nbytes()); + return l.release(); +} + test_initializer numpy_array([](py::module &m) { auto sm = m.def_submodule("array"); @@ -191,6 +206,7 @@ test_initializer numpy_array([](py::module &m) { for (size_t j = 0; j < r.shape(1); j++) r(i, j) += v; }, py::arg().noconvert(), py::arg()); + sm.def("proxy_init3", [](double start) { py::array_t a({ 3, 3, 3 }); auto r = a.mutable_unchecked<3>(); @@ -216,4 +232,36 @@ test_initializer numpy_array([](py::module &m) { sumsq += r[i] * r(i); // Either notation works for a 1D array return sumsq; }); + + sm.def("proxy_auxiliaries2", [](py::array_t a) { + auto r = a.unchecked<2>(); + auto r2 = a.mutable_unchecked<2>(); + return auxiliaries(r, r2); + }); + + // Same as the above, but without a compile-time dimensions specification: + sm.def("proxy_add2_dyn", [](py::array_t a, double v) { + auto r = a.mutable_unchecked(); + if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); + for (size_t i = 0; i < r.shape(0); i++) + for (size_t j = 0; j < r.shape(1); j++) + r(i, j) += v; + }, py::arg().noconvert(), py::arg()); + sm.def("proxy_init3_dyn", [](double start) { + py::array_t a({ 3, 3, 3 }); + auto r = a.mutable_unchecked(); + if (r.ndim() != 3) throw std::domain_error("error: ndim != 3"); + for (size_t i = 0; i < r.shape(0); i++) + for (size_t j = 0; j < r.shape(1); j++) + for (size_t k = 0; k < r.shape(2); k++) + r(i, j, k) = start++; + return a; + }); + sm.def("proxy_auxiliaries2_dyn", [](py::array_t a) { + return auxiliaries(a.unchecked(), a.mutable_unchecked()); + }); + + sm.def("array_auxiliaries2", [](py::array_t a) { + return auxiliaries(a, a); + }); }); -- cgit v1.2.3 From 5749b5023936e23d0d2ae77bd776e1f519e2e412 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Mon, 10 Apr 2017 11:05:26 -0400 Subject: array: set exception message on failure When attempting to get a raw array pointer we return nullptr if given a nullptr, which triggers an error_already_set(), but we haven't set an exception message, which results in "Unknown internal error". Callers that want explicit allowing of a nullptr here already handle it (by clearing the exception after the call). --- tests/test_numpy_array.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index cd64872..0b54704 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -264,4 +264,8 @@ test_initializer numpy_array([](py::module &m) { sm.def("array_auxiliaries2", [](py::array_t a) { return auxiliaries(a, a); }); + + // Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object: + sm.def("array_fail_test", []() { return py::array(py::object()); }); + sm.def("array_t_fail_test", []() { return py::array_t(py::object()); }); }); -- cgit v1.2.3 From 5f38386293f989db87545ce8a619e55085068060 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 7 Apr 2017 15:49:54 -0400 Subject: Accept abitrary containers and iterators for shape/strides This adds support for constructing `buffer_info` and `array`s using arbitrary containers or iterator pairs instead of requiring a vector. This is primarily needed by PR #782 (which makes strides signed to properly support negative strides, and will likely also make shape and itemsize to avoid mixed integer issues), but also needs to preserve backwards compatibility with 2.1 and earlier which accepts the strides parameter as a vector of size_t's. Rather than adding nearly duplicate constructors for each stride-taking constructor, it seems nicer to simply allow any type of container (or iterator pairs). This works by replacing the existing vector arguments with a new `detail::any_container` class that handles implicit conversion of arbitrary containers into a vector of the desired type. It can also be explicitly instantiated with a pair of iterators (e.g. by passing {begin, end} instead of the container). --- tests/test_numpy_array.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 0b54704..7252547 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -13,7 +13,6 @@ #include #include -#include using arr = py::array; using arr_t = py::array_t; @@ -119,8 +118,8 @@ test_initializer numpy_array([](py::module &m) { sm.def("wrap", [](py::array a) { return py::array( a.dtype(), - std::vector(a.shape(), a.shape() + a.ndim()), - std::vector(a.strides(), a.strides() + a.ndim()), + {a.shape(), a.shape() + a.ndim()}, + {a.strides(), a.strides() + a.ndim()}, a.data(), a ); -- cgit v1.2.3 From 51d18aa252332e883c61b7cb45ed317146b372ce Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 28 Apr 2017 11:06:16 -0400 Subject: Fix ambiguous initialize_list arguments This removes the convert-from-arithemtic-scalar constructor of any_container as it can result in ambiguous calls, as in: py::array_t({ 1, 2 }) which could be intepreted as either of: py::array_t(py::array_t(1, 2)) py::array_t(py::detail::any_container({ 1, 2 })) Removing the convert-from-arithmetic constructor reduces the number of implicit conversions, avoiding the ambiguity for array and array_t. This also re-adds the array/array_t constructors taking a scalar argument for backwards compatibility. --- tests/test_numpy_array.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 7252547..269f18b 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -267,4 +267,10 @@ test_initializer numpy_array([](py::module &m) { // Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object: sm.def("array_fail_test", []() { return py::array(py::object()); }); sm.def("array_t_fail_test", []() { return py::array_t(py::object()); }); + + // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous + sm.def("array_initializer_list", []() { return py::array_t(1); }); // { 1 } also works, but clang warns about it + sm.def("array_initializer_list", []() { return py::array_t({ 1, 2 }); }); + sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3 }); }); + sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3, 4 }); }); }); -- cgit v1.2.3 From 083a0219b5962a146a5e1556d7ce5e5187cb8bca Mon Sep 17 00:00:00 2001 From: uentity Date: Thu, 13 Apr 2017 21:41:55 +0500 Subject: array: implement array resize --- tests/test_numpy_array.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 269f18b..85c185f 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -273,4 +273,25 @@ test_initializer numpy_array([](py::module &m) { sm.def("array_initializer_list", []() { return py::array_t({ 1, 2 }); }); sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3 }); }); sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3, 4 }); }); -}); + + // reshape array to 2D without changing size + sm.def("array_reshape2", [](py::array_t a) { + const size_t dim_sz = (size_t)std::sqrt(a.size()); + if (dim_sz * dim_sz != a.size()) + throw std::domain_error("array_reshape2: input array total size is not a squared integer"); + a.resize({dim_sz, dim_sz}); + }); + + // resize to 3D array with each dimension = N + sm.def("array_resize3", [](py::array_t a, size_t N, bool refcheck) { + a.resize({N, N, N}, refcheck); + }); + + // return 2D array with Nrows = Ncols = N + sm.def("create_and_resize", [](size_t N) { + py::array_t a; + a.resize({N, N}); + std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.); + return a; + }); +}); \ No newline at end of file -- cgit v1.2.3 From d400f60c96312f32fafcb15168bbf56970ff3d3a Mon Sep 17 00:00:00 2001 From: Cris Luengo Date: Wed, 5 Apr 2017 16:13:04 -0600 Subject: Python buffer objects can have negative strides. --- tests/test_numpy_array.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 85c185f..e431e09 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -13,6 +13,7 @@ #include #include +#include using arr = py::array; using arr_t = py::array_t; @@ -294,4 +295,4 @@ test_initializer numpy_array([](py::module &m) { std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.); return a; }); -}); \ No newline at end of file +}); -- cgit v1.2.3 From b68959e822a619d1ad140f52c7197a29f4e6a06a Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 6 Apr 2017 18:16:35 -0400 Subject: Use numpy rather than Eigen for copying We're current copy by creating an Eigen::Map into the input numpy array, then assigning that to the basic eigen type, effectively having Eigen do the copy. That doesn't work for negative strides, though: Eigen doesn't allow them. This commit makes numpy do the copying instead by allocating the eigen type, then having numpy copy from the input array into a numpy reference into the eigen object's data. This also saves a copy when type conversion is required: numpy can do the conversion on-the-fly as part of the copy. Finally this commit also makes non-reference parameters respect the convert flag, declining the load when called in a noconvert pass with a convertible, but non-array input or an array with the wrong dtype. --- tests/test_numpy_array.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index e431e09..2e7004e 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -20,11 +20,11 @@ using arr_t = py::array_t; static_assert(std::is_same::value, ""); template arr data(const arr& a, Ix... index) { - return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...)); + return arr(a.nbytes() - size_t(a.offset_at(index...)), (const uint8_t *) a.data(index...)); } template arr data_t(const arr_t& a, Ix... index) { - return arr(a.size() - a.index_at(index...), a.data(index...)); + return arr(a.size() - size_t(a.index_at(index...)), a.data(index...)); } arr& mutate_data(arr& a) { @@ -43,23 +43,23 @@ arr_t& mutate_data_t(arr_t& a) { template arr& mutate_data(arr& a, Ix... index) { auto ptr = (uint8_t *) a.mutable_data(index...); - for (size_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) + for (size_t i = 0; i < a.nbytes() - size_t(a.offset_at(index...)); i++) ptr[i] = (uint8_t) (ptr[i] * 2); return a; } template arr_t& mutate_data_t(arr_t& a, Ix... index) { auto ptr = a.mutable_data(index...); - for (size_t i = 0; i < a.size() - a.index_at(index...); i++) + for (size_t i = 0; i < a.size() - size_t(a.index_at(index...)); i++) ptr[i]++; return a; } -template size_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } -template size_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } -template size_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } -template size_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } -template size_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } +template py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } +template py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } +template py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } +template py::ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } +template py::ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } template arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } #define def_index_fn(name, type) \ -- cgit v1.2.3 From 30d43c4992f90ac7592fdbedd56c332717278c91 Mon Sep 17 00:00:00 2001 From: Cris Luengo Date: Fri, 14 Apr 2017 14:33:44 -0600 Subject: Now `shape`, `size`, `ndims` and `itemsize` are also signed integers. --- tests/test_numpy_array.cpp | 60 ++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 29 deletions(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 2e7004e..ea5be79 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -13,53 +13,52 @@ #include #include -#include using arr = py::array; using arr_t = py::array_t; static_assert(std::is_same::value, ""); template arr data(const arr& a, Ix... index) { - return arr(a.nbytes() - size_t(a.offset_at(index...)), (const uint8_t *) a.data(index...)); + return arr(a.nbytes() - a.offset_at(index...), (const uint8_t *) a.data(index...)); } template arr data_t(const arr_t& a, Ix... index) { - return arr(a.size() - size_t(a.index_at(index...)), a.data(index...)); + return arr(a.size() - a.index_at(index...), a.data(index...)); } arr& mutate_data(arr& a) { auto ptr = (uint8_t *) a.mutable_data(); - for (size_t i = 0; i < a.nbytes(); i++) + for (ssize_t i = 0; i < a.nbytes(); i++) ptr[i] = (uint8_t) (ptr[i] * 2); return a; } arr_t& mutate_data_t(arr_t& a) { auto ptr = a.mutable_data(); - for (size_t i = 0; i < a.size(); i++) + for (ssize_t i = 0; i < a.size(); i++) ptr[i]++; return a; } template arr& mutate_data(arr& a, Ix... index) { auto ptr = (uint8_t *) a.mutable_data(index...); - for (size_t i = 0; i < a.nbytes() - size_t(a.offset_at(index...)); i++) + for (ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) ptr[i] = (uint8_t) (ptr[i] * 2); return a; } template arr_t& mutate_data_t(arr_t& a, Ix... index) { auto ptr = a.mutable_data(index...); - for (size_t i = 0; i < a.size() - size_t(a.index_at(index...)); i++) + for (ssize_t i = 0; i < a.size() - a.index_at(index...); i++) ptr[i]++; return a; } -template py::ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } -template py::ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } -template py::ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } -template py::ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } -template py::ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } +template ssize_t index_at(const arr& a, Ix... idx) { return a.index_at(idx...); } +template ssize_t index_at_t(const arr_t& a, Ix... idx) { return a.index_at(idx...); } +template ssize_t offset_at(const arr& a, Ix... idx) { return a.offset_at(idx...); } +template ssize_t offset_at_t(const arr_t& a, Ix... idx) { return a.offset_at(idx...); } +template ssize_t at_t(const arr_t& a, Ix... idx) { return a.at(idx...); } template arr_t& mutate_at_t(arr_t& a, Ix... idx) { a.mutable_at(idx...)++; return a; } #define def_index_fn(name, type) \ @@ -88,9 +87,9 @@ test_initializer numpy_array([](py::module &m) { sm.def("ndim", [](const arr& a) { return a.ndim(); }); sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); }); - sm.def("shape", [](const arr& a, size_t dim) { return a.shape(dim); }); + sm.def("shape", [](const arr& a, ssize_t dim) { return a.shape(dim); }); sm.def("strides", [](const arr& a) { return arr(a.ndim(), a.strides()); }); - sm.def("strides", [](const arr& a, size_t dim) { return a.strides(dim); }); + sm.def("strides", [](const arr& a, ssize_t dim) { return a.strides(dim); }); sm.def("writeable", [](const arr& a) { return a.writeable(); }); sm.def("size", [](const arr& a) { return a.size(); }); sm.def("itemsize", [](const arr& a) { return a.itemsize(); }); @@ -202,33 +201,33 @@ test_initializer numpy_array([](py::module &m) { sm.def("proxy_add2", [](py::array_t a, double v) { auto r = a.mutable_unchecked<2>(); - for (size_t i = 0; i < r.shape(0); i++) - for (size_t j = 0; j < r.shape(1); j++) + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) r(i, j) += v; }, py::arg().noconvert(), py::arg()); sm.def("proxy_init3", [](double start) { py::array_t a({ 3, 3, 3 }); auto r = a.mutable_unchecked<3>(); - for (size_t i = 0; i < r.shape(0); i++) - for (size_t j = 0; j < r.shape(1); j++) - for (size_t k = 0; k < r.shape(2); k++) + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t k = 0; k < r.shape(2); k++) r(i, j, k) = start++; return a; }); sm.def("proxy_init3F", [](double start) { py::array_t a({ 3, 3, 3 }); auto r = a.mutable_unchecked<3>(); - for (size_t k = 0; k < r.shape(2); k++) - for (size_t j = 0; j < r.shape(1); j++) - for (size_t i = 0; i < r.shape(0); i++) + for (ssize_t k = 0; k < r.shape(2); k++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t i = 0; i < r.shape(0); i++) r(i, j, k) = start++; return a; }); sm.def("proxy_squared_L2_norm", [](py::array_t a) { auto r = a.unchecked<1>(); double sumsq = 0; - for (size_t i = 0; i < r.shape(0); i++) + for (ssize_t i = 0; i < r.shape(0); i++) sumsq += r[i] * r(i); // Either notation works for a 1D array return sumsq; }); @@ -243,17 +242,17 @@ test_initializer numpy_array([](py::module &m) { sm.def("proxy_add2_dyn", [](py::array_t a, double v) { auto r = a.mutable_unchecked(); if (r.ndim() != 2) throw std::domain_error("error: ndim != 2"); - for (size_t i = 0; i < r.shape(0); i++) - for (size_t j = 0; j < r.shape(1); j++) + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) r(i, j) += v; }, py::arg().noconvert(), py::arg()); sm.def("proxy_init3_dyn", [](double start) { py::array_t a({ 3, 3, 3 }); auto r = a.mutable_unchecked(); if (r.ndim() != 3) throw std::domain_error("error: ndim != 3"); - for (size_t i = 0; i < r.shape(0); i++) - for (size_t j = 0; j < r.shape(1); j++) - for (size_t k = 0; k < r.shape(2); k++) + for (ssize_t i = 0; i < r.shape(0); i++) + for (ssize_t j = 0; j < r.shape(1); j++) + for (ssize_t k = 0; k < r.shape(2); k++) r(i, j, k) = start++; return a; }); @@ -269,6 +268,9 @@ test_initializer numpy_array([](py::module &m) { sm.def("array_fail_test", []() { return py::array(py::object()); }); sm.def("array_t_fail_test", []() { return py::array_t(py::object()); }); + // Make sure the error from numpy is being passed through: + sm.def("array_fail_test_negative_size", []() { int c = 0; return py::array(-1, &c); }); + // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous sm.def("array_initializer_list", []() { return py::array_t(1); }); // { 1 } also works, but clang warns about it sm.def("array_initializer_list", []() { return py::array_t({ 1, 2 }); }); @@ -277,7 +279,7 @@ test_initializer numpy_array([](py::module &m) { // reshape array to 2D without changing size sm.def("array_reshape2", [](py::array_t a) { - const size_t dim_sz = (size_t)std::sqrt(a.size()); + const ssize_t dim_sz = (ssize_t)std::sqrt(a.size()); if (dim_sz * dim_sz != a.size()) throw std::domain_error("array_reshape2: input array total size is not a squared integer"); a.resize({dim_sz, dim_sz}); -- cgit v1.2.3 From 391c75447da13ef44cc2e119e005c0aeee442862 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Tue, 25 Jul 2017 16:47:36 -0400 Subject: Update all remaining tests to new test styles This udpates all the remaining tests to the new test suite code and comment styles started in #898. For the most part, the test coverage here is unchanged, with a few minor exceptions as noted below. - test_constants_and_functions: this adds more overload tests with overloads with different number of arguments for more comprehensive overload_cast testing. The test style conversion broke the overload tests under MSVC 2015, prompting the additional tests while looking for a workaround. - test_eigen: this dropped the unused functions `get_cm_corners` and `get_cm_corners_const`--these same tests were duplicates of the same things provided (and used) via ReturnTester methods. - test_opaque_types: this test had a hidden dependence on ExampleMandA which is now fixed by using the global UserType which suffices for the relevant test. - test_methods_and_attributes: this required some additions to UserType to make it usable as a replacement for the test's previous SimpleType: UserType gained a value mutator, and the `value` property is not mutable (it was previously readonly). Some overload tests were also added to better test overload_cast (as described above). - test_numpy_array: removed the untemplated mutate_data/mutate_data_t: the templated versions with an empty parameter pack expand to the same thing. - test_stl: this was already mostly in the new style; this just tweaks things a bit, localizing a class, and adding some missing `// test_whatever` comments. - test_virtual_functions: like `test_stl`, this was mostly in the new test style already, but needed some `// test_whatever` comments. This commit also moves the inherited virtual example code to the end of the file, after the main set of tests (since it is less important than the other tests, and rather length); it also got renamed to `test_inherited_virtuals` (from `test_inheriting_repeat`) because it tests both inherited virtual approaches, not just the repeat approach. --- tests/test_numpy_array.cpp | 65 +++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 35 deletions(-) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index ea5be79..2046c0e 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -26,20 +26,6 @@ template arr data_t(const arr_t& a, Ix... index) { return arr(a.size() - a.index_at(index...), a.data(index...)); } -arr& mutate_data(arr& a) { - auto ptr = (uint8_t *) a.mutable_data(); - for (ssize_t i = 0; i < a.nbytes(); i++) - ptr[i] = (uint8_t) (ptr[i] * 2); - return a; -} - -arr_t& mutate_data_t(arr_t& a) { - auto ptr = a.mutable_data(); - for (ssize_t i = 0; i < a.size(); i++) - ptr[i]++; - return a; -} - template arr& mutate_data(arr& a, Ix... index) { auto ptr = (uint8_t *) a.mutable_data(index...); for (ssize_t i = 0; i < a.nbytes() - a.offset_at(index...); i++) @@ -82,9 +68,11 @@ template py::handle auxiliaries(T &&r, T2 &&r2) { return l.release(); } -test_initializer numpy_array([](py::module &m) { - auto sm = m.def_submodule("array"); +TEST_SUBMODULE(numpy_array, sm) { + try { py::module::import("numpy"); } + catch (...) { return; } + // test_array_attributes sm.def("ndim", [](const arr& a) { return a.ndim(); }); sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); }); sm.def("shape", [](const arr& a, ssize_t dim) { return a.shape(dim); }); @@ -96,25 +84,25 @@ test_initializer numpy_array([](py::module &m) { sm.def("nbytes", [](const arr& a) { return a.nbytes(); }); sm.def("owndata", [](const arr& a) { return a.owndata(); }); - def_index_fn(data, const arr&); - def_index_fn(data_t, const arr_t&); + // test_index_offset def_index_fn(index_at, const arr&); def_index_fn(index_at_t, const arr_t&); def_index_fn(offset_at, const arr&); def_index_fn(offset_at_t, const arr_t&); + // test_data + def_index_fn(data, const arr&); + def_index_fn(data_t, const arr_t&); + // test_mutate_data, test_mutate_readonly def_index_fn(mutate_data, arr&); def_index_fn(mutate_data_t, arr_t&); def_index_fn(at_t, const arr_t&); def_index_fn(mutate_at_t, arr_t&); - sm.def("make_f_array", [] { - return py::array_t({ 2, 2 }, { 4, 8 }); - }); - - sm.def("make_c_array", [] { - return py::array_t({ 2, 2 }, { 8, 4 }); - }); + // test_make_c_f_array + sm.def("make_f_array", [] { return py::array_t({ 2, 2 }, { 4, 8 }); }); + sm.def("make_c_array", [] { return py::array_t({ 2, 2 }, { 8, 4 }); }); + // test_wrap sm.def("wrap", [](py::array a) { return py::array( a.dtype(), @@ -125,12 +113,12 @@ test_initializer numpy_array([](py::module &m) { ); }); + // test_numpy_view struct ArrayClass { int data[2] = { 1, 2 }; ArrayClass() { py::print("ArrayClass()"); } ~ArrayClass() { py::print("~ArrayClass()"); } }; - py::class_(sm, "ArrayClass") .def(py::init<>()) .def("numpy_view", [](py::object &obj) { @@ -140,16 +128,18 @@ test_initializer numpy_array([](py::module &m) { } ); + // test_cast_numpy_int64_to_uint64 sm.def("function_taking_uint64", [](uint64_t) { }); + // test_isinstance sm.def("isinstance_untyped", [](py::object yes, py::object no) { return py::isinstance(yes) && !py::isinstance(no); }); - sm.def("isinstance_typed", [](py::object o) { return py::isinstance>(o) && !py::isinstance>(o); }); + // test_constructors sm.def("default_constructors", []() { return py::dict( "array"_a=py::array(), @@ -157,7 +147,6 @@ test_initializer numpy_array([](py::module &m) { "array_t"_a=py::array_t() ); }); - sm.def("converting_constructors", [](py::object o) { return py::dict( "array"_a=py::array(o), @@ -166,7 +155,7 @@ test_initializer numpy_array([](py::module &m) { ); }); - // Overload resolution tests: + // test_overload_resolution sm.def("overloaded", [](py::array_t) { return "double"; }); sm.def("overloaded", [](py::array_t) { return "float"; }); sm.def("overloaded", [](py::array_t) { return "int"; }); @@ -194,11 +183,13 @@ test_initializer numpy_array([](py::module &m) { sm.def("overloaded5", [](py::array_t) { return "unsigned int"; }); sm.def("overloaded5", [](py::array_t) { return "double"; }); + // test_greedy_string_overload // Issue 685: ndarray shouldn't go to std::string overload sm.def("issue685", [](std::string) { return "string"; }); sm.def("issue685", [](py::array) { return "array"; }); sm.def("issue685", [](py::object) { return "other"; }); + // test_array_unchecked_fixed_dims sm.def("proxy_add2", [](py::array_t a, double v) { auto r = a.mutable_unchecked<2>(); for (ssize_t i = 0; i < r.shape(0); i++) @@ -238,6 +229,7 @@ test_initializer numpy_array([](py::module &m) { return auxiliaries(r, r2); }); + // test_array_unchecked_dyn_dims // Same as the above, but without a compile-time dimensions specification: sm.def("proxy_add2_dyn", [](py::array_t a, double v) { auto r = a.mutable_unchecked(); @@ -264,19 +256,21 @@ test_initializer numpy_array([](py::module &m) { return auxiliaries(a, a); }); + // test_array_failures // Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object: sm.def("array_fail_test", []() { return py::array(py::object()); }); sm.def("array_t_fail_test", []() { return py::array_t(py::object()); }); - // Make sure the error from numpy is being passed through: sm.def("array_fail_test_negative_size", []() { int c = 0; return py::array(-1, &c); }); + // test_initializer_list // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous - sm.def("array_initializer_list", []() { return py::array_t(1); }); // { 1 } also works, but clang warns about it - sm.def("array_initializer_list", []() { return py::array_t({ 1, 2 }); }); - sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3 }); }); - sm.def("array_initializer_list", []() { return py::array_t({ 1, 2, 3, 4 }); }); + sm.def("array_initializer_list1", []() { return py::array_t(1); }); // { 1 } also works, but clang warns about it + sm.def("array_initializer_list2", []() { return py::array_t({ 1, 2 }); }); + sm.def("array_initializer_list3", []() { return py::array_t({ 1, 2, 3 }); }); + sm.def("array_initializer_list4", []() { return py::array_t({ 1, 2, 3, 4 }); }); + // test_array_resize // reshape array to 2D without changing size sm.def("array_reshape2", [](py::array_t a) { const ssize_t dim_sz = (ssize_t)std::sqrt(a.size()); @@ -290,6 +284,7 @@ test_initializer numpy_array([](py::module &m) { a.resize({N, N, N}, refcheck); }); + // test_array_create_and_resize // return 2D array with Nrows = Ncols = N sm.def("create_and_resize", [](size_t N) { py::array_t a; @@ -297,4 +292,4 @@ test_initializer numpy_array([](py::module &m) { std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.); return a; }); -}); +} -- cgit v1.2.3 From 5ef1af138dc3ef94c05274ae554e70d64cd589bf Mon Sep 17 00:00:00 2001 From: Naotoshi Seo Date: Sun, 6 May 2018 13:59:25 +0000 Subject: Fix SEGV to create empty shaped numpy array (#1371) Fix a segfault when creating a 0-dimension, c-strides array. --- tests/test_numpy_array.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 2046c0e..79a157e 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -102,6 +102,9 @@ TEST_SUBMODULE(numpy_array, sm) { sm.def("make_f_array", [] { return py::array_t({ 2, 2 }, { 4, 8 }); }); sm.def("make_c_array", [] { return py::array_t({ 2, 2 }, { 8, 4 }); }); + // test_empty_shaped_array + sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); }); + // test_wrap sm.def("wrap", [](py::array a) { return py::array( -- cgit v1.2.3 From d4b37a284aa37f1d78f136fc854e41a54d5f67f2 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 28 Aug 2018 00:23:59 +0200 Subject: added py::ellipsis() method for slicing of multidimensional NumPy arrays This PR adds a new py::ellipsis() method which can be used in conjunction with NumPy's generalized slicing support. For instance, the following is now valid (where "a" is a NumPy array): py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; --- tests/test_numpy_array.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 79a157e..5702594 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -295,4 +295,10 @@ TEST_SUBMODULE(numpy_array, sm) { std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.); return a; }); + +#if PY_MAJOR_VERSION >= 3 + sm.def("index_using_ellipsis", [](py::array a) { + return a[py::make_tuple(0, py::ellipsis(), 0)]; + }); +#endif } -- cgit v1.2.3 From 000aabb2a715fcea1a9857d244321bdbc2847d82 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Tue, 11 Jun 2019 14:00:05 +0200 Subject: Test: Numpy Scalar Creation (#1530) I found that the numpy array tests already contained an empty-shaped array test, but none with data in it. Following PEP 3118, scalars have an empty shape and ndim 0. This works already and is now also documented/covered by a test. --- tests/test_numpy_array.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 5702594..cdf0b82 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -68,6 +68,9 @@ template py::handle auxiliaries(T &&r, T2 &&r2) { return l.release(); } +// note: declaration at local scope would create a dangling reference! +static int data_i = 42; + TEST_SUBMODULE(numpy_array, sm) { try { py::module::import("numpy"); } catch (...) { return; } @@ -104,6 +107,8 @@ TEST_SUBMODULE(numpy_array, sm) { // test_empty_shaped_array sm.def("make_empty_shaped_array", [] { return py::array(py::dtype("f"), {}, {}); }); + // test numpy scalars (empty shape, ndim==0) + sm.def("scalar_int", []() { return py::array(py::dtype("i"), {}, {}, &data_i); }); // test_wrap sm.def("wrap", [](py::array a) { -- cgit v1.2.3 From e9ca89f453cf81bea9a73b976c6a2fa59a6b1fe2 Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Tue, 20 Mar 2018 16:55:29 -0400 Subject: numpy: Add test for explicit dtype checks. At present, int64 + uint64 do not exactly match dtype(...).num --- tests/test_numpy_array.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'tests/test_numpy_array.cpp') diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index cdf0b82..156a3bf 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -14,6 +14,67 @@ #include +// Size / dtype checks. +struct DtypeCheck { + py::dtype numpy{}; + py::dtype pybind11{}; +}; + +template +DtypeCheck get_dtype_check(const char* name) { + py::module np = py::module::import("numpy"); + DtypeCheck check{}; + check.numpy = np.attr("dtype")(np.attr(name)); + check.pybind11 = py::dtype::of(); + return check; +} + +std::vector get_concrete_dtype_checks() { + return { + // Normalization + get_dtype_check("int8"), + get_dtype_check("uint8"), + get_dtype_check("int16"), + get_dtype_check("uint16"), + get_dtype_check("int32"), + get_dtype_check("uint32"), + get_dtype_check("int64"), + get_dtype_check("uint64") + }; +} + +struct DtypeSizeCheck { + std::string name{}; + int size_cpp{}; + int size_numpy{}; + // For debugging. + py::dtype dtype{}; +}; + +template +DtypeSizeCheck get_dtype_size_check() { + DtypeSizeCheck check{}; + check.name = py::type_id(); + check.size_cpp = sizeof(T); + check.dtype = py::dtype::of(); + check.size_numpy = check.dtype.attr("itemsize").template cast(); + return check; +} + +std::vector get_platform_dtype_size_checks() { + return { + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + get_dtype_size_check(), + }; +} + +// Arrays. using arr = py::array; using arr_t = py::array_t; static_assert(std::is_same::value, ""); @@ -75,6 +136,26 @@ TEST_SUBMODULE(numpy_array, sm) { try { py::module::import("numpy"); } catch (...) { return; } + // test_dtypes + py::class_(sm, "DtypeCheck") + .def_readonly("numpy", &DtypeCheck::numpy) + .def_readonly("pybind11", &DtypeCheck::pybind11) + .def("__repr__", [](const DtypeCheck& self) { + return py::str("").format( + self.numpy, self.pybind11); + }); + sm.def("get_concrete_dtype_checks", &get_concrete_dtype_checks); + + py::class_(sm, "DtypeSizeCheck") + .def_readonly("name", &DtypeSizeCheck::name) + .def_readonly("size_cpp", &DtypeSizeCheck::size_cpp) + .def_readonly("size_numpy", &DtypeSizeCheck::size_numpy) + .def("__repr__", [](const DtypeSizeCheck& self) { + return py::str("").format( + self.name, self.size_cpp, self.size_numpy, self.dtype); + }); + sm.def("get_platform_dtype_size_checks", &get_platform_dtype_size_checks); + // test_array_attributes sm.def("ndim", [](const arr& a) { return a.ndim(); }); sm.def("shape", [](const arr& a) { return arr(a.ndim(), a.shape()); }); -- cgit v1.2.3