diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2021-08-05 20:57:51 -0500 |
---|---|---|
committer | Sebastian Berg <sebastian@sipsolutions.net> | 2021-08-24 14:42:17 -0500 |
commit | 62f74111e9fff995b9fa32c3db8ca369d77c7ce6 (patch) | |
tree | 40a5b5b7450e06b4dbad9852049232ca5603754c /numpy | |
parent | f702b26fff3271ba6a6ba29a021fc19051d1f007 (diff) | |
download | numpy-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 :))
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/umath/legacy_array_method.c | 4 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 31 |
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) |