summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2021-08-05 20:57:51 -0500
committerSebastian Berg <sebastian@sipsolutions.net>2021-08-24 14:42:17 -0500
commit62f74111e9fff995b9fa32c3db8ca369d77c7ce6 (patch)
tree40a5b5b7450e06b4dbad9852049232ca5603754c
parentf702b26fff3271ba6a6ba29a021fc19051d1f007 (diff)
downloadnumpy-62f74111e9fff995b9fa32c3db8ca369d77c7ce6.tar.gz
BUG: The normal cast-safety for ufunc loops is "no" casting
This can now be set for a loop, which allows specialized loops that include the cast for example (or may make sense for units). However, the default here was just wrong, and apparently we missed any tests at all. (sklearn caught it luckily :))
-rw-r--r--numpy/core/src/umath/legacy_array_method.c4
-rw-r--r--numpy/core/tests/test_ufunc.py31
2 files changed, 33 insertions, 2 deletions
diff --git a/numpy/core/src/umath/legacy_array_method.c b/numpy/core/src/umath/legacy_array_method.c
index a5e123baa..4351f1d25 100644
--- a/numpy/core/src/umath/legacy_array_method.c
+++ b/numpy/core/src/umath/legacy_array_method.c
@@ -142,7 +142,7 @@ simple_legacy_resolve_descriptors(
}
}
- return NPY_SAFE_CASTING;
+ return NPY_NO_CASTING;
fail:
for (int i = 0; i < nin + nout; i++) {
@@ -244,7 +244,7 @@ PyArray_NewLegacyWrappingArrayMethod(PyUFuncObject *ufunc,
.dtypes = signature,
.flags = flags,
.slots = slots,
- .casting = NPY_EQUIV_CASTING,
+ .casting = NPY_NO_CASTING,
};
PyBoundArrayMethodObject *bound_res = PyArrayMethod_FromSpec_int(&spec, 1);
diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py
index 657b6a79b..4fed7c5a8 100644
--- a/numpy/core/tests/test_ufunc.py
+++ b/numpy/core/tests/test_ufunc.py
@@ -536,6 +536,37 @@ class TestUfunc:
np.add(arr, arr, dtype="m")
np.maximum(arr, arr, dtype="m")
+ @pytest.mark.parametrize("ufunc", [np.add, np.sqrt])
+ def test_cast_safety(self, ufunc):
+ """Basic test for the safest casts, because ufuncs inner loops can
+ inidicate a cast-safety as well (which is normally always "no").
+ """
+ def call_ufunc(arr, **kwargs):
+ return ufunc(*(arr,) * ufunc.nin, **kwargs)
+
+ arr = np.array([1., 2., 3.], dtype=np.float32)
+ arr_bs = arr.astype(arr.dtype.newbyteorder())
+ expected = call_ufunc(arr)
+ # Normally, a "no" cast:
+ res = call_ufunc(arr, casting="no")
+ assert_array_equal(expected, res)
+ # Byte-swapping is not allowed with "no" though:
+ with pytest.raises(TypeError):
+ call_ufunc(arr_bs, casting="no")
+
+ # But is allowed with "equiv":
+ res = call_ufunc(arr_bs, casting="equiv")
+ assert_array_equal(expected, res)
+
+ # Casting to float64 is safe, but not equiv:
+ with pytest.raises(TypeError):
+ call_ufunc(arr_bs, dtype=np.float64, casting="equiv")
+
+ # but it is safe cast:
+ res = call_ufunc(arr_bs, dtype=np.float64, casting="safe")
+ expected = call_ufunc(arr.astype(np.float64)) # upcast
+ assert_array_equal(expected, res)
+
def test_true_divide(self):
a = np.array(10)
b = np.array(20)