diff options
Diffstat (limited to 'numpy/core/tests')
39 files changed, 2400 insertions, 899 deletions
diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py index 5006e77f2..b9f2f8ae9 100644 --- a/numpy/core/tests/test_api.py +++ b/numpy/core/tests/test_api.py @@ -102,6 +102,16 @@ def test_array_array(): assert_raises(ValueError, np.array, [nested], dtype=np.float64) # Try with lists... + # float32 + assert_equal(np.array([None] * 10, dtype=np.float32), + np.full((10,), np.nan, dtype=np.float32)) + assert_equal(np.array([[None]] * 10, dtype=np.float32), + np.full((10, 1), np.nan, dtype=np.float32)) + assert_equal(np.array([[None] * 10], dtype=np.float32), + np.full((1, 10), np.nan, dtype=np.float32)) + assert_equal(np.array([[None] * 10] * 10, dtype=np.float32), + np.full((10, 10), np.nan, dtype=np.float32)) + # float64 assert_equal(np.array([None] * 10, dtype=np.float64), np.full((10,), np.nan, dtype=np.float64)) assert_equal(np.array([[None]] * 10, dtype=np.float64), @@ -310,7 +320,7 @@ def test_array_astype_warning(t): @pytest.mark.parametrize(["dtype", "out_dtype"], [(np.bytes_, np.bool_), - (np.unicode_, np.bool_), + (np.str_, np.bool_), (np.dtype("S10,S9"), np.dtype("?,?"))]) def test_string_to_boolean_cast(dtype, out_dtype): """ @@ -324,7 +334,7 @@ def test_string_to_boolean_cast(dtype, out_dtype): @pytest.mark.parametrize(["dtype", "out_dtype"], [(np.bytes_, np.bool_), - (np.unicode_, np.bool_), + (np.str_, np.bool_), (np.dtype("S10,S9"), np.dtype("?,?"))]) def test_string_to_boolean_cast_errors(dtype, out_dtype): """ diff --git a/numpy/core/tests/test_argparse.py b/numpy/core/tests/test_argparse.py index 63a01dee4..fae227027 100644 --- a/numpy/core/tests/test_argparse.py +++ b/numpy/core/tests/test_argparse.py @@ -53,8 +53,8 @@ def test_multiple_values(): def test_string_fallbacks(): # We can (currently?) use numpy strings to test the "slow" fallbacks # that should normally not be taken due to string interning. - arg2 = np.unicode_("arg2") - missing_arg = np.unicode_("missing_arg") + arg2 = np.str_("arg2") + missing_arg = np.str_("missing_arg") func(1, **{arg2: 3}) with pytest.raises(TypeError, match="got an unexpected keyword argument 'missing_arg'"): diff --git a/numpy/core/tests/test_array_coercion.py b/numpy/core/tests/test_array_coercion.py index fade57292..d5373f642 100644 --- a/numpy/core/tests/test_array_coercion.py +++ b/numpy/core/tests/test_array_coercion.py @@ -1,6 +1,6 @@ """ Tests for array coercion, mainly through testing `np.array` results directly. -Note that other such tests exist e.g. in `test_api.py` and many corner-cases +Note that other such tests exist, e.g., in `test_api.py` and many corner-cases are tested (sometimes indirectly) elsewhere. """ @@ -20,8 +20,8 @@ from numpy.testing import ( def arraylikes(): """ Generator for functions converting an array into various array-likes. - If full is True (default) includes array-likes not capable of handling - all dtypes + If full is True (default) it includes array-likes not capable of handling + all dtypes. """ # base array: def ndarray(a): @@ -39,9 +39,9 @@ def arraylikes(): yield subclass class _SequenceLike(): - # We are giving a warning that array-like's were also expected to be - # sequence-like in `np.array([array_like])`, this can be removed - # when the deprecation exired (started NumPy 1.20) + # Older NumPy versions, sometimes cared whether a protocol array was + # also _SequenceLike. This shouldn't matter, but keep it for now + # for __array__ and not the others. def __len__(self): raise TypeError @@ -62,7 +62,7 @@ def arraylikes(): yield param(memoryview, id="memoryview") # Array-interface - class ArrayInterface(_SequenceLike): + class ArrayInterface: def __init__(self, a): self.a = a # need to hold on to keep interface valid self.__array_interface__ = a.__array_interface__ @@ -70,7 +70,7 @@ def arraylikes(): yield param(ArrayInterface, id="__array_interface__") # Array-Struct - class ArrayStruct(_SequenceLike): + class ArrayStruct: def __init__(self, a): self.a = a # need to hold on to keep struct valid self.__array_struct__ = a.__array_struct__ @@ -130,7 +130,7 @@ def scalar_instances(times=True, extended_precision=True, user_dtype=True): # Strings and unstructured void: yield param(np.bytes_(b"1234"), id="bytes") - yield param(np.unicode_("2345"), id="unicode") + yield param(np.str_("2345"), id="unicode") yield param(np.void(b"4321"), id="unstructured_void") @@ -161,8 +161,12 @@ class TestStringDiscovery: # A nested array is also discovered correctly arr = np.array(obj, dtype="O") assert np.array(arr, dtype="S").dtype == expected + # Also if we use the dtype class + assert np.array(arr, dtype=type(expected)).dtype == expected # Check that .astype() behaves identical assert arr.astype("S").dtype == expected + # The DType class is accepted by `.astype()` + assert arr.astype(type(np.dtype("S"))).dtype == expected @pytest.mark.parametrize("obj", [object(), 1.2, 10**43, None, "string"], @@ -257,7 +261,7 @@ class TestScalarDiscovery: @pytest.mark.parametrize("scalar", scalar_instances()) def test_scalar_coercion(self, scalar): # This tests various scalar coercion paths, mainly for the numerical - # types. It includes some paths not directly related to `np.array` + # types. It includes some paths not directly related to `np.array`. if isinstance(scalar, np.inexact): # Ensure we have a full-precision number if available scalar = type(scalar)((scalar * 2)**0.5) @@ -292,7 +296,7 @@ class TestScalarDiscovery: * `np.array(scalar, dtype=dtype)` * `np.empty((), dtype=dtype)[()] = scalar` * `np.array(scalar).astype(dtype)` - should behave the same. The only exceptions are paramteric dtypes + should behave the same. The only exceptions are parametric dtypes (mainly datetime/timedelta without unit) and void without fields. """ dtype = cast_to.dtype # use to parametrize only the target dtype @@ -384,7 +388,7 @@ class TestScalarDiscovery: """ dtype = np.dtype(dtype) - # This is a special case using casting logic. It warns for the NaN + # This is a special case using casting logic. It warns for the NaN # but allows the cast (giving undefined behaviour). with np.errstate(invalid="ignore"): coerced = np.array(scalar, dtype=dtype) @@ -650,6 +654,14 @@ class TestArrayLikes: res = np.array([obj], dtype=object) assert res[0] is obj + @pytest.mark.parametrize("arraylike", arraylikes()) + @pytest.mark.parametrize("arr", [np.array(0.), np.arange(4)]) + def test_object_assignment_special_case(self, arraylike, arr): + obj = arraylike(arr) + empty = np.arange(1, dtype=object) + empty[:] = [obj] + assert empty[0] is obj + def test_0d_generic_special_case(self): class ArraySubclass(np.ndarray): def __float__(self): @@ -833,3 +845,28 @@ class TestSpecialAttributeLookupFailure: np.array(self.WeirdArrayLike()) with pytest.raises(RuntimeError): np.array(self.WeirdArrayInterface()) + + +def test_subarray_from_array_construction(): + # Arrays are more complex, since they "broadcast" on success: + arr = np.array([1, 2]) + + res = arr.astype("(2)i,") + assert_array_equal(res, [[1, 1], [2, 2]]) + + res = np.array(arr, dtype="(2)i,") + + assert_array_equal(res, [[1, 1], [2, 2]]) + + res = np.array([[(1,), (2,)], arr], dtype="(2)i,") + assert_array_equal(res, [[[1, 1], [2, 2]], [[1, 1], [2, 2]]]) + + # Also try a multi-dimensional example: + arr = np.arange(5 * 2).reshape(5, 2) + expected = np.broadcast_to(arr[:, :, np.newaxis, np.newaxis], (5, 2, 2, 2)) + + res = arr.astype("(2,2)f") + assert_array_equal(res, expected) + + res = np.array(arr, dtype="(2,2)f") + assert_array_equal(res, expected) diff --git a/numpy/core/tests/test_arraymethod.py b/numpy/core/tests/test_arraymethod.py index 6b75d1921..4fd4d5558 100644 --- a/numpy/core/tests/test_arraymethod.py +++ b/numpy/core/tests/test_arraymethod.py @@ -64,7 +64,6 @@ class TestSimpleStridedCall: self.method._simple_strided_call(*args) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires python 3.9") @pytest.mark.parametrize( "cls", [np.ndarray, np.recarray, np.chararray, np.matrix, np.memmap] ) @@ -84,10 +83,3 @@ class TestClassGetItem: match = f"Too {'few' if arg_len == 0 else 'many'} arguments" with pytest.raises(TypeError, match=match): cls[arg_tup] - - -@pytest.mark.skipif(sys.version_info >= (3, 9), reason="Requires python 3.8") -def test_class_getitem_38() -> None: - match = "Type subscription requires python >= 3.9" - with pytest.raises(TypeError, match=match): - np.ndarray[Any, Any] diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py index f1883f703..6796b4077 100644 --- a/numpy/core/tests/test_arrayprint.py +++ b/numpy/core/tests/test_arrayprint.py @@ -9,6 +9,7 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_warns, HAS_REFCOUNT, assert_raises_regex, ) +from numpy.core.arrayprint import _typelessdata import textwrap class TestArrayRepr: @@ -257,8 +258,7 @@ class TestArray2String: assert_(np.array2string(s, formatter={'numpystr':lambda s: s*2}) == '[abcabc defdef]') - - def test_structure_format(self): + def test_structure_format_mixed(self): dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))]) x = np.array([('Sarah', (8.0, 7.0)), ('John', (6.0, 7.0))], dtype=dt) assert_equal(np.array2string(x), @@ -300,6 +300,7 @@ class TestArray2String: ( 'NaT',) ( 'NaT',) ( 'NaT',)]""") ) + def test_structure_format_int(self): # See #8160 struct_int = np.array([([1, -1],), ([123, 1],)], dtype=[('B', 'i4', 2)]) assert_equal(np.array2string(struct_int), @@ -309,6 +310,7 @@ class TestArray2String: assert_equal(np.array2string(struct_2dint), "[([[ 0, 1], [ 2, 3]],) ([[12, 0], [ 0, 0]],)]") + def test_structure_format_float(self): # See #8172 array_scalar = np.array( (1., 2.1234567890123456789, 3.), dtype=('f8,f8,f8')) @@ -352,6 +354,33 @@ class TestArray2String: ' [ 501, 502, 503, ..., 999, 1000, 1001]])' assert_equal(repr(A), reprA) + def test_summarize_structure(self): + A = (np.arange(2002, dtype="<i8").reshape(2, 1001) + .view([('i', "<i8", (1001,))])) + strA = ("[[([ 0, 1, 2, ..., 998, 999, 1000],)]\n" + " [([1001, 1002, 1003, ..., 1999, 2000, 2001],)]]") + assert_equal(str(A), strA) + + reprA = ("array([[([ 0, 1, 2, ..., 998, 999, 1000],)],\n" + " [([1001, 1002, 1003, ..., 1999, 2000, 2001],)]],\n" + " dtype=[('i', '<i8', (1001,))])") + assert_equal(repr(A), reprA) + + B = np.ones(2002, dtype=">i8").view([('i', ">i8", (2, 1001))]) + strB = "[([[1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1]],)]" + assert_equal(str(B), strB) + + reprB = ( + "array([([[1, 1, 1, ..., 1, 1, 1], [1, 1, 1, ..., 1, 1, 1]],)],\n" + " dtype=[('i', '>i8', (2, 1001))])" + ) + assert_equal(repr(B), reprB) + + C = (np.arange(22, dtype="<i8").reshape(2, 11) + .view([('i1', "<i8"), ('i10', "<i8", (10,))])) + strC = "[[( 0, [ 1, ..., 10])]\n [(11, [12, ..., 21])]]" + assert_equal(np.array2string(C, threshold=1, edgeitems=1), strC) + def test_linewidth(self): a = np.full(6, 1) @@ -796,6 +825,47 @@ class TestPrintOptions: array(['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'], dtype='{}')""".format(styp))) + @pytest.mark.parametrize( + ['native'], + [ + ('bool',), + ('uint8',), + ('uint16',), + ('uint32',), + ('uint64',), + ('int8',), + ('int16',), + ('int32',), + ('int64',), + ('float16',), + ('float32',), + ('float64',), + ('U1',), # 4-byte width string + ], + ) + def test_dtype_endianness_repr(self, native): + ''' + there was an issue where + repr(array([0], dtype='<u2')) and repr(array([0], dtype='>u2')) + both returned the same thing: + array([0], dtype=uint16) + even though their dtypes have different endianness. + ''' + native_dtype = np.dtype(native) + non_native_dtype = native_dtype.newbyteorder() + non_native_repr = repr(np.array([1], non_native_dtype)) + native_repr = repr(np.array([1], native_dtype)) + # preserve the sensible default of only showing dtype if nonstandard + assert ('dtype' in native_repr) ^ (native_dtype in _typelessdata),\ + ("an array's repr should show dtype if and only if the type " + 'of the array is NOT one of the standard types ' + '(e.g., int32, bool, float64).') + if non_native_dtype.itemsize > 1: + # if the type is >1 byte, the non-native endian version + # must show endianness. + assert non_native_repr != native_repr + assert f"dtype='{non_native_dtype.byteorder}" in non_native_repr + def test_linewidth_repr(self): a = np.full(7, fill_value=2) np.set_printoptions(linewidth=17) @@ -921,6 +991,16 @@ class TestPrintOptions: [[ 0.]]]])""") ) + def test_edgeitems_structured(self): + np.set_printoptions(edgeitems=1, threshold=1) + A = np.arange(5*2*3, dtype="<i8").view([('i', "<i8", (5, 2, 3))]) + reprA = ( + "array([([[[ 0, ..., 2], [ 3, ..., 5]], ..., " + "[[24, ..., 26], [27, ..., 29]]],)],\n" + " dtype=[('i', '<i8', (5, 2, 3))])" + ) + assert_equal(repr(A), reprA) + def test_bad_args(self): assert_raises(ValueError, np.set_printoptions, threshold=float('nan')) assert_raises(TypeError, np.set_printoptions, threshold='1') diff --git a/numpy/core/tests/test_cpu_features.py b/numpy/core/tests/test_cpu_features.py index 1a76897e2..2fad4dfd9 100644 --- a/numpy/core/tests/test_cpu_features.py +++ b/numpy/core/tests/test_cpu_features.py @@ -1,5 +1,14 @@ import sys, platform, re, pytest -from numpy.core._multiarray_umath import __cpu_features__ +from numpy.core._multiarray_umath import ( + __cpu_features__, + __cpu_baseline__, + __cpu_dispatch__, +) +import numpy as np +import subprocess +import pathlib +import os +import re def assert_features_equal(actual, desired, fname): __tracebackhide__ = True # Hide traceback for py.test @@ -8,7 +17,7 @@ def assert_features_equal(actual, desired, fname): return detected = str(__cpu_features__).replace("'", "") try: - with open("/proc/cpuinfo", "r") as fd: + with open("/proc/cpuinfo") as fd: cpuinfo = fd.read(2048) except Exception as err: cpuinfo = str(err) @@ -48,6 +57,10 @@ def assert_features_equal(actual, desired, fname): "%s" ) % (fname, actual, desired, error_report)) +def _text_to_list(txt): + out = txt.strip("][\n").replace("'", "").split(', ') + return None if out[0] == "" else out + class AbstractTest: features = [] features_groups = {} @@ -92,7 +105,6 @@ class AbstractTest: return values def load_flags_auxv(self): - import subprocess auxv = subprocess.check_output(['/bin/true'], env=dict(LD_SHOW_AUXV="1")) for at in auxv.split(b'\n'): if not at.startswith(b"AT_HWCAP"): @@ -103,6 +115,208 @@ class AbstractTest: hwcap_value[1].upper().decode().split() ) +@pytest.mark.skipif( + sys.platform == 'emscripten', + reason= ( + "The subprocess module is not available on WASM platforms and" + " therefore this test class cannot be properly executed." + ), +) +class TestEnvPrivation: + cwd = pathlib.Path(__file__).parent.resolve() + env = os.environ.copy() + _enable = os.environ.pop('NPY_ENABLE_CPU_FEATURES', None) + _disable = os.environ.pop('NPY_DISABLE_CPU_FEATURES', None) + SUBPROCESS_ARGS = dict(cwd=cwd, capture_output=True, text=True, check=True) + unavailable_feats = [ + feat for feat in __cpu_dispatch__ if not __cpu_features__[feat] + ] + UNAVAILABLE_FEAT = ( + None if len(unavailable_feats) == 0 + else unavailable_feats[0] + ) + BASELINE_FEAT = None if len(__cpu_baseline__) == 0 else __cpu_baseline__[0] + SCRIPT = """ +def main(): + from numpy.core._multiarray_umath import __cpu_features__, __cpu_dispatch__ + + detected = [feat for feat in __cpu_dispatch__ if __cpu_features__[feat]] + print(detected) + +if __name__ == "__main__": + main() + """ + + @pytest.fixture(autouse=True) + def setup_class(self, tmp_path_factory): + file = tmp_path_factory.mktemp("runtime_test_script") + file /= "_runtime_detect.py" + file.write_text(self.SCRIPT) + self.file = file + return + + def _run(self): + return subprocess.run( + [sys.executable, self.file], + env=self.env, + **self.SUBPROCESS_ARGS, + ) + + # Helper function mimicing pytest.raises for subprocess call + def _expect_error( + self, + msg, + err_type, + no_error_msg="Failed to generate error" + ): + try: + self._run() + except subprocess.CalledProcessError as e: + assertion_message = f"Expected: {msg}\nGot: {e.stderr}" + assert re.search(msg, e.stderr), assertion_message + + assertion_message = ( + f"Expected error of type: {err_type}; see full " + f"error:\n{e.stderr}" + ) + assert re.search(err_type, e.stderr), assertion_message + else: + assert False, no_error_msg + + def setup_method(self): + """Ensure that the environment is reset""" + self.env = os.environ.copy() + return + + def test_runtime_feature_selection(self): + """ + Ensure that when selecting `NPY_ENABLE_CPU_FEATURES`, only the + features exactly specified are dispatched. + """ + + # Capture runtime-enabled features + out = self._run() + non_baseline_features = _text_to_list(out.stdout) + + if non_baseline_features is None: + pytest.skip( + "No dispatchable features outside of baseline detected." + ) + feature = non_baseline_features[0] + + # Capture runtime-enabled features when `NPY_ENABLE_CPU_FEATURES` is + # specified + self.env['NPY_ENABLE_CPU_FEATURES'] = feature + out = self._run() + enabled_features = _text_to_list(out.stdout) + + # Ensure that only one feature is enabled, and it is exactly the one + # specified by `NPY_ENABLE_CPU_FEATURES` + assert set(enabled_features) == {feature} + + if len(non_baseline_features) < 2: + pytest.skip("Only one non-baseline feature detected.") + # Capture runtime-enabled features when `NPY_ENABLE_CPU_FEATURES` is + # specified + self.env['NPY_ENABLE_CPU_FEATURES'] = ",".join(non_baseline_features) + out = self._run() + enabled_features = _text_to_list(out.stdout) + + # Ensure that both features are enabled, and they are exactly the ones + # specified by `NPY_ENABLE_CPU_FEATURES` + assert set(enabled_features) == set(non_baseline_features) + return + + @pytest.mark.parametrize("enabled, disabled", + [ + ("feature", "feature"), + ("feature", "same"), + ]) + def test_both_enable_disable_set(self, enabled, disabled): + """ + Ensure that when both environment variables are set then an + ImportError is thrown + """ + self.env['NPY_ENABLE_CPU_FEATURES'] = enabled + self.env['NPY_DISABLE_CPU_FEATURES'] = disabled + msg = "Both NPY_DISABLE_CPU_FEATURES and NPY_ENABLE_CPU_FEATURES" + err_type = "ImportError" + self._expect_error(msg, err_type) + + @pytest.mark.skipif( + not __cpu_dispatch__, + reason=( + "NPY_*_CPU_FEATURES only parsed if " + "`__cpu_dispatch__` is non-empty" + ) + ) + @pytest.mark.parametrize("action", ["ENABLE", "DISABLE"]) + def test_variable_too_long(self, action): + """ + Test that an error is thrown if the environment variables are too long + to be processed. Current limit is 1024, but this may change later. + """ + MAX_VAR_LENGTH = 1024 + # Actual length is MAX_VAR_LENGTH + 1 due to null-termination + self.env[f'NPY_{action}_CPU_FEATURES'] = "t" * MAX_VAR_LENGTH + msg = ( + f"Length of environment variable 'NPY_{action}_CPU_FEATURES' is " + f"{MAX_VAR_LENGTH + 1}, only {MAX_VAR_LENGTH} accepted" + ) + err_type = "RuntimeError" + self._expect_error(msg, err_type) + + @pytest.mark.skipif( + not __cpu_dispatch__, + reason=( + "NPY_*_CPU_FEATURES only parsed if " + "`__cpu_dispatch__` is non-empty" + ) + ) + def test_impossible_feature_disable(self): + """ + Test that a RuntimeError is thrown if an impossible feature-disabling + request is made. This includes disabling a baseline feature. + """ + + if self.BASELINE_FEAT is None: + pytest.skip("There are no unavailable features to test with") + bad_feature = self.BASELINE_FEAT + self.env['NPY_DISABLE_CPU_FEATURES'] = bad_feature + msg = ( + f"You cannot disable CPU feature '{bad_feature}', since it is " + "part of the baseline optimizations" + ) + err_type = "RuntimeError" + self._expect_error(msg, err_type) + + def test_impossible_feature_enable(self): + """ + Test that a RuntimeError is thrown if an impossible feature-enabling + request is made. This includes enabling a feature not supported by the + machine, or disabling a baseline optimization. + """ + + if self.UNAVAILABLE_FEAT is None: + pytest.skip("There are no unavailable features to test with") + bad_feature = self.UNAVAILABLE_FEAT + self.env['NPY_ENABLE_CPU_FEATURES'] = bad_feature + msg = ( + f"You cannot enable CPU features \\({bad_feature}\\), since " + "they are not supported by your machine." + ) + err_type = "RuntimeError" + self._expect_error(msg, err_type) + + # Ensure that only the bad feature gets reported + feats = f"{bad_feature}, {self.BASELINE_FEAT}" + self.env['NPY_ENABLE_CPU_FEATURES'] = feats + msg = ( + f"You cannot enable CPU features \\({bad_feature}\\), since they " + "are not supported by your machine." + ) + self._expect_error(msg, err_type) + is_linux = sys.platform.startswith('linux') is_cygwin = sys.platform.startswith('cygwin') machine = platform.machine() @@ -116,7 +330,7 @@ class Test_X86_Features(AbstractTest): "AVX", "F16C", "XOP", "FMA4", "FMA3", "AVX2", "AVX512F", "AVX512CD", "AVX512ER", "AVX512PF", "AVX5124FMAPS", "AVX5124VNNIW", "AVX512VPOPCNTDQ", "AVX512VL", "AVX512BW", "AVX512DQ", "AVX512VNNI", "AVX512IFMA", - "AVX512VBMI", "AVX512VBMI2", "AVX512BITALG", + "AVX512VBMI", "AVX512VBMI2", "AVX512BITALG", "AVX512FP16", ] features_groups = dict( AVX512_KNL = ["AVX512F", "AVX512CD", "AVX512ER", "AVX512PF"], @@ -128,6 +342,10 @@ class Test_X86_Features(AbstractTest): "AVX512VBMI"], AVX512_ICL = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ", "AVX512VL", "AVX512IFMA", "AVX512VBMI", "AVX512VNNI", "AVX512VBMI2", "AVX512BITALG", "AVX512VPOPCNTDQ"], + AVX512_SPR = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ", + "AVX512VL", "AVX512IFMA", "AVX512VBMI", "AVX512VNNI", + "AVX512VBMI2", "AVX512BITALG", "AVX512VPOPCNTDQ", + "AVX512FP16"], ) features_map = dict( SSE3="PNI", SSE41="SSE4_1", SSE42="SSE4_2", FMA3="FMA", diff --git a/numpy/core/tests/test_custom_dtypes.py b/numpy/core/tests/test_custom_dtypes.py index 6bcc45d6b..da6a4bd50 100644 --- a/numpy/core/tests/test_custom_dtypes.py +++ b/numpy/core/tests/test_custom_dtypes.py @@ -45,6 +45,9 @@ class TestSFloat: # Check the repr, mainly to cover the code paths: assert repr(SF(scaling=1.)) == "_ScaledFloatTestDType(scaling=1.0)" + def test_dtype_name(self): + assert SF(1.).name == "_ScaledFloatTestDType64" + @pytest.mark.parametrize("scaling", [1., -1., 2.]) def test_sfloat_from_float(self, scaling): a = np.array([1., 2., 3.]).astype(dtype=SF(scaling)) @@ -199,3 +202,52 @@ class TestSFloat: # The output casting does not match the bool, bool -> bool loop: with pytest.raises(TypeError): ufunc(a, a, out=np.empty(a.shape, dtype=int), casting="equiv") + + def test_wrapped_and_wrapped_reductions(self): + a = self._get_array(2.) + float_equiv = a.astype(float) + + expected = np.hypot(float_equiv, float_equiv) + res = np.hypot(a, a) + assert res.dtype == a.dtype + res_float = res.view(np.float64) * 2 + assert_array_equal(res_float, expected) + + # Also check reduction (keepdims, due to incorrect getitem) + res = np.hypot.reduce(a, keepdims=True) + assert res.dtype == a.dtype + expected = np.hypot.reduce(float_equiv, keepdims=True) + assert res.view(np.float64) * 2 == expected + + def test_astype_class(self): + # Very simple test that we accept `.astype()` also on the class. + # ScaledFloat always returns the default descriptor, but it does + # check the relevant code paths. + arr = np.array([1., 2., 3.], dtype=object) + + res = arr.astype(SF) # passing the class class + expected = arr.astype(SF(1.)) # above will have discovered 1. scaling + assert_array_equal(res.view(np.float64), expected.view(np.float64)) + + def test_creation_class(self): + arr1 = np.array([1., 2., 3.], dtype=SF) + assert arr1.dtype == SF(1.) + arr2 = np.array([1., 2., 3.], dtype=SF(1.)) + assert_array_equal(arr1.view(np.float64), arr2.view(np.float64)) + + +def test_type_pickle(): + # can't actually unpickle, but we can pickle (if in namespace) + import pickle + + np._ScaledFloatTestDType = SF + + s = pickle.dumps(SF) + res = pickle.loads(s) + assert res is SF + + del np._ScaledFloatTestDType + + +def test_is_numeric(): + assert SF._is_numeric diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index 693eed0e2..547ebf9d6 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -719,8 +719,8 @@ class TestDateTime: assert_equal(uni_a, uni_b) # Datetime to long string - gh-9712 - assert_equal(str_a, dt_a.astype((np.string_, 128))) - str_b = np.empty(str_a.shape, dtype=(np.string_, 128)) + assert_equal(str_a, dt_a.astype((np.bytes_, 128))) + str_b = np.empty(str_a.shape, dtype=(np.bytes_, 128)) str_b[...] = dt_a assert_equal(str_a, str_b) @@ -1003,12 +1003,11 @@ class TestDateTime: casting='unsafe')) # Shouldn't be able to compare datetime and timedelta - # TODO: Changing to 'same_kind' or 'safe' casting in the ufuncs by - # default is needed to properly catch this kind of thing... a = np.array('2012-12-21', dtype='M8[D]') b = np.array(3, dtype='m8[D]') - #assert_raises(TypeError, np.less, a, b) - assert_raises(TypeError, np.less, a, b, casting='same_kind') + assert_raises(TypeError, np.less, a, b) + # not even if "unsafe" + assert_raises(TypeError, np.less, a, b, casting='unsafe') def test_datetime_like(self): a = np.array([3], dtype='m8[4D]') @@ -2331,16 +2330,23 @@ class TestDateTime: assert_equal(np.busday_count('2011-01-01', dates, busdaycal=bdd), np.arange(366)) # Returns negative value when reversed + # -1 since the '2011-01-01' is not a busday assert_equal(np.busday_count(dates, '2011-01-01', busdaycal=bdd), - -np.arange(366)) + -np.arange(366) - 1) + # 2011-12-31 is a saturday dates = np.busday_offset('2011-12-31', -np.arange(366), roll='forward', busdaycal=bdd) + # only the first generated date is in the future of 2011-12-31 + expected = np.arange(366) + expected[0] = -1 assert_equal(np.busday_count(dates, '2011-12-31', busdaycal=bdd), - np.arange(366)) + expected) # Returns negative value when reversed + expected = -np.arange(366)+1 + expected[0] = 0 assert_equal(np.busday_count('2011-12-31', dates, busdaycal=bdd), - -np.arange(366)) + expected) # Can't supply both a weekmask/holidays and busdaycal assert_raises(ValueError, np.busday_offset, '2012-01-03', '2012-02-03', @@ -2353,6 +2359,17 @@ class TestDateTime: # Returns negative value when reversed assert_equal(np.busday_count('2011-04', '2011-03', weekmask='Mon'), -4) + sunday = np.datetime64('2023-03-05') + monday = sunday + 1 + friday = sunday + 5 + saturday = sunday + 6 + assert_equal(np.busday_count(sunday, monday), 0) + assert_equal(np.busday_count(monday, sunday), -1) + + assert_equal(np.busday_count(friday, saturday), 1) + assert_equal(np.busday_count(saturday, friday), 0) + + def test_datetime_is_busday(self): holidays = ['2011-01-01', '2011-10-10', '2011-11-11', '2011-11-24', '2011-12-25', '2011-05-30', '2011-02-21', '2011-01-17', @@ -2530,3 +2547,23 @@ class TestDateTimeData: dt = np.datetime64('2000', '5μs') assert np.datetime_data(dt.dtype) == ('us', 5) + + +def test_comparisons_return_not_implemented(): + # GH#17017 + + class custom: + __array_priority__ = 10000 + + obj = custom() + + dt = np.datetime64('2000', 'ns') + td = dt - dt + + for item in [dt, td]: + assert item.__eq__(obj) is NotImplemented + assert item.__ne__(obj) is NotImplemented + assert item.__le__(obj) is NotImplemented + assert item.__lt__(obj) is NotImplemented + assert item.__ge__(obj) is NotImplemented + assert item.__gt__(obj) is NotImplemented diff --git a/numpy/core/tests/test_defchararray.py b/numpy/core/tests/test_defchararray.py index 22296604e..39699f457 100644 --- a/numpy/core/tests/test_defchararray.py +++ b/numpy/core/tests/test_defchararray.py @@ -1,3 +1,5 @@ +import pytest + import numpy as np from numpy.core.multiarray import _vec_string from numpy.testing import ( @@ -29,7 +31,7 @@ class TestBasic: def test_from_string_array(self): A = np.array([[b'abc', b'foo'], [b'long ', b'0123456789']]) - assert_equal(A.dtype.type, np.string_) + assert_equal(A.dtype.type, np.bytes_) B = np.char.array(A) assert_array_equal(B, A) assert_equal(B.dtype, A.dtype) @@ -46,7 +48,7 @@ class TestBasic: def test_from_unicode_array(self): A = np.array([['abc', 'Sigma \u03a3'], ['long ', '0123456789']]) - assert_equal(A.dtype.type, np.unicode_) + assert_equal(A.dtype.type, np.str_) B = np.char.array(A) assert_array_equal(B, A) assert_equal(B.dtype, A.dtype) @@ -64,40 +66,40 @@ class TestBasic: def test_unicode_upconvert(self): A = np.char.array(['abc']) B = np.char.array(['\u03a3']) - assert_(issubclass((A + B).dtype.type, np.unicode_)) + assert_(issubclass((A + B).dtype.type, np.str_)) def test_from_string(self): A = np.char.array(b'abc') assert_equal(len(A), 1) assert_equal(len(A[0]), 3) - assert_(issubclass(A.dtype.type, np.string_)) + assert_(issubclass(A.dtype.type, np.bytes_)) def test_from_unicode(self): A = np.char.array('\u03a3') assert_equal(len(A), 1) assert_equal(len(A[0]), 1) assert_equal(A.itemsize, 4) - assert_(issubclass(A.dtype.type, np.unicode_)) + assert_(issubclass(A.dtype.type, np.str_)) class TestVecString: def test_non_existent_method(self): def fail(): - _vec_string('a', np.string_, 'bogus') + _vec_string('a', np.bytes_, 'bogus') assert_raises(AttributeError, fail) def test_non_string_array(self): def fail(): - _vec_string(1, np.string_, 'strip') + _vec_string(1, np.bytes_, 'strip') assert_raises(TypeError, fail) def test_invalid_args_tuple(self): def fail(): - _vec_string(['a'], np.string_, 'strip', 1) + _vec_string(['a'], np.bytes_, 'strip', 1) assert_raises(TypeError, fail) @@ -111,7 +113,7 @@ class TestVecString: def test_invalid_function_args(self): def fail(): - _vec_string(['a'], np.string_, 'strip', (1,)) + _vec_string(['a'], np.bytes_, 'strip', (1,)) assert_raises(TypeError, fail) @@ -190,7 +192,7 @@ class TestComparisonsMixed1(TestComparisons): def setup_method(self): TestComparisons.setup_method(self) self.B = np.array([['efg', '123 '], - ['051', 'tuv']], np.unicode_).view(np.chararray) + ['051', 'tuv']], np.str_).view(np.chararray) class TestComparisonsMixed2(TestComparisons): """Ticket #1276""" @@ -198,7 +200,7 @@ class TestComparisonsMixed2(TestComparisons): def setup_method(self): TestComparisons.setup_method(self) self.A = np.array([['abc', '123'], - ['789', 'xyz']], np.unicode_).view(np.chararray) + ['789', 'xyz']], np.str_).view(np.chararray) class TestInformation: def setup_method(self): @@ -320,17 +322,17 @@ class TestMethods: tgt = [[b' abc ', b''], [b'12345', b'Mixedcase'], [b'123 \t 345 \0 ', b'Upper']] - assert_(issubclass(self.A.capitalize().dtype.type, np.string_)) + assert_(issubclass(self.A.capitalize().dtype.type, np.bytes_)) assert_array_equal(self.A.capitalize(), tgt) tgt = [[' \u03c3 ', ''], ['12345', 'Mixedcase'], ['123 \t 345 \0 ', 'Upper']] - assert_(issubclass(self.B.capitalize().dtype.type, np.unicode_)) + assert_(issubclass(self.B.capitalize().dtype.type, np.str_)) assert_array_equal(self.B.capitalize(), tgt) def test_center(self): - assert_(issubclass(self.A.center(10).dtype.type, np.string_)) + assert_(issubclass(self.A.center(10).dtype.type, np.bytes_)) C = self.A.center([10, 20]) assert_array_equal(np.char.str_len(C), [[10, 20], [10, 20], [12, 20]]) @@ -341,7 +343,7 @@ class TestMethods: C = np.char.center(b'FOO', [[10, 20], [15, 8]]) tgt = [[b' FOO ', b' FOO '], [b' FOO ', b' FOO ']] - assert_(issubclass(C.dtype.type, np.string_)) + assert_(issubclass(C.dtype.type, np.bytes_)) assert_array_equal(C, tgt) def test_decode(self): @@ -362,14 +364,14 @@ class TestMethods: A0 = self.A.decode('ascii') A = np.char.join([',', '#'], A0) - assert_(issubclass(A.dtype.type, np.unicode_)) + assert_(issubclass(A.dtype.type, np.str_)) tgt = np.array([[' ,a,b,c, ', ''], ['1,2,3,4,5', 'M#i#x#e#d#C#a#s#e'], ['1,2,3, ,\t, ,3,4,5, ,\x00, ', 'U#P#P#E#R']]) assert_array_equal(np.char.join([',', '#'], A0), tgt) def test_ljust(self): - assert_(issubclass(self.A.ljust(10).dtype.type, np.string_)) + assert_(issubclass(self.A.ljust(10).dtype.type, np.bytes_)) C = self.A.ljust([10, 20]) assert_array_equal(np.char.str_len(C), [[10, 20], [10, 20], [12, 20]]) @@ -382,27 +384,27 @@ class TestMethods: C = np.char.ljust(b'FOO', [[10, 20], [15, 8]]) tgt = [[b'FOO ', b'FOO '], [b'FOO ', b'FOO ']] - assert_(issubclass(C.dtype.type, np.string_)) + assert_(issubclass(C.dtype.type, np.bytes_)) assert_array_equal(C, tgt) def test_lower(self): tgt = [[b' abc ', b''], [b'12345', b'mixedcase'], [b'123 \t 345 \0 ', b'upper']] - assert_(issubclass(self.A.lower().dtype.type, np.string_)) + assert_(issubclass(self.A.lower().dtype.type, np.bytes_)) assert_array_equal(self.A.lower(), tgt) tgt = [[' \u03c3 ', ''], ['12345', 'mixedcase'], ['123 \t 345 \0 ', 'upper']] - assert_(issubclass(self.B.lower().dtype.type, np.unicode_)) + assert_(issubclass(self.B.lower().dtype.type, np.str_)) assert_array_equal(self.B.lower(), tgt) def test_lstrip(self): tgt = [[b'abc ', b''], [b'12345', b'MixedCase'], [b'123 \t 345 \0 ', b'UPPER']] - assert_(issubclass(self.A.lstrip().dtype.type, np.string_)) + assert_(issubclass(self.A.lstrip().dtype.type, np.bytes_)) assert_array_equal(self.A.lstrip(), tgt) tgt = [[b' abc', b''], @@ -413,7 +415,7 @@ class TestMethods: tgt = [['\u03a3 ', ''], ['12345', 'MixedCase'], ['123 \t 345 \0 ', 'UPPER']] - assert_(issubclass(self.B.lstrip().dtype.type, np.unicode_)) + assert_(issubclass(self.B.lstrip().dtype.type, np.str_)) assert_array_equal(self.B.lstrip(), tgt) def test_partition(self): @@ -421,7 +423,7 @@ class TestMethods: tgt = [[(b' abc ', b'', b''), (b'', b'', b'')], [(b'12', b'3', b'45'), (b'', b'M', b'ixedCase')], [(b'12', b'3', b' \t 345 \0 '), (b'UPPER', b'', b'')]] - assert_(issubclass(P.dtype.type, np.string_)) + assert_(issubclass(P.dtype.type, np.bytes_)) assert_array_equal(P, tgt) def test_replace(self): @@ -430,11 +432,11 @@ class TestMethods: tgt = [[b' abc ', b''], [b'12##########45', b'MixedC@se'], [b'12########## \t ##########45 \x00', b'UPPER']] - assert_(issubclass(R.dtype.type, np.string_)) + assert_(issubclass(R.dtype.type, np.bytes_)) assert_array_equal(R, tgt) def test_rjust(self): - assert_(issubclass(self.A.rjust(10).dtype.type, np.string_)) + assert_(issubclass(self.A.rjust(10).dtype.type, np.bytes_)) C = self.A.rjust([10, 20]) assert_array_equal(np.char.str_len(C), [[10, 20], [10, 20], [12, 20]]) @@ -447,7 +449,7 @@ class TestMethods: C = np.char.rjust(b'FOO', [[10, 20], [15, 8]]) tgt = [[b' FOO', b' FOO'], [b' FOO', b' FOO']] - assert_(issubclass(C.dtype.type, np.string_)) + assert_(issubclass(C.dtype.type, np.bytes_)) assert_array_equal(C, tgt) def test_rpartition(self): @@ -455,7 +457,7 @@ class TestMethods: tgt = [[(b'', b'', b' abc '), (b'', b'', b'')], [(b'12', b'3', b'45'), (b'', b'M', b'ixedCase')], [(b'123 \t ', b'3', b'45 \0 '), (b'', b'', b'UPPER')]] - assert_(issubclass(P.dtype.type, np.string_)) + assert_(issubclass(P.dtype.type, np.bytes_)) assert_array_equal(P, tgt) def test_rsplit(self): @@ -467,7 +469,7 @@ class TestMethods: assert_equal(A.tolist(), tgt) def test_rstrip(self): - assert_(issubclass(self.A.rstrip().dtype.type, np.string_)) + assert_(issubclass(self.A.rstrip().dtype.type, np.bytes_)) tgt = [[b' abc', b''], [b'12345', b'MixedCase'], @@ -483,14 +485,14 @@ class TestMethods: tgt = [[' \u03a3', ''], ['12345', 'MixedCase'], ['123 \t 345', 'UPPER']] - assert_(issubclass(self.B.rstrip().dtype.type, np.unicode_)) + assert_(issubclass(self.B.rstrip().dtype.type, np.str_)) assert_array_equal(self.B.rstrip(), tgt) def test_strip(self): tgt = [[b'abc', b''], [b'12345', b'MixedCase'], [b'123 \t 345', b'UPPER']] - assert_(issubclass(self.A.strip().dtype.type, np.string_)) + assert_(issubclass(self.A.strip().dtype.type, np.bytes_)) assert_array_equal(self.A.strip(), tgt) tgt = [[b' abc ', b''], @@ -501,7 +503,7 @@ class TestMethods: tgt = [['\u03a3', ''], ['12345', 'MixedCase'], ['123 \t 345', 'UPPER']] - assert_(issubclass(self.B.strip().dtype.type, np.unicode_)) + assert_(issubclass(self.B.strip().dtype.type, np.str_)) assert_array_equal(self.B.strip(), tgt) def test_split(self): @@ -523,39 +525,39 @@ class TestMethods: tgt = [[b' ABC ', b''], [b'12345', b'mIXEDcASE'], [b'123 \t 345 \0 ', b'upper']] - assert_(issubclass(self.A.swapcase().dtype.type, np.string_)) + assert_(issubclass(self.A.swapcase().dtype.type, np.bytes_)) assert_array_equal(self.A.swapcase(), tgt) tgt = [[' \u03c3 ', ''], ['12345', 'mIXEDcASE'], ['123 \t 345 \0 ', 'upper']] - assert_(issubclass(self.B.swapcase().dtype.type, np.unicode_)) + assert_(issubclass(self.B.swapcase().dtype.type, np.str_)) assert_array_equal(self.B.swapcase(), tgt) def test_title(self): tgt = [[b' Abc ', b''], [b'12345', b'Mixedcase'], [b'123 \t 345 \0 ', b'Upper']] - assert_(issubclass(self.A.title().dtype.type, np.string_)) + assert_(issubclass(self.A.title().dtype.type, np.bytes_)) assert_array_equal(self.A.title(), tgt) tgt = [[' \u03a3 ', ''], ['12345', 'Mixedcase'], ['123 \t 345 \0 ', 'Upper']] - assert_(issubclass(self.B.title().dtype.type, np.unicode_)) + assert_(issubclass(self.B.title().dtype.type, np.str_)) assert_array_equal(self.B.title(), tgt) def test_upper(self): tgt = [[b' ABC ', b''], [b'12345', b'MIXEDCASE'], [b'123 \t 345 \0 ', b'UPPER']] - assert_(issubclass(self.A.upper().dtype.type, np.string_)) + assert_(issubclass(self.A.upper().dtype.type, np.bytes_)) assert_array_equal(self.A.upper(), tgt) tgt = [[' \u03a3 ', ''], ['12345', 'MIXEDCASE'], ['123 \t 345 \0 ', 'UPPER']] - assert_(issubclass(self.B.upper().dtype.type, np.unicode_)) + assert_(issubclass(self.B.upper().dtype.type, np.str_)) assert_array_equal(self.B.upper(), tgt) def test_isnumeric(self): @@ -670,3 +672,15 @@ def test_empty_indexing(): # empty chararray instead of a chararray with a single empty string in it. s = np.chararray((4,)) assert_(s[[]].size == 0) + + +@pytest.mark.parametrize(["dt1", "dt2"], + [("S", "U"), ("U", "S"), ("S", "O"), ("U", "O"), + ("S", "d"), ("S", "V")]) +def test_add_types(dt1, dt2): + arr1 = np.array([1234234], dtype=dt1) + # If the following fails, e.g. use a number and test "V" explicitly + arr2 = np.array([b"423"], dtype=dt2) + with pytest.raises(TypeError, + match=f".*same dtype kind.*{arr1.dtype}.*{arr2.dtype}"): + np.char.add(arr1, arr2) diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 4ec1f83d4..3ada39e90 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -310,12 +310,6 @@ class TestGeneratorSum(_DeprecationTestCase): self.assert_deprecated(np.sum, args=((i for i in range(5)),)) -class TestPositiveOnNonNumerical(_DeprecationTestCase): - # 2018-06-28, 1.16.0 - def test_positive_on_non_number(self): - self.assert_deprecated(operator.pos, args=(np.array('foo'),)) - - class TestFromstring(_DeprecationTestCase): # 2017-10-19, 1.14 def test_fromstring(self): @@ -544,134 +538,6 @@ class FlatteningConcatenateUnsafeCast(_DeprecationTestCase): casting="same_kind") -class TestDeprecateSubarrayDTypeDuringArrayCoercion(_DeprecationTestCase): - warning_cls = FutureWarning - message = "(creating|casting) an array (with|to) a subarray dtype" - - def test_deprecated_array(self): - # Arrays are more complex, since they "broadcast" on success: - arr = np.array([1, 2]) - - self.assert_deprecated(lambda: arr.astype("(2)i,")) - with pytest.warns(FutureWarning): - res = arr.astype("(2)i,") - - assert_array_equal(res, [[1, 2], [1, 2]]) - - self.assert_deprecated(lambda: np.array(arr, dtype="(2)i,")) - with pytest.warns(FutureWarning): - res = np.array(arr, dtype="(2)i,") - - assert_array_equal(res, [[1, 2], [1, 2]]) - - with pytest.warns(FutureWarning): - res = np.array([[(1,), (2,)], arr], dtype="(2)i,") - - assert_array_equal(res, [[[1, 1], [2, 2]], [[1, 2], [1, 2]]]) - - def test_deprecated_and_error(self): - # These error paths do not give a warning, but will succeed in the - # future. - arr = np.arange(5 * 2).reshape(5, 2) - def check(): - with pytest.raises(ValueError): - arr.astype("(2,2)f") - - self.assert_deprecated(check) - - def check(): - with pytest.raises(ValueError): - np.array(arr, dtype="(2,2)f") - - self.assert_deprecated(check) - - -class TestFutureWarningArrayLikeNotIterable(_DeprecationTestCase): - # Deprecated 2020-12-09, NumPy 1.20 - warning_cls = FutureWarning - message = "The input object of type.*but not a sequence" - - @pytest.mark.parametrize("protocol", - ["__array__", "__array_interface__", "__array_struct__"]) - def test_deprecated(self, protocol): - """Test that these objects give a warning since they are not 0-D, - not coerced at the top level `np.array(obj)`, but nested, and do - *not* define the sequence protocol. - - NOTE: Tests for the versions including __len__ and __getitem__ exist - in `test_array_coercion.py` and they can be modified or amended - when this deprecation expired. - """ - blueprint = np.arange(10) - MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol)}) - self.assert_deprecated(lambda: np.array([MyArr()], dtype=object)) - - @pytest.mark.parametrize("protocol", - ["__array__", "__array_interface__", "__array_struct__"]) - def test_0d_not_deprecated(self, protocol): - # 0-D always worked (albeit it would use __float__ or similar for the - # conversion, which may not happen anymore) - blueprint = np.array(1.) - MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol)}) - myarr = MyArr() - - self.assert_not_deprecated(lambda: np.array([myarr], dtype=object)) - res = np.array([myarr], dtype=object) - expected = np.empty(1, dtype=object) - expected[0] = myarr - assert_array_equal(res, expected) - - @pytest.mark.parametrize("protocol", - ["__array__", "__array_interface__", "__array_struct__"]) - def test_unnested_not_deprecated(self, protocol): - blueprint = np.arange(10) - MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol)}) - myarr = MyArr() - - self.assert_not_deprecated(lambda: np.array(myarr)) - res = np.array(myarr) - assert_array_equal(res, blueprint) - - @pytest.mark.parametrize("protocol", - ["__array__", "__array_interface__", "__array_struct__"]) - def test_strange_dtype_handling(self, protocol): - """The old code would actually use the dtype from the array, but - then end up not using the array (for dimension discovery) - """ - blueprint = np.arange(10).astype("f4") - MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol), - "__float__": lambda _: 0.5}) - myarr = MyArr() - - # Make sure we warn (and capture the FutureWarning) - with pytest.warns(FutureWarning, match=self.message): - res = np.array([[myarr]]) - - assert res.shape == (1, 1) - assert res.dtype == "f4" - assert res[0, 0] == 0.5 - - @pytest.mark.parametrize("protocol", - ["__array__", "__array_interface__", "__array_struct__"]) - def test_assignment_not_deprecated(self, protocol): - # If the result is dtype=object we do not unpack a nested array or - # array-like, if it is nested at exactly the right depth. - # NOTE: We actually do still call __array__, etc. but ignore the result - # in the end. For `dtype=object` we could optimize that away. - blueprint = np.arange(10).astype("f4") - MyArr = type("MyArr", (), {protocol: getattr(blueprint, protocol), - "__float__": lambda _: 0.5}) - myarr = MyArr() - - res = np.empty(3, dtype=object) - def set(): - res[:] = [myarr, myarr, myarr] - self.assert_not_deprecated(set) - assert res[0] is myarr - assert res[1] is myarr - assert res[2] is myarr - - class TestDeprecatedUnpickleObjectScalar(_DeprecationTestCase): # Deprecated 2020-11-24, NumPy 1.20 """ @@ -685,218 +551,6 @@ class TestDeprecatedUnpickleObjectScalar(_DeprecationTestCase): ctor = np.core.multiarray.scalar self.assert_deprecated(lambda: ctor(np.dtype("O"), 1)) -try: - with warnings.catch_warnings(): - warnings.simplefilter("always") - import nose # noqa: F401 -except ImportError: - HAVE_NOSE = False -else: - HAVE_NOSE = True - - -@pytest.mark.skipif(not HAVE_NOSE, reason="Needs nose") -class TestNoseDecoratorsDeprecated(_DeprecationTestCase): - class DidntSkipException(Exception): - pass - - def test_slow(self): - def _test_slow(): - @np.testing.dec.slow - def slow_func(x, y, z): - pass - - assert_(slow_func.slow) - self.assert_deprecated(_test_slow) - - def test_setastest(self): - def _test_setastest(): - @np.testing.dec.setastest() - def f_default(a): - pass - - @np.testing.dec.setastest(True) - def f_istest(a): - pass - - @np.testing.dec.setastest(False) - def f_isnottest(a): - pass - - assert_(f_default.__test__) - assert_(f_istest.__test__) - assert_(not f_isnottest.__test__) - self.assert_deprecated(_test_setastest, num=3) - - def test_skip_functions_hardcoded(self): - def _test_skip_functions_hardcoded(): - @np.testing.dec.skipif(True) - def f1(x): - raise self.DidntSkipException - - try: - f1('a') - except self.DidntSkipException: - raise Exception('Failed to skip') - except SkipTest().__class__: - pass - - @np.testing.dec.skipif(False) - def f2(x): - raise self.DidntSkipException - - try: - f2('a') - except self.DidntSkipException: - pass - except SkipTest().__class__: - raise Exception('Skipped when not expected to') - self.assert_deprecated(_test_skip_functions_hardcoded, num=2) - - def test_skip_functions_callable(self): - def _test_skip_functions_callable(): - def skip_tester(): - return skip_flag == 'skip me!' - - @np.testing.dec.skipif(skip_tester) - def f1(x): - raise self.DidntSkipException - - try: - skip_flag = 'skip me!' - f1('a') - except self.DidntSkipException: - raise Exception('Failed to skip') - except SkipTest().__class__: - pass - - @np.testing.dec.skipif(skip_tester) - def f2(x): - raise self.DidntSkipException - - try: - skip_flag = 'five is right out!' - f2('a') - except self.DidntSkipException: - pass - except SkipTest().__class__: - raise Exception('Skipped when not expected to') - self.assert_deprecated(_test_skip_functions_callable, num=2) - - def test_skip_generators_hardcoded(self): - def _test_skip_generators_hardcoded(): - @np.testing.dec.knownfailureif(True, "This test is known to fail") - def g1(x): - yield from range(x) - - try: - for j in g1(10): - pass - except KnownFailureException().__class__: - pass - else: - raise Exception('Failed to mark as known failure') - - @np.testing.dec.knownfailureif(False, "This test is NOT known to fail") - def g2(x): - yield from range(x) - raise self.DidntSkipException('FAIL') - - try: - for j in g2(10): - pass - except KnownFailureException().__class__: - raise Exception('Marked incorrectly as known failure') - except self.DidntSkipException: - pass - self.assert_deprecated(_test_skip_generators_hardcoded, num=2) - - def test_skip_generators_callable(self): - def _test_skip_generators_callable(): - def skip_tester(): - return skip_flag == 'skip me!' - - @np.testing.dec.knownfailureif(skip_tester, "This test is known to fail") - def g1(x): - yield from range(x) - - try: - skip_flag = 'skip me!' - for j in g1(10): - pass - except KnownFailureException().__class__: - pass - else: - raise Exception('Failed to mark as known failure') - - @np.testing.dec.knownfailureif(skip_tester, "This test is NOT known to fail") - def g2(x): - yield from range(x) - raise self.DidntSkipException('FAIL') - - try: - skip_flag = 'do not skip' - for j in g2(10): - pass - except KnownFailureException().__class__: - raise Exception('Marked incorrectly as known failure') - except self.DidntSkipException: - pass - self.assert_deprecated(_test_skip_generators_callable, num=2) - - def test_deprecated(self): - def _test_deprecated(): - @np.testing.dec.deprecated(True) - def non_deprecated_func(): - pass - - @np.testing.dec.deprecated() - def deprecated_func(): - import warnings - warnings.warn("TEST: deprecated func", DeprecationWarning, stacklevel=1) - - @np.testing.dec.deprecated() - def deprecated_func2(): - import warnings - warnings.warn("AHHHH", stacklevel=1) - raise ValueError - - @np.testing.dec.deprecated() - def deprecated_func3(): - import warnings - warnings.warn("AHHHH", stacklevel=1) - - # marked as deprecated, but does not raise DeprecationWarning - assert_raises(AssertionError, non_deprecated_func) - # should be silent - deprecated_func() - with warnings.catch_warnings(record=True): - warnings.simplefilter("always") # do not propagate unrelated warnings - # fails if deprecated decorator just disables test. See #1453. - assert_raises(ValueError, deprecated_func2) - # warning is not a DeprecationWarning - assert_raises(AssertionError, deprecated_func3) - self.assert_deprecated(_test_deprecated, num=4) - - def test_parametrize(self): - def _test_parametrize(): - # dec.parametrize assumes that it is being run by nose. Because - # we are running under pytest, we need to explicitly check the - # results. - @np.testing.dec.parametrize('base, power, expected', - [(1, 1, 1), - (2, 1, 2), - (2, 2, 4)]) - def check_parametrize(base, power, expected): - assert_(base**power == expected) - - count = 0 - for test in check_parametrize(): - test[0](*test[1:]) - count += 1 - assert_(count == 3) - self.assert_deprecated(_test_parametrize) - class TestSingleElementSignature(_DeprecationTestCase): # Deprecated 2021-04-01, NumPy 1.21 @@ -1040,6 +694,18 @@ class TestLoadtxtParseIntsViaFloat(_DeprecationTestCase): assert isinstance(e.__cause__, DeprecationWarning) +class TestScalarConversion(_DeprecationTestCase): + # 2023-01-02, 1.25.0 + def test_float_conversion(self): + self.assert_deprecated(float, args=(np.array([3.14]),)) + + def test_behaviour(self): + b = np.array([[3.14]]) + c = np.zeros(5) + with pytest.warns(DeprecationWarning): + c[0] = b + + class TestPyIntConversion(_DeprecationTestCase): message = r".*stop allowing conversion of out-of-bound.*" @@ -1098,3 +764,54 @@ def test_future_scalar_attributes(name): # Unfortunately, they are currently still valid via `np.dtype()` np.dtype(name) name in np.sctypeDict + + +# Ignore the above future attribute warning for this test. +@pytest.mark.filterwarnings("ignore:In the future:FutureWarning") +class TestRemovedGlobals: + # Removed 2023-01-12, NumPy 1.24.0 + # Not a deprecation, but the large error was added to aid those who missed + # the previous deprecation, and should be removed similarly to one + # (or faster). + @pytest.mark.parametrize("name", + ["object", "bool", "float", "complex", "str", "int"]) + def test_attributeerror_includes_info(self, name): + msg = f".*\n`np.{name}` was a deprecated alias for the builtin" + with pytest.raises(AttributeError, match=msg): + getattr(np, name) + + +class TestDeprecatedFinfo(_DeprecationTestCase): + # Deprecated in NumPy 1.25, 2023-01-16 + def test_deprecated_none(self): + self.assert_deprecated(np.finfo, args=(None,)) + +class TestFromnumeric(_DeprecationTestCase): + # 2023-02-28, 1.25.0 + def test_round_(self): + self.assert_deprecated(lambda: np.round_(np.array([1.5, 2.5, 3.5]))) + + # 2023-03-02, 1.25.0 + def test_cumproduct(self): + self.assert_deprecated(lambda: np.cumproduct(np.array([1, 2, 3]))) + + # 2023-03-02, 1.25.0 + def test_product(self): + self.assert_deprecated(lambda: np.product(np.array([1, 2, 3]))) + + # 2023-03-02, 1.25.0 + def test_sometrue(self): + self.assert_deprecated(lambda: np.sometrue(np.array([True, False]))) + + # 2023-03-02, 1.25.0 + def test_alltrue(self): + self.assert_deprecated(lambda: np.alltrue(np.array([True, False]))) + + +class TestMathAlias(_DeprecationTestCase): + # Deprecated in Numpy 1.25, 2023-04-06 + def test_deprecated_np_math(self): + self.assert_deprecated(lambda: np.math) + + def test_deprecated_np_lib_math(self): + self.assert_deprecated(lambda: np.lib.math) diff --git a/numpy/core/tests/test_dlpack.py b/numpy/core/tests/test_dlpack.py index 278bdd12d..49249bc6a 100644 --- a/numpy/core/tests/test_dlpack.py +++ b/numpy/core/tests/test_dlpack.py @@ -38,13 +38,14 @@ class TestDLPack: assert sys.getrefcount(x) == 2 @pytest.mark.parametrize("dtype", [ + np.bool_, np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64, np.float16, np.float32, np.float64, np.complex64, np.complex128 ]) def test_dtype_passthrough(self, dtype): - x = np.arange(5, dtype=dtype) + x = np.arange(5).astype(dtype) y = np.from_dlpack(x) assert y.dtype == x.dtype diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 0a56483d6..57831f46f 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -7,6 +7,7 @@ import types from typing import Any import numpy as np +import numpy.dtypes from numpy.core._rational_tests import rational from numpy.core._multiarray_tests import create_custom_field_dtype from numpy.testing import ( @@ -195,6 +196,34 @@ class TestBuiltin: # This is an safe cast (not equiv) due to the different names: assert np.can_cast(x, y, casting="safe") + @pytest.mark.parametrize( + ["type_char", "char_size", "scalar_type"], + [["U", 4, np.str_], + ["S", 1, np.bytes_]]) + def test_create_string_dtypes_directly( + self, type_char, char_size, scalar_type): + dtype_class = type(np.dtype(type_char)) + + dtype = dtype_class(8) + assert dtype.type is scalar_type + assert dtype.itemsize == 8*char_size + + def test_create_invalid_string_errors(self): + one_too_big = np.iinfo(np.intc).max + 1 + with pytest.raises(TypeError): + type(np.dtype("U"))(one_too_big // 4) + + with pytest.raises(TypeError): + # Code coverage for very large numbers: + type(np.dtype("U"))(np.iinfo(np.intp).max // 4 + 1) + + if one_too_big < sys.maxsize: + with pytest.raises(TypeError): + type(np.dtype("S"))(one_too_big) + + with pytest.raises(ValueError): + type(np.dtype("U"))(-1) + class TestRecord: def test_equivalent_record(self): @@ -523,6 +552,14 @@ class TestRecord: assert_equal(np.zeros((1, 2), dtype=[]) == a, np.ones((1, 2), dtype=bool)) + def test_nonstructured_with_object(self): + # See gh-23277, the dtype here thinks it contain objects, if the + # assert about that fails, the test becomes meaningless (which is OK) + arr = np.recarray((0,), dtype="O") + assert arr.dtype.names is None # no fields + assert arr.dtype.hasobject # but claims to contain objects + del arr # the deletion failed previously. + class TestSubarray: def test_single_subarray(self): @@ -1527,8 +1564,21 @@ class TestDTypeClasses: dtype = np.dtype(dtype) assert isinstance(dtype, np.dtype) assert type(dtype) is not np.dtype - assert type(dtype).__name__ == f"dtype[{dtype.type.__name__}]" - assert type(dtype).__module__ == "numpy" + if dtype.type.__name__ != "rational": + dt_name = type(dtype).__name__.lower().removesuffix("dtype") + if dt_name == "uint" or dt_name == "int": + # The scalar names has a `c` attached because "int" is Python + # int and that is long... + dt_name += "c" + sc_name = dtype.type.__name__ + assert dt_name == sc_name.strip("_") + assert type(dtype).__module__ == "numpy.dtypes" + + assert getattr(numpy.dtypes, type(dtype).__name__) is type(dtype) + else: + assert type(dtype).__name__ == "dtype[rational]" + assert type(dtype).__module__ == "numpy" + assert not type(dtype)._abstract # the flexible dtypes and datetime/timedelta have additional parameters @@ -1551,6 +1601,32 @@ class TestDTypeClasses: assert type(np.dtype).__module__ == "numpy" assert np.dtype._abstract + def test_is_numeric(self): + all_codes = set(np.typecodes['All']) + numeric_codes = set(np.typecodes['AllInteger'] + + np.typecodes['AllFloat'] + '?') + non_numeric_codes = all_codes - numeric_codes + + for code in numeric_codes: + assert type(np.dtype(code))._is_numeric + + for code in non_numeric_codes: + assert not type(np.dtype(code))._is_numeric + + @pytest.mark.parametrize("int_", ["UInt", "Int"]) + @pytest.mark.parametrize("size", [8, 16, 32, 64]) + def test_integer_alias_names(self, int_, size): + DType = getattr(numpy.dtypes, f"{int_}{size}DType") + sctype = getattr(numpy, f"{int_.lower()}{size}") + assert DType.type is sctype + assert DType.__name__.lower().removesuffix("dtype") == sctype.__name__ + + @pytest.mark.parametrize("name", + ["Half", "Float", "Double", "CFloat", "CDouble"]) + def test_float_alias_names(self, name): + with pytest.raises(AttributeError): + getattr(numpy.dtypes, name + "DType") is numpy.dtypes.Float16DType + class TestFromCTypes: @@ -1785,7 +1861,6 @@ class TestUserDType: create_custom_field_dtype(blueprint, mytype, 2) -@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires python 3.9") class TestClassGetItem: def test_dtype(self) -> None: alias = np.dtype[Any] @@ -1818,10 +1893,3 @@ def test_result_type_integers_and_unitless_timedelta64(): td = np.timedelta64(4) result = np.result_type(0, td) assert_dtype_equal(result, td.dtype) - - -@pytest.mark.skipif(sys.version_info >= (3, 9), reason="Requires python 3.8") -def test_class_getitem_38() -> None: - match = "Type subscription requires python >= 3.9" - with pytest.raises(TypeError, match=match): - np.dtype[Any] diff --git a/numpy/core/tests/test_einsum.py b/numpy/core/tests/test_einsum.py index 7c0e8d97c..3a06d119f 100644 --- a/numpy/core/tests/test_einsum.py +++ b/numpy/core/tests/test_einsum.py @@ -100,6 +100,69 @@ class TestEinsum: assert_raises(ValueError, np.einsum, "i->i", np.arange(6).reshape(-1, 1), optimize=do_opt, order='d') + def test_einsum_object_errors(self): + # Exceptions created by object arithmetic should + # successfully propogate + + class CustomException(Exception): + pass + + class DestructoBox: + + def __init__(self, value, destruct): + self._val = value + self._destruct = destruct + + def __add__(self, other): + tmp = self._val + other._val + if tmp >= self._destruct: + raise CustomException + else: + self._val = tmp + return self + + def __radd__(self, other): + if other == 0: + return self + else: + return self.__add__(other) + + def __mul__(self, other): + tmp = self._val * other._val + if tmp >= self._destruct: + raise CustomException + else: + self._val = tmp + return self + + def __rmul__(self, other): + if other == 0: + return self + else: + return self.__mul__(other) + + a = np.array([DestructoBox(i, 5) for i in range(1, 10)], + dtype='object').reshape(3, 3) + + # raised from unbuffered_loop_nop1_ndim2 + assert_raises(CustomException, np.einsum, "ij->i", a) + + # raised from unbuffered_loop_nop1_ndim3 + b = np.array([DestructoBox(i, 100) for i in range(0, 27)], + dtype='object').reshape(3, 3, 3) + assert_raises(CustomException, np.einsum, "i...k->...", b) + + # raised from unbuffered_loop_nop2_ndim2 + b = np.array([DestructoBox(i, 55) for i in range(1, 4)], + dtype='object') + assert_raises(CustomException, np.einsum, "ij, j", a, b) + + # raised from unbuffered_loop_nop2_ndim3 + assert_raises(CustomException, np.einsum, "ij, jh", a, a) + + # raised from PyArray_EinsteinSum + assert_raises(CustomException, np.einsum, "ij->", a) + def test_einsum_views(self): # pass-through for do_opt in [True, False]: @@ -247,47 +310,50 @@ class TestEinsum: # sum(a, axis=-1) for n in range(1, 17): a = np.arange(n, dtype=dtype) - assert_equal(np.einsum("i->", a, optimize=do_opt), - np.sum(a, axis=-1).astype(dtype)) - assert_equal(np.einsum(a, [0], [], optimize=do_opt), - np.sum(a, axis=-1).astype(dtype)) + b = np.sum(a, axis=-1) + if hasattr(b, 'astype'): + b = b.astype(dtype) + assert_equal(np.einsum("i->", a, optimize=do_opt), b) + assert_equal(np.einsum(a, [0], [], optimize=do_opt), b) for n in range(1, 17): a = np.arange(2*3*n, dtype=dtype).reshape(2, 3, n) - assert_equal(np.einsum("...i->...", a, optimize=do_opt), - np.sum(a, axis=-1).astype(dtype)) - assert_equal(np.einsum(a, [Ellipsis, 0], [Ellipsis], optimize=do_opt), - np.sum(a, axis=-1).astype(dtype)) + b = np.sum(a, axis=-1) + if hasattr(b, 'astype'): + b = b.astype(dtype) + assert_equal(np.einsum("...i->...", a, optimize=do_opt), b) + assert_equal(np.einsum(a, [Ellipsis, 0], [Ellipsis], optimize=do_opt), b) # sum(a, axis=0) for n in range(1, 17): a = np.arange(2*n, dtype=dtype).reshape(2, n) - assert_equal(np.einsum("i...->...", a, optimize=do_opt), - np.sum(a, axis=0).astype(dtype)) - assert_equal(np.einsum(a, [0, Ellipsis], [Ellipsis], optimize=do_opt), - np.sum(a, axis=0).astype(dtype)) + b = np.sum(a, axis=0) + if hasattr(b, 'astype'): + b = b.astype(dtype) + assert_equal(np.einsum("i...->...", a, optimize=do_opt), b) + assert_equal(np.einsum(a, [0, Ellipsis], [Ellipsis], optimize=do_opt), b) for n in range(1, 17): a = np.arange(2*3*n, dtype=dtype).reshape(2, 3, n) - assert_equal(np.einsum("i...->...", a, optimize=do_opt), - np.sum(a, axis=0).astype(dtype)) - assert_equal(np.einsum(a, [0, Ellipsis], [Ellipsis], optimize=do_opt), - np.sum(a, axis=0).astype(dtype)) + b = np.sum(a, axis=0) + if hasattr(b, 'astype'): + b = b.astype(dtype) + assert_equal(np.einsum("i...->...", a, optimize=do_opt), b) + assert_equal(np.einsum(a, [0, Ellipsis], [Ellipsis], optimize=do_opt), b) # trace(a) for n in range(1, 17): a = np.arange(n*n, dtype=dtype).reshape(n, n) - assert_equal(np.einsum("ii", a, optimize=do_opt), - np.trace(a).astype(dtype)) - assert_equal(np.einsum(a, [0, 0], optimize=do_opt), - np.trace(a).astype(dtype)) + b = np.trace(a) + if hasattr(b, 'astype'): + b = b.astype(dtype) + assert_equal(np.einsum("ii", a, optimize=do_opt), b) + assert_equal(np.einsum(a, [0, 0], optimize=do_opt), b) # gh-15961: should accept numpy int64 type in subscript list np_array = np.asarray([0, 0]) - assert_equal(np.einsum(a, np_array, optimize=do_opt), - np.trace(a).astype(dtype)) - assert_equal(np.einsum(a, list(np_array), optimize=do_opt), - np.trace(a).astype(dtype)) + assert_equal(np.einsum(a, np_array, optimize=do_opt), b) + assert_equal(np.einsum(a, list(np_array), optimize=do_opt), b) # multiply(a, b) assert_equal(np.einsum("..., ...", 3, 4), 12) # scalar case @@ -489,11 +555,15 @@ class TestEinsum: b = np.einsum("i->", a, dtype=dtype, casting='unsafe') assert_equal(b, np.sum(a)) - assert_equal(b.dtype, np.dtype(dtype)) + if hasattr(b, "dtype"): + # Can be a python object when dtype is object + assert_equal(b.dtype, np.dtype(dtype)) b = np.einsum(a, [0], [], dtype=dtype, casting='unsafe') assert_equal(b, np.sum(a)) - assert_equal(b.dtype, np.dtype(dtype)) + if hasattr(b, "dtype"): + # Can be a python object when dtype is object + assert_equal(b.dtype, np.dtype(dtype)) # A case which was failing (ticket #1885) p = np.arange(2) + 1 @@ -587,6 +657,10 @@ class TestEinsum: def test_einsum_sums_clongdouble(self): self.check_einsum_sums(np.clongdouble) + def test_einsum_sums_object(self): + self.check_einsum_sums('object') + self.check_einsum_sums('object', True) + def test_einsum_misc(self): # This call used to crash because of a bug in # PyArray_AssignZero @@ -625,6 +699,21 @@ class TestEinsum: # see issue gh-15776 and issue gh-15256 assert_equal(np.einsum('i,j', [1], [2], out=None), [[2]]) + def test_object_loop(self): + + class Mult: + def __mul__(self, other): + return 42 + + objMult = np.array([Mult()]) + objNULL = np.ndarray(buffer = b'\0' * np.intp(0).itemsize, shape=1, dtype=object) + + with pytest.raises(TypeError): + np.einsum("i,j", [1], objNULL) + with pytest.raises(TypeError): + np.einsum("i,j", objNULL, [1]) + assert np.einsum("i,j", objMult, objMult) == 42 + def test_subscript_range(self): # Issue #7741, make sure that all letters of Latin alphabet (both uppercase & lowercase) can be used # when creating a subscript from arrays @@ -755,7 +844,7 @@ class TestEinsum: # Test originally added to cover broken float16 path: gh-20305 # Likely most are covered elsewhere, at least partially. dtype = np.dtype(dtype) - # Simple test, designed to excersize most specialized code paths, + # Simple test, designed to exercise most specialized code paths, # note the +0.5 for floats. This makes sure we use a float value # where the results must be exact. arr = (np.arange(7) + 0.5).astype(dtype) diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py index dad7a5883..79f1ecfc9 100644 --- a/numpy/core/tests/test_function_base.py +++ b/numpy/core/tests/test_function_base.py @@ -1,3 +1,4 @@ +import pytest from numpy import ( logspace, linspace, geomspace, dtype, array, sctypes, arange, isnan, ndarray, sqrt, nextafter, stack, errstate @@ -65,6 +66,33 @@ class TestLogspace: t5 = logspace(start, stop, 6, axis=-1) assert_equal(t5, t2.T) + @pytest.mark.parametrize("axis", [0, 1, -1]) + def test_base_array(self, axis: int): + start = 1 + stop = 2 + num = 6 + base = array([1, 2]) + t1 = logspace(start, stop, num=num, base=base, axis=axis) + t2 = stack( + [logspace(start, stop, num=num, base=_base) for _base in base], + axis=(axis + 1) % t1.ndim, + ) + assert_equal(t1, t2) + + @pytest.mark.parametrize("axis", [0, 1, -1]) + def test_stop_base_array(self, axis: int): + start = 1 + stop = array([2, 3]) + num = 6 + base = array([1, 2]) + t1 = logspace(start, stop, num=num, base=base, axis=axis) + t2 = stack( + [logspace(start, _stop, num=num, base=_base) + for _stop, _base in zip(stop, base)], + axis=(axis + 1) % t1.ndim, + ) + assert_equal(t1, t2) + def test_dtype(self): y = logspace(0, 6, dtype='float32') assert_equal(y.dtype, dtype('float32')) @@ -407,3 +435,12 @@ class TestLinspace: y = linspace(-1, 3, num=8, dtype=int) t = array([-1, -1, 0, 0, 1, 1, 2, 3], dtype=int) assert_array_equal(y, t) + + def test_any_step_zero_and_not_mult_inplace(self): + # any_step_zero is True, _mult_inplace is False + start = array([0.0, 1.0]) + stop = array([2.0, 1.0]) + y = linspace(start, stop, 3) + assert_array_equal(y, array([[0.0, 1.0], [1.0, 1.0], [2.0, 1.0]])) + + diff --git a/numpy/core/tests/test_getlimits.py b/numpy/core/tests/test_getlimits.py index b8aaba386..63217c38c 100644 --- a/numpy/core/tests/test_getlimits.py +++ b/numpy/core/tests/test_getlimits.py @@ -3,6 +3,7 @@ """ import warnings import numpy as np +import pytest from numpy.core import finfo, iinfo from numpy import half, single, double, longdouble from numpy.testing import assert_equal, assert_, assert_raises @@ -40,20 +41,38 @@ class TestLongdouble: ftype2 = finfo(longdouble) assert_equal(id(ftype), id(ftype2)) +def assert_finfo_equal(f1, f2): + # assert two finfo instances have the same attributes + for attr in ('bits', 'eps', 'epsneg', 'iexp', 'machep', + 'max', 'maxexp', 'min', 'minexp', 'negep', 'nexp', + 'nmant', 'precision', 'resolution', 'tiny', + 'smallest_normal', 'smallest_subnormal'): + assert_equal(getattr(f1, attr), getattr(f2, attr), + f'finfo instances {f1} and {f2} differ on {attr}') + +def assert_iinfo_equal(i1, i2): + # assert two iinfo instances have the same attributes + for attr in ('bits', 'min', 'max'): + assert_equal(getattr(i1, attr), getattr(i2, attr), + f'iinfo instances {i1} and {i2} differ on {attr}') + class TestFinfo: def test_basic(self): dts = list(zip(['f2', 'f4', 'f8', 'c8', 'c16'], [np.float16, np.float32, np.float64, np.complex64, np.complex128])) for dt1, dt2 in dts: - for attr in ('bits', 'eps', 'epsneg', 'iexp', 'machep', - 'max', 'maxexp', 'min', 'minexp', 'negep', 'nexp', - 'nmant', 'precision', 'resolution', 'tiny', - 'smallest_normal', 'smallest_subnormal'): - assert_equal(getattr(finfo(dt1), attr), - getattr(finfo(dt2), attr), attr) + assert_finfo_equal(finfo(dt1), finfo(dt2)) + assert_raises(ValueError, finfo, 'i4') + def test_regression_gh23108(self): + # np.float32(1.0) and np.float64(1.0) have the same hash and are + # equal under the == operator + f1 = np.finfo(np.float32(1.0)) + f2 = np.finfo(np.float64(1.0)) + assert f1 != f2 + class TestIinfo: def test_basic(self): dts = list(zip(['i1', 'i2', 'i4', 'i8', @@ -61,9 +80,8 @@ class TestIinfo: [np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64])) for dt1, dt2 in dts: - for attr in ('bits', 'min', 'max'): - assert_equal(getattr(iinfo(dt1), attr), - getattr(iinfo(dt2), attr), attr) + assert_iinfo_equal(iinfo(dt1), iinfo(dt2)) + assert_raises(ValueError, iinfo, 'f4') def test_unsigned_max(self): @@ -85,8 +103,28 @@ class TestRepr: def test_instances(): - iinfo(10) - finfo(3.0) + # Test the finfo and iinfo results on numeric instances agree with + # the results on the corresponding types + + for c in [int, np.int16, np.int32, np.int64]: + class_iinfo = iinfo(c) + instance_iinfo = iinfo(c(12)) + + assert_iinfo_equal(class_iinfo, instance_iinfo) + + for c in [float, np.float16, np.float32, np.float64]: + class_finfo = finfo(c) + instance_finfo = finfo(c(1.2)) + assert_finfo_equal(class_finfo, instance_finfo) + + with pytest.raises(ValueError): + iinfo(10.) + + with pytest.raises(ValueError): + iinfo('hi') + + with pytest.raises(ValueError): + finfo(np.int64(1)) def assert_ma_equal(discovered, ma_like): diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index 74075639c..042936702 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -1062,7 +1062,7 @@ class TestMultiIndexingAutomated: if np.any(_indx >= _size) or np.any(_indx < -_size): raise IndexError if len(indx[1:]) == len(orig_slice): - if np.product(orig_slice) == 0: + if np.prod(orig_slice) == 0: # Work around for a crash or IndexError with 'wrap' # in some 0-sized cases. try: diff --git a/numpy/core/tests/test_item_selection.py b/numpy/core/tests/test_item_selection.py index 3c35245a3..5660ef583 100644 --- a/numpy/core/tests/test_item_selection.py +++ b/numpy/core/tests/test_item_selection.py @@ -1,5 +1,7 @@ import sys +import pytest + import numpy as np from numpy.testing import ( assert_, assert_raises, assert_array_equal, HAS_REFCOUNT @@ -84,3 +86,80 @@ class TestTake: b = np.array([0, 1, 2, 3, 4, 5]) assert_array_equal(a, b) + + +class TestPutMask: + @pytest.mark.parametrize("dtype", list(np.typecodes["All"]) + ["i,O"]) + def test_simple(self, dtype): + if dtype.lower() == "m": + dtype += "8[ns]" + + # putmask is weird and doesn't care about value length (even shorter) + vals = np.arange(1001).astype(dtype=dtype) + + mask = np.random.randint(2, size=1000).astype(bool) + # Use vals.dtype in case of flexible dtype (i.e. string) + arr = np.zeros(1000, dtype=vals.dtype) + zeros = arr.copy() + + np.putmask(arr, mask, vals) + assert_array_equal(arr[mask], vals[:len(mask)][mask]) + assert_array_equal(arr[~mask], zeros[~mask]) + + @pytest.mark.parametrize("dtype", list(np.typecodes["All"])[1:] + ["i,O"]) + @pytest.mark.parametrize("mode", ["raise", "wrap", "clip"]) + def test_empty(self, dtype, mode): + arr = np.zeros(1000, dtype=dtype) + arr_copy = arr.copy() + mask = np.random.randint(2, size=1000).astype(bool) + + # Allowing empty values like this is weird... + np.put(arr, mask, []) + assert_array_equal(arr, arr_copy) + + +class TestPut: + @pytest.mark.parametrize("dtype", list(np.typecodes["All"])[1:] + ["i,O"]) + @pytest.mark.parametrize("mode", ["raise", "wrap", "clip"]) + def test_simple(self, dtype, mode): + if dtype.lower() == "m": + dtype += "8[ns]" + + # put is weird and doesn't care about value length (even shorter) + vals = np.arange(1001).astype(dtype=dtype) + + # Use vals.dtype in case of flexible dtype (i.e. string) + arr = np.zeros(1000, dtype=vals.dtype) + zeros = arr.copy() + + if mode == "clip": + # Special because 0 and -1 value are "reserved" for clip test + indx = np.random.permutation(len(arr) - 2)[:-500] + 1 + + indx[-1] = 0 + indx[-2] = len(arr) - 1 + indx_put = indx.copy() + indx_put[-1] = -1389 + indx_put[-2] = 1321 + else: + # Avoid duplicates (for simplicity) and fill half only + indx = np.random.permutation(len(arr) - 3)[:-500] + indx_put = indx + if mode == "wrap": + indx_put = indx_put + len(arr) + + np.put(arr, indx_put, vals, mode=mode) + assert_array_equal(arr[indx], vals[:len(indx)]) + untouched = np.ones(len(arr), dtype=bool) + untouched[indx] = False + assert_array_equal(arr[untouched], zeros[:untouched.sum()]) + + @pytest.mark.parametrize("dtype", list(np.typecodes["All"])[1:] + ["i,O"]) + @pytest.mark.parametrize("mode", ["raise", "wrap", "clip"]) + def test_empty(self, dtype, mode): + arr = np.zeros(1000, dtype=dtype) + arr_copy = arr.copy() + + # Allowing empty values like this is weird... + np.put(arr, [1, 2, 3], []) + assert_array_equal(arr, arr_copy) diff --git a/numpy/core/tests/test_longdouble.py b/numpy/core/tests/test_longdouble.py index 1a54e62d8..45721950c 100644 --- a/numpy/core/tests/test_longdouble.py +++ b/numpy/core/tests/test_longdouble.py @@ -1,10 +1,11 @@ import warnings +import platform import pytest import numpy as np from numpy.testing import ( assert_, assert_equal, assert_raises, assert_warns, assert_array_equal, - temppath, + temppath, IS_MUSL ) from numpy.core.tests._locales import CommaDecimalPointLocale @@ -30,6 +31,10 @@ def test_scalar_extraction(): # 0.1 not exactly representable in base 2 floating point. repr_precision = len(repr(np.longdouble(0.1))) # +2 from macro block starting around line 842 in scalartypes.c.src. + + +@pytest.mark.skipif(IS_MUSL, + reason="test flaky on musllinux") @pytest.mark.skipif(LD_INFO.precision + 2 >= repr_precision, reason="repr precision not enough to show eps") def test_repr_roundtrip(): @@ -142,7 +147,7 @@ class TestFileBased: def test_fromfile_bogus(self): with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1. 2. 3. flop 4.\n") with assert_warns(DeprecationWarning): @@ -153,7 +158,7 @@ class TestFileBased: for ctype in ["complex", "cdouble", "cfloat"]: # Check spacing between separator and only real component specified with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1, 2 , 3 ,4\n") res = np.fromfile(path, dtype=ctype, sep=",") @@ -161,7 +166,7 @@ class TestFileBased: # Real component not specified with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1j, -2j, 3j, 4e1j\n") res = np.fromfile(path, dtype=ctype, sep=",") @@ -169,7 +174,7 @@ class TestFileBased: # Both components specified with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1+1j,2-2j, -3+3j, -4e1+4j\n") res = np.fromfile(path, dtype=ctype, sep=",") @@ -177,7 +182,7 @@ class TestFileBased: # Spaces at wrong places with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1+2 j,3\n") with assert_warns(DeprecationWarning): @@ -186,7 +191,7 @@ class TestFileBased: # Spaces at wrong places with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1+ 2j,3\n") with assert_warns(DeprecationWarning): @@ -195,7 +200,7 @@ class TestFileBased: # Spaces at wrong places with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1 +2j,3\n") with assert_warns(DeprecationWarning): @@ -204,7 +209,7 @@ class TestFileBased: # Spaces at wrong places with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1+j\n") with assert_warns(DeprecationWarning): @@ -213,7 +218,7 @@ class TestFileBased: # Spaces at wrong places with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1+\n") with assert_warns(DeprecationWarning): @@ -222,7 +227,7 @@ class TestFileBased: # Spaces at wrong places with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write("1j+1\n") with assert_warns(DeprecationWarning): @@ -235,7 +240,7 @@ class TestFileBased: reason="Need strtold_l") def test_fromfile(self): with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write(self.out) res = np.fromfile(path, dtype=np.longdouble, sep="\n") assert_equal(res, self.tgt) @@ -244,7 +249,7 @@ class TestFileBased: reason="Need strtold_l") def test_genfromtxt(self): with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write(self.out) res = np.genfromtxt(path, dtype=np.longdouble) assert_equal(res, self.tgt) @@ -253,7 +258,7 @@ class TestFileBased: reason="Need strtold_l") def test_loadtxt(self): with temppath() as path: - with open(path, 'wt') as f: + with open(path, 'w') as f: f.write(self.out) res = np.loadtxt(path, dtype=np.longdouble) assert_equal(res, self.tgt) @@ -368,3 +373,23 @@ def test_longdouble_from_int(int_val): True, False]) def test_longdouble_from_bool(bool_val): assert np.longdouble(bool_val) == np.longdouble(int(bool_val)) + + +@pytest.mark.skipif( + not (IS_MUSL and platform.machine() == "x86_64"), + reason="only need to run on musllinux_x86_64" +) +def test_musllinux_x86_64_signature(): + # this test may fail if you're emulating musllinux_x86_64 on a different + # architecture, but should pass natively. + known_sigs = [b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf'] + sig = (np.longdouble(-1.0) / np.longdouble(10.0) + ).newbyteorder('<').tobytes()[:10] + assert sig in known_sigs + + +def test_eps_positive(): + # np.finfo('g').eps should be positive on all platforms. If this isn't true + # then something may have gone wrong with the MachArLike, e.g. if + # np.core.getlimits._discovered_machar didn't work properly + assert np.finfo(np.longdouble).eps > 0. diff --git a/numpy/core/tests/test_mem_overlap.py b/numpy/core/tests/test_mem_overlap.py index d66decfda..1fd4c4d41 100644 --- a/numpy/core/tests/test_mem_overlap.py +++ b/numpy/core/tests/test_mem_overlap.py @@ -55,7 +55,7 @@ def _indices(ndims): def _check_assignment(srcidx, dstidx): """Check assignment arr[dstidx] = arr[srcidx] works.""" - arr = np.arange(np.product(shape)).reshape(shape) + arr = np.arange(np.prod(shape)).reshape(shape) cpy = arr.copy() diff --git a/numpy/core/tests/test_mem_policy.py b/numpy/core/tests/test_mem_policy.py index d5dfbc38b..479f702ea 100644 --- a/numpy/core/tests/test_mem_policy.py +++ b/numpy/core/tests/test_mem_policy.py @@ -89,6 +89,7 @@ def get_module(tmp_path): """), ] prologue = ''' + #define NPY_TARGET_VERSION NPY_1_22_API_VERSION #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include <numpy/arrayobject.h> /* diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py index 914f86f14..ad074b312 100644 --- a/numpy/core/tests/test_memmap.py +++ b/numpy/core/tests/test_memmap.py @@ -6,7 +6,7 @@ from pathlib import Path from tempfile import NamedTemporaryFile, TemporaryFile from numpy import ( - memmap, sum, average, product, ndarray, isscalar, add, subtract, multiply) + memmap, sum, average, prod, ndarray, isscalar, add, subtract, multiply) from numpy import arange, allclose, asarray from numpy.testing import ( @@ -153,7 +153,7 @@ class TestMemmap: with suppress_warnings() as sup: sup.filter(FutureWarning, "np.average currently does not preserve") - for unary_op in [sum, average, product]: + for unary_op in [sum, average, prod]: result = unary_op(fp) assert_(isscalar(result)) assert_(result.__class__ is self.data[0, 0].__class__) diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 63ac32f20..196c2dc13 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import collections.abc import tempfile import sys @@ -28,11 +30,12 @@ from numpy.testing import ( assert_, assert_raises, assert_warns, assert_equal, assert_almost_equal, assert_array_equal, assert_raises_regex, assert_array_almost_equal, assert_allclose, IS_PYPY, IS_PYSTON, HAS_REFCOUNT, assert_array_less, - runstring, temppath, suppress_warnings, break_cycles, + runstring, temppath, suppress_warnings, break_cycles, _SUPPORTS_SVE, ) from numpy.testing._private.utils import requires_memory, _no_tracing from numpy.core.tests._locales import CommaDecimalPointLocale from numpy.lib.recfunctions import repack_fields +from numpy.core.multiarray import _get_ndarray_c_version # Need to test an object that does not fully implement math interface from datetime import timedelta, datetime @@ -403,6 +406,13 @@ class TestAttributes: assert_array_equal(x['a'], [3.5, 3.5]) assert_array_equal(x['b'], [-2, -2]) + def test_fill_readonly(self): + # gh-22922 + a = np.zeros(11) + a.setflags(write=False) + with pytest.raises(ValueError, match=".*read-only"): + a.fill(0) + class TestArrayConstruction: def test_array(self): @@ -1186,6 +1196,17 @@ class TestCreation: expected = expected * (arr.nbytes // len(expected)) assert arr.tobytes() == expected + @pytest.mark.parametrize("func", [ + np.array, np.asarray, np.asanyarray, np.ascontiguousarray, + np.asfortranarray]) + def test_creation_from_dtypemeta(self, func): + dtype = np.dtype('i') + arr1 = func([1, 2, 3], dtype=dtype) + arr2 = func([1, 2, 3], dtype=type(dtype)) + assert_array_equal(arr1, arr2) + assert arr2.dtype == dtype + + class TestStructured: def test_subarray_field_access(self): a = np.zeros((3, 5), dtype=[('a', ('i4', (2, 2)))]) @@ -1698,7 +1719,7 @@ class TestBool: @pytest.mark.xfail(reason="See gh-9847") def test_cast_from_unicode(self): - self._test_cast_from_flexible(np.unicode_) + self._test_cast_from_flexible(np.str_) @pytest.mark.xfail(reason="See gh-9847") def test_cast_from_bytes(self): @@ -1913,8 +1934,9 @@ class TestMethods: assert_array_equal(a2.prod(axis=-1), np.array([24, 1890, 600], ctype)) - def test_repeat(self): - m = np.array([1, 2, 3, 4, 5, 6]) + @pytest.mark.parametrize('dtype', [None, object]) + def test_repeat(self, dtype): + m = np.array([1, 2, 3, 4, 5, 6], dtype=dtype) m_rect = m.reshape((2, 3)) A = m.repeat([1, 3, 2, 1, 1, 2]) @@ -2080,7 +2102,7 @@ class TestMethods: msg = 'byte-swapped complex sort, dtype={0}'.format(dt) assert_equal(c, arr, msg) - @pytest.mark.parametrize('dtype', [np.bytes_, np.unicode_]) + @pytest.mark.parametrize('dtype', [np.bytes_, np.str_]) def test_sort_string(self, dtype): # np.array will perform the encoding to bytes for us in the bytes test a = np.array(['aaaaaaaa' + chr(i) for i in range(101)], dtype=dtype) @@ -2108,19 +2130,26 @@ class TestMethods: c.sort(kind=kind) assert_equal(c, a, msg) - def test_sort_structured(self): + @pytest.mark.parametrize("dt", [ + np.dtype([('f', float), ('i', int)]), + np.dtype([('f', float), ('i', object)])]) + @pytest.mark.parametrize("step", [1, 2]) + def test_sort_structured(self, dt, step): # test record array sorts. - dt = np.dtype([('f', float), ('i', int)]) - a = np.array([(i, i) for i in range(101)], dtype=dt) + a = np.array([(i, i) for i in range(101*step)], dtype=dt) b = a[::-1] for kind in ['q', 'h', 'm']: msg = "kind=%s" % kind - c = a.copy() + c = a.copy()[::step] + indx = c.argsort(kind=kind) c.sort(kind=kind) - assert_equal(c, a, msg) - c = b.copy() + assert_equal(c, a[::step], msg) + assert_equal(a[::step][indx], a[::step], msg) + c = b.copy()[::step] + indx = c.argsort(kind=kind) c.sort(kind=kind) - assert_equal(c, a, msg) + assert_equal(c, a[step-1::step], msg) + assert_equal(b[::step][indx], a[step-1::step], msg) @pytest.mark.parametrize('dtype', ['datetime64[D]', 'timedelta64[D]']) def test_sort_time(self, dtype): @@ -2354,7 +2383,7 @@ class TestMethods: # test unicode argsorts. s = 'aaaaaaaa' - a = np.array([s + chr(i) for i in range(101)], dtype=np.unicode_) + a = np.array([s + chr(i) for i in range(101)], dtype=np.str_) b = a[::-1] r = np.arange(101) rr = r[::-1] @@ -2437,7 +2466,7 @@ class TestMethods: a = np.array(['aaaaaaaaa' for i in range(100)]) assert_equal(a.argsort(kind='m'), r) # unicode - a = np.array(['aaaaaaaaa' for i in range(100)], dtype=np.unicode_) + a = np.array(['aaaaaaaaa' for i in range(100)], dtype=np.str_) assert_equal(a.argsort(kind='m'), r) def test_sort_unicode_kind(self): @@ -2581,7 +2610,7 @@ class TestMethods: 'P:\\20x_dapi_cy3\\20x_dapi_cy3_20100197_1', 'P:\\20x_dapi_cy3\\20x_dapi_cy3_20100198_1', 'P:\\20x_dapi_cy3\\20x_dapi_cy3_20100199_1'], - dtype=np.unicode_) + dtype=np.str_) ind = np.arange(len(a)) assert_equal([a.searchsorted(v, 'left') for v in a], ind) assert_equal([a.searchsorted(v, 'right') for v in a], ind + 1) @@ -3616,9 +3645,13 @@ class TestMethods: msg = 'dtype: {0}'.format(dt) ap = complex(a) assert_equal(ap, a, msg) - bp = complex(b) + + with assert_warns(DeprecationWarning): + bp = complex(b) assert_equal(bp, b, msg) - cp = complex(c) + + with assert_warns(DeprecationWarning): + cp = complex(c) assert_equal(cp, c, msg) def test__complex__should_not_work(self): @@ -3641,7 +3674,8 @@ class TestMethods: assert_raises(TypeError, complex, d) e = np.array(['1+1j'], 'U') - assert_raises(TypeError, complex, e) + with assert_warns(DeprecationWarning): + assert_raises(TypeError, complex, e) class TestCequenceMethods: def test_array_contains(self): @@ -3703,7 +3737,7 @@ class TestBinop: 'and': (np.bitwise_and, True, int), 'xor': (np.bitwise_xor, True, int), 'or': (np.bitwise_or, True, int), - 'matmul': (np.matmul, False, float), + 'matmul': (np.matmul, True, float), # 'ge': (np.less_equal, False), # 'gt': (np.less, False), # 'le': (np.greater_equal, False), @@ -4721,23 +4755,23 @@ class TestArgmax: a = np.array([1, 2**7 - 1, -2**7], dtype=np.int8) assert_equal(np.argmax(a), 1) - a.repeat(129) - assert_equal(np.argmax(a), 1) + a = a.repeat(129) + assert_equal(np.argmax(a), 129) a = np.array([1, 2**15 - 1, -2**15], dtype=np.int16) assert_equal(np.argmax(a), 1) - a.repeat(129) - assert_equal(np.argmax(a), 1) + a = a.repeat(129) + assert_equal(np.argmax(a), 129) a = np.array([1, 2**31 - 1, -2**31], dtype=np.int32) assert_equal(np.argmax(a), 1) - a.repeat(129) - assert_equal(np.argmax(a), 1) + a = a.repeat(129) + assert_equal(np.argmax(a), 129) a = np.array([1, 2**63 - 1, -2**63], dtype=np.int64) assert_equal(np.argmax(a), 1) - a.repeat(129) - assert_equal(np.argmax(a), 1) + a = a.repeat(129) + assert_equal(np.argmax(a), 129) class TestArgmin: usg_data = [ @@ -4863,23 +4897,23 @@ class TestArgmin: a = np.array([1, -2**7, -2**7 + 1, 2**7 - 1], dtype=np.int8) assert_equal(np.argmin(a), 1) - a.repeat(129) - assert_equal(np.argmin(a), 1) + a = a.repeat(129) + assert_equal(np.argmin(a), 129) a = np.array([1, -2**15, -2**15 + 1, 2**15 - 1], dtype=np.int16) assert_equal(np.argmin(a), 1) - a.repeat(129) - assert_equal(np.argmin(a), 1) + a = a.repeat(129) + assert_equal(np.argmin(a), 129) a = np.array([1, -2**31, -2**31 + 1, 2**31 - 1], dtype=np.int32) assert_equal(np.argmin(a), 1) - a.repeat(129) - assert_equal(np.argmin(a), 1) + a = a.repeat(129) + assert_equal(np.argmin(a), 129) a = np.array([1, -2**63, -2**63 + 1, 2**63 - 1], dtype=np.int64) assert_equal(np.argmin(a), 1) - a.repeat(129) - assert_equal(np.argmin(a), 1) + a = a.repeat(129) + assert_equal(np.argmin(a), 129) class TestMinMax: @@ -5077,6 +5111,22 @@ class TestPutmask: with pytest.raises(ValueError): np.putmask(a, a >= 2, 3) + def test_kwargs(self): + x = np.array([0, 0]) + np.putmask(x, [0, 1], [-1, -2]) + assert_array_equal(x, [0, -2]) + + x = np.array([0, 0]) + np.putmask(x, mask=[0, 1], values=[-1, -2]) + assert_array_equal(x, [0, -2]) + + x = np.array([0, 0]) + np.putmask(x, values=[-1, -2], mask=[0, 1]) + assert_array_equal(x, [0, -2]) + + with pytest.raises(TypeError): + np.putmask(a=x, values=[-1, -2], mask=[0, 1]) + class TestTake: def tst_basic(self, x): @@ -5528,33 +5578,6 @@ class TestIO: tmp_filename, dtype='<f4') - @pytest.mark.slow # takes > 1 minute on mechanical hard drive - def test_big_binary(self): - """Test workarounds for 32-bit limit for MSVC fwrite, fseek, and ftell - - These normally would hang doing something like this. - See : https://github.com/numpy/numpy/issues/2256 - """ - if sys.platform != 'win32' or '[GCC ' in sys.version: - return - try: - # before workarounds, only up to 2**32-1 worked - fourgbplus = 2**32 + 2**16 - testbytes = np.arange(8, dtype=np.int8) - n = len(testbytes) - flike = tempfile.NamedTemporaryFile() - f = flike.file - np.tile(testbytes, fourgbplus // testbytes.nbytes).tofile(f) - flike.seek(0) - a = np.fromfile(f, dtype=np.int8) - flike.close() - assert_(len(a) == fourgbplus) - # check only start and end for speed: - assert_((a[:n] == testbytes).all()) - assert_((a[-n:] == testbytes).all()) - except (MemoryError, ValueError): - pass - def test_string(self, tmp_filename): self._check_from(b'1,2,3,4', [1., 2., 3., 4.], tmp_filename, sep=',') @@ -6658,6 +6681,22 @@ class TestDot: r = np.empty((1024, 32), dtype=int) assert_raises(ValueError, dot, f, v, r) + def test_dot_out_result(self): + x = np.ones((), dtype=np.float16) + y = np.ones((5,), dtype=np.float16) + z = np.zeros((5,), dtype=np.float16) + res = x.dot(y, out=z) + assert np.array_equal(res, y) + assert np.array_equal(z, y) + + def test_dot_out_aliasing(self): + x = np.ones((), dtype=np.float16) + y = np.ones((5,), dtype=np.float16) + z = np.zeros((5,), dtype=np.float16) + res = x.dot(y, out=z) + z[0] = 2 + assert np.array_equal(res, z) + def test_dot_array_order(self): a = np.array([[1, 2], [3, 4]], order='C') b = np.array([[1, 2], [3, 4]], order='F') @@ -7165,16 +7204,69 @@ class TestMatmulOperator(MatmulCommon): assert_raises(TypeError, self.matmul, np.void(b'abc'), np.void(b'abc')) assert_raises(TypeError, self.matmul, np.arange(10), np.void(b'abc')) -def test_matmul_inplace(): - # It would be nice to support in-place matmul eventually, but for now - # we don't have a working implementation, so better just to error out - # and nudge people to writing "a = a @ b". - a = np.eye(3) - b = np.eye(3) - assert_raises(TypeError, a.__imatmul__, b) - import operator - assert_raises(TypeError, operator.imatmul, a, b) - assert_raises(TypeError, exec, "a @= b", globals(), locals()) + +class TestMatmulInplace: + DTYPES = {} + for i in MatmulCommon.types: + for j in MatmulCommon.types: + if np.can_cast(j, i): + DTYPES[f"{i}-{j}"] = (np.dtype(i), np.dtype(j)) + + @pytest.mark.parametrize("dtype1,dtype2", DTYPES.values(), ids=DTYPES) + def test_basic(self, dtype1: np.dtype, dtype2: np.dtype) -> None: + a = np.arange(10).reshape(5, 2).astype(dtype1) + a_id = id(a) + b = np.ones((2, 2), dtype=dtype2) + + ref = a @ b + a @= b + + assert id(a) == a_id + assert a.dtype == dtype1 + assert a.shape == (5, 2) + if dtype1.kind in "fc": + np.testing.assert_allclose(a, ref) + else: + np.testing.assert_array_equal(a, ref) + + SHAPES = { + "2d_large": ((10**5, 10), (10, 10)), + "3d_large": ((10**4, 10, 10), (1, 10, 10)), + "1d": ((3,), (3,)), + "2d_1d": ((3, 3), (3,)), + "1d_2d": ((3,), (3, 3)), + "2d_broadcast": ((3, 3), (3, 1)), + "2d_broadcast_reverse": ((1, 3), (3, 3)), + "3d_broadcast1": ((3, 3, 3), (1, 3, 1)), + "3d_broadcast2": ((3, 3, 3), (1, 3, 3)), + "3d_broadcast3": ((3, 3, 3), (3, 3, 1)), + "3d_broadcast_reverse1": ((1, 3, 3), (3, 3, 3)), + "3d_broadcast_reverse2": ((3, 1, 3), (3, 3, 3)), + "3d_broadcast_reverse3": ((1, 1, 3), (3, 3, 3)), + } + + @pytest.mark.parametrize("a_shape,b_shape", SHAPES.values(), ids=SHAPES) + def test_shapes(self, a_shape: tuple[int, ...], b_shape: tuple[int, ...]): + a_size = np.prod(a_shape) + a = np.arange(a_size).reshape(a_shape).astype(np.float64) + a_id = id(a) + + b_size = np.prod(b_shape) + b = np.arange(b_size).reshape(b_shape) + + ref = a @ b + if ref.shape != a_shape: + with pytest.raises(ValueError): + a @= b + return + else: + a @= b + + assert id(a) == a_id + assert a.dtype.type == np.float64 + assert a.shape == a_shape + np.testing.assert_allclose(a, ref) + def test_matmul_axes(): a = np.arange(3*4*5).reshape(3, 4, 5) @@ -8670,14 +8762,16 @@ class TestConversion: int_funcs = (int, lambda x: x.__int__()) for int_func in int_funcs: assert_equal(int_func(np.array(0)), 0) - assert_equal(int_func(np.array([1])), 1) - assert_equal(int_func(np.array([[42]])), 42) + with assert_warns(DeprecationWarning): + assert_equal(int_func(np.array([1])), 1) + with assert_warns(DeprecationWarning): + assert_equal(int_func(np.array([[42]])), 42) assert_raises(TypeError, int_func, np.array([1, 2])) # gh-9972 assert_equal(4, int_func(np.array('4'))) assert_equal(5, int_func(np.bytes_(b'5'))) - assert_equal(6, int_func(np.unicode_('6'))) + assert_equal(6, int_func(np.str_('6'))) # The delegation of int() to __trunc__ was deprecated in # Python 3.11. @@ -8686,7 +8780,8 @@ class TestConversion: def __trunc__(self): return 3 assert_equal(3, int_func(np.array(HasTrunc()))) - assert_equal(3, int_func(np.array([HasTrunc()]))) + with assert_warns(DeprecationWarning): + assert_equal(3, int_func(np.array([HasTrunc()]))) else: pass @@ -8695,8 +8790,9 @@ class TestConversion: raise NotImplementedError assert_raises(NotImplementedError, int_func, np.array(NotConvertible())) - assert_raises(NotImplementedError, - int_func, np.array([NotConvertible()])) + with assert_warns(DeprecationWarning): + assert_raises(NotImplementedError, + int_func, np.array([NotConvertible()])) class TestWhere: @@ -8863,6 +8959,11 @@ class TestWhere: result = array.nonzero() assert_array_equal(benchmark, result) + def test_kwargs(self): + a = np.zeros(1) + with assert_raises(TypeError): + np.where(a, x=a, y=a) + if not IS_PYPY: # sys.getsizeof() is not valid on PyPy @@ -9044,33 +9145,33 @@ class TestUnicodeEncoding: def test_assign_scalar(self): # gh-3258 l = np.array(['aa', 'bb']) - l[:] = np.unicode_('cc') + l[:] = np.str_('cc') assert_equal(l, ['cc', 'cc']) def test_fill_scalar(self): # gh-7227 l = np.array(['aa', 'bb']) - l.fill(np.unicode_('cc')) + l.fill(np.str_('cc')) assert_equal(l, ['cc', 'cc']) class TestUnicodeArrayNonzero: def test_empty_ustring_array_is_falsey(self): - assert_(not np.array([''], dtype=np.unicode_)) + assert_(not np.array([''], dtype=np.str_)) def test_whitespace_ustring_array_is_falsey(self): - a = np.array(['eggs'], dtype=np.unicode_) + a = np.array(['eggs'], dtype=np.str_) a[0] = ' \0\0' assert_(not a) def test_all_null_ustring_array_is_falsey(self): - a = np.array(['eggs'], dtype=np.unicode_) + a = np.array(['eggs'], dtype=np.str_) a[0] = '\0\0\0\0' assert_(not a) def test_null_inside_ustring_array_is_truthy(self): - a = np.array(['eggs'], dtype=np.unicode_) + a = np.array(['eggs'], dtype=np.str_) a[0] = ' \0 \0' assert_(a) @@ -9505,7 +9606,7 @@ def test_equal_override(): @pytest.mark.parametrize("op", [operator.eq, operator.ne]) @pytest.mark.parametrize(["dt1", "dt2"], [ - ([("f", "i")], [("f", "i")]), # structured comparison (successfull) + ([("f", "i")], [("f", "i")]), # structured comparison (successful) ("M8", "d"), # impossible comparison: result is all True or False ("d", "d"), # valid comparison ]) @@ -9828,40 +9929,50 @@ class TestViewDtype: assert_array_equal(x.view('<i2'), expected) +@pytest.mark.xfail(_SUPPORTS_SVE, reason="gh-22982") # Test various array sizes that hit different code paths in quicksort-avx512 -@pytest.mark.parametrize("N", [8, 16, 24, 32, 48, 64, 96, 128, 151, 191, - 256, 383, 512, 1023, 2047]) -def test_sort_float(N): +@pytest.mark.parametrize("N", np.arange(1, 512)) +@pytest.mark.parametrize("dtype", ['e', 'f', 'd']) +def test_sort_float(N, dtype): # Regular data with nan sprinkled np.random.seed(42) - arr = -0.5 + np.random.sample(N).astype('f') + arr = -0.5 + np.random.sample(N).astype(dtype) arr[np.random.choice(arr.shape[0], 3)] = np.nan assert_equal(np.sort(arr, kind='quick'), np.sort(arr, kind='heap')) # (2) with +INF - infarr = np.inf*np.ones(N, dtype='f') + infarr = np.inf*np.ones(N, dtype=dtype) infarr[np.random.choice(infarr.shape[0], 5)] = -1.0 assert_equal(np.sort(infarr, kind='quick'), np.sort(infarr, kind='heap')) # (3) with -INF - neginfarr = -np.inf*np.ones(N, dtype='f') + neginfarr = -np.inf*np.ones(N, dtype=dtype) neginfarr[np.random.choice(neginfarr.shape[0], 5)] = 1.0 assert_equal(np.sort(neginfarr, kind='quick'), np.sort(neginfarr, kind='heap')) # (4) with +/-INF - infarr = np.inf*np.ones(N, dtype='f') + infarr = np.inf*np.ones(N, dtype=dtype) infarr[np.random.choice(infarr.shape[0], (int)(N/2))] = -np.inf assert_equal(np.sort(infarr, kind='quick'), np.sort(infarr, kind='heap')) - -def test_sort_int(): - # Random data with NPY_MAX_INT32 and NPY_MIN_INT32 sprinkled - rng = np.random.default_rng(42) - N = 2047 - minv = np.iinfo(np.int32).min - maxv = np.iinfo(np.int32).max - arr = rng.integers(low=minv, high=maxv, size=N).astype('int32') +def test_sort_float16(): + arr = np.arange(65536, dtype=np.int16) + temp = np.frombuffer(arr.tobytes(), dtype=np.float16) + data = np.copy(temp) + np.random.shuffle(data) + data_backup = data + assert_equal(np.sort(data, kind='quick'), + np.sort(data_backup, kind='heap')) + + +@pytest.mark.parametrize("N", np.arange(1, 512)) +@pytest.mark.parametrize("dtype", ['h', 'H', 'i', 'I', 'l', 'L']) +def test_sort_int(N, dtype): + # Random data with MAX and MIN sprinkled + minv = np.iinfo(dtype).min + maxv = np.iinfo(dtype).max + arr = np.random.randint(low=minv, high=maxv-1, size=N, dtype=dtype) arr[np.random.choice(arr.shape[0], 10)] = minv arr[np.random.choice(arr.shape[0], 10)] = maxv assert_equal(np.sort(arr, kind='quick'), np.sort(arr, kind='heap')) @@ -9875,3 +9986,6 @@ def test_sort_uint(): arr = rng.integers(low=0, high=maxv, size=N).astype('uint32') arr[np.random.choice(arr.shape[0], 10)] = maxv assert_equal(np.sort(arr, kind='quick'), np.sort(arr, kind='heap')) + +def test_private_get_ndarray_c_version(): + assert isinstance(_get_ndarray_c_version(), int) diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py index b88afdfa1..9f639c4c1 100644 --- a/numpy/core/tests/test_nditer.py +++ b/numpy/core/tests/test_nditer.py @@ -1457,6 +1457,38 @@ def test_iter_copy_casts_structured(): assert_array_equal(res2["b"][field], expected) +def test_iter_copy_casts_structured2(): + # Similar to the above, this is a fairly arcane test to cover internals + in_dtype = np.dtype([("a", np.dtype("O,O")), + ("b", np.dtype("(5)O,(3)O,(1,)O,(1,)i,(1,)O"))]) + out_dtype = np.dtype([("a", np.dtype("O")), + ("b", np.dtype("O,(3)i,(4)O,(4)O,(4)i"))]) + + arr = np.ones(1, dtype=in_dtype) + it = np.nditer((arr,), ["buffered", "external_loop", "refs_ok"], + op_dtypes=[out_dtype], casting="unsafe") + it_copy = it.copy() + + res1 = next(it) + del it + res2 = next(it_copy) + del it_copy + + # Array of two structured scalars: + for res in res1, res2: + # Cast to tuple by getitem, which may be weird and changable?: + assert type(res["a"][0]) == tuple + assert res["a"][0] == (1, 1) + + for res in res1, res2: + assert_array_equal(res["b"]["f0"][0], np.ones(5, dtype=object)) + assert_array_equal(res["b"]["f1"], np.ones((1, 3), dtype="i")) + assert res["b"]["f2"].shape == (1, 4) + assert_array_equal(res["b"]["f2"][0], np.ones(4, dtype=object)) + assert_array_equal(res["b"]["f3"][0], np.ones(4, dtype=object)) + assert_array_equal(res["b"]["f3"][0], np.ones(4, dtype="i")) + + def test_iter_allocate_output_simple(): # Check that the iterator will properly allocate outputs @@ -2257,7 +2289,7 @@ def test_iter_buffering_string(): assert_equal(i[0], b'abc') assert_equal(i[0].dtype, np.dtype('S6')) - a = np.array(['abc', 'a', 'abcd'], dtype=np.unicode_) + a = np.array(['abc', 'a', 'abcd'], dtype=np.str_) assert_equal(a.dtype, np.dtype('U4')) assert_raises(TypeError, nditer, a, ['buffered'], ['readonly'], op_dtypes='U2') diff --git a/numpy/core/tests/test_nep50_promotions.py b/numpy/core/tests/test_nep50_promotions.py index 3c0316960..7d52c5089 100644 --- a/numpy/core/tests/test_nep50_promotions.py +++ b/numpy/core/tests/test_nep50_promotions.py @@ -131,7 +131,7 @@ def test_nep50_weak_integers_with_inexact(dtype): @pytest.mark.parametrize("op", [operator.add, operator.pow, operator.eq]) def test_weak_promotion_scalar_path(op): - # Some additional paths excercising the weak scalars. + # Some additional paths exercising the weak scalars. np._set_promotion_state("weak") # Integer path: @@ -180,3 +180,55 @@ def test_nep50_integer_regression(): arr = np.array(1) assert (arr + 2**63).dtype == np.float64 assert (arr[()] + 2**63).dtype == np.float64 + + +def test_nep50_with_axisconcatenator(): + # I promised that this will be an error in the future in the 1.25 + # release notes; test this (NEP 50 opt-in makes the deprecation an error). + np._set_promotion_state("weak") + + with pytest.raises(OverflowError): + np.r_[np.arange(5, dtype=np.int8), 255] + + +@pytest.mark.parametrize("ufunc", [np.add, np.power]) +@pytest.mark.parametrize("state", ["weak", "weak_and_warn"]) +def test_nep50_huge_integers(ufunc, state): + # Very large integers are complicated, because they go to uint64 or + # object dtype. This tests covers a few possible paths (some of which + # cannot give the NEP 50 warnings). + np._set_promotion_state(state) + + with pytest.raises(OverflowError): + ufunc(np.int64(0), 2**63) # 2**63 too large for int64 + + if state == "weak_and_warn": + with pytest.warns(UserWarning, + match="result dtype changed.*float64.*uint64"): + with pytest.raises(OverflowError): + ufunc(np.uint64(0), 2**64) + else: + with pytest.raises(OverflowError): + ufunc(np.uint64(0), 2**64) # 2**64 cannot be represented by uint64 + + # However, 2**63 can be represented by the uint64 (and that is used): + if state == "weak_and_warn": + with pytest.warns(UserWarning, + match="result dtype changed.*float64.*uint64"): + res = ufunc(np.uint64(1), 2**63) + else: + res = ufunc(np.uint64(1), 2**63) + + assert res.dtype == np.uint64 + assert res == ufunc(1, 2**63, dtype=object) + + # The following paths fail to warn correctly about the change: + with pytest.raises(OverflowError): + ufunc(np.int64(1), 2**63) # np.array(2**63) would go to uint + + with pytest.raises(OverflowError): + ufunc(np.int64(1), 2**100) # np.array(2**100) would go to object + + # This would go to object and thus a Python float, not a NumPy one: + res = ufunc(1.0, 2**100) + assert isinstance(res, np.float64) diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 3cc168b34..832a47c92 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -114,7 +114,9 @@ class TestNonarrayArgs: def test_cumproduct(self): A = [[1, 2, 3], [4, 5, 6]] - assert_(np.all(np.cumproduct(A) == np.array([1, 2, 6, 24, 120, 720]))) + with assert_warns(DeprecationWarning): + expected = np.array([1, 2, 6, 24, 120, 720]) + assert_(np.all(np.cumproduct(A) == expected)) def test_diagonal(self): a = [[0, 1, 2, 3], @@ -1193,8 +1195,8 @@ class TestFromiter: expected = np.array(list(self.makegen())) a = np.fromiter(self.makegen(), int) a20 = np.fromiter(self.makegen(), int, 20) - assert_(np.alltrue(a == expected, axis=0)) - assert_(np.alltrue(a20 == expected[:20], axis=0)) + assert_(np.all(a == expected, axis=0)) + assert_(np.all(a20 == expected[:20], axis=0)) def load_data(self, n, eindex): # Utility method for the issue 2592 tests. @@ -1822,20 +1824,11 @@ class TestClip: self.nr = 5 self.nc = 3 - def fastclip(self, a, m, M, out=None, casting=None): - if out is None: - if casting is None: - return a.clip(m, M) - else: - return a.clip(m, M, casting=casting) - else: - if casting is None: - return a.clip(m, M, out) - else: - return a.clip(m, M, out, casting=casting) + def fastclip(self, a, m, M, out=None, **kwargs): + return a.clip(m, M, out=out, **kwargs) def clip(self, a, m, M, out=None): - # use slow-clip + # use a.choose to verify fastclip result selector = np.less(a, m) + 2*np.greater(a, M) return selector.choose((a, m, M), out=out) @@ -1991,14 +1984,13 @@ class TestClip: ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() if casting is None: - with assert_warns(DeprecationWarning): - # NumPy 1.17.0, 2018-02-24 - casting is unsafe + with pytest.raises(TypeError): self.fastclip(a, m, M, ac, casting=casting) else: # explicitly passing "unsafe" will silence warning self.fastclip(a, m, M, ac, casting=casting) - self.clip(a, m, M, act) - assert_array_strict_equal(ac, act) + self.clip(a, m, M, act) + assert_array_strict_equal(ac, act) def test_simple_int64_out(self): # Test native int32 input with int32 scalar min/max and int64 out. @@ -2018,9 +2010,7 @@ class TestClip: M = np.float64(1) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - with assert_warns(DeprecationWarning): - # NumPy 1.17.0, 2018-02-24 - casting is unsafe - self.fastclip(a, m, M, ac) + self.fastclip(a, m, M, out=ac, casting="unsafe") self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -2031,9 +2021,7 @@ class TestClip: M = 2.0 ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - with assert_warns(DeprecationWarning): - # NumPy 1.17.0, 2018-02-24 - casting is unsafe - self.fastclip(a, m, M, ac) + self.fastclip(a, m, M, out=ac, casting="unsafe") self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -2209,9 +2197,7 @@ class TestClip: M = np.float64(2) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - with assert_warns(DeprecationWarning): - # NumPy 1.17.0, 2018-02-24 - casting is unsafe - self.fastclip(a, m, M, ac) + self.fastclip(a, m, M, out=ac, casting="unsafe") self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -2233,9 +2219,7 @@ class TestClip: M = np.float64(1) ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - with assert_warns(DeprecationWarning): - # NumPy 1.17.0, 2018-02-24 - casting is unsafe - self.fastclip(a, m, M, ac) + self.fastclip(a, m, M, out=ac, casting="unsafe") self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -2246,9 +2230,7 @@ class TestClip: M = 2.0 ac = np.zeros(a.shape, dtype=np.int32) act = ac.copy() - with assert_warns(DeprecationWarning): - # NumPy 1.17.0, 2018-02-24 - casting is unsafe - self.fastclip(a, m, M, ac) + self.fastclip(a, m, M, out=ac, casting="unsafe") self.clip(a, m, M, act) assert_array_strict_equal(ac, act) @@ -2301,16 +2283,11 @@ class TestClip: def test_clip_nan(self): d = np.arange(7.) - with assert_warns(DeprecationWarning): - assert_equal(d.clip(min=np.nan), d) - with assert_warns(DeprecationWarning): - assert_equal(d.clip(max=np.nan), d) - with assert_warns(DeprecationWarning): - assert_equal(d.clip(min=np.nan, max=np.nan), d) - with assert_warns(DeprecationWarning): - assert_equal(d.clip(min=-2, max=np.nan), d) - with assert_warns(DeprecationWarning): - assert_equal(d.clip(min=np.nan, max=10), d) + assert_equal(d.clip(min=np.nan), np.nan) + assert_equal(d.clip(max=np.nan), np.nan) + assert_equal(d.clip(min=np.nan, max=np.nan), np.nan) + assert_equal(d.clip(min=-2, max=np.nan), np.nan) + assert_equal(d.clip(min=np.nan, max=10), np.nan) def test_object_clip(self): a = np.arange(10, dtype=object) @@ -2362,16 +2339,12 @@ class TestClip: actual = np.clip(arr, amin, amax) assert_equal(actual, exp) - @pytest.mark.xfail(reason="no scalar nan propagation yet", - raises=AssertionError, - strict=True) @pytest.mark.parametrize("arr, amin, amax", [ # problematic scalar nan case from hypothesis (np.zeros(10, dtype=np.int64), np.array(np.nan), np.zeros(10, dtype=np.int32)), ]) - @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_clip_scalar_nan_propagation(self, arr, amin, amax): # enforcement of scalar nan propagation for comparisons # called through clip() diff --git a/numpy/core/tests/test_numerictypes.py b/numpy/core/tests/test_numerictypes.py index 072cd65fe..bab5bf246 100644 --- a/numpy/core/tests/test_numerictypes.py +++ b/numpy/core/tests/test_numerictypes.py @@ -339,23 +339,28 @@ class TestEmptyField: class TestCommonType: def test_scalar_loses1(self): - res = np.find_common_type(['f4', 'f4', 'i2'], ['f8']) + with pytest.warns(DeprecationWarning, match="np.find_common_type"): + res = np.find_common_type(['f4', 'f4', 'i2'], ['f8']) assert_(res == 'f4') def test_scalar_loses2(self): - res = np.find_common_type(['f4', 'f4'], ['i8']) + with pytest.warns(DeprecationWarning, match="np.find_common_type"): + res = np.find_common_type(['f4', 'f4'], ['i8']) assert_(res == 'f4') def test_scalar_wins(self): - res = np.find_common_type(['f4', 'f4', 'i2'], ['c8']) + with pytest.warns(DeprecationWarning, match="np.find_common_type"): + res = np.find_common_type(['f4', 'f4', 'i2'], ['c8']) assert_(res == 'c8') def test_scalar_wins2(self): - res = np.find_common_type(['u4', 'i4', 'i4'], ['f4']) + with pytest.warns(DeprecationWarning, match="np.find_common_type"): + res = np.find_common_type(['u4', 'i4', 'i4'], ['f4']) assert_(res == 'f8') def test_scalar_wins3(self): # doesn't go up to 'f16' on purpose - res = np.find_common_type(['u8', 'i8', 'i8'], ['f8']) + with pytest.warns(DeprecationWarning, match="np.find_common_type"): + res = np.find_common_type(['u8', 'i8', 'i8'], ['f8']) assert_(res == 'f8') class TestMultipleFields: @@ -473,7 +478,8 @@ class TestMaximumSctype: def test_complex(self, t): assert_equal(np.maximum_sctype(t), np.sctypes['complex'][-1]) - @pytest.mark.parametrize('t', [np.bool_, np.object_, np.unicode_, np.bytes_, np.void]) + @pytest.mark.parametrize('t', [np.bool_, np.object_, np.str_, np.bytes_, + np.void]) def test_other(self, t): assert_equal(np.maximum_sctype(t), t) @@ -485,7 +491,7 @@ class Test_sctype2char: def test_scalar_type(self): assert_equal(np.sctype2char(np.double), 'd') assert_equal(np.sctype2char(np.int_), 'l') - assert_equal(np.sctype2char(np.unicode_), 'U') + assert_equal(np.sctype2char(np.str_), 'U') assert_equal(np.sctype2char(np.bytes_), 'S') def test_other_type(self): diff --git a/numpy/core/tests/test_overrides.py b/numpy/core/tests/test_overrides.py index 63432ffa7..5924358ea 100644 --- a/numpy/core/tests/test_overrides.py +++ b/numpy/core/tests/test_overrides.py @@ -10,16 +10,11 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_raises_regex) from numpy.core.overrides import ( _get_implementing_args, array_function_dispatch, - verify_matching_signatures, ARRAY_FUNCTION_ENABLED) + verify_matching_signatures) from numpy.compat import pickle import pytest -requires_array_function = pytest.mark.skipif( - not ARRAY_FUNCTION_ENABLED, - reason="__array_function__ dispatch not enabled.") - - def _return_not_implemented(self, *args, **kwargs): return NotImplemented @@ -150,7 +145,6 @@ class TestGetImplementingArgs: class TestNDArrayArrayFunction: - @requires_array_function def test_method(self): class Other: @@ -209,7 +203,6 @@ class TestNDArrayArrayFunction: args=(array,), kwargs={}) -@requires_array_function class TestArrayFunctionDispatch: def test_pickle(self): @@ -248,8 +241,20 @@ class TestArrayFunctionDispatch: with assert_raises_regex(TypeError, 'no implementation found'): dispatched_one_arg(array) + def test_where_dispatch(self): + + class DuckArray: + def __array_function__(self, ufunc, method, *inputs, **kwargs): + return "overridden" + + array = np.array(1) + duck_array = DuckArray() + + result = np.std(array, where=duck_array) + + assert_equal(result, "overridden") + -@requires_array_function class TestVerifyMatchingSignatures: def test_verify_matching_signatures(self): @@ -302,7 +307,6 @@ def _new_duck_type_and_implements(): return (MyArray, implements) -@requires_array_function class TestArrayFunctionImplementation: def test_one_arg(self): @@ -355,6 +359,17 @@ class TestArrayFunctionImplementation: TypeError, "no implementation found for 'my.func'"): func(MyArray()) + @pytest.mark.parametrize("name", ["concatenate", "mean", "asarray"]) + def test_signature_error_message_simple(self, name): + func = getattr(np, name) + try: + # all of these functions need an argument: + func() + except TypeError as e: + exc = e + + assert exc.args[0].startswith(f"{name}()") + def test_signature_error_message(self): # The lambda function will be named "<lambda>", but the TypeError # should show the name as "func" @@ -366,7 +381,7 @@ class TestArrayFunctionImplementation: pass try: - func(bad_arg=3) + func._implementation(bad_arg=3) except TypeError as e: expected_exception = e @@ -374,6 +389,12 @@ class TestArrayFunctionImplementation: func(bad_arg=3) raise AssertionError("must fail") except TypeError as exc: + if exc.args[0].startswith("_dispatcher"): + # We replace the qualname currently, but it used `__name__` + # (relevant functions have the same name and qualname anyway) + pytest.skip("Python version is not using __qualname__ for " + "TypeError formatting.") + assert exc.args == expected_exception.args @pytest.mark.parametrize("value", [234, "this func is not replaced"]) @@ -394,6 +415,56 @@ class TestArrayFunctionImplementation: except TypeError as exc: assert exc is error # unmodified exception + def test_properties(self): + # Check that str and repr are sensible + func = dispatched_two_arg + assert str(func) == str(func._implementation) + repr_no_id = repr(func).split("at ")[0] + repr_no_id_impl = repr(func._implementation).split("at ")[0] + assert repr_no_id == repr_no_id_impl + + @pytest.mark.parametrize("func", [ + lambda x, y: 0, # no like argument + lambda like=None: 0, # not keyword only + lambda *, like=None, a=3: 0, # not last (not that it matters) + ]) + def test_bad_like_sig(self, func): + # We sanity check the signature, and these should fail. + with pytest.raises(RuntimeError): + array_function_dispatch()(func) + + def test_bad_like_passing(self): + # Cover internal sanity check for passing like as first positional arg + def func(*, like=None): + pass + + func_with_like = array_function_dispatch()(func) + with pytest.raises(TypeError): + func_with_like() + with pytest.raises(TypeError): + func_with_like(like=234) + + def test_too_many_args(self): + # Mainly a unit-test to increase coverage + objs = [] + for i in range(40): + class MyArr: + def __array_function__(self, *args, **kwargs): + return NotImplemented + + objs.append(MyArr()) + + def _dispatch(*args): + return args + + @array_function_dispatch(_dispatch) + def func(*args): + pass + + with pytest.raises(TypeError, match="maximum number"): + func(*objs) + + class TestNDArrayMethods: @@ -422,7 +493,6 @@ class TestNumPyFunctions: signature = inspect.signature(np.sum) assert_('axis' in signature.parameters) - @requires_array_function def test_override_sum(self): MyArray, implements = _new_duck_type_and_implements() @@ -432,7 +502,6 @@ class TestNumPyFunctions: assert_equal(np.sum(MyArray()), 'yes') - @requires_array_function def test_sum_on_mock_array(self): # We need a proxy for mocks because __array_function__ is only looked @@ -453,7 +522,6 @@ class TestNumPyFunctions: np.sum, (ArrayProxy,), (proxy,), {}) proxy.value.__array__.assert_not_called() - @requires_array_function def test_sum_forwarding_implementation(self): class MyArray(np.ndarray): @@ -505,7 +573,6 @@ class TestArrayLike: def func_args(*args, **kwargs): return args, kwargs - @requires_array_function def test_array_like_not_implemented(self): self.add_method('array', self.MyArray) @@ -538,7 +605,6 @@ class TestArrayLike: @pytest.mark.parametrize('function, args, kwargs', _array_tests) @pytest.mark.parametrize('numpy_ref', [True, False]) - @requires_array_function def test_array_like(self, function, args, kwargs, numpy_ref): self.add_method('array', self.MyArray) self.add_method(function, self.MyArray) @@ -571,7 +637,6 @@ class TestArrayLike: @pytest.mark.parametrize('function, args, kwargs', _array_tests) @pytest.mark.parametrize('ref', [1, [1], "MyNoArrayFunctionArray"]) - @requires_array_function def test_no_array_function_like(self, function, args, kwargs, ref): self.add_method('array', self.MyNoArrayFunctionArray) self.add_method(function, self.MyNoArrayFunctionArray) @@ -613,7 +678,6 @@ class TestArrayLike: assert type(array_like) is self.MyArray assert array_like.function is self.MyArray.fromfile - @requires_array_function def test_exception_handling(self): self.add_method('array', self.MyArray, enable_value_error=True) @@ -640,3 +704,56 @@ class TestArrayLike: array_like.fill(1) expected.fill(1) assert_equal(array_like, expected) + + +def test_function_like(): + # We provide a `__get__` implementation, make sure it works + assert type(np.mean) is np.core._multiarray_umath._ArrayFunctionDispatcher + + class MyClass: + def __array__(self): + # valid argument to mean: + return np.arange(3) + + func1 = staticmethod(np.mean) + func2 = np.mean + func3 = classmethod(np.mean) + + m = MyClass() + assert m.func1([10]) == 10 + assert m.func2() == 1 # mean of the arange + with pytest.raises(TypeError, match="unsupported operand type"): + # Tries to operate on the class + m.func3() + + # Manual binding also works (the above may shortcut): + bound = np.mean.__get__(m, MyClass) + assert bound() == 1 + + bound = np.mean.__get__(None, MyClass) # unbound actually + assert bound([10]) == 10 + + bound = np.mean.__get__(MyClass) # classmethod + with pytest.raises(TypeError, match="unsupported operand type"): + bound() + + +def test_scipy_trapz_support_shim(): + # SciPy 1.10 and earlier "clone" trapz in this way, so we have a + # support shim in place: https://github.com/scipy/scipy/issues/17811 + # That should be removed eventually. This test copies what SciPy does. + # Hopefully removable 1 year after SciPy 1.11; shim added to NumPy 1.25. + import types + import functools + + def _copy_func(f): + # Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard) + g = types.FunctionType(f.__code__, f.__globals__, name=f.__name__, + argdefs=f.__defaults__, closure=f.__closure__) + g = functools.update_wrapper(g, f) + g.__kwdefaults__ = f.__kwdefaults__ + return g + + trapezoid = _copy_func(np.trapz) + + assert np.trapz([1, 2]) == trapezoid([1, 2]) diff --git a/numpy/core/tests/test_print.py b/numpy/core/tests/test_print.py index 89a8b48bf..162686ee0 100644 --- a/numpy/core/tests/test_print.py +++ b/numpy/core/tests/test_print.py @@ -3,7 +3,7 @@ import sys import pytest import numpy as np -from numpy.testing import assert_, assert_equal +from numpy.testing import assert_, assert_equal, IS_MUSL from numpy.core.tests._locales import CommaDecimalPointLocale @@ -196,5 +196,7 @@ class TestCommaDecimalPointLocale(CommaDecimalPointLocale): def test_locale_double(self): assert_equal(str(np.double(1.2)), str(float(1.2))) + @pytest.mark.skipif(IS_MUSL, + reason="test flaky on musllinux") def test_locale_longdouble(self): assert_equal(str(np.longdouble('1.2')), str(float(1.2))) diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index f638284de..841144790 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -292,7 +292,7 @@ class TestRegression: def test_unicode_string_comparison(self): # Ticket #190 - a = np.array('hello', np.unicode_) + a = np.array('hello', np.str_) b = np.array('world') a == b @@ -455,7 +455,7 @@ class TestRegression: test_data = [ # (original, py2_pickle) - (np.unicode_('\u6f2c'), + (np.str_('\u6f2c'), b"cnumpy.core.multiarray\nscalar\np0\n(cnumpy\ndtype\np1\n" b"(S'U1'\np2\nI0\nI1\ntp3\nRp4\n(I3\nS'<'\np5\nNNNI4\nI4\n" b"I0\ntp6\nbS',o\\x00\\x00'\np7\ntp8\nRp9\n."), @@ -516,22 +516,15 @@ class TestRegression: def test_method_args(self): # Make sure methods and functions have same default axis # keyword and arguments - funcs1 = ['argmax', 'argmin', 'sum', ('product', 'prod'), - ('sometrue', 'any'), - ('alltrue', 'all'), 'cumsum', ('cumproduct', 'cumprod'), - 'ptp', 'cumprod', 'prod', 'std', 'var', 'mean', - 'round', 'min', 'max', 'argsort', 'sort'] + funcs1 = ['argmax', 'argmin', 'sum', 'any', 'all', 'cumsum', + 'ptp', 'cumprod', 'prod', 'std', 'var', 'mean', + 'round', 'min', 'max', 'argsort', 'sort'] funcs2 = ['compress', 'take', 'repeat'] for func in funcs1: arr = np.random.rand(8, 7) arr2 = arr.copy() - if isinstance(func, tuple): - func_meth = func[1] - func = func[0] - else: - func_meth = func - res1 = getattr(arr, func_meth)() + res1 = getattr(arr, func)() res2 = getattr(np, func)(arr2) if res1 is None: res1 = arr @@ -1336,8 +1329,8 @@ class TestRegression: # Ticket #1058 a = np.fromiter(list(range(10)), dtype='b') b = np.fromiter(list(range(10)), dtype='B') - assert_(np.alltrue(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) - assert_(np.alltrue(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) + assert_(np.all(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) + assert_(np.all(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) def test_array_from_sequence_scalar_array(self): # Ticket #1078: segfaults when creating an array with a sequence of @@ -1515,8 +1508,8 @@ class TestRegression: def test_fromiter_comparison(self): a = np.fromiter(list(range(10)), dtype='b') b = np.fromiter(list(range(10)), dtype='B') - assert_(np.alltrue(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) - assert_(np.alltrue(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) + assert_(np.all(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) + assert_(np.all(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))) def test_fromstring_crash(self): # Ticket #1345: the following should not cause a crash @@ -1535,9 +1528,12 @@ class TestRegression: for y in dtypes: c = a.astype(y) try: - np.dot(b, c) + d = np.dot(b, c) except TypeError: failures.append((x, y)) + else: + if d != 0: + failures.append((x, y)) if failures: raise AssertionError("Failures: %r" % failures) @@ -1671,7 +1667,9 @@ class TestRegression: def test_find_common_type_boolean(self): # Ticket #1695 - assert_(np.find_common_type([], ['?', '?']) == '?') + with pytest.warns(DeprecationWarning, match="np.find_common_type"): + res = np.find_common_type([], ['?', '?']) + assert res == '?' def test_empty_mul(self): a = np.array([1.]) @@ -1685,7 +1683,7 @@ class TestRegression: # number 2, and the exception hung around until something checked # PyErr_Occurred() and returned an error. assert_equal(np.dtype('S10').itemsize, 10) - np.array([['abc', 2], ['long ', '0123456789']], dtype=np.string_) + np.array([['abc', 2], ['long ', '0123456789']], dtype=np.bytes_) assert_equal(np.dtype('S10').itemsize, 10) def test_any_float(self): @@ -1954,7 +1952,7 @@ class TestRegression: # Python2 output for pickle.dumps(...) datas = [ # (original, python2_pickle, koi8r_validity) - (np.unicode_('\u6bd2'), + (np.str_('\u6bd2'), (b"cnumpy.core.multiarray\nscalar\np0\n(cnumpy\ndtype\np1\n" b"(S'U1'\np2\nI0\nI1\ntp3\nRp4\n(I3\nS'<'\np5\nNNNI4\nI4\nI0\n" b"tp6\nbS'\\xd2k\\x00\\x00'\np7\ntp8\nRp9\n."), @@ -2078,7 +2076,7 @@ class TestRegression: # Ticket #1578, the mismatch only showed up when running # python-debug for python versions >= 2.7, and then as # a core dump and error message. - a = np.array(['abc'], dtype=np.unicode_)[0] + a = np.array(['abc'], dtype=np.str_)[0] del a def test_refcount_error_in_clip(self): @@ -2225,7 +2223,7 @@ class TestRegression: def test_pickle_empty_string(self): # gh-3926 for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): - test_string = np.string_('') + test_string = np.bytes_('') assert_equal(pickle.loads( pickle.dumps(test_string, protocol=proto)), test_string) @@ -2346,7 +2344,7 @@ class TestRegression: values = { np.void: b"a", np.bytes_: b"a", - np.unicode_: "a", + np.str_: "a", np.datetime64: "2017-08-25", } for sctype in scalar_types: @@ -2553,3 +2551,14 @@ class TestRegression: f"Unexpected types order of ufunc in {operation}" f"for {order}. Possible fix: Use signed before unsigned" "in generate_umath.py") + + def test_nonbool_logical(self): + # gh-22845 + # create two arrays with bit patterns that do not overlap. + # needs to be large enough to test both SIMD and scalar paths + size = 100 + a = np.frombuffer(b'\x01' * size, dtype=np.bool_) + b = np.frombuffer(b'\x80' * size, dtype=np.bool_) + expected = np.ones(size, dtype=np.bool_) + assert_array_equal(np.logical_and(a, b), expected) + diff --git a/numpy/core/tests/test_scalar_methods.py b/numpy/core/tests/test_scalar_methods.py index a53e47b19..18a7bc828 100644 --- a/numpy/core/tests/test_scalar_methods.py +++ b/numpy/core/tests/test_scalar_methods.py @@ -1,7 +1,6 @@ """ Test the scalar constructors, which also do type-coercion """ -import sys import fractions import platform import types @@ -10,7 +9,7 @@ from typing import Any, Type import pytest import numpy as np -from numpy.testing import assert_equal, assert_raises +from numpy.testing import assert_equal, assert_raises, IS_MUSL class TestAsIntegerRatio: @@ -99,6 +98,8 @@ class TestAsIntegerRatio: try: nf = np.longdouble(n) df = np.longdouble(d) + if not np.isfinite(df): + raise OverflowError except (OverflowError, RuntimeWarning): # the values may not fit in any float type pytest.skip("longdouble too small on this platform") @@ -132,7 +133,6 @@ class TestIsInteger: assert not value.is_integer() -@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires python 3.9") class TestClassGetItem: @pytest.mark.parametrize("cls", [ np.number, @@ -186,14 +186,6 @@ class TestClassGetItem: assert np.number[Any] -@pytest.mark.skipif(sys.version_info >= (3, 9), reason="Requires python 3.8") -@pytest.mark.parametrize("cls", [np.number, np.complexfloating, np.int64]) -def test_class_getitem_38(cls: Type[np.number]) -> None: - match = "Type subscription requires python >= 3.9" - with pytest.raises(TypeError, match=match): - cls[Any] - - class TestBitCount: # derived in part from the cpython test "test_bit_count" diff --git a/numpy/core/tests/test_scalarbuffer.py b/numpy/core/tests/test_scalarbuffer.py index 0e6ab1015..31b0494cf 100644 --- a/numpy/core/tests/test_scalarbuffer.py +++ b/numpy/core/tests/test_scalarbuffer.py @@ -68,11 +68,11 @@ class TestScalarPEP3118: get_buffer_info(x, ["WRITABLE"]) def test_void_scalar_structured_data(self): - dt = np.dtype([('name', np.unicode_, 16), ('grades', np.float64, (2,))]) + dt = np.dtype([('name', np.str_, 16), ('grades', np.float64, (2,))]) x = np.array(('ndarray_scalar', (1.2, 3.0)), dtype=dt)[()] assert_(isinstance(x, np.void)) mv_x = memoryview(x) - expected_size = 16 * np.dtype((np.unicode_, 1)).itemsize + expected_size = 16 * np.dtype((np.str_, 1)).itemsize expected_size += 2 * np.dtype(np.float64).itemsize assert_equal(mv_x.itemsize, expected_size) assert_equal(mv_x.ndim, 0) diff --git a/numpy/core/tests/test_scalarinherit.py b/numpy/core/tests/test_scalarinherit.py index f697c3c81..f9c574d57 100644 --- a/numpy/core/tests/test_scalarinherit.py +++ b/numpy/core/tests/test_scalarinherit.py @@ -58,8 +58,8 @@ class TestInherit: class TestCharacter: def test_char_radd(self): # GH issue 9620, reached gentype_add and raise TypeError - np_s = np.string_('abc') - np_u = np.unicode_('abc') + np_s = np.bytes_('abc') + np_u = np.str_('abc') s = b'def' u = 'def' assert_(np_s.__radd__(np_s) is NotImplemented) @@ -90,8 +90,8 @@ class TestCharacter: assert ret == b"defabc" def test_char_repeat(self): - np_s = np.string_('abc') - np_u = np.unicode_('abc') + np_s = np.bytes_('abc') + np_u = np.str_('abc') res_s = b'abc' * 5 res_u = 'abc' * 5 assert_(np_s * 5 == res_s) diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py index 8de821340..c737099c1 100644 --- a/numpy/core/tests/test_scalarmath.py +++ b/numpy/core/tests/test_scalarmath.py @@ -14,7 +14,7 @@ import numpy as np from numpy.testing import ( assert_, assert_equal, assert_raises, assert_almost_equal, assert_array_equal, IS_PYPY, suppress_warnings, _gen_alignment_data, - assert_warns, + assert_warns, _SUPPORTS_SVE, ) types = [np.bool_, np.byte, np.ubyte, np.short, np.ushort, np.intc, np.uintc, @@ -75,17 +75,7 @@ class TestTypes: np.add(1, 1) -@pytest.mark.slow -@settings(max_examples=10000, deadline=2000) -@given(sampled_from(reasonable_operators_for_scalars), - hynp.arrays(dtype=hynp.scalar_dtypes(), shape=()), - hynp.arrays(dtype=hynp.scalar_dtypes(), shape=())) -def test_array_scalar_ufunc_equivalence(op, arr1, arr2): - """ - This is a thorough test attempting to cover important promotion paths - and ensuring that arrays and scalars stay as aligned as possible. - However, if it creates troubles, it should maybe just be removed. - """ +def check_ufunc_scalar_equivalence(op, arr1, arr2): scalar1 = arr1[()] scalar2 = arr2[()] assert isinstance(scalar1, np.generic) @@ -95,6 +85,11 @@ def test_array_scalar_ufunc_equivalence(op, arr1, arr2): comp_ops = {operator.ge, operator.gt, operator.le, operator.lt} if op in comp_ops and (np.isnan(scalar1) or np.isnan(scalar2)): pytest.xfail("complex comp ufuncs use sort-order, scalars do not.") + if op == operator.pow and arr2.item() in [-1, 0, 0.5, 1, 2]: + # array**scalar special case can have different result dtype + # (Other powers may have issues also, but are not hit here.) + # TODO: It would be nice to resolve this issue. + pytest.skip("array**2 can have incorrect/weird result dtype") # ignore fpe's since they may just mismatch for integers anyway. with warnings.catch_warnings(), np.errstate(all="ignore"): @@ -107,10 +102,51 @@ def test_array_scalar_ufunc_equivalence(op, arr1, arr2): op(scalar1, scalar2) else: scalar_res = op(scalar1, scalar2) - assert_array_equal(scalar_res, res) + assert_array_equal(scalar_res, res, strict=True) + + +@pytest.mark.slow +@settings(max_examples=10000, deadline=2000) +@given(sampled_from(reasonable_operators_for_scalars), + hynp.arrays(dtype=hynp.scalar_dtypes(), shape=()), + hynp.arrays(dtype=hynp.scalar_dtypes(), shape=())) +def test_array_scalar_ufunc_equivalence(op, arr1, arr2): + """ + This is a thorough test attempting to cover important promotion paths + and ensuring that arrays and scalars stay as aligned as possible. + However, if it creates troubles, it should maybe just be removed. + """ + check_ufunc_scalar_equivalence(op, arr1, arr2) + + +@pytest.mark.slow +@given(sampled_from(reasonable_operators_for_scalars), + hynp.scalar_dtypes(), hynp.scalar_dtypes()) +def test_array_scalar_ufunc_dtypes(op, dt1, dt2): + # Same as above, but don't worry about sampling weird values so that we + # do not have to sample as much + arr1 = np.array(2, dtype=dt1) + arr2 = np.array(3, dtype=dt2) # some power do weird things. + + check_ufunc_scalar_equivalence(op, arr1, arr2) + + +@pytest.mark.parametrize("fscalar", [np.float16, np.float32]) +def test_int_float_promotion_truediv(fscalar): + # Promotion for mixed int and float32/float16 must not go to float64 + i = np.int8(1) + f = fscalar(1) + expected = np.result_type(i, f) + assert (i / f).dtype == expected + assert (f / i).dtype == expected + # But normal int / int true division goes to float64: + assert (i / i).dtype == np.dtype("float64") + # For int16, result has to be ast least float32 (takes ufunc path): + assert (np.int16(1) / f).dtype == np.dtype("float32") class TestBaseMath: + @pytest.mark.xfail(_SUPPORTS_SVE, reason="gh-22982") def test_blocked(self): # test alignments offsets for simd instructions # alignments for vz + 2 * (vs - 1) + 1 @@ -860,27 +896,29 @@ def test_operator_scalars(op, type1, type2): @pytest.mark.parametrize("op", reasonable_operators_for_scalars) -def test_longdouble_inf_loop(op): +@pytest.mark.parametrize("val", [None, 2**64]) +def test_longdouble_inf_loop(op, val): + # Note: The 2**64 value will pass once NEP 50 is adopted. try: - op(np.longdouble(3), None) + op(np.longdouble(3), val) except TypeError: pass try: - op(None, np.longdouble(3)) + op(val, np.longdouble(3)) except TypeError: pass @pytest.mark.parametrize("op", reasonable_operators_for_scalars) -def test_clongdouble_inf_loop(op): - if op in {operator.mod} and False: - pytest.xfail("The modulo operator is known to be broken") +@pytest.mark.parametrize("val", [None, 2**64]) +def test_clongdouble_inf_loop(op, val): + # Note: The 2**64 value will pass once NEP 50 is adopted. try: - op(np.clongdouble(3), None) + op(np.clongdouble(3), val) except TypeError: pass try: - op(None, np.longdouble(3)) + op(val, np.longdouble(3)) except TypeError: pass diff --git a/numpy/core/tests/test_scalarprint.py b/numpy/core/tests/test_scalarprint.py index 4deb5a0a4..98d1f4aa1 100644 --- a/numpy/core/tests/test_scalarprint.py +++ b/numpy/core/tests/test_scalarprint.py @@ -8,7 +8,7 @@ import sys from tempfile import TemporaryFile import numpy as np -from numpy.testing import assert_, assert_equal, assert_raises +from numpy.testing import assert_, assert_equal, assert_raises, IS_MUSL class TestRealScalars: def test_str(self): @@ -260,10 +260,10 @@ class TestRealScalars: assert_equal(fpos64('324', unique=False, precision=5, fractional=False), "324.00") - def test_dragon4_interface(self): tps = [np.float16, np.float32, np.float64] - if hasattr(np, 'float128'): + # test is flaky for musllinux on np.float128 + if hasattr(np, 'float128') and not IS_MUSL: tps.append(np.float128) fpos = np.format_float_positional diff --git a/numpy/core/tests/test_shape_base.py b/numpy/core/tests/test_shape_base.py index 570d006f5..0428b95a9 100644 --- a/numpy/core/tests/test_shape_base.py +++ b/numpy/core/tests/test_shape_base.py @@ -152,9 +152,9 @@ class TestHstack: assert_array_equal(res, desired) def test_generator(self): - with assert_warns(FutureWarning): + with pytest.raises(TypeError, match="arrays to stack must be"): hstack((np.arange(3) for _ in range(2))) - with assert_warns(FutureWarning): + with pytest.raises(TypeError, match="arrays to stack must be"): hstack(map(lambda x: x, np.ones((3, 2)))) def test_casting_and_dtype(self): @@ -207,7 +207,7 @@ class TestVstack: assert_array_equal(res, desired) def test_generator(self): - with assert_warns(FutureWarning): + with pytest.raises(TypeError, match="arrays to stack must be"): vstack((np.arange(3) for _ in range(2))) def test_casting_and_dtype(self): @@ -472,10 +472,11 @@ def test_stack(): stack, [np.zeros((3, 3)), np.zeros(3)], axis=1) assert_raises_regex(ValueError, 'must have the same shape', stack, [np.arange(2), np.arange(3)]) - # generator is deprecated - with assert_warns(FutureWarning): - result = stack((x for x in range(3))) - assert_array_equal(result, np.array([0, 1, 2])) + + # do not accept generators + with pytest.raises(TypeError, match="arrays to stack must be"): + stack((x for x in range(3))) + #casting and dtype test a = np.array([1, 2, 3]) b = np.array([2.5, 3.5, 4.5]) diff --git a/numpy/core/tests/test_simd.py b/numpy/core/tests/test_simd.py index 264300621..92b567446 100644 --- a/numpy/core/tests/test_simd.py +++ b/numpy/core/tests/test_simd.py @@ -2,9 +2,24 @@ # may be involved in their functionality. import pytest, math, re import itertools -from numpy.core._simd import targets +import operator +from numpy.core._simd import targets, clear_floatstatus, get_floatstatus from numpy.core._multiarray_umath import __cpu_baseline__ +def check_floatstatus(divbyzero=False, overflow=False, + underflow=False, invalid=False, + all=False): + #define NPY_FPE_DIVIDEBYZERO 1 + #define NPY_FPE_OVERFLOW 2 + #define NPY_FPE_UNDERFLOW 4 + #define NPY_FPE_INVALID 8 + err = get_floatstatus() + ret = (all or divbyzero) and (err & 1) != 0 + ret |= (all or overflow) and (err & 2) != 0 + ret |= (all or underflow) and (err & 4) != 0 + ret |= (all or invalid) and (err & 8) != 0 + return ret + class _Test_Utility: # submodule of the desired SIMD extension, e.g. targets["AVX512F"] npyv = None @@ -20,6 +35,9 @@ class _Test_Utility: """ return getattr(self.npyv, attr + "_" + self.sfx) + def _x2(self, intrin_name): + return getattr(self.npyv, f"{intrin_name}_{self.sfx}x2") + def _data(self, start=None, count=None, reverse=False): """ Create list of consecutive numbers according to number of vector's lanes. @@ -365,6 +383,11 @@ class _SIMD_FP(_Test_Utility): nfms = self.nmulsub(vdata_a, vdata_b, vdata_c) data_nfms = self.mul(data_fma, self.setall(-1)) assert nfms == data_nfms + # multiply, add for odd elements and subtract even elements. + # (a * b) -+ c + fmas = list(self.muladdsub(vdata_a, vdata_b, vdata_c)) + assert fmas[0::2] == list(data_fms)[0::2] + assert fmas[1::2] == list(data_fma)[1::2] def test_abs(self): pinf, ninf, nan = self._pinfinity(), self._ninfinity(), self._nan() @@ -553,7 +576,16 @@ class _SIMD_FP(_Test_Utility): nnan = self.notnan(self.setall(self._nan())) assert nnan == [0]*self.nlanes - import operator + @pytest.mark.parametrize("intrin_name", [ + "rint", "trunc", "ceil", "floor" + ]) + def test_unary_invalid_fpexception(self, intrin_name): + intrin = getattr(self, intrin_name) + for d in [float("nan"), float("inf"), -float("inf")]: + v = self.setall(d) + clear_floatstatus() + intrin(v) + assert check_floatstatus(invalid=True) == False @pytest.mark.parametrize('py_comp,np_comp', [ (operator.lt, "cmplt"), @@ -571,7 +603,8 @@ class _SIMD_FP(_Test_Utility): return [lane == mask_true for lane in vector] intrin = getattr(self, np_comp) - cmp_cases = ((0, nan), (nan, 0), (nan, nan), (pinf, nan), (ninf, nan)) + cmp_cases = ((0, nan), (nan, 0), (nan, nan), (pinf, nan), + (ninf, nan), (-0.0, +0.0)) for case_operand1, case_operand2 in cmp_cases: data_a = [case_operand1]*self.nlanes data_b = [case_operand2]*self.nlanes @@ -653,25 +686,34 @@ class _SIMD_ALL(_Test_Utility): assert store_h[:self.nlanes//2] == data[self.nlanes//2:] assert store_h != vdata # detect overflow - def test_memory_partial_load(self): - if self.sfx in ("u8", "s8", "u16", "s16"): + @pytest.mark.parametrize("intrin, elsizes, scale, fill", [ + ("self.load_tillz, self.load_till", (32, 64), 1, [0xffff]), + ("self.load2_tillz, self.load2_till", (32, 64), 2, [0xffff, 0x7fff]), + ]) + def test_memory_partial_load(self, intrin, elsizes, scale, fill): + if self._scalar_size() not in elsizes: return - + npyv_load_tillz, npyv_load_till = eval(intrin) data = self._data() lanes = list(range(1, self.nlanes + 1)) lanes += [self.nlanes**2, self.nlanes**4] # test out of range for n in lanes: - load_till = self.load_till(data, n, 15) - data_till = data[:n] + [15] * (self.nlanes-n) + load_till = npyv_load_till(data, n, *fill) + load_tillz = npyv_load_tillz(data, n) + n *= scale + data_till = data[:n] + fill * ((self.nlanes-n) // scale) assert load_till == data_till - load_tillz = self.load_tillz(data, n) data_tillz = data[:n] + [0] * (self.nlanes-n) assert load_tillz == data_tillz - def test_memory_partial_store(self): - if self.sfx in ("u8", "s8", "u16", "s16"): + @pytest.mark.parametrize("intrin, elsizes, scale", [ + ("self.store_till", (32, 64), 1), + ("self.store2_till", (32, 64), 2), + ]) + def test_memory_partial_store(self, intrin, elsizes, scale): + if self._scalar_size() not in elsizes: return - + npyv_store_till = eval(intrin) data = self._data() data_rev = self._data(reverse=True) vdata = self.load(data) @@ -679,105 +721,159 @@ class _SIMD_ALL(_Test_Utility): lanes += [self.nlanes**2, self.nlanes**4] for n in lanes: data_till = data_rev.copy() - data_till[:n] = data[:n] + data_till[:n*scale] = data[:n*scale] store_till = self._data(reverse=True) - self.store_till(store_till, n, vdata) + npyv_store_till(store_till, n, vdata) assert store_till == data_till - def test_memory_noncont_load(self): - if self.sfx in ("u8", "s8", "u16", "s16"): + @pytest.mark.parametrize("intrin, elsizes, scale", [ + ("self.loadn", (32, 64), 1), + ("self.loadn2", (32, 64), 2), + ]) + def test_memory_noncont_load(self, intrin, elsizes, scale): + if self._scalar_size() not in elsizes: return - - for stride in range(1, 64): - data = self._data(count=stride*self.nlanes) - data_stride = data[::stride] - loadn = self.loadn(data, stride) - assert loadn == data_stride - - for stride in range(-64, 0): - data = self._data(stride, -stride*self.nlanes) - data_stride = self.load(data[::stride]) # cast unsigned - loadn = self.loadn(data, stride) + npyv_loadn = eval(intrin) + for stride in range(-64, 64): + if stride < 0: + data = self._data(stride, -stride*self.nlanes) + data_stride = list(itertools.chain( + *zip(*[data[-i::stride] for i in range(scale, 0, -1)]) + )) + elif stride == 0: + data = self._data() + data_stride = data[0:scale] * (self.nlanes//scale) + else: + data = self._data(count=stride*self.nlanes) + data_stride = list(itertools.chain( + *zip(*[data[i::stride] for i in range(scale)])) + ) + data_stride = self.load(data_stride) # cast unsigned + loadn = npyv_loadn(data, stride) assert loadn == data_stride - def test_memory_noncont_partial_load(self): - if self.sfx in ("u8", "s8", "u16", "s16"): + @pytest.mark.parametrize("intrin, elsizes, scale, fill", [ + ("self.loadn_tillz, self.loadn_till", (32, 64), 1, [0xffff]), + ("self.loadn2_tillz, self.loadn2_till", (32, 64), 2, [0xffff, 0x7fff]), + ]) + def test_memory_noncont_partial_load(self, intrin, elsizes, scale, fill): + if self._scalar_size() not in elsizes: return - + npyv_loadn_tillz, npyv_loadn_till = eval(intrin) lanes = list(range(1, self.nlanes + 1)) lanes += [self.nlanes**2, self.nlanes**4] - for stride in range(1, 64): - data = self._data(count=stride*self.nlanes) - data_stride = data[::stride] - for n in lanes: - data_stride_till = data_stride[:n] + [15] * (self.nlanes-n) - loadn_till = self.loadn_till(data, stride, n, 15) - assert loadn_till == data_stride_till - data_stride_tillz = data_stride[:n] + [0] * (self.nlanes-n) - loadn_tillz = self.loadn_tillz(data, stride, n) - assert loadn_tillz == data_stride_tillz - - for stride in range(-64, 0): - data = self._data(stride, -stride*self.nlanes) - data_stride = list(self.load(data[::stride])) # cast unsigned + for stride in range(-64, 64): + if stride < 0: + data = self._data(stride, -stride*self.nlanes) + data_stride = list(itertools.chain( + *zip(*[data[-i::stride] for i in range(scale, 0, -1)]) + )) + elif stride == 0: + data = self._data() + data_stride = data[0:scale] * (self.nlanes//scale) + else: + data = self._data(count=stride*self.nlanes) + data_stride = list(itertools.chain( + *zip(*[data[i::stride] for i in range(scale)]) + )) + data_stride = list(self.load(data_stride)) # cast unsigned for n in lanes: - data_stride_till = data_stride[:n] + [15] * (self.nlanes-n) - loadn_till = self.loadn_till(data, stride, n, 15) + nscale = n * scale + llanes = self.nlanes - nscale + data_stride_till = ( + data_stride[:nscale] + fill * (llanes//scale) + ) + loadn_till = npyv_loadn_till(data, stride, n, *fill) assert loadn_till == data_stride_till - data_stride_tillz = data_stride[:n] + [0] * (self.nlanes-n) - loadn_tillz = self.loadn_tillz(data, stride, n) + data_stride_tillz = data_stride[:nscale] + [0] * llanes + loadn_tillz = npyv_loadn_tillz(data, stride, n) assert loadn_tillz == data_stride_tillz - def test_memory_noncont_store(self): - if self.sfx in ("u8", "s8", "u16", "s16"): + @pytest.mark.parametrize("intrin, elsizes, scale", [ + ("self.storen", (32, 64), 1), + ("self.storen2", (32, 64), 2), + ]) + def test_memory_noncont_store(self, intrin, elsizes, scale): + if self._scalar_size() not in elsizes: return - - vdata = self.load(self._data()) + npyv_storen = eval(intrin) + data = self._data() + vdata = self.load(data) + hlanes = self.nlanes // scale for stride in range(1, 64): - data = [15] * stride * self.nlanes - data[::stride] = vdata - storen = [15] * stride * self.nlanes - storen += [127]*64 - self.storen(storen, stride, vdata) - assert storen[:-64] == data - assert storen[-64:] == [127]*64 # detect overflow + data_storen = [0xff] * stride * self.nlanes + for s in range(0, hlanes*stride, stride): + i = (s//stride)*scale + data_storen[s:s+scale] = data[i:i+scale] + storen = [0xff] * stride * self.nlanes + storen += [0x7f]*64 + npyv_storen(storen, stride, vdata) + assert storen[:-64] == data_storen + assert storen[-64:] == [0x7f]*64 # detect overflow for stride in range(-64, 0): - data = [15] * -stride * self.nlanes - data[::stride] = vdata - storen = [127]*64 - storen += [15] * -stride * self.nlanes - self.storen(storen, stride, vdata) - assert storen[64:] == data - assert storen[:64] == [127]*64 # detect overflow - - def test_memory_noncont_partial_store(self): - if self.sfx in ("u8", "s8", "u16", "s16"): + data_storen = [0xff] * -stride * self.nlanes + for s in range(0, hlanes*stride, stride): + i = (s//stride)*scale + data_storen[s-scale:s or None] = data[i:i+scale] + storen = [0x7f]*64 + storen += [0xff] * -stride * self.nlanes + npyv_storen(storen, stride, vdata) + assert storen[64:] == data_storen + assert storen[:64] == [0x7f]*64 # detect overflow + # stride 0 + data_storen = [0x7f] * self.nlanes + storen = data_storen.copy() + data_storen[0:scale] = data[-scale:] + npyv_storen(storen, 0, vdata) + assert storen == data_storen + + @pytest.mark.parametrize("intrin, elsizes, scale", [ + ("self.storen_till", (32, 64), 1), + ("self.storen2_till", (32, 64), 2), + ]) + def test_memory_noncont_partial_store(self, intrin, elsizes, scale): + if self._scalar_size() not in elsizes: return - + npyv_storen_till = eval(intrin) data = self._data() vdata = self.load(data) lanes = list(range(1, self.nlanes + 1)) lanes += [self.nlanes**2, self.nlanes**4] + hlanes = self.nlanes // scale for stride in range(1, 64): for n in lanes: - data_till = [15] * stride * self.nlanes - data_till[::stride] = data[:n] + [15] * (self.nlanes-n) - storen_till = [15] * stride * self.nlanes - storen_till += [127]*64 - self.storen_till(storen_till, stride, n, vdata) + data_till = [0xff] * stride * self.nlanes + tdata = data[:n*scale] + [0xff] * (self.nlanes-n*scale) + for s in range(0, hlanes*stride, stride)[:n]: + i = (s//stride)*scale + data_till[s:s+scale] = tdata[i:i+scale] + storen_till = [0xff] * stride * self.nlanes + storen_till += [0x7f]*64 + npyv_storen_till(storen_till, stride, n, vdata) assert storen_till[:-64] == data_till - assert storen_till[-64:] == [127]*64 # detect overflow + assert storen_till[-64:] == [0x7f]*64 # detect overflow for stride in range(-64, 0): for n in lanes: - data_till = [15] * -stride * self.nlanes - data_till[::stride] = data[:n] + [15] * (self.nlanes-n) - storen_till = [127]*64 - storen_till += [15] * -stride * self.nlanes - self.storen_till(storen_till, stride, n, vdata) + data_till = [0xff] * -stride * self.nlanes + tdata = data[:n*scale] + [0xff] * (self.nlanes-n*scale) + for s in range(0, hlanes*stride, stride)[:n]: + i = (s//stride)*scale + data_till[s-scale:s or None] = tdata[i:i+scale] + storen_till = [0x7f]*64 + storen_till += [0xff] * -stride * self.nlanes + npyv_storen_till(storen_till, stride, n, vdata) assert storen_till[64:] == data_till - assert storen_till[:64] == [127]*64 # detect overflow + assert storen_till[:64] == [0x7f]*64 # detect overflow + + # stride 0 + for n in lanes: + data_till = [0x7f] * self.nlanes + storen_till = data_till.copy() + data_till[0:scale] = data[:n*scale][-scale:] + npyv_storen_till(storen_till, 0, n, vdata) + assert storen_till == data_till @pytest.mark.parametrize("intrin, table_size, elsize", [ ("self.lut32", 32, 32), @@ -861,13 +957,27 @@ class _SIMD_ALL(_Test_Utility): combineh = self.combineh(vdata_a, vdata_b) assert combineh == data_a_hi + data_b_hi # combine x2 - combine = self.combine(vdata_a, vdata_b) + combine = self.combine(vdata_a, vdata_b) assert combine == (data_a_lo + data_b_lo, data_a_hi + data_b_hi) + # zip(interleave) - data_zipl = [v for p in zip(data_a_lo, data_b_lo) for v in p] - data_ziph = [v for p in zip(data_a_hi, data_b_hi) for v in p] - vzip = self.zip(vdata_a, vdata_b) + data_zipl = self.load([ + v for p in zip(data_a_lo, data_b_lo) for v in p + ]) + data_ziph = self.load([ + v for p in zip(data_a_hi, data_b_hi) for v in p + ]) + vzip = self.zip(vdata_a, vdata_b) assert vzip == (data_zipl, data_ziph) + vzip = [0]*self.nlanes*2 + self._x2("store")(vzip, (vdata_a, vdata_b)) + assert vzip == list(data_zipl) + list(data_ziph) + + # unzip(deinterleave) + unzip = self.unzip(data_zipl, data_ziph) + assert unzip == (data_a, data_b) + unzip = self._x2("load")(list(data_zipl) + list(data_ziph)) + assert unzip == (data_a, data_b) def test_reorder_rev64(self): # Reverse elements of each 64-bit lane @@ -881,41 +991,51 @@ class _SIMD_ALL(_Test_Utility): rev64 = self.rev64(self.load(range(self.nlanes))) assert rev64 == data_rev64 - def test_operators_comparison(self): + def test_reorder_permi128(self): + """ + Test permuting elements for each 128-bit lane. + npyv_permi128_##sfx + """ + ssize = self._scalar_size() + if ssize < 32: + return + data = self.load(self._data()) + permn = 128//ssize + permd = permn-1 + nlane128 = self.nlanes//permn + shfl = [0, 1] if ssize == 64 else [0, 2, 4, 6] + for i in range(permn): + indices = [(i >> shf) & permd for shf in shfl] + vperm = self.permi128(data, *indices) + data_vperm = [ + data[j + (e & -permn)] + for e, j in enumerate(indices*nlane128) + ] + assert vperm == data_vperm + + @pytest.mark.parametrize('func, intrin', [ + (operator.lt, "cmplt"), + (operator.le, "cmple"), + (operator.gt, "cmpgt"), + (operator.ge, "cmpge"), + (operator.eq, "cmpeq") + ]) + def test_operators_comparison(self, func, intrin): if self._is_fp(): data_a = self._data() else: data_a = self._data(self._int_max() - self.nlanes) data_b = self._data(self._int_min(), reverse=True) vdata_a, vdata_b = self.load(data_a), self.load(data_b) + intrin = getattr(self, intrin) mask_true = self._true_mask() def to_bool(vector): return [lane == mask_true for lane in vector] - # equal - data_eq = [a == b for a, b in zip(data_a, data_b)] - cmpeq = to_bool(self.cmpeq(vdata_a, vdata_b)) - assert cmpeq == data_eq - # not equal - data_neq = [a != b for a, b in zip(data_a, data_b)] - cmpneq = to_bool(self.cmpneq(vdata_a, vdata_b)) - assert cmpneq == data_neq - # greater than - data_gt = [a > b for a, b in zip(data_a, data_b)] - cmpgt = to_bool(self.cmpgt(vdata_a, vdata_b)) - assert cmpgt == data_gt - # greater than and equal - data_ge = [a >= b for a, b in zip(data_a, data_b)] - cmpge = to_bool(self.cmpge(vdata_a, vdata_b)) - assert cmpge == data_ge - # less than - data_lt = [a < b for a, b in zip(data_a, data_b)] - cmplt = to_bool(self.cmplt(vdata_a, vdata_b)) - assert cmplt == data_lt - # less than and equal - data_le = [a <= b for a, b in zip(data_a, data_b)] - cmple = to_bool(self.cmple(vdata_a, vdata_b)) - assert cmple == data_le + + data_cmp = [func(a, b) for a, b in zip(data_a, data_b)] + cmp = to_bool(intrin(vdata_a, vdata_b)) + assert cmp == data_cmp def test_operators_logical(self): if self._is_fp(): @@ -1155,6 +1275,18 @@ class _SIMD_ALL(_Test_Utility): ifadd = self.ifadd(false_mask, vdata_a, vdata_b, vdata_b) assert ifadd == vdata_b + if not self._is_fp(): + return + data_div = self.div(vdata_b, vdata_a) + ifdiv = self.ifdiv(true_mask, vdata_b, vdata_a, vdata_b) + assert ifdiv == data_div + ifdivz = self.ifdivz(true_mask, vdata_b, vdata_a) + assert ifdivz == data_div + ifdiv = self.ifdiv(false_mask, vdata_a, vdata_b, vdata_b) + assert ifdiv == vdata_b + ifdivz = self.ifdivz(false_mask, vdata_a, vdata_b) + assert ifdivz == self.zero() + bool_sfx = ("b8", "b16", "b32", "b64") int_sfx = ("u8", "s8", "u16", "s16", "u32", "s32", "u64", "s64") fp_sfx = ("f32", "f64") diff --git a/numpy/core/tests/test_strings.py b/numpy/core/tests/test_strings.py index 2b87ed654..42f775e85 100644 --- a/numpy/core/tests/test_strings.py +++ b/numpy/core/tests/test_strings.py @@ -83,3 +83,17 @@ def test_string_comparisons_empty(op, ufunc, sym, dtypes): assert_array_equal(op(arr, arr2), expected) assert_array_equal(ufunc(arr, arr2), expected) assert_array_equal(np.compare_chararrays(arr, arr2, sym, False), expected) + + +@pytest.mark.parametrize("str_dt", ["S", "U"]) +@pytest.mark.parametrize("float_dt", np.typecodes["AllFloat"]) +def test_float_to_string_cast(str_dt, float_dt): + float_dt = np.dtype(float_dt) + fi = np.finfo(float_dt) + arr = np.array([np.nan, np.inf, -np.inf, fi.max, fi.min], dtype=float_dt) + expected = ["nan", "inf", "-inf", repr(fi.max), repr(fi.min)] + if float_dt.kind == 'c': + expected = [f"({r}+0j)" for r in expected] + + res = arr.astype(str_dt) + assert_array_equal(res, np.array(expected, dtype=str_dt)) diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 00aa48647..f716e2104 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -1650,6 +1650,19 @@ class TestUfunc: a = a[1:, 1:, 1:] self.check_identityless_reduction(a) + def test_reduce_identity_depends_on_loop(self): + """ + The type of the result should always depend on the selected loop, not + necessarily the output (only relevant for object arrays). + """ + # For an object loop, the default value 0 with type int is used: + assert type(np.add.reduce([], dtype=object)) is int + out = np.array(None, dtype=object) + # When the loop is float64 but `out` is object this does not happen, + # the result is float64 cast to object (which gives Python `float`). + np.add.reduce([], out=out, dtype=np.float64) + assert type(out[()]) is float + def test_initial_reduction(self): # np.minimum.reduce is an identityless reduction @@ -1670,6 +1683,9 @@ class TestUfunc: # Check initial=None raises ValueError for both types of ufunc reductions assert_raises(ValueError, np.minimum.reduce, [], initial=None) assert_raises(ValueError, np.add.reduce, [], initial=None) + # Also in the somewhat special object case: + with pytest.raises(ValueError): + np.add.reduce([], initial=None, dtype=object) # Check that np._NoValue gives default behavior. assert_equal(np.add.reduce([], initial=np._NoValue), 0) @@ -1679,6 +1695,23 @@ class TestUfunc: res = np.add.reduce(a, initial=5) assert_equal(res, 15) + def test_empty_reduction_and_idenity(self): + arr = np.zeros((0, 5)) + # OK, since the reduction itself is *not* empty, the result is + assert np.true_divide.reduce(arr, axis=1).shape == (0,) + # Not OK, the reduction itself is empty and we have no idenity + with pytest.raises(ValueError): + np.true_divide.reduce(arr, axis=0) + + # Test that an empty reduction fails also if the result is empty + arr = np.zeros((0, 0, 5)) + with pytest.raises(ValueError): + np.true_divide.reduce(arr, axis=1) + + # Division reduction makes sense with `initial=1` (empty or not): + res = np.true_divide.reduce(arr, axis=1, initial=1) + assert_array_equal(res, np.ones((0, 5))) + @pytest.mark.parametrize('axis', (0, 1, None)) @pytest.mark.parametrize('where', (np.array([False, True, True]), np.array([[True], [False], [True]]), @@ -1923,17 +1956,133 @@ class TestUfunc: assert_(MyThing.rmul_count == 1, MyThing.rmul_count) assert_(MyThing.getitem_count <= 2, MyThing.getitem_count) - def test_inplace_fancy_indexing(self): + @pytest.mark.parametrize("a", ( + np.arange(10, dtype=int), + np.arange(10, dtype=_rational_tests.rational), + )) + def test_ufunc_at_basic(self, a): - a = np.arange(10) - np.add.at(a, [2, 5, 2], 1) - assert_equal(a, [0, 1, 4, 3, 4, 6, 6, 7, 8, 9]) + aa = a.copy() + np.add.at(aa, [2, 5, 2], 1) + assert_equal(aa, [0, 1, 4, 3, 4, 6, 6, 7, 8, 9]) - a = np.arange(10) + with pytest.raises(ValueError): + # missing second operand + np.add.at(aa, [2, 5, 3]) + + aa = a.copy() + np.negative.at(aa, [2, 5, 3]) + assert_equal(aa, [0, 1, -2, -3, 4, -5, 6, 7, 8, 9]) + + aa = a.copy() b = np.array([100, 100, 100]) - np.add.at(a, [2, 5, 2], b) - assert_equal(a, [0, 1, 202, 3, 4, 105, 6, 7, 8, 9]) + np.add.at(aa, [2, 5, 2], b) + assert_equal(aa, [0, 1, 202, 3, 4, 105, 6, 7, 8, 9]) + + with pytest.raises(ValueError): + # extraneous second operand + np.negative.at(a, [2, 5, 3], [1, 2, 3]) + with pytest.raises(ValueError): + # second operand cannot be converted to an array + np.add.at(a, [2, 5, 3], [[1, 2], 1]) + + # ufuncs with indexed loops for performance in ufunc.at + indexed_ufuncs = [np.add, np.subtract, np.multiply, np.floor_divide, + np.maximum, np.minimum, np.fmax, np.fmin] + + @pytest.mark.parametrize( + "typecode", np.typecodes['AllInteger'] + np.typecodes['Float']) + @pytest.mark.parametrize("ufunc", indexed_ufuncs) + def test_ufunc_at_inner_loops(self, typecode, ufunc): + if ufunc is np.divide and typecode in np.typecodes['AllInteger']: + # Avoid divide-by-zero and inf for integer divide + a = np.ones(100, dtype=typecode) + indx = np.random.randint(100, size=30, dtype=np.intp) + vals = np.arange(1, 31, dtype=typecode) + else: + a = np.ones(1000, dtype=typecode) + indx = np.random.randint(1000, size=3000, dtype=np.intp) + vals = np.arange(3000, dtype=typecode) + atag = a.copy() + # Do the calculation twice and compare the answers + with warnings.catch_warnings(record=True) as w_at: + warnings.simplefilter('always') + ufunc.at(a, indx, vals) + with warnings.catch_warnings(record=True) as w_loop: + warnings.simplefilter('always') + for i, v in zip(indx, vals): + # Make sure all the work happens inside the ufunc + # in order to duplicate error/warning handling + ufunc(atag[i], v, out=atag[i:i+1], casting="unsafe") + assert_equal(atag, a) + # If w_loop warned, make sure w_at warned as well + if len(w_loop) > 0: + # + assert len(w_at) > 0 + assert w_at[0].category == w_loop[0].category + assert str(w_at[0].message)[:10] == str(w_loop[0].message)[:10] + + @pytest.mark.parametrize("typecode", np.typecodes['Complex']) + @pytest.mark.parametrize("ufunc", [np.add, np.subtract, np.multiply]) + def test_ufunc_at_inner_loops_complex(self, typecode, ufunc): + a = np.ones(10, dtype=typecode) + indx = np.concatenate([np.ones(6, dtype=np.intp), + np.full(18, 4, dtype=np.intp)]) + value = a.dtype.type(1j) + ufunc.at(a, indx, value) + expected = np.ones_like(a) + if ufunc is np.multiply: + expected[1] = expected[4] = -1 + else: + expected[1] += 6 * (value if ufunc is np.add else -value) + expected[4] += 18 * (value if ufunc is np.add else -value) + + assert_array_equal(a, expected) + + def test_ufunc_at_ellipsis(self): + # Make sure the indexed loop check does not choke on iters + # with subspaces + arr = np.zeros(5) + np.add.at(arr, slice(None), np.ones(5)) + assert_array_equal(arr, np.ones(5)) + + def test_ufunc_at_negative(self): + arr = np.ones(5, dtype=np.int32) + indx = np.arange(5) + umt.indexed_negative.at(arr, indx) + # If it is [-1, -1, -1, -100, 0] then the regular strided loop was used + assert np.all(arr == [-1, -1, -1, -200, -1]) + + def test_ufunc_at_large(self): + # issue gh-23457 + indices = np.zeros(8195, dtype=np.int16) + b = np.zeros(8195, dtype=float) + b[0] = 10 + b[1] = 5 + b[8192:] = 100 + a = np.zeros(1, dtype=float) + np.add.at(a, indices, b) + assert a[0] == b.sum() + + def test_cast_index_fastpath(self): + arr = np.zeros(10) + values = np.ones(100000) + # index must be cast, which may be buffered in chunks: + index = np.zeros(len(values), dtype=np.uint8) + np.add.at(arr, index, values) + assert arr[0] == len(values) + + @pytest.mark.parametrize("value", [ + np.ones(1), np.ones(()), np.float64(1.), 1.]) + def test_ufunc_at_scalar_value_fastpath(self, value): + arr = np.zeros(1000) + # index must be cast, which may be buffered in chunks: + index = np.repeat(np.arange(1000), 2) + np.add.at(arr, index, value) + assert_array_equal(arr, np.full_like(arr, 2 * value)) + + def test_ufunc_at_multiD(self): a = np.arange(9).reshape(3, 3) b = np.array([[100, 100, 100], [200, 200, 200], [300, 300, 300]]) np.add.at(a, (slice(None), [1, 2, 1]), b) @@ -2013,11 +2162,7 @@ class TestUfunc: [121, 222, 323], [124, 225, 326]]]) - a = np.arange(10) - np.negative.at(a, [2, 5, 2]) - assert_equal(a, [0, 1, 2, 3, 4, -5, 6, 7, 8, 9]) - - # Test 0-dim array + def test_ufunc_at_0D(self): a = np.array(0) np.add.at(a, (), 1) assert_equal(a, 1) @@ -2025,11 +2170,13 @@ class TestUfunc: assert_raises(IndexError, np.add.at, a, 0, 1) assert_raises(IndexError, np.add.at, a, [], 1) + def test_ufunc_at_dtypes(self): # Test mixed dtypes a = np.arange(10) np.power.at(a, [1, 2, 3, 2], 3.5) assert_equal(a, np.array([0, 1, 4414, 46, 4, 5, 6, 7, 8, 9])) + def test_ufunc_at_boolean(self): # Test boolean indexing and boolean ufuncs a = np.arange(10) index = a % 2 == 0 @@ -2041,6 +2188,7 @@ class TestUfunc: np.invert.at(a, [2, 5, 2]) assert_equal(a, [0, 1, 2, 3, 4, 5 ^ 0xffffffff, 6, 7, 8, 9]) + def test_ufunc_at_advanced(self): # Test empty subspace orig = np.arange(4) a = orig[:, None][:, 0:0] @@ -2064,7 +2212,7 @@ class TestUfunc: # Test maximum a = np.array([1, 2, 3]) np.maximum.at(a, [0], 0) - assert_equal(np.array([1, 2, 3]), a) + assert_equal(a, np.array([1, 2, 3])) def test_at_not_none_signature(self): # Test ufuncs with non-trivial signature raise a TypeError @@ -2075,6 +2223,23 @@ class TestUfunc: a = np.array([[[1, 2], [3, 4]]]) assert_raises(TypeError, np.linalg._umath_linalg.det.at, a, [0]) + def test_at_no_loop_for_op(self): + # str dtype does not have a ufunc loop for np.add + arr = np.ones(10, dtype=str) + with pytest.raises(np.core._exceptions._UFuncNoLoopError): + np.add.at(arr, [0, 1], [0, 1]) + + def test_at_output_casting(self): + arr = np.array([-1]) + np.equal.at(arr, [0], [0]) + assert arr[0] == 0 + + def test_at_broadcast_failure(self): + arr = np.arange(5) + with pytest.raises(ValueError): + np.add.at(arr, [0, 1], [1, 2, 3]) + + def test_reduce_arguments(self): f = np.add.reduce d = np.ones((5,2), dtype=int) @@ -2580,7 +2745,9 @@ def test_reduce_casterrors(offset): with pytest.raises(ValueError, match="invalid literal"): # This is an unsafe cast, but we currently always allow that. # Note that the double loop is picked, but the cast fails. - np.add.reduce(arr, dtype=np.intp, out=out) + # `initial=None` disables the use of an identity here to test failures + # while copying the first values path (not used when identity exists). + np.add.reduce(arr, dtype=np.intp, out=out, initial=None) assert count == sys.getrefcount(value) # If an error occurred during casting, the operation is done at most until # the error occurs (the result of which would be `value * offset`) and -1 @@ -2589,6 +2756,15 @@ def test_reduce_casterrors(offset): assert out[()] < value * offset +def test_object_reduce_cleanup_on_failure(): + # Test cleanup, including of the initial value (manually provided or not) + with pytest.raises(TypeError): + np.add.reduce([1, 2, None], initial=4) + + with pytest.raises(TypeError): + np.add.reduce([1, 2, None]) + + @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm") @pytest.mark.parametrize("method", [np.add.accumulate, np.add.reduce, @@ -2689,10 +2865,10 @@ class TestLowlevelAPIAccess: r = np.equal.resolve_dtypes((S0, S0, None)) assert r == (S0, S0, np.dtype(bool)) - # Subarray dtypes are weird and only really exist nested, they need - # the shift to full NEP 50 to be implemented nicely: + # Subarray dtypes are weird and may not work fully, we preserve them + # leading to a TypeError (currently no equal loop for void/structured) dts = np.dtype("10i") - with pytest.raises(NotImplementedError): + with pytest.raises(TypeError): np.equal.resolve_dtypes((dts, dts, None)) def test_resolve_dtypes_reduction(self): @@ -2791,3 +2967,10 @@ class TestLowlevelAPIAccess: with pytest.raises(TypeError): # cannot call it a second time: np.negative._get_strided_loop(call_info) + + def test_long_arrays(self): + t = np.zeros((1029, 917), dtype=np.single) + t[0][0] = 1 + t[28][414] = 1 + tc = np.cos(t) + assert_equal(tc[0][0], tc[28][414]) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 88ab7e014..9e3fe387b 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -17,10 +17,26 @@ from numpy.testing import ( assert_, assert_equal, assert_raises, assert_raises_regex, assert_array_equal, assert_almost_equal, assert_array_almost_equal, assert_array_max_ulp, assert_allclose, assert_no_warnings, suppress_warnings, - _gen_alignment_data, assert_array_almost_equal_nulp, IS_WASM + _gen_alignment_data, assert_array_almost_equal_nulp, IS_WASM, IS_MUSL ) from numpy.testing._private.utils import _glibc_older_than +UFUNCS = [obj for obj in np.core.umath.__dict__.values() + if isinstance(obj, np.ufunc)] + +UFUNCS_UNARY = [ + uf for uf in UFUNCS if uf.nin == 1 +] +UFUNCS_UNARY_FP = [ + uf for uf in UFUNCS_UNARY if 'f->f' in uf.types +] + +UFUNCS_BINARY = [ + uf for uf in UFUNCS if uf.nin == 2 +] +UFUNCS_BINARY_ACC = [ + uf for uf in UFUNCS_BINARY if hasattr(uf, "accumulate") and uf.nout == 1 +] def interesting_binop_operands(val1, val2, dtype): """ @@ -275,16 +291,16 @@ class TestComparisons: b_lst = b.tolist() # (Binary) Comparison (x1=array, x2=array) - comp_b = np_comp(a, b) - comp_b_list = [py_comp(x, y) for x, y in zip(a_lst, b_lst)] + comp_b = np_comp(a, b).view(np.uint8) + comp_b_list = [int(py_comp(x, y)) for x, y in zip(a_lst, b_lst)] # (Scalar1) Comparison (x1=scalar, x2=array) - comp_s1 = np_comp(np_scalar, b) - comp_s1_list = [py_comp(scalar, x) for x in b_lst] + comp_s1 = np_comp(np_scalar, b).view(np.uint8) + comp_s1_list = [int(py_comp(scalar, x)) for x in b_lst] # (Scalar2) Comparison (x1=array, x2=scalar) - comp_s2 = np_comp(a, np_scalar) - comp_s2_list = [py_comp(x, scalar) for x in a_lst] + comp_s2 = np_comp(a, np_scalar).view(np.uint8) + comp_s2_list = [int(py_comp(x, scalar)) for x in a_lst] # Sequence: Binary, Scalar1 and Scalar2 assert_(comp_b.tolist() == comp_b_list, @@ -353,6 +369,64 @@ class TestComparisons: with pytest.raises(TypeError, match="No loop matching"): np.equal(1, 1, sig=(None, None, "l")) + @pytest.mark.parametrize("dtypes", ["qQ", "Qq"]) + @pytest.mark.parametrize('py_comp, np_comp', [ + (operator.lt, np.less), + (operator.le, np.less_equal), + (operator.gt, np.greater), + (operator.ge, np.greater_equal), + (operator.eq, np.equal), + (operator.ne, np.not_equal) + ]) + @pytest.mark.parametrize("vals", [(2**60, 2**60+1), (2**60+1, 2**60)]) + def test_large_integer_direct_comparison( + self, dtypes, py_comp, np_comp, vals): + # Note that float(2**60) + 1 == float(2**60). + a1 = np.array([2**60], dtype=dtypes[0]) + a2 = np.array([2**60 + 1], dtype=dtypes[1]) + expected = py_comp(2**60, 2**60+1) + + assert py_comp(a1, a2) == expected + assert np_comp(a1, a2) == expected + # Also check the scalars: + s1 = a1[0] + s2 = a2[0] + assert isinstance(s1, np.integer) + assert isinstance(s2, np.integer) + # The Python operator here is mainly interesting: + assert py_comp(s1, s2) == expected + assert np_comp(s1, s2) == expected + + @pytest.mark.parametrize("dtype", np.typecodes['UnsignedInteger']) + @pytest.mark.parametrize('py_comp_func, np_comp_func', [ + (operator.lt, np.less), + (operator.le, np.less_equal), + (operator.gt, np.greater), + (operator.ge, np.greater_equal), + (operator.eq, np.equal), + (operator.ne, np.not_equal) + ]) + @pytest.mark.parametrize("flip", [True, False]) + def test_unsigned_signed_direct_comparison( + self, dtype, py_comp_func, np_comp_func, flip): + if flip: + py_comp = lambda x, y: py_comp_func(y, x) + np_comp = lambda x, y: np_comp_func(y, x) + else: + py_comp = py_comp_func + np_comp = np_comp_func + + arr = np.array([np.iinfo(dtype).max], dtype=dtype) + expected = py_comp(int(arr[0]), -1) + + assert py_comp(arr, -1) == expected + assert np_comp(arr, -1) == expected + scalar = arr[0] + assert isinstance(scalar, np.integer) + # The Python operator here is mainly interesting: + assert py_comp(scalar, -1) == expected + assert np_comp(scalar, -1) == expected + class TestAdd: def test_reduce_alignment(self): @@ -408,7 +482,7 @@ class TestDivision: def test_division_int_boundary(self, dtype, ex_val): fo = np.iinfo(dtype) neg = -1 if fo.min < 0 else 1 - # Large enough to test SIMD loops and remaind elements + # Large enough to test SIMD loops and remainder elements lsize = 512 + 7 a, b, divisors = eval(ex_val) a_lst, b_lst = a.tolist(), b.tolist() @@ -573,6 +647,8 @@ class TestDivision: assert_equal(np.signbit(x//1), 0) assert_equal(np.signbit((-x)//1), 1) + @pytest.mark.skipif(hasattr(np.__config__, "blas_ssl2_info"), + reason="gh-22982") @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm") @pytest.mark.parametrize('dtype', np.typecodes['Float']) def test_floor_division_errors(self, dtype): @@ -715,6 +791,8 @@ class TestRemainder: # inf / 0 does not set any flags, only the modulo creates a NaN np.divmod(finf, fzero) + @pytest.mark.skipif(hasattr(np.__config__, "blas_ssl2_info"), + reason="gh-22982") @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm") @pytest.mark.xfail(sys.platform.startswith("darwin"), reason="MacOS seems to not give the correct 'invalid' warning for " @@ -1086,7 +1164,7 @@ class TestPower: assert_complex_equal(np.power(zero, 1+1j), zero) assert_complex_equal(np.power(zero, 1+0j), zero) assert_complex_equal(np.power(zero, 1-1j), zero) - #Complex powers will negative real part or 0 (provided imaginary + #Complex powers will negative real part or 0 (provided imaginary # part is not zero) will generate a NAN and hence a RUNTIME warning with pytest.warns(expected_warning=RuntimeWarning) as r: assert_complex_equal(np.power(zero, -1+1j), cnan) @@ -1276,9 +1354,20 @@ class TestLog: # test log() of max for dtype does not raise for dt in ['f', 'd', 'g']: - with np.errstate(all='raise'): - x = np.finfo(dt).max - np.log(x) + try: + with np.errstate(all='raise'): + x = np.finfo(dt).max + np.log(x) + except FloatingPointError as exc: + if dt == 'g' and IS_MUSL: + # FloatingPointError is known to occur on longdouble + # for musllinux_x86_64 x is very large + pytest.skip( + "Overflow has occurred for" + " np.log(np.finfo(np.longdouble).max)" + ) + else: + raise exc def test_log_strides(self): np.random.seed(42) @@ -1395,23 +1484,50 @@ class TestSpecialFloats: np.log(a) @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm") - def test_sincos_values(self): + @pytest.mark.parametrize('dtype', ['e', 'f', 'd', 'g']) + def test_sincos_values(self, dtype): with np.errstate(all='ignore'): x = [np.nan, np.nan, np.nan, np.nan] y = [np.nan, -np.nan, np.inf, -np.inf] - for dt in ['e', 'f', 'd', 'g']: - xf = np.array(x, dtype=dt) - yf = np.array(y, dtype=dt) - assert_equal(np.sin(yf), xf) - assert_equal(np.cos(yf), xf) + xf = np.array(x, dtype=dtype) + yf = np.array(y, dtype=dtype) + assert_equal(np.sin(yf), xf) + assert_equal(np.cos(yf), xf) + @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm") + @pytest.mark.xfail( + sys.platform.startswith("darwin"), + reason="underflow is triggered for scalar 'sin'" + ) + def test_sincos_underflow(self): + with np.errstate(under='raise'): + underflow_trigger = np.array( + float.fromhex("0x1.f37f47a03f82ap-511"), + dtype=np.float64 + ) + np.sin(underflow_trigger) + np.cos(underflow_trigger) + @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm") + @pytest.mark.parametrize('callable', [np.sin, np.cos]) + @pytest.mark.parametrize('dtype', ['e', 'f', 'd']) + @pytest.mark.parametrize('value', [np.inf, -np.inf]) + def test_sincos_errors(self, callable, dtype, value): with np.errstate(invalid='raise'): - for callable in [np.sin, np.cos]: - for value in [np.inf, -np.inf]: - for dt in ['e', 'f', 'd']: - assert_raises(FloatingPointError, callable, - np.array([value], dtype=dt)) + assert_raises(FloatingPointError, callable, + np.array([value], dtype=dtype)) + + @pytest.mark.parametrize('callable', [np.sin, np.cos]) + @pytest.mark.parametrize('dtype', ['f', 'd']) + @pytest.mark.parametrize('stride', [-1, 1, 2, 4, 5]) + def test_sincos_overlaps(self, callable, dtype, stride): + N = 100 + M = N // abs(stride) + rng = np.random.default_rng(42) + x = rng.standard_normal(N, dtype) + y = callable(x[::stride]) + callable(x[::stride], out=x[:M]) + assert_equal(x[:M], y) @pytest.mark.parametrize('dt', ['e', 'f', 'd', 'g']) def test_sqrt_values(self, dt): @@ -1631,18 +1747,78 @@ class TestSpecialFloats: np.array(value, dtype=dt)) # test to ensure no spurious FP exceptions are raised due to SIMD - def test_spurious_fpexception(self): - for dt in ['e', 'f', 'd']: - arr = np.array([1.0, 2.0], dtype=dt) - with assert_no_warnings(): - np.log(arr) - np.log2(arr) - np.log10(arr) - np.arccosh(arr) - + INF_INVALID_ERR = [ + np.cos, np.sin, np.tan, np.arccos, np.arcsin, np.spacing, np.arctanh + ] + NEG_INVALID_ERR = [ + np.log, np.log2, np.log10, np.log1p, np.sqrt, np.arccosh, + np.arctanh + ] + ONE_INVALID_ERR = [ + np.arctanh, + ] + LTONE_INVALID_ERR = [ + np.arccosh, + ] + BYZERO_ERR = [ + np.log, np.log2, np.log10, np.reciprocal, np.arccosh + ] + + @pytest.mark.parametrize("ufunc", UFUNCS_UNARY_FP) + @pytest.mark.parametrize("dtype", ('e', 'f', 'd')) + @pytest.mark.parametrize("data, escape", ( + ([0.03], LTONE_INVALID_ERR), + ([0.03]*32, LTONE_INVALID_ERR), + # neg + ([-1.0], NEG_INVALID_ERR), + ([-1.0]*32, NEG_INVALID_ERR), + # flat + ([1.0], ONE_INVALID_ERR), + ([1.0]*32, ONE_INVALID_ERR), + # zero + ([0.0], BYZERO_ERR), + ([0.0]*32, BYZERO_ERR), + ([-0.0], BYZERO_ERR), + ([-0.0]*32, BYZERO_ERR), + # nan + ([0.5, 0.5, 0.5, np.nan], LTONE_INVALID_ERR), + ([0.5, 0.5, 0.5, np.nan]*32, LTONE_INVALID_ERR), + ([np.nan, 1.0, 1.0, 1.0], ONE_INVALID_ERR), + ([np.nan, 1.0, 1.0, 1.0]*32, ONE_INVALID_ERR), + ([np.nan], []), + ([np.nan]*32, []), + # inf + ([0.5, 0.5, 0.5, np.inf], INF_INVALID_ERR + LTONE_INVALID_ERR), + ([0.5, 0.5, 0.5, np.inf]*32, INF_INVALID_ERR + LTONE_INVALID_ERR), + ([np.inf, 1.0, 1.0, 1.0], INF_INVALID_ERR), + ([np.inf, 1.0, 1.0, 1.0]*32, INF_INVALID_ERR), + ([np.inf], INF_INVALID_ERR), + ([np.inf]*32, INF_INVALID_ERR), + # ninf + ([0.5, 0.5, 0.5, -np.inf], + NEG_INVALID_ERR + INF_INVALID_ERR + LTONE_INVALID_ERR), + ([0.5, 0.5, 0.5, -np.inf]*32, + NEG_INVALID_ERR + INF_INVALID_ERR + LTONE_INVALID_ERR), + ([-np.inf, 1.0, 1.0, 1.0], NEG_INVALID_ERR + INF_INVALID_ERR), + ([-np.inf, 1.0, 1.0, 1.0]*32, NEG_INVALID_ERR + INF_INVALID_ERR), + ([-np.inf], NEG_INVALID_ERR + INF_INVALID_ERR), + ([-np.inf]*32, NEG_INVALID_ERR + INF_INVALID_ERR), + )) + def test_unary_spurious_fpexception(self, ufunc, dtype, data, escape): + if escape and ufunc in escape: + return + # FIXME: NAN raises FP invalid exception: + # - ceil/float16 on MSVC:32-bit + # - spacing/float16 on almost all platforms + if ufunc in (np.spacing, np.ceil) and dtype == 'e': + return + array = np.array(data, dtype=dtype) + with assert_no_warnings(): + ufunc(array) class TestFPClass: - @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) + @pytest.mark.parametrize("stride", [-5, -4, -3, -2, -1, 1, + 2, 4, 5, 6, 7, 8, 9, 10]) def test_fpclass(self, stride): arr_f64 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 2.2251e-308, -2.2251e-308], dtype='d') arr_f32 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 1.4013e-045, -1.4013e-045], dtype='f') @@ -1659,6 +1835,52 @@ class TestFPClass: assert_equal(np.isfinite(arr_f32[::stride]), finite[::stride]) assert_equal(np.isfinite(arr_f64[::stride]), finite[::stride]) + @pytest.mark.parametrize("dtype", ['d', 'f']) + def test_fp_noncontiguous(self, dtype): + data = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, + 1.0, -0.0, 0.0, 2.2251e-308, + -2.2251e-308], dtype=dtype) + nan = np.array([True, True, False, False, False, False, + False, False, False, False]) + inf = np.array([False, False, True, True, False, False, + False, False, False, False]) + sign = np.array([False, True, False, True, True, False, + True, False, False, True]) + finite = np.array([False, False, False, False, True, True, + True, True, True, True]) + out = np.ndarray(data.shape, dtype='bool') + ncontig_in = data[1::3] + ncontig_out = out[1::3] + contig_in = np.array(ncontig_in) + assert_equal(ncontig_in.flags.c_contiguous, False) + assert_equal(ncontig_out.flags.c_contiguous, False) + assert_equal(contig_in.flags.c_contiguous, True) + # ncontig in, ncontig out + assert_equal(np.isnan(ncontig_in, out=ncontig_out), nan[1::3]) + assert_equal(np.isinf(ncontig_in, out=ncontig_out), inf[1::3]) + assert_equal(np.signbit(ncontig_in, out=ncontig_out), sign[1::3]) + assert_equal(np.isfinite(ncontig_in, out=ncontig_out), finite[1::3]) + # contig in, ncontig out + assert_equal(np.isnan(contig_in, out=ncontig_out), nan[1::3]) + assert_equal(np.isinf(contig_in, out=ncontig_out), inf[1::3]) + assert_equal(np.signbit(contig_in, out=ncontig_out), sign[1::3]) + assert_equal(np.isfinite(contig_in, out=ncontig_out), finite[1::3]) + # ncontig in, contig out + assert_equal(np.isnan(ncontig_in), nan[1::3]) + assert_equal(np.isinf(ncontig_in), inf[1::3]) + assert_equal(np.signbit(ncontig_in), sign[1::3]) + assert_equal(np.isfinite(ncontig_in), finite[1::3]) + # contig in, contig out, nd stride + data_split = np.array(np.array_split(data, 2)) + nan_split = np.array(np.array_split(nan, 2)) + inf_split = np.array(np.array_split(inf, 2)) + sign_split = np.array(np.array_split(sign, 2)) + finite_split = np.array(np.array_split(finite, 2)) + assert_equal(np.isnan(data_split), nan_split) + assert_equal(np.isinf(data_split), inf_split) + assert_equal(np.signbit(data_split), sign_split) + assert_equal(np.isfinite(data_split), finite_split) + class TestLDExp: @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) @pytest.mark.parametrize("dtype", ['f', 'd']) @@ -1672,6 +1894,7 @@ class TestLDExp: class TestFRExp: @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) @pytest.mark.parametrize("dtype", ['f', 'd']) + @pytest.mark.xfail(IS_MUSL, reason="gh23048") @pytest.mark.skipif(not sys.platform.startswith('linux'), reason="np.frexp gives different answers for NAN/INF on windows and linux") def test_frexp(self, dtype, stride): @@ -3359,6 +3582,79 @@ class TestSpecialMethods: assert_raises(ValueError, np.modf, a, out=('one', 'two', 'three')) assert_raises(ValueError, np.modf, a, out=('one',)) + def test_ufunc_override_where(self): + + class OverriddenArrayOld(np.ndarray): + + def _unwrap(self, objs): + cls = type(self) + result = [] + for obj in objs: + if isinstance(obj, cls): + obj = np.array(obj) + elif type(obj) != np.ndarray: + return NotImplemented + result.append(obj) + return result + + def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + + inputs = self._unwrap(inputs) + if inputs is NotImplemented: + return NotImplemented + + kwargs = kwargs.copy() + if "out" in kwargs: + kwargs["out"] = self._unwrap(kwargs["out"]) + if kwargs["out"] is NotImplemented: + return NotImplemented + + r = super().__array_ufunc__(ufunc, method, *inputs, **kwargs) + if r is not NotImplemented: + r = r.view(type(self)) + + return r + + class OverriddenArrayNew(OverriddenArrayOld): + def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + + kwargs = kwargs.copy() + if "where" in kwargs: + kwargs["where"] = self._unwrap((kwargs["where"], )) + if kwargs["where"] is NotImplemented: + return NotImplemented + else: + kwargs["where"] = kwargs["where"][0] + + r = super().__array_ufunc__(ufunc, method, *inputs, **kwargs) + if r is not NotImplemented: + r = r.view(type(self)) + + return r + + ufunc = np.negative + + array = np.array([1, 2, 3]) + where = np.array([True, False, True]) + expected = ufunc(array, where=where) + + with pytest.raises(TypeError): + ufunc(array, where=where.view(OverriddenArrayOld)) + + result_1 = ufunc( + array, + where=where.view(OverriddenArrayNew) + ) + assert isinstance(result_1, OverriddenArrayNew) + assert np.all(np.array(result_1) == expected, where=where) + + result_2 = ufunc( + array.view(OverriddenArrayNew), + where=where.view(OverriddenArrayNew) + ) + assert isinstance(result_2, OverriddenArrayNew) + assert np.all(np.array(result_2) == expected, where=where) + def test_ufunc_override_exception(self): class A: @@ -3780,6 +4076,7 @@ class TestComplexFunctions: assert_almost_equal(fz.real, fr, err_msg='real part %s' % f) assert_almost_equal(fz.imag, 0., err_msg='imag part %s' % f) + @pytest.mark.xfail(IS_MUSL, reason="gh23049") @pytest.mark.xfail(IS_WASM, reason="doesn't work") def test_precisions_consistent(self): z = 1 + 1j @@ -3790,6 +4087,7 @@ class TestComplexFunctions: assert_almost_equal(fcf, fcd, decimal=6, err_msg='fch-fcd %s' % f) assert_almost_equal(fcl, fcd, decimal=15, err_msg='fch-fcl %s' % f) + @pytest.mark.xfail(IS_MUSL, reason="gh23049") @pytest.mark.xfail(IS_WASM, reason="doesn't work") def test_branch_cuts(self): # check branch cuts and continuity on them @@ -3816,6 +4114,7 @@ class TestComplexFunctions: _check_branch_cut(np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1) _check_branch_cut(np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1) + @pytest.mark.xfail(IS_MUSL, reason="gh23049") @pytest.mark.xfail(IS_WASM, reason="doesn't work") def test_branch_cuts_complex64(self): # check branch cuts and continuity on them @@ -3861,6 +4160,7 @@ class TestComplexFunctions: b = cfunc(p) assert_(abs(a - b) < atol, "%s %s: %s; cmath: %s" % (fname, p, a, b)) + @pytest.mark.xfail(IS_MUSL, reason="gh23049") @pytest.mark.xfail(IS_WASM, reason="doesn't work") @pytest.mark.parametrize('dtype', [np.complex64, np.complex_, np.longcomplex]) def test_loss_of_precision(self, dtype): @@ -3952,6 +4252,14 @@ class TestComplexFunctions: check(func, pts, 1j) check(func, pts, 1+1j) + @np.errstate(all="ignore") + def test_promotion_corner_cases(self): + for func in self.funcs: + assert func(np.float16(1)).dtype == np.float16 + # Integer to low precision float promotion is a dubious choice: + assert func(np.uint8(1)).dtype == np.float16 + assert func(np.int16(1)).dtype == np.float32 + class TestAttributes: def test_attributes(self): @@ -4125,7 +4433,7 @@ def _test_spacing(t): nan = t(np.nan) inf = t(np.inf) with np.errstate(invalid='ignore'): - assert_(np.spacing(one) == eps) + assert_equal(np.spacing(one), eps) assert_(np.isnan(np.spacing(nan))) assert_(np.isnan(np.spacing(inf))) assert_(np.isnan(np.spacing(-inf))) @@ -4261,6 +4569,7 @@ def test_rint_big_int(): # Rint should not change the value assert_equal(val, np.rint(val)) + @pytest.mark.parametrize('ftype', [np.float32, np.float64]) def test_memoverlap_accumulate(ftype): # Reproduces bug https://github.com/numpy/numpy/issues/15597 @@ -4270,6 +4579,38 @@ def test_memoverlap_accumulate(ftype): assert_equal(np.maximum.accumulate(arr), out_max) assert_equal(np.minimum.accumulate(arr), out_min) +@pytest.mark.parametrize("ufunc, dtype", [ + (ufunc, t[0]) + for ufunc in UFUNCS_BINARY_ACC + for t in ufunc.types + if t[-1] == '?' and t[0] not in 'DFGMmO' +]) +def test_memoverlap_accumulate_cmp(ufunc, dtype): + if ufunc.signature: + pytest.skip('For generic signatures only') + for size in (2, 8, 32, 64, 128, 256): + arr = np.array([0, 1, 1]*size, dtype=dtype) + acc = ufunc.accumulate(arr, dtype='?') + acc_u8 = acc.view(np.uint8) + exp = np.array(list(itertools.accumulate(arr, ufunc)), dtype=np.uint8) + assert_equal(exp, acc_u8) + +@pytest.mark.parametrize("ufunc, dtype", [ + (ufunc, t[0]) + for ufunc in UFUNCS_BINARY_ACC + for t in ufunc.types + if t[0] == t[1] and t[0] == t[-1] and t[0] not in 'DFGMmO?' +]) +def test_memoverlap_accumulate_symmetric(ufunc, dtype): + if ufunc.signature: + pytest.skip('For generic signatures only') + with np.errstate(all='ignore'): + for size in (2, 8, 32, 64, 128, 256): + arr = np.array([0, 1, 2]*size).astype(dtype) + acc = ufunc.accumulate(arr, dtype=dtype) + exp = np.array(list(itertools.accumulate(arr, ufunc)), dtype=dtype) + assert_equal(exp, acc) + def test_signaling_nan_exceptions(): with assert_no_warnings(): a = np.ndarray(shape=(), dtype='float32', buffer=b'\x00\xe0\xbf\xff') |