From db127d4c2ea48cd5fcf02b76f53c16afc77d4a08 Mon Sep 17 00:00:00 2001 From: vtavana <120411540+vtavana@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:19:25 -0600 Subject: [PATCH] implement dpnp.prod and dpnp.nanprod (#1613) * implement dpnp.prod and dpnp.nanprod * address comments * updates for nanprod input array * allow fall back on numpy - needed for Win tests --- dpnp/backend/include/dpnp_iface_fptr.hpp | 4 +- dpnp/backend/kernels/dpnp_krnl_reduction.cpp | 49 ----- dpnp/dpnp_algo/dpnp_algo.pxd | 2 - dpnp/dpnp_algo/dpnp_algo_mathematical.pxi | 84 -------- dpnp/dpnp_array.py | 4 +- dpnp/dpnp_iface_mathematical.py | 185 +++++++++++++----- tests/skipped_tests.tbl | 12 +- tests/skipped_tests_gpu.tbl | 66 ++----- tests/test_arithmetic.py | 4 +- tests/test_mathematical.py | 105 ++++++++++ tests/test_usm_type.py | 2 + .../cupy/math_tests/test_sumprod.py | 25 ++- 12 files changed, 279 insertions(+), 263 deletions(-) diff --git a/dpnp/backend/include/dpnp_iface_fptr.hpp b/dpnp/backend/include/dpnp_iface_fptr.hpp index 6f282e1e1f6..be64c6727f9 100644 --- a/dpnp/backend/include/dpnp_iface_fptr.hpp +++ b/dpnp/backend/include/dpnp_iface_fptr.hpp @@ -244,9 +244,7 @@ enum class DPNPFuncName : size_t DPNP_FN_PLACE, /**< Used in numpy.place() impl */ DPNP_FN_POWER, /**< Used in numpy.power() impl */ DPNP_FN_PROD, /**< Used in numpy.prod() impl */ - DPNP_FN_PROD_EXT, /**< Used in numpy.prod() impl, requires extra parameters - */ - DPNP_FN_PTP, /**< Used in numpy.ptp() impl */ + DPNP_FN_PTP, /**< Used in numpy.ptp() impl */ DPNP_FN_PTP_EXT, /**< Used in numpy.ptp() impl, requires extra parameters */ DPNP_FN_PUT, /**< Used in numpy.put() impl */ DPNP_FN_PUT_ALONG_AXIS, /**< Used in numpy.put_along_axis() impl */ diff --git a/dpnp/backend/kernels/dpnp_krnl_reduction.cpp b/dpnp/backend/kernels/dpnp_krnl_reduction.cpp index 439e10d34af..d9534379102 100644 --- a/dpnp/backend/kernels/dpnp_krnl_reduction.cpp +++ b/dpnp/backend/kernels/dpnp_krnl_reduction.cpp @@ -294,19 +294,6 @@ void (*dpnp_prod_default_c)(void *, const long *) = dpnp_prod_c<_DataType_output, _DataType_input>; -template -DPCTLSyclEventRef (*dpnp_prod_ext_c)(DPCTLSyclQueueRef, - void *, - const void *, - const shape_elem_type *, - const size_t, - const shape_elem_type *, - const size_t, - const void *, - const long *, - const DPCTLEventVectorRef) = - dpnp_prod_c<_DataType_output, _DataType_input>; - void func_map_init_reduction(func_map_t &fmap) { // WARNING. The meaning of the fmap is changed. Second argument represents @@ -349,42 +336,6 @@ void func_map_init_reduction(func_map_t &fmap) fmap[DPNPFuncName::DPNP_FN_PROD][eft_DBL][eft_DBL] = { eft_DBL, (void *)dpnp_prod_default_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_INT][eft_INT] = { - eft_LNG, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_INT][eft_LNG] = { - eft_LNG, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_INT][eft_FLT] = { - eft_FLT, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_INT][eft_DBL] = { - eft_DBL, (void *)dpnp_prod_ext_c}; - - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_LNG][eft_INT] = { - eft_INT, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_LNG][eft_LNG] = { - eft_LNG, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_LNG][eft_FLT] = { - eft_FLT, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_LNG][eft_DBL] = { - eft_DBL, (void *)dpnp_prod_ext_c}; - - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_FLT][eft_INT] = { - eft_INT, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_FLT][eft_LNG] = { - eft_LNG, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_FLT][eft_FLT] = { - eft_FLT, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_FLT][eft_DBL] = { - eft_DBL, (void *)dpnp_prod_ext_c}; - - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_DBL][eft_INT] = { - eft_INT, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_DBL][eft_LNG] = { - eft_LNG, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_DBL][eft_FLT] = { - eft_FLT, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PROD_EXT][eft_DBL][eft_DBL] = { - eft_DBL, (void *)dpnp_prod_ext_c}; - fmap[DPNPFuncName::DPNP_FN_SUM][eft_INT][eft_INT] = { eft_LNG, (void *)dpnp_sum_default_c}; fmap[DPNPFuncName::DPNP_FN_SUM][eft_INT][eft_LNG] = { diff --git a/dpnp/dpnp_algo/dpnp_algo.pxd b/dpnp/dpnp_algo/dpnp_algo.pxd index 7c0d00dd03d..7a71531c72a 100644 --- a/dpnp/dpnp_algo/dpnp_algo.pxd +++ b/dpnp/dpnp_algo/dpnp_algo.pxd @@ -126,8 +126,6 @@ cdef extern from "dpnp_iface_fptr.hpp" namespace "DPNPFuncName": # need this na DPNP_FN_PARTITION DPNP_FN_PARTITION_EXT DPNP_FN_PLACE - DPNP_FN_PROD - DPNP_FN_PROD_EXT DPNP_FN_PTP DPNP_FN_PTP_EXT DPNP_FN_QR diff --git a/dpnp/dpnp_algo/dpnp_algo_mathematical.pxi b/dpnp/dpnp_algo/dpnp_algo_mathematical.pxi index 21f7768cf2b..f9828229b53 100644 --- a/dpnp/dpnp_algo/dpnp_algo_mathematical.pxi +++ b/dpnp/dpnp_algo/dpnp_algo_mathematical.pxi @@ -50,9 +50,7 @@ __all__ += [ "dpnp_modf", "dpnp_nancumprod", "dpnp_nancumsum", - "dpnp_nanprod", "dpnp_nansum", - "dpnp_prod", "dpnp_sum", "dpnp_trapz", ] @@ -319,26 +317,6 @@ cpdef utils.dpnp_descriptor dpnp_nancumsum(utils.dpnp_descriptor x1): return dpnp_cumsum(x1_desc) -cpdef utils.dpnp_descriptor dpnp_nanprod(utils.dpnp_descriptor x1): - x1_obj = x1.get_array() - cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(x1.shape, - x1.dtype, - None, - device=x1_obj.sycl_device, - usm_type=x1_obj.usm_type, - sycl_queue=x1_obj.sycl_queue) - - for i in range(result.size): - input_elem = x1.get_pyobj().flat[i] - - if dpnp.isnan(input_elem): - result.get_pyobj().flat[i] = 1 - else: - result.get_pyobj().flat[i] = input_elem - - return dpnp_prod(result) - - cpdef utils.dpnp_descriptor dpnp_nansum(utils.dpnp_descriptor x1): x1_obj = x1.get_array() cdef utils.dpnp_descriptor result = utils_py.create_output_descriptor_py(x1.shape, @@ -359,68 +337,6 @@ cpdef utils.dpnp_descriptor dpnp_nansum(utils.dpnp_descriptor x1): return dpnp_sum(result) -cpdef utils.dpnp_descriptor dpnp_prod(utils.dpnp_descriptor x1, - object axis=None, - object dtype=None, - utils.dpnp_descriptor out=None, - cpp_bool keepdims=False, - object initial=None, - object where=True): - """ - input:float64 : output:float64 : name:prod - input:float32 : output:float32 : name:prod - input:int64 : output:int64 : name:prod - input:int32 : output:int64 : name:prod - input:bool : output:int64 : name:prod - input:complex64 : output:complex64 : name:prod - input:complex128: output:complex128: name:prod - """ - - cdef shape_type_c x1_shape = x1.shape - cdef DPNPFuncType x1_c_type = dpnp_dtype_to_DPNPFuncType(x1.dtype) - - cdef shape_type_c axis_shape = utils._object_to_tuple(axis) - - cdef shape_type_c result_shape = utils.get_reduction_output_shape(x1_shape, axis, keepdims) - cdef DPNPFuncType result_c_type = utils.get_output_c_type(DPNP_FN_PROD_EXT, x1_c_type, out, dtype) - - """ select kernel """ - cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_PROD_EXT, x1_c_type, result_c_type) - - x1_obj = x1.get_array() - - """ Create result array """ - cdef utils.dpnp_descriptor result = utils.create_output_descriptor(result_shape, - result_c_type, - out, - device=x1_obj.sycl_device, - usm_type=x1_obj.usm_type, - sycl_queue=x1_obj.sycl_queue) - cdef dpnp_reduction_c_t func = kernel_data.ptr - - result_sycl_queue = result.get_array().sycl_queue - - cdef c_dpctl.SyclQueue q = result_sycl_queue - cdef c_dpctl.DPCTLSyclQueueRef q_ref = q.get_queue_ref() - - """ Call FPTR interface function """ - cdef c_dpctl.DPCTLSyclEventRef event_ref = func(q_ref, - result.get_data(), - x1.get_data(), - x1_shape.data(), - x1_shape.size(), - axis_shape.data(), - axis_shape.size(), - NULL, - NULL, - NULL) # dep_events_ref - - with nogil: c_dpctl.DPCTLEvent_WaitAndThrow(event_ref) - c_dpctl.DPCTLEvent_Delete(event_ref) - - return result - - cpdef utils.dpnp_descriptor dpnp_sum(utils.dpnp_descriptor x1, object axis=None, object dtype=None, diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index ce80de31be5..9e8a8096a0f 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -1026,9 +1026,7 @@ def prod( """ Returns the prod along a given axis. - .. seealso:: - :obj:`dpnp.prod` for full documentation, - :meth:`dpnp.dparray.sum` + For full documentation refer to :obj:`dpnp.prod`. """ diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index 650c0478e17..416c8492fc9 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -41,6 +41,7 @@ import dpctl.tensor as dpt +import dpctl.utils as du import numpy from numpy.core.numeric import normalize_axis_tuple @@ -1750,34 +1751,73 @@ def nancumsum(x1, **kwargs): return call_origin(numpy.nancumsum, x1, **kwargs) -def nanprod(x1, **kwargs): +def nanprod( + a, + axis=None, + dtype=None, + out=None, + keepdims=False, + initial=None, + where=True, +): """ - Calculate prod() function treating 'Not a Numbers' (NaN) as ones. + Return the product of array elements over a given axis treating Not a Numbers (NaNs) as ones. For full documentation refer to :obj:`numpy.nanprod`. + Returns + ------- + out : dpnp.ndarray + A new array holding the result is returned unless `out` is specified, in which case it is returned. + + See Also + -------- + :obj:`dpnp.prod` : Returns product across array propagating NaNs. + :obj:`dpnp.isnan` : Test element-wise for NaN and return result as a boolean array. + Limitations ----------- - Parameter `x1` is supported as :obj:`dpnp.ndarray`. - Keyword argument `kwargs` is currently unsupported. + Input array is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`. + Parameters `initial`, and `where` are only supported with their default values. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`. Examples -------- >>> import dpnp as np - >>> np.nanprod(np.array([1, 2])) - 2 - >>> np.nanprod(np.array([[1, 2], [3, 4]])) - 24 - - """ - - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False) - if x1_desc and not kwargs: - return dpnp_nanprod(x1_desc).get_pyobj() + >>> np.nanprod(np.array(1)) + array(1) + >>> np.nanprod(np.array([1])) + array(1) + >>> np.nanprod(np.array([1, np.nan])) + array(1.0) + >>> a = np.array([[1, 2], [3, np.nan]]) + >>> np.nanprod(a) + array(6.0) + >>> np.nanprod(a, axis=0) + array([3., 2.]) + + """ + + if dpnp.is_supported_array_or_scalar(a): + if issubclass(a.dtype.type, dpnp.inexact): + mask = dpnp.isnan(a) + a = dpnp.array(a, copy=True) + dpnp.copyto(a, 1, where=mask) + else: + raise TypeError( + "An array must be any of supported type, but got {}".format(type(a)) + ) - return call_origin(numpy.nanprod, x1, **kwargs) + return dpnp.prod( + a, + axis=axis, + dtype=dtype, + out=out, + keepdims=keepdims, + initial=initial, + where=where, + ) def nansum(x1, **kwargs): @@ -2030,7 +2070,7 @@ def power( def prod( - x1, + a, axis=None, dtype=None, out=None, @@ -2039,54 +2079,105 @@ def prod( where=True, ): """ - Calculate product of array elements over a given axis. + Return the product of array elements over a given axis. For full documentation refer to :obj:`numpy.prod`. + Returns + ------- + out : dpnp.ndarray + A new array holding the result is returned unless `out` is specified, in which case it is returned. + Limitations ----------- - Parameter `where` is unsupported. + Input array is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`. + Parameters `initial`, and `where` are only supported with their default values. + Otherwise the function will be executed sequentially on CPU. Input array data types are limited by DPNP :ref:`Data types`. + See Also + -------- + :obj:`dpnp.nanprod` : Return the product of array elements over a given axis treating Not a Numbers (NaNs) as ones. + Examples -------- >>> import dpnp as np - >>> np.prod(np.array([[1, 2], [3, 4]])) - 24 >>> np.prod(np.array([1, 2])) - 2 + array(2) + + >>> a = np.array([[1, 2], [3, 4]]) + >>> np.prod(a) + array(24) + + >>> np.prod(a, axis=1) + array([ 2, 12]) + >>> np.prod(a, axis=0) + array([3, 8]) + + >>> x = np.array([1, 2, 3], dtype=np.int8) + >>> np.prod(x).dtype == int + True """ - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False) - if x1_desc: - if where is not True: - pass - else: - out_desc = ( - dpnp.get_dpnp_descriptor(out, copy_when_nondefault_queue=False) - if out is not None - else None - ) - result_obj = dpnp_prod( - x1_desc, axis, dtype, out_desc, keepdims, initial, where - ).get_pyobj() - result = dpnp.convert_single_elem_array_to_scalar( - result_obj, keepdims - ) + # Product reduction for complex output are known to fail for Gen9 with 2024.0 compiler + # TODO: get rid of this temporary work around when OneAPI 2024.1 is released + if not isinstance(a, (dpnp_array, dpt.usm_ndarray)): + raise TypeError( + "An array must be any of supported type, but got {}".format(type(a)) + ) + _dtypes = (a.dtype, dtype) + _any_complex = any( + dpnp.issubdtype(dt, dpnp.complexfloating) for dt in _dtypes + ) + device_mask = ( + du.intel_device_info(a.sycl_device).get("device_id", 0) & 0xFF00 + ) + if _any_complex and device_mask in [0x3E00, 0x9B00]: + return call_origin( + numpy.prod, + a, + axis=axis, + dtype=dtype, + out=out, + keepdims=keepdims, + initial=initial, + where=where, + ) + elif initial is not None: + raise NotImplementedError( + "initial keyword argument is only supported by its default value." + ) + elif where is not True: + raise NotImplementedError( + "where keyword argument is only supported by its default value." + ) + else: + dpt_array = dpnp.get_usm_ndarray(a) + result = dpnp_array._create_from_usm_ndarray( + dpt.prod(dpt_array, axis=axis, dtype=dtype, keepdims=keepdims) + ) + if out is None: return result + else: + if out.shape != result.shape: + raise ValueError( + f"Output array of shape {result.shape} is needed, got {out.shape}." + ) + elif not isinstance(out, dpnp_array): + if isinstance(out, dpt.usm_ndarray): + out = dpnp_array._create_from_usm_ndarray(out) + else: + raise TypeError( + "Output array must be any of supported type, but got {}".format( + type(out) + ) + ) - return call_origin( - numpy.prod, - x1, - axis=axis, - dtype=dtype, - out=out, - keepdims=keepdims, - initial=initial, - where=where, - ) + dpnp.copyto(out, result, casting="safe") + + return out def proj( diff --git a/tests/skipped_tests.tbl b/tests/skipped_tests.tbl index 60b9408b690..801b29fdf3c 100644 --- a/tests/skipped_tests.tbl +++ b/tests/skipped_tests.tbl @@ -11,9 +11,6 @@ tests/test_random.py::TestPermutationsTestShuffle::test_shuffle1[lambda x: (dpnp tests/test_random.py::TestPermutationsTestShuffle::test_shuffle1[lambda x: dpnp.asarray([(i, i) for i in x], [("a", object), ("b", dpnp.int32)])]] tests/test_random.py::TestPermutationsTestShuffle::test_shuffle1[lambda x: dpnp.asarray(x).astype(dpnp.int8)] -tests/test_sycl_queue.py::test_1in_1out[opencl:gpu:0-trapz-data19] -tests/test_sycl_queue.py::test_1in_1out[opencl:cpu:0-trapz-data19] - tests/third_party/cupy/fft_tests/test_fft.py::TestFft2_param_1_{axes=None, norm=None, s=(1, None), shape=(3, 4)}::test_fft2 tests/third_party/cupy/fft_tests/test_fft.py::TestFft2_param_7_{axes=(), norm=None, s=None, shape=(3, 4)}::test_fft2 tests/third_party/cupy/fft_tests/test_fft.py::TestFft2_param_7_{axes=(), norm=None, s=None, shape=(3, 4)}::test_ifft2 @@ -619,14 +616,7 @@ tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodAxes_param_2 tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodAxes_param_3_{axis=(0, 2, 3), shape=(20, 30, 40, 50)}::test_nansum_axes tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodExtra_param_0_{shape=(2, 3, 4)}::test_nansum_out tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodExtra_param_1_{shape=(20, 30, 40)}::test_nansum_out -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_11_{axis=0, func='nanprod', keepdims=True, shape=(20, 30, 40), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_11_{axis=0, func='nanprod', keepdims=True, shape=(20, 30, 40), transpose_axes=False}::test_nansum_axis_transposed -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_13_{axis=0, func='nanprod', keepdims=False, shape=(2, 3, 4), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_13_{axis=0, func='nanprod', keepdims=False, shape=(2, 3, 4), transpose_axes=False}::test_nansum_axis_transposed -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_15_{axis=0, func='nanprod', keepdims=False, shape=(20, 30, 40), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_15_{axis=0, func='nanprod', keepdims=False, shape=(20, 30, 40), transpose_axes=False}::test_nansum_axis_transposed -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_9_{axis=0, func='nanprod', keepdims=True, shape=(2, 3, 4), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_9_{axis=0, func='nanprod', keepdims=True, shape=(2, 3, 4), transpose_axes=False}::test_nansum_axis_transposed + tests/third_party/cupy/math_tests/test_trigonometric.py::TestUnwrap::test_unwrap_1dim tests/third_party/cupy/math_tests/test_trigonometric.py::TestUnwrap::test_unwrap_1dim_with_discont tests/third_party/cupy/math_tests/test_trigonometric.py::TestUnwrap::test_unwrap_1dim_with_period diff --git a/tests/skipped_tests_gpu.tbl b/tests/skipped_tests_gpu.tbl index fc9931e93d6..446ca789f25 100644 --- a/tests/skipped_tests_gpu.tbl +++ b/tests/skipped_tests_gpu.tbl @@ -17,30 +17,6 @@ tests/test_random.py::TestPermutationsTestShuffle::test_shuffle1[lambda x: (dpnp tests/test_random.py::TestPermutationsTestShuffle::test_shuffle1[lambda x: dpnp.asarray([(i, i) for i in x], [("a", object), ("b", dpnp.int32)])]] tests/test_random.py::TestPermutationsTestShuffle::test_shuffle1[lambda x: dpnp.asarray(x).astype(dpnp.int8)] -tests/test_sycl_queue.py::test_1in_1out[opencl:gpu:0-copy-data3] -tests/test_sycl_queue.py::test_1in_1out[opencl:gpu:0-cumprod-data4] -tests/test_sycl_queue.py::test_1in_1out[opencl:gpu:0-cumsum-data5] -tests/test_sycl_queue.py::test_1in_1out[opencl:gpu:0-ediff1d-data7] -tests/test_sycl_queue.py::test_1in_1out[opencl:gpu:0-fabs-data8] - -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-copy-data3] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-cumprod-data4] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-cumsum-data5] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-ediff1d-data7] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-fabs-data8] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-nancumprod-data11] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-nancumsum-data12] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-nanprod-data13] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-nansum-data14] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-prod-data16] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-sum-data18] -tests/test_sycl_queue.py::test_1in_1out[level_zero:gpu:0-trapz-data19] - -tests/test_sycl_queue.py::test_modf[level_zero:gpu:0] - -tests/test_sycl_queue.py::test_1in_1out[opencl:gpu:0-trapz-data19] -tests/test_sycl_queue.py::test_1in_1out[opencl:cpu:0-trapz-data19] - tests/test_umath.py::test_umaths[('divmod', 'ii')] tests/test_umath.py::test_umaths[('divmod', 'll')] tests/test_umath.py::test_umaths[('divmod', 'ff')] @@ -83,20 +59,6 @@ tests/third_party/cupy/indexing_tests/test_insert.py::TestDiagIndicesFrom_param_ tests/third_party/cupy/indexing_tests/test_insert.py::TestDiagIndicesFrom_param_1_{shape=(0, 0)}::test_diag_indices_from tests/third_party/cupy/indexing_tests/test_insert.py::TestDiagIndicesFrom_param_2_{shape=(2, 2, 2)}::test_diag_indices_from -tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_external_prod_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_external_prod_axis -tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_prod_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_prod_axis -tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_sum_out -tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_sum_out_wrong_shape -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_1dim -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_2dim_without_axis -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum_2dim -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_1_{axis=1}::test_cumsum -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_1_{axis=1}::test_cumsum_2dim -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_2_{axis=2}::test_cumsum -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_2_{axis=2}::test_cumsum_2dim tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsGeometric_param_2_{p_shape=(3, 2), shape=(4, 3, 2)}::test_geometric tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsGeometric_param_3_{p_shape=(3, 2), shape=(3, 2)}::test_geometric tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsHyperGeometric_param_0_{nbad_shape=(), ngood_shape=(), nsample_dtype=int32, nsample_shape=(), shape=(4, 3, 2)}::test_hypergeometric @@ -197,13 +159,6 @@ tests/third_party/cupy/linalg_tests/test_einsum.py::TestListArgEinSumError::test tests/third_party/cupy/linalg_tests/test_product.py::TestProduct::test_reversed_vdot -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_out_noncontiguous -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum_axis_out_noncontiguous -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum_out_noncontiguous -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_1_{axis=1}::test_cumsum_axis_out_noncontiguous -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_1_{axis=1}::test_cumsum_out_noncontiguous -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_2_{axis=2}::test_cumsum_axis_out_noncontiguous -tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_2_{axis=2}::test_cumsum_out_noncontiguous tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMultivariateNormal_param_0_{d=2, shape=(4, 3, 2)}::test_normal tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMultivariateNormal_param_1_{d=2, shape=(3, 2)}::test_normal tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMultivariateNormal_param_2_{d=4, shape=(4, 3, 2)}::test_normal @@ -273,6 +228,7 @@ tests/third_party/cupy/core_tests/test_ndarray_reduction.py::TestCubReduction_pa tests/third_party/cupy/core_tests/test_ndarray_reduction.py::TestCubReduction_param_6_{order='F', shape=(10, 20, 30)}::test_cub_min tests/third_party/cupy/core_tests/test_ndarray_reduction.py::TestCubReduction_param_7_{order='F', shape=(10, 20, 30, 40)}::test_cub_max tests/third_party/cupy/core_tests/test_ndarray_reduction.py::TestCubReduction_param_7_{order='F', shape=(10, 20, 30, 40)}::test_cub_min + tests/third_party/cupy/creation_tests/test_basic.py::TestBasicReshape_param_0_{shape=4}::test_empty_like_K_strides_reshape tests/third_party/cupy/creation_tests/test_basic.py::TestBasicReshape_param_1_{shape=(4,)}::test_empty_like_K_strides_reshape tests/third_party/cupy/creation_tests/test_basic.py::TestBasicReshape_param_2_{shape=(4, 2)}::test_empty_like_K_strides_reshape @@ -729,6 +685,8 @@ tests/third_party/cupy/math_tests/test_misc.py::TestConvolve::test_convolve_diff tests/third_party/cupy/math_tests/test_misc.py::TestConvolve::test_convolve_diff_types[full] tests/third_party/cupy/math_tests/test_rounding.py::TestRounding::test_fix +tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_sum_out +tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_sum_out_wrong_shape tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_ndarray_cumprod_2dim_with_axis tests/third_party/cupy/math_tests/test_sumprod.py::TestDiff::test_diff_1dim tests/third_party/cupy/math_tests/test_sumprod.py::TestDiff::test_diff_1dim_with_n @@ -736,6 +694,15 @@ tests/third_party/cupy/math_tests/test_sumprod.py::TestDiff::test_diff_2dim_with tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_arraylike tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_huge_array tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_numpy_array +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_out_noncontiguous +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_1dim +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_2dim_without_axis +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum_2dim +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_1_{axis=1}::test_cumsum +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_1_{axis=1}::test_cumsum_2dim +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_2_{axis=2}::test_cumsum +tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_2_{axis=2}::test_cumsum_2dim tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum_arraylike tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_0_{axis=0}::test_cumsum_numpy_array tests/third_party/cupy/math_tests/test_sumprod.py::TestCumsum_param_1_{axis=1}::test_cumsum_arraylike @@ -752,14 +719,7 @@ tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodAxes_param_2 tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodAxes_param_3_{axis=(0, 2, 3), shape=(20, 30, 40, 50)}::test_nansum_axes tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodExtra_param_0_{shape=(2, 3, 4)}::test_nansum_out tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodExtra_param_1_{shape=(20, 30, 40)}::test_nansum_out -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_11_{axis=0, func='nanprod', keepdims=True, shape=(20, 30, 40), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_11_{axis=0, func='nanprod', keepdims=True, shape=(20, 30, 40), transpose_axes=False}::test_nansum_axis_transposed -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_13_{axis=0, func='nanprod', keepdims=False, shape=(2, 3, 4), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_13_{axis=0, func='nanprod', keepdims=False, shape=(2, 3, 4), transpose_axes=False}::test_nansum_axis_transposed -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_15_{axis=0, func='nanprod', keepdims=False, shape=(20, 30, 40), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_15_{axis=0, func='nanprod', keepdims=False, shape=(20, 30, 40), transpose_axes=False}::test_nansum_axis_transposed -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_9_{axis=0, func='nanprod', keepdims=True, shape=(2, 3, 4), transpose_axes=False}::test_nansum_all -tests/third_party/cupy/math_tests/test_sumprod.py::TestNansumNanprodLong_param_9_{axis=0, func='nanprod', keepdims=True, shape=(2, 3, 4), transpose_axes=False}::test_nansum_axis_transposed + tests/third_party/cupy/math_tests/test_trigonometric.py::TestUnwrap::test_unwrap_1dim tests/third_party/cupy/math_tests/test_trigonometric.py::TestUnwrap::test_unwrap_1dim_with_discont tests/third_party/cupy/math_tests/test_trigonometric.py::TestUnwrap::test_unwrap_1dim_with_period diff --git a/tests/test_arithmetic.py b/tests/test_arithmetic.py index 9d138e0d843..60dc7a1c9af 100644 --- a/tests/test_arithmetic.py +++ b/tests/test_arithmetic.py @@ -22,14 +22,12 @@ def test_modf_part2(self, xp, dtype): return c - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_float_dtypes() - @testing.numpy_cupy_allclose() + @testing.numpy_cupy_allclose(type_check=False) def test_nanprod(self, xp, dtype): a = xp.array([-2.5, -1.5, xp.nan, 10.5, 1.5, xp.nan], dtype=dtype) return xp.nanprod(a) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_float_dtypes() @testing.numpy_cupy_allclose() def test_nansum(self, xp, dtype): diff --git a/tests/test_mathematical.py b/tests/test_mathematical.py index 2a25e7de573..63d5172c08e 100644 --- a/tests/test_mathematical.py +++ b/tests/test_mathematical.py @@ -1,5 +1,6 @@ from itertools import permutations +import dpctl.tensor as dpt import numpy import pytest from numpy.testing import ( @@ -16,6 +17,7 @@ assert_dtype_allclose, get_all_dtypes, get_complex_dtypes, + get_float_complex_dtypes, get_float_dtypes, has_support_aspect64, is_cpu_device, @@ -459,6 +461,109 @@ def test_positive_boolean(): dpnp.positive(dpnp_a) +@pytest.mark.usefixtures("allow_fall_back_on_numpy") +@pytest.mark.parametrize("func", ["prod", "nanprod"]) +@pytest.mark.parametrize("axis", [None, 0, 1, -1, 2, -2, (1, 2), (0, -2)]) +@pytest.mark.parametrize("keepdims", [False, True]) +@pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) +def test_prod_nanprod(func, axis, keepdims, dtype): + a = numpy.arange(1, 13, dtype=dtype).reshape((2, 2, 3)) + if func == "nanprod" and issubclass(a.dtype.type, dpnp.inexact): + a[1:2:] = numpy.nan + ia = dpnp.array(a) + + np_res = getattr(numpy, func)(a, axis=axis, keepdims=keepdims) + dpnp_res = getattr(dpnp, func)(ia, axis=axis, keepdims=keepdims) + + assert dpnp_res.shape == np_res.shape + assert_allclose(dpnp_res, np_res) + + +@pytest.mark.parametrize("axis", [None, 0, 1, -1, 2, -2, (1, 2), (0, -2)]) +def test_prod_zero_size(axis): + a = numpy.empty((2, 3, 0)) + ia = dpnp.array(a) + + np_res = numpy.prod(a, axis=axis) + dpnp_res = dpnp.prod(ia, axis=axis) + + assert dpnp_res.shape == np_res.shape + assert_allclose(dpnp_res, np_res) + + +@pytest.mark.parametrize("func", ["prod", "nanprod"]) +@pytest.mark.parametrize("axis", [None, 0, 1, -1]) +@pytest.mark.parametrize("keepdims", [False, True]) +def test_prod_nanprod_bool(func, axis, keepdims): + a = numpy.arange(2, dtype=dpnp.bool) + a = numpy.tile(a, (2, 2)) + ia = dpnp.array(a) + + np_res = getattr(numpy, func)(a, axis=axis, keepdims=keepdims) + dpnp_res = getattr(dpnp, func)(ia, axis=axis, keepdims=keepdims) + + assert dpnp_res.shape == np_res.shape + assert_allclose(dpnp_res, np_res) + + +@pytest.mark.usefixtures("allow_fall_back_on_numpy") +@pytest.mark.usefixtures("suppress_complex_warning") +@pytest.mark.usefixtures("suppress_invalid_numpy_warnings") +@pytest.mark.parametrize("func", ["prod", "nanprod"]) +@pytest.mark.parametrize("in_dtype", get_all_dtypes(no_bool=True)) +@pytest.mark.parametrize("out_dtype", get_all_dtypes(no_bool=True)) +def test_prod_nanprod_dtype(func, in_dtype, out_dtype): + a = numpy.arange(1, 13, dtype=in_dtype).reshape((2, 2, 3)) + if func == "nanprod" and issubclass(a.dtype.type, dpnp.inexact): + a[1:2:] = numpy.nan + ia = dpnp.array(a) + + np_res = getattr(numpy, func)(a, dtype=out_dtype) + dpnp_res = getattr(dpnp, func)(ia, dtype=out_dtype) + + if out_dtype is not None: + assert dpnp_res.dtype == out_dtype + assert_allclose(dpnp_res, np_res) + + +@pytest.mark.parametrize("func", ["prod", "nanprod"]) +def test_prod_nanprod_out(func): + a = numpy.arange(1, 7).reshape((2, 3)) + if func == "nanprod" and issubclass(a.dtype.type, dpnp.inexact): + a[1:2:] = numpy.nan + ia = dpnp.array(a) + + np_res = getattr(numpy, func)(a, axis=0) + dpnp_res = dpnp.array(numpy.empty_like(np_res)) + getattr(dpnp, func)(ia, axis=0, out=dpnp_res) + assert_allclose(dpnp_res, np_res) + + dpnp_res = dpt.asarray(numpy.empty_like(np_res)) + getattr(dpnp, func)(ia, axis=0, out=dpnp_res) + assert_allclose(dpnp_res, np_res) + + dpnp_res = numpy.empty_like(np_res) + with pytest.raises(TypeError): + getattr(dpnp, func)(ia, axis=0, out=dpnp_res) + + dpnp_res = dpnp.array(numpy.empty((2, 3))) + with pytest.raises(ValueError): + getattr(dpnp, func)(ia, axis=0, out=dpnp_res) + + +def test_prod_nanprod_Error(): + ia = dpnp.arange(5) + + with pytest.raises(TypeError): + dpnp.prod(dpnp.asnumpy(ia)) + with pytest.raises(TypeError): + dpnp.nanprod(dpnp.asnumpy(ia)) + with pytest.raises(NotImplementedError): + dpnp.prod(ia, where=False) + with pytest.raises(NotImplementedError): + dpnp.prod(ia, initial=6) + + @pytest.mark.parametrize( "data", [[2, 0, -2], [1.1, -1.1]], diff --git a/tests/test_usm_type.py b/tests/test_usm_type.py index fd94ff01fdb..206cae64326 100644 --- a/tests/test_usm_type.py +++ b/tests/test_usm_type.py @@ -349,8 +349,10 @@ def test_meshgrid(usm_type_x, usm_type_y): pytest.param("log10", [1.0, 2.0, 4.0, 7.0]), pytest.param("log1p", [1.0e-10, 1.0, 2.0, 4.0, 7.0]), pytest.param("log2", [1.0, 2.0, 4.0, 7.0]), + pytest.param("nanprod", [1.0, 2.0, dp.nan]), pytest.param("negative", [1.0, 0.0, -1.0]), pytest.param("positive", [1.0, 0.0, -1.0]), + pytest.param("prod", [1.0, 2.0]), pytest.param("proj", [complex(1.0, 2.0), complex(dp.inf, -1.0)]), pytest.param( "real", [complex(1.0, 2.0), complex(3.0, 4.0), complex(5.0, 6.0)] diff --git a/tests/third_party/cupy/math_tests/test_sumprod.py b/tests/third_party/cupy/math_tests/test_sumprod.py index e6b7adfa5c5..5834ac94fe2 100644 --- a/tests/third_party/cupy/math_tests/test_sumprod.py +++ b/tests/third_party/cupy/math_tests/test_sumprod.py @@ -214,25 +214,25 @@ def test_sum_out_wrong_shape(self): a.sum(axis=1, out=b) @testing.for_all_dtypes() - @testing.numpy_cupy_allclose() + @testing.numpy_cupy_allclose(type_check=False) def test_prod_all(self, xp, dtype): a = testing.shaped_arange((2, 3), xp, dtype) return a.prod() @testing.for_all_dtypes() - @testing.numpy_cupy_allclose() + @testing.numpy_cupy_allclose(type_check=False) def test_external_prod_all(self, xp, dtype): a = testing.shaped_arange((2, 3), xp, dtype) return xp.prod(a) @testing.for_all_dtypes() - @testing.numpy_cupy_allclose() + @testing.numpy_cupy_allclose(type_check=False) def test_prod_axis(self, xp, dtype): a = testing.shaped_arange((2, 3, 4), xp, dtype) return a.prod(axis=1) @testing.for_all_dtypes() - @testing.numpy_cupy_allclose() + @testing.numpy_cupy_allclose(type_check=False) def test_external_prod_axis(self, xp, dtype): a = testing.shaped_arange((2, 3, 4), xp, dtype) return xp.prod(a, axis=1) @@ -267,6 +267,17 @@ def _numpy_nanprod_implemented(self): ) def _test(self, xp, dtype): + if ( + self.func == "nanprod" + and self.shape == (20, 30, 40) + and has_support_aspect64() + ): + # If input type is float, NumPy returns the same data type but + # dpctl (and dpnp) returns default platform float following array api. + # When input is `float32` and output is a very large number, dpnp returns + # the number because it is `float64` but NumPy returns `inf` since it is `float32`. + pytest.skip("Output is a very large number.") + a = testing.shaped_arange(self.shape, xp, dtype) if self.transpose_axes: a = a.transpose(2, 0, 1) @@ -276,7 +287,7 @@ def _test(self, xp, dtype): return func(a, axis=self.axis, keepdims=self.keepdims) @testing.for_all_dtypes(no_bool=True, no_float16=True) - @testing.numpy_cupy_allclose(type_check=has_support_aspect64()) + @testing.numpy_cupy_allclose(type_check=False) def test_nansum_all(self, xp, dtype): if ( not self._numpy_nanprod_implemented() @@ -286,9 +297,7 @@ def test_nansum_all(self, xp, dtype): return self._test(xp, dtype) @testing.for_all_dtypes(no_bool=True, no_float16=True) - @testing.numpy_cupy_allclose( - contiguous_check=False, type_check=has_support_aspect64() - ) + @testing.numpy_cupy_allclose(contiguous_check=False, type_check=False) def test_nansum_axis_transposed(self, xp, dtype): if ( not self._numpy_nanprod_implemented()