diff --git a/azure-pipelines.yml b/azure-pipelines.yml index be47b5bf16..0aa6b8d1d0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,14 +12,14 @@ jobs: name: macOS vmImage: macos-11 matrix: - py38_np121: + py38_np122: PYTHON: '3.8' - NUMPY: '1.21' + NUMPY: '1.22' CONDA_ENV: 'azure_ci' TEST_START_INDEX: 0 - py311_np124: + py311_np125: PYTHON: '3.11' - NUMPY: '1.24' + NUMPY: '1.25' CONDA_ENV: 'azure_ci' TEST_THREADING: 'tbb' TEST_START_INDEX: 1 @@ -29,46 +29,35 @@ jobs: name: Linux vmImage: ubuntu-20.04 matrix: - py38_np121_vanilla: - PYTHON: '3.8' - NUMPY: '1.21' - CONDA_ENV: azure_ci - VANILLA_INSTALL: yes - TEST_START_INDEX: 2 py38_np122_tbb: PYTHON: '3.8' NUMPY: '1.22' CONDA_ENV: azure_ci TEST_THREADING: 'tbb' - TEST_START_INDEX: 3 + TEST_START_INDEX: 2 py38_np122_omp: PYTHON: '3.8' NUMPY: '1.22' CONDA_ENV: azure_ci TEST_THREADING: omp - TEST_START_INDEX: 4 + TEST_START_INDEX: 3 py38_np122_workqueue: PYTHON: '3.8' NUMPY: '1.22' CONDA_ENV: azure_ci TEST_THREADING: workqueue - TEST_START_INDEX: 5 + TEST_START_INDEX: 4 py38_np123_svml: PYTHON: '3.8' NUMPY: '1.23' CONDA_ENV: azure_ci TEST_SVML: yes - TEST_START_INDEX: 6 + TEST_START_INDEX: 5 py38_np124: PYTHON: '3.8' NUMPY: '1.24' CONDA_ENV: azure_ci - TEST_START_INDEX: 7 - py39_np121: - PYTHON: '3.9' - NUMPY: '1.21' - CONDA_ENV: azure_ci - TEST_START_INDEX: 8 + TEST_START_INDEX: 6 py39_np122_cov_doc: PYTHON: '3.9' NUMPY: '1.22' @@ -77,47 +66,57 @@ jobs: RUN_FLAKE8: yes RUN_MYPY: yes BUILD_DOC: yes - TEST_START_INDEX: 9 + TEST_START_INDEX: 7 py39_np123_typeguard: PYTHON: '3.9' NUMPY: '1.23' CONDA_ENV: azure_ci RUN_TYPEGUARD: yes - TEST_START_INDEX: 10 + TEST_START_INDEX: 8 py39_np124: PYTHON: '3.9' NUMPY: '1.24' CONDA_ENV: azure_ci - TEST_START_INDEX: 11 - py310_np121: - PYTHON: '3.10' - NUMPY: '1.21' + TEST_START_INDEX: 9 + py39_np125: + PYTHON: '3.9' + NUMPY: '1.25' CONDA_ENV: azure_ci - TEST_START_INDEX: 12 + TEST_START_INDEX: 10 py310_np122: PYTHON: '3.10' NUMPY: '1.22' CONDA_ENV: azure_ci - TEST_START_INDEX: 13 + TEST_START_INDEX: 11 py310_np123: PYTHON: '3.10' NUMPY: '1.23' CONDA_ENV: azure_ci - TEST_START_INDEX: 14 + TEST_START_INDEX: 12 py310_np124: PYTHON: '3.10' NUMPY: '1.24' CONDA_ENV: azure_ci - TEST_START_INDEX: 15 + TEST_START_INDEX: 13 + py310_np125: + PYTHON: '3.10' + NUMPY: '1.25' + CONDA_ENV: azure_ci + TEST_START_INDEX: 14 py311_np123: PYTHON: '3.11' NUMPY: '1.23' CONDA_ENV: azure_ci - TEST_START_INDEX: 16 + TEST_START_INDEX: 15 py311_np124: PYTHON: '3.11' NUMPY: '1.24' CONDA_ENV: azure_ci + TEST_START_INDEX: 16 + py311_np125: + PYTHON: '3.11' + NUMPY: '1.25' + CONDA_ENV: azure_ci TEST_START_INDEX: 17 - template: buildscripts/azure/azure-windows.yml diff --git a/buildscripts/azure/azure-windows.yml b/buildscripts/azure/azure-windows.yml index 65a8415e87..e5186f185a 100644 --- a/buildscripts/azure/azure-windows.yml +++ b/buildscripts/azure/azure-windows.yml @@ -8,14 +8,14 @@ jobs: vmImage: ${{ parameters.vmImage }} strategy: matrix: - py38_np121: + py38_np122: PYTHON: '3.8' - NUMPY: '1.21' + NUMPY: '1.22.3' CONDA_ENV: 'testenv' TEST_START_INDEX: 18 - py311_np124: + py311_np125: PYTHON: '3.11' - NUMPY: '1.24' + NUMPY: '1.25' CONDA_ENV: 'testenv' TEST_START_INDEX: 19 diff --git a/buildscripts/condarecipe.local/meta.yaml b/buildscripts/condarecipe.local/meta.yaml index ff152a932c..f4cfd307f2 100644 --- a/buildscripts/condarecipe.local/meta.yaml +++ b/buildscripts/condarecipe.local/meta.yaml @@ -42,7 +42,7 @@ requirements: run: - python >=3.8 # NumPy 1.22.0, 1.22.1, 1.22.2 are all broken for ufuncs, see #7756 - - numpy >=1.21, !=1.22.0, !=1.22.1, !=1.22.2 + - numpy >=1.22.3 - importlib_metadata # [py<39] # On channel https://anaconda.org/numba/ - llvmlite >=0.41.0dev0,<0.41 diff --git a/buildscripts/gpuci/build.sh b/buildscripts/gpuci/build.sh index 5dbf6d681f..a0f2e35fbe 100644 --- a/buildscripts/gpuci/build.sh +++ b/buildscripts/gpuci/build.sh @@ -35,7 +35,7 @@ fi; # Test with different NumPy versions with each toolkit (it's not worth testing # the Cartesian product of versions here, we just need to test with different # CUDA and NumPy versions). -declare -A CTK_NUMPY_VMAP=( ["11.2"]="1.21" ["11.3"]="1.22" ["11.5"]="1.23" ["11.8"]="1.24") +declare -A CTK_NUMPY_VMAP=( ["11.2"]="1.22" ["11.3"]="1.23" ["11.5"]="1.24" ["11.8"]="1.25") NUMPY_VER="${CTK_NUMPY_VMAP[$CUDA_TOOLKIT_VER]}" ################################################################################ @@ -51,7 +51,7 @@ nvidia-smi gpuci_logger "Create testing env" . /opt/conda/etc/profile.d/conda.sh gpuci_mamba_retry create -n numba_ci -y \ - "python=${PYTHON_VER}" \ + "python=3.10" \ "cudatoolkit=${CUDA_TOOLKIT_VER}" \ "rapidsai::cubinlinker" \ "conda-forge::ptxcompiler" \ diff --git a/buildscripts/incremental/setup_conda_environment.cmd b/buildscripts/incremental/setup_conda_environment.cmd index f306d746e5..76c828be02 100644 --- a/buildscripts/incremental/setup_conda_environment.cmd +++ b/buildscripts/incremental/setup_conda_environment.cmd @@ -14,10 +14,6 @@ cmd /C conda info set CONDA_INSTALL=cmd /C conda install -q -y set PIP_INSTALL=pip install -q -@rem Use conda-forge for NumPy 1.24 - at the time of writing it is not available -@rem on the defaults channel. -if %NUMPY%==1.24 (set NUMPY_CHANNEL_PKG="conda-forge::numpy") else (set NUMPY_CHANNEL_PKG="numpy") - @echo on @rem Deactivate any environment @@ -25,7 +21,7 @@ call deactivate @rem Display root environment (for debugging) conda list @rem Scipy, CFFI, jinja2 and IPython are optional dependencies, but exercised in the test suite -conda create -n %CONDA_ENV% -q -y python=%PYTHON% %NUMPY_CHANNEL_PKG%=%NUMPY% cffi pip scipy jinja2 ipython gitpython pyyaml +conda create -n %CONDA_ENV% -q -y python=%PYTHON% numpy=%NUMPY% cffi pip scipy jinja2 ipython gitpython pyyaml call activate %CONDA_ENV% @rem Install latest llvmlite build diff --git a/buildscripts/incremental/setup_conda_environment.sh b/buildscripts/incremental/setup_conda_environment.sh index 75d095d8de..e9c98c0051 100755 --- a/buildscripts/incremental/setup_conda_environment.sh +++ b/buildscripts/incremental/setup_conda_environment.sh @@ -27,15 +27,6 @@ source deactivate # Display root environment (for debugging) conda list -# Use conda-forge for NumPy 1.24 - at the time of writing it is not available -# on the defaults channel. - -if [ "${NUMPY}" == "1.24" ]; then - NUMPY_CHANNEL_PKG=conda-forge::numpy -else - NUMPY_CHANNEL_PKG=numpy -fi - # If VANILLA_INSTALL is yes, then only Python, NumPy and pip are installed, this # is to catch tests/code paths that require an optional package and are not # guarding against the possibility that it does not exist in the environment. @@ -43,7 +34,7 @@ fi # NOTE: gitpython is needed for CI testing to do the test slicing # NOTE: pyyaml is used to ensure that the Azure CI config is valid -conda create -n $CONDA_ENV -q -y ${EXTRA_CHANNELS} python=$PYTHON $NUMPY_CHANNEL_PKG=$NUMPY pip gitpython pyyaml +conda create -n $CONDA_ENV -q -y ${EXTRA_CHANNELS} python=$PYTHON numpy=$NUMPY pip gitpython pyyaml # Activate first set +v diff --git a/docs/source/user/installing.rst b/docs/source/user/installing.rst index 6ae5279cef..5d0201f658 100644 --- a/docs/source/user/installing.rst +++ b/docs/source/user/installing.rst @@ -262,6 +262,8 @@ information. +----------++--------------+---------------------------+----------------------------+------------------------------+-------------------+-----------------------------+ | Numba | Release date | Python | NumPy | llvmlite | LLVM | TBB | +===========+==============+===========================+============================+==============================+===================+=============================+ +| 0.58.0 | UNRELEASED | 3.8.x <= version < 3.12 | 1.21 <= version < 1.26 | 0.41.x | 14.x | 2021.6 <= version | ++-----------+--------------+---------------------------+----------------------------+------------------------------+-------------------+-----------------------------+ | 0.57.1 | 2023-06-21 | 3.8.x <= version < 3.12 | 1.21 <= version < 1.25 | 0.40.x | 14.x | 2021.6 <= version | +-----------+--------------+---------------------------+----------------------------+------------------------------+-------------------+-----------------------------+ | 0.57.0 | 2023-05-01 | 3.8.x <= version < 3.12 | 1.21 <= version < 1.25 | 0.40.x | 14.x | 2021.6 <= version | diff --git a/docs/upcoming_changes/9011.np_support.rst b/docs/upcoming_changes/9011.np_support.rst new file mode 100644 index 0000000000..8f65036ea6 --- /dev/null +++ b/docs/upcoming_changes/9011.np_support.rst @@ -0,0 +1,5 @@ + +Add support for NumPy 1.25 +========================== + +Extend Numba to support new and changed features released in NumPy 1.25. diff --git a/numba/core/typing/npdatetime.py b/numba/core/typing/npdatetime.py index 4ab05f4396..8760189f50 100644 --- a/numba/core/typing/npdatetime.py +++ b/numba/core/typing/npdatetime.py @@ -11,6 +11,7 @@ AbstractTemplate, infer_global, infer, infer_getattr, signature) from numba.np import npdatetime_helpers +from numba.np.numpy_support import numpy_version # timedelta64-only operations @@ -144,13 +145,22 @@ class TimedeltaTrueDiv(TimedeltaDivOp): class TimedeltaFloorDiv(TimedeltaDivOp): key = operator.floordiv -@infer_global(operator.eq) -class TimedeltaCmpEq(TimedeltaCmpOp): - key = operator.eq +if numpy_version >= (1, 25): + @infer_global(operator.eq) + class TimedeltaCmpEq(TimedeltaOrderedCmpOp): + key = operator.eq -@infer_global(operator.ne) -class TimedeltaCmpNe(TimedeltaCmpOp): - key = operator.ne + @infer_global(operator.ne) + class TimedeltaCmpNe(TimedeltaOrderedCmpOp): + key = operator.ne +else: + @infer_global(operator.eq) + class TimedeltaCmpEq(TimedeltaCmpOp): + key = operator.eq + + @infer_global(operator.ne) + class TimedeltaCmpNe(TimedeltaCmpOp): + key = operator.ne @infer_global(operator.lt) class TimedeltaCmpLt(TimedeltaOrderedCmpOp): @@ -228,7 +238,6 @@ def generic(self, args, kws): left, right = args if isinstance(left, types.NPDatetime) and isinstance(right, types.NPDatetime): - # All units compatible... unit = npdatetime_helpers.get_best_unit(left.unit, right.unit) return signature(types.NPTimedelta(unit), left, right) diff --git a/numba/cpython/numbers.py b/numba/cpython/numbers.py index d011523596..9c616cb8e3 100644 --- a/numba/cpython/numbers.py +++ b/numba/cpython/numbers.py @@ -361,6 +361,38 @@ def int_ne_impl(context, builder, sig, args): return impl_ret_untracked(context, builder, sig.return_type, res) +def int_signed_unsigned_cmp(op): + def impl(context, builder, sig, args): + (left, right) = args + # This code is translated from the NumPy source. + # What we're going to do is divide the range of a signed value at zero. + # If the signed value is less than zero, then we can treat zero as the + # unsigned value since the unsigned value is necessarily zero or larger + # and any signed comparison between a negative value and zero/infinity + # will yield the same result. If the signed value is greater than or + # equal to zero, then we can safely cast it to an unsigned value and do + # the expected unsigned-unsigned comparison operation. + # Original: https://github.com/numpy/numpy/pull/23713 + cmp_zero = builder.icmp_signed('<', left, Constant(left.type, 0)) + lt_zero = builder.icmp_signed(op, left, Constant(left.type, 0)) + ge_zero = builder.icmp_unsigned(op, left, right) + res = builder.select(cmp_zero, lt_zero, ge_zero) + return impl_ret_untracked(context, builder, sig.return_type, res) + return impl + + +def int_unsigned_signed_cmp(op): + def impl(context, builder, sig, args): + (left, right) = args + # See the function `int_signed_unsigned_cmp` for implementation notes. + cmp_zero = builder.icmp_signed('<', right, Constant(right.type, 0)) + lt_zero = builder.icmp_signed(op, Constant(right.type, 0), right) + ge_zero = builder.icmp_unsigned(op, left, right) + res = builder.select(cmp_zero, lt_zero, ge_zero) + return impl_ret_untracked(context, builder, sig.return_type, res) + return impl + + def int_abs_impl(context, builder, sig, args): [x] = args ZERO = Constant(x.type, None) diff --git a/numba/cuda/tests/cudapy/test_gufunc.py b/numba/cuda/tests/cudapy/test_gufunc.py index 5f9914e478..8f4de36ea7 100644 --- a/numba/cuda/tests/cudapy/test_gufunc.py +++ b/numba/cuda/tests/cudapy/test_gufunc.py @@ -1,5 +1,4 @@ import numpy as np -import numpy.core.umath_tests as ut from collections import namedtuple from numba import void, int32, float32, float64 @@ -42,7 +41,7 @@ def test_gufunc_small(self): 5) C = gufunc(A, B) - Gold = ut.matrix_multiply(A, B) + Gold = np.matmul(A, B) self.assertTrue(np.allclose(C, Gold)) def test_gufunc_auto_transfer(self): @@ -58,7 +57,7 @@ def test_gufunc_auto_transfer(self): dB = cuda.to_device(B) C = gufunc(A, dB).copy_to_host() - Gold = ut.matrix_multiply(A, B) + Gold = np.matmul(A, B) self.assertTrue(np.allclose(C, Gold)) def test_gufunc(self): @@ -72,7 +71,7 @@ def test_gufunc(self): 5) C = gufunc(A, B) - Gold = ut.matrix_multiply(A, B) + Gold = np.matmul(A, B) self.assertTrue(np.allclose(C, Gold)) def test_gufunc_hidim(self): @@ -84,7 +83,7 @@ def test_gufunc_hidim(self): B = np.arange(matrix_ct * 4 * 5, dtype=np.float32).reshape(4, 25, 4, 5) C = gufunc(A, B) - Gold = ut.matrix_multiply(A, B) + Gold = np.matmul(A, B) self.assertTrue(np.allclose(C, Gold)) def test_gufunc_new_axis(self): @@ -94,7 +93,7 @@ def test_gufunc_new_axis(self): X = np.random.randn(10, 3, 3) Y = np.random.randn(3, 3) - gold = ut.matrix_multiply(X, Y) + gold = np.matmul(X, Y) res1 = gufunc(X, Y) np.testing.assert_allclose(gold, res1) @@ -122,7 +121,7 @@ def test_gufunc_stream(self): C = dC.copy_to_host(stream=stream) stream.synchronize() - Gold = ut.matrix_multiply(A, B) + Gold = np.matmul(A, B) self.assertTrue(np.allclose(C, Gold)) diff --git a/numba/cuda/tests/cudapy/test_ufuncs.py b/numba/cuda/tests/cudapy/test_ufuncs.py index 1d56057975..64c472eaed 100644 --- a/numba/cuda/tests/cudapy/test_ufuncs.py +++ b/numba/cuda/tests/cudapy/test_ufuncs.py @@ -199,22 +199,22 @@ def test_radians_ufunc(self): ############################################################################ # Comparison functions def test_greater_ufunc(self): - self.basic_ufunc_test(np.greater) + self.signed_unsigned_cmp_test(np.greater) def test_greater_equal_ufunc(self): - self.basic_ufunc_test(np.greater_equal) + self.signed_unsigned_cmp_test(np.greater_equal) def test_less_ufunc(self): - self.basic_ufunc_test(np.less) + self.signed_unsigned_cmp_test(np.less) def test_less_equal_ufunc(self): - self.basic_ufunc_test(np.less_equal) + self.signed_unsigned_cmp_test(np.less_equal) def test_not_equal_ufunc(self): - self.basic_ufunc_test(np.not_equal) + self.signed_unsigned_cmp_test(np.not_equal) def test_equal_ufunc(self): - self.basic_ufunc_test(np.equal) + self.signed_unsigned_cmp_test(np.equal) def test_logical_and_ufunc(self): self.basic_ufunc_test(np.logical_and) diff --git a/numba/cuda/ufuncs.py b/numba/cuda/ufuncs.py index 8657220282..10b9308804 100644 --- a/numba/cuda/ufuncs.py +++ b/numba/cuda/ufuncs.py @@ -23,6 +23,7 @@ def ufunc_db(): # Imports here are at function scope to avoid circular imports from numba.cpython import cmathimpl, mathimpl, numbers from numba.np import npyfuncs + from numba.np.numpy_support import numpy_version def np_unary_impl(fn, context, builder, sig, args): npyfuncs._check_arity_and_homogeneity(sig, args, 1) @@ -287,6 +288,10 @@ def np_real_atanh_impl(context, builder, sig, args): 'FF->?': npyfuncs.np_complex_gt_impl, 'DD->?': npyfuncs.np_complex_gt_impl, } + if numpy_version >= (1, 25): + db[np.greater].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('>'), + 'Qq->?': numbers.int_unsigned_signed_cmp('>')}) db[np.greater_equal] = { '??->?': numbers.int_uge_impl, @@ -305,6 +310,10 @@ def np_real_atanh_impl(context, builder, sig, args): 'FF->?': npyfuncs.np_complex_ge_impl, 'DD->?': npyfuncs.np_complex_ge_impl, } + if numpy_version >= (1, 25): + db[np.greater_equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('>='), + 'Qq->?': numbers.int_unsigned_signed_cmp('>=')}) db[np.less] = { '??->?': numbers.int_ult_impl, @@ -323,6 +332,10 @@ def np_real_atanh_impl(context, builder, sig, args): 'FF->?': npyfuncs.np_complex_lt_impl, 'DD->?': npyfuncs.np_complex_lt_impl, } + if numpy_version >= (1, 25): + db[np.less].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('<'), + 'Qq->?': numbers.int_unsigned_signed_cmp('<')}) db[np.less_equal] = { '??->?': numbers.int_ule_impl, @@ -341,6 +354,10 @@ def np_real_atanh_impl(context, builder, sig, args): 'FF->?': npyfuncs.np_complex_le_impl, 'DD->?': npyfuncs.np_complex_le_impl, } + if numpy_version >= (1, 25): + db[np.less_equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('<='), + 'Qq->?': numbers.int_unsigned_signed_cmp('<=')}) db[np.not_equal] = { '??->?': numbers.int_ne_impl, @@ -359,6 +376,10 @@ def np_real_atanh_impl(context, builder, sig, args): 'FF->?': npyfuncs.np_complex_ne_impl, 'DD->?': npyfuncs.np_complex_ne_impl, } + if numpy_version >= (1, 25): + db[np.not_equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('!='), + 'Qq->?': numbers.int_unsigned_signed_cmp('!=')}) db[np.equal] = { '??->?': numbers.int_eq_impl, @@ -377,6 +398,10 @@ def np_real_atanh_impl(context, builder, sig, args): 'FF->?': npyfuncs.np_complex_eq_impl, 'DD->?': npyfuncs.np_complex_eq_impl, } + if numpy_version >= (1, 25): + db[np.equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('=='), + 'Qq->?': numbers.int_unsigned_signed_cmp('==')}) db[np.logical_and] = { '??->?': npyfuncs.np_logical_and_impl, diff --git a/numba/np/arraymath.py b/numba/np/arraymath.py index e9d1032b47..a81d77c773 100644 --- a/numba/np/arraymath.py +++ b/numba/np/arraymath.py @@ -489,6 +489,7 @@ def return_false(a): @overload(np.min) +@overload(np.amin) @overload_method(types.Array, "min") def npy_min(a): if not isinstance(a, types.Array): @@ -538,6 +539,7 @@ def impl_min(a): @overload(np.max) +@overload(np.amax) @overload_method(types.Array, "max") def npy_max(a): if not isinstance(a, types.Array): @@ -3138,6 +3140,7 @@ def round_ndigits(x, ndigits): @overload(np.around) @overload(np.round) +@overload(np.round_) def impl_np_round(a, decimals=0, out=None): if not type_can_asarray(a): raise TypingError('The argument "a" must be array-like') diff --git a/numba/np/ufunc_db.py b/numba/np/ufunc_db.py index 6ce1c3ab3d..cf2422f6db 100644 --- a/numba/np/ufunc_db.py +++ b/numba/np/ufunc_db.py @@ -620,6 +620,10 @@ def _fill_ufunc_db(ufunc_db): 'FF->?': npyfuncs.np_complex_gt_impl, 'DD->?': npyfuncs.np_complex_gt_impl, } + if numpy_version >= (1, 25): + ufunc_db[np.greater].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('>'), + 'Qq->?': numbers.int_unsigned_signed_cmp('>')}) ufunc_db[np.greater_equal] = { '??->?': numbers.int_uge_impl, @@ -638,6 +642,10 @@ def _fill_ufunc_db(ufunc_db): 'FF->?': npyfuncs.np_complex_ge_impl, 'DD->?': npyfuncs.np_complex_ge_impl, } + if numpy_version >= (1, 25): + ufunc_db[np.greater_equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('>='), + 'Qq->?': numbers.int_unsigned_signed_cmp('>=')}) ufunc_db[np.less] = { '??->?': numbers.int_ult_impl, @@ -656,6 +664,10 @@ def _fill_ufunc_db(ufunc_db): 'FF->?': npyfuncs.np_complex_lt_impl, 'DD->?': npyfuncs.np_complex_lt_impl, } + if numpy_version >= (1, 25): + ufunc_db[np.less].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('<'), + 'Qq->?': numbers.int_unsigned_signed_cmp('<')}) ufunc_db[np.less_equal] = { '??->?': numbers.int_ule_impl, @@ -674,6 +686,10 @@ def _fill_ufunc_db(ufunc_db): 'FF->?': npyfuncs.np_complex_le_impl, 'DD->?': npyfuncs.np_complex_le_impl, } + if numpy_version >= (1, 25): + ufunc_db[np.less_equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('<='), + 'Qq->?': numbers.int_unsigned_signed_cmp('<=')}) ufunc_db[np.not_equal] = { '??->?': numbers.int_ne_impl, @@ -692,6 +708,10 @@ def _fill_ufunc_db(ufunc_db): 'FF->?': npyfuncs.np_complex_ne_impl, 'DD->?': npyfuncs.np_complex_ne_impl, } + if numpy_version >= (1, 25): + ufunc_db[np.not_equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('!='), + 'Qq->?': numbers.int_unsigned_signed_cmp('!=')}) ufunc_db[np.equal] = { '??->?': numbers.int_eq_impl, @@ -710,6 +730,10 @@ def _fill_ufunc_db(ufunc_db): 'FF->?': npyfuncs.np_complex_eq_impl, 'DD->?': npyfuncs.np_complex_eq_impl, } + if numpy_version >= (1, 25): + ufunc_db[np.equal].update({ + 'qQ->?': numbers.int_signed_unsigned_cmp('=='), + 'Qq->?': numbers.int_unsigned_signed_cmp('==')}) ufunc_db[np.logical_and] = { '??->?': npyfuncs.np_logical_and_impl, diff --git a/numba/tests/npyufunc/test_gufunc.py b/numba/tests/npyufunc/test_gufunc.py index c5c6f9c94c..44fff6fb78 100644 --- a/numba/tests/npyufunc/test_gufunc.py +++ b/numba/tests/npyufunc/test_gufunc.py @@ -2,7 +2,6 @@ import pickle import numpy as np -import numpy.core.umath_tests as ut from numba import void, float32, int64, jit, guvectorize from numba.np.ufunc import GUVectorize @@ -33,7 +32,7 @@ def check_matmul_gufunc(self, gufunc): B = np.arange(matrix_ct * 4 * 5, dtype=np.float32).reshape(matrix_ct, 4, 5) C = gufunc(A, B) - Gold = ut.matrix_multiply(A, B) + Gold = np.matmul(A, B) np.testing.assert_allclose(C, Gold, rtol=1e-5, atol=1e-8) @@ -108,7 +107,7 @@ class TestDynamicGUFunc(TestCase): def test_dynamic_matmul(self): def check_matmul_gufunc(gufunc, A, B, C): - Gold = ut.matrix_multiply(A, B) + Gold = np.matmul(A, B) gufunc(A, B, C) np.testing.assert_allclose(C, Gold, rtol=1e-5, atol=1e-8) diff --git a/numba/tests/test_array_methods.py b/numba/tests/test_array_methods.py index c73db5403b..a4dc2d5f70 100644 --- a/numba/tests/test_array_methods.py +++ b/numba/tests/test_array_methods.py @@ -30,6 +30,9 @@ def np_around_unary(val): def np_round_array(arr, decimals, out): np.round(arr, decimals, out) +def np_round__array(arr, decimals, out): + np.round_(arr, decimals, out) + def np_round_binary(val, decimals): return np.round(val, decimals) @@ -365,6 +368,9 @@ def test_round_array(self): def test_around_array(self): self.check_round_array(np_around_array) + def test_round__array(self): + self.check_round_array(np_round__array) + def test_around_bad_array(self): for pyfunc in (np_round_unary, np_around_unary): cfunc = jit(nopython=True)(pyfunc) @@ -373,7 +379,7 @@ def test_around_bad_array(self): cfunc(None) def test_around_bad_out(self): - for py_func in (np_round_array, np_around_array): + for py_func in (np_round_array, np_around_array, np_round__array): cfunc = jit(nopython=True)(py_func) msg = '.*The argument "out" must be an array if it is provided.*' with self.assertRaisesRegex(TypingError, msg): diff --git a/numba/tests/test_array_reductions.py b/numba/tests/test_array_reductions.py index 40491927f2..5f096456fa 100644 --- a/numba/tests/test_array_reductions.py +++ b/numba/tests/test_array_reductions.py @@ -75,12 +75,18 @@ def array_min(arr): def array_min_global(arr): return np.min(arr) +def array_amin(arr): + return np.amin(arr) + def array_max(arr): return arr.max() def array_max_global(arr): return np.max(arr) +def array_amax(arr): + return np.amax(arr) + def array_argmin(arr): return arr.argmin() @@ -1110,6 +1116,7 @@ def install_generated_tests(cls): array_all, array_all_global, array_any, array_any_global, array_min, array_min_global, + array_amax, array_amin, array_max, array_max_global, array_nanmax, array_nanmin, array_nansum, diff --git a/numba/tests/test_fancy_indexing.py b/numba/tests/test_fancy_indexing.py index 5d909963fd..e160063c04 100644 --- a/numba/tests/test_fancy_indexing.py +++ b/numba/tests/test_fancy_indexing.py @@ -406,7 +406,7 @@ def numba_get_item(array, idx): expected = get_item(arr, index) got = numba_get_item(arr, index) # Sanity check: In advanced indexing, the result is always a copy. - self.assertNotIn(expected.base, orig_base) + self.assertIsNot(expected.base, orig_base) # Note: Numba may not return the same array strides and # contiguity as NumPy diff --git a/numba/tests/test_npdatetime.py b/numba/tests/test_npdatetime.py index 022f3ad313..1ffd000aee 100644 --- a/numba/tests/test_npdatetime.py +++ b/numba/tests/test_npdatetime.py @@ -411,10 +411,18 @@ def check(a, b, expected): check(TD('Nat', 'ms'), TD('Nat', 's'), True) check(TD('Nat'), TD(1), False) # Incompatible units => timedeltas compare unequal - check(TD(1, 'Y'), TD(365, 'D'), False) - check(TD(1, 'Y'), TD(366, 'D'), False) - # ... except when both are NaT! - check(TD('NaT', 'W'), TD('NaT', 'D'), True) + if numpy_version < (1, 25): + check(TD(1, 'Y'), TD(365, 'D'), False) + check(TD(1, 'Y'), TD(366, 'D'), False) + # ... except when both are NaT! + check(TD('NaT', 'W'), TD('NaT', 'D'), True) + else: + # incompatible units raise + # The exception is different depending on Python mode + with self.assertRaises((TypeError, TypingError)): + eq(TD(1, 'Y'), TD(365, 'D')) + with self.assertRaises((TypeError, TypingError)): + ne(TD(1, 'Y'), TD(365, 'D')) def test_lt_ge(self): lt = self.jit(lt_usecase) diff --git a/numba/tests/test_ufuncs.py b/numba/tests/test_ufuncs.py index 1a8502e096..f296480243 100644 --- a/numba/tests/test_ufuncs.py +++ b/numba/tests/test_ufuncs.py @@ -256,6 +256,56 @@ def basic_ufunc_test(self, ufunc, skip_inputs=[], additional_inputs=[], else: raise + def signed_unsigned_cmp_test(self, comparison_ufunc): + self.basic_ufunc_test(comparison_ufunc) + + if numpy_support.numpy_version < (1, 25): + return + + # Test additional implementations that specifically handle signed / + # unsigned comparisons added in NumPy 1.25: + # https://github.com/numpy/numpy/pull/23713 + additional_inputs = ( + (np.int64(-1), np.uint64(0)), + (np.int64(-1), np.uint64(1)), + (np.int64(0), np.uint64(0)), + (np.int64(0), np.uint64(1)), + (np.int64(1), np.uint64(0)), + (np.int64(1), np.uint64(1)), + + (np.uint64(0), np.int64(-1)), + (np.uint64(0), np.int64(0)), + (np.uint64(0), np.int64(1)), + (np.uint64(1), np.int64(-1)), + (np.uint64(1), np.int64(0)), + (np.uint64(1), np.int64(1)), + + (np.array([-1, -1, 0, 0, 1, 1], dtype=np.int64), + np.array([0, 1, 0, 1, 0, 1], dtype=np.uint64)), + + (np.array([0, 1, 0, 1, 0, 1], dtype=np.uint64), + np.array([-1, -1, 0, 0, 1, 1], dtype=np.int64)) + ) + + pyfunc = self._make_ufunc_usecase(comparison_ufunc) + + for a, b in additional_inputs: + input_types = (typeof(a), typeof(b)) + output_type = types.Array(types.bool_, 1, 'C') + argtys = input_types + (output_type,) + cfunc = self._compile(pyfunc, argtys) + + if isinstance(a, np.ndarray): + result = np.zeros(a.shape, dtype=np.bool_) + else: + result = np.zeros(1, dtype=np.bool_) + + expected = np.zeros_like(result) + + pyfunc(a, b, expected) + cfunc(a, b, result) + np.testing.assert_equal(expected, result) + class TestUFuncs(BasicUFuncTest, TestCase): def basic_int_ufunc_test(self, name=None): @@ -505,22 +555,22 @@ def test_bitwise_not_ufunc(self): ############################################################################ # Comparison functions def test_greater_ufunc(self): - self.basic_ufunc_test(np.greater) + self.signed_unsigned_cmp_test(np.greater) def test_greater_equal_ufunc(self): - self.basic_ufunc_test(np.greater_equal) + self.signed_unsigned_cmp_test(np.greater_equal) def test_less_ufunc(self): - self.basic_ufunc_test(np.less) + self.signed_unsigned_cmp_test(np.less) def test_less_equal_ufunc(self): - self.basic_ufunc_test(np.less_equal) + self.signed_unsigned_cmp_test(np.less_equal) def test_not_equal_ufunc(self): - self.basic_ufunc_test(np.not_equal) + self.signed_unsigned_cmp_test(np.not_equal) def test_equal_ufunc(self): - self.basic_ufunc_test(np.equal) + self.signed_unsigned_cmp_test(np.equal) def test_logical_and_ufunc(self): self.basic_ufunc_test(np.logical_and)