diff --git a/dpnp/backend/include/dpnp_iface_fptr.hpp b/dpnp/backend/include/dpnp_iface_fptr.hpp index e7cdc0d6cea0..40f161cd9262 100644 --- a/dpnp/backend/include/dpnp_iface_fptr.hpp +++ b/dpnp/backend/include/dpnp_iface_fptr.hpp @@ -76,11 +76,7 @@ enum class DPNPFuncName : size_t DPNP_FN_ARCTAN2, /**< Used in numpy.arctan2() impl */ DPNP_FN_ARCTANH, /**< Used in numpy.arctanh() impl */ DPNP_FN_ARGMAX, /**< Used in numpy.argmax() impl */ - DPNP_FN_ARGMAX_EXT, /**< Used in numpy.argmax() impl, requires extra - parameters */ DPNP_FN_ARGMIN, /**< Used in numpy.argmin() impl */ - DPNP_FN_ARGMIN_EXT, /**< Used in numpy.argmin() impl, requires extra - parameters */ DPNP_FN_ARGSORT, /**< Used in numpy.argsort() impl */ DPNP_FN_ARGSORT_EXT, /**< Used in numpy.argsort() impl, requires extra parameters */ diff --git a/dpnp/backend/kernels/dpnp_krnl_searching.cpp b/dpnp/backend/kernels/dpnp_krnl_searching.cpp index ae08e9c4bf53..045d405056c5 100644 --- a/dpnp/backend/kernels/dpnp_krnl_searching.cpp +++ b/dpnp/backend/kernels/dpnp_krnl_searching.cpp @@ -78,14 +78,6 @@ void (*dpnp_argmax_default_c)(void *, void *, size_t) = dpnp_argmax_c<_DataType, _idx_DataType>; -template -DPCTLSyclEventRef (*dpnp_argmax_ext_c)(DPCTLSyclQueueRef, - void *, - void *, - size_t, - const DPCTLEventVectorRef) = - dpnp_argmax_c<_DataType, _idx_DataType>; - template class dpnp_argmin_c_kernel; @@ -133,14 +125,6 @@ void (*dpnp_argmin_default_c)(void *, void *, size_t) = dpnp_argmin_c<_DataType, _idx_DataType>; -template -DPCTLSyclEventRef (*dpnp_argmin_ext_c)(DPCTLSyclQueueRef, - void *, - void *, - size_t, - const DPCTLEventVectorRef) = - dpnp_argmin_c<_DataType, _idx_DataType>; - void func_map_init_searching(func_map_t &fmap) { fmap[DPNPFuncName::DPNP_FN_ARGMAX][eft_INT][eft_INT] = { @@ -160,23 +144,6 @@ void func_map_init_searching(func_map_t &fmap) fmap[DPNPFuncName::DPNP_FN_ARGMAX][eft_DBL][eft_LNG] = { eft_LNG, (void *)dpnp_argmax_default_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_INT][eft_INT] = { - eft_INT, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_INT][eft_LNG] = { - eft_LNG, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_LNG][eft_INT] = { - eft_INT, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_LNG][eft_LNG] = { - eft_LNG, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_FLT][eft_INT] = { - eft_INT, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_FLT][eft_LNG] = { - eft_LNG, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_DBL][eft_INT] = { - eft_INT, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMAX_EXT][eft_DBL][eft_LNG] = { - eft_LNG, (void *)dpnp_argmax_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN][eft_INT][eft_INT] = { eft_INT, (void *)dpnp_argmin_default_c}; fmap[DPNPFuncName::DPNP_FN_ARGMIN][eft_INT][eft_LNG] = { @@ -194,22 +161,5 @@ void func_map_init_searching(func_map_t &fmap) fmap[DPNPFuncName::DPNP_FN_ARGMIN][eft_DBL][eft_LNG] = { eft_LNG, (void *)dpnp_argmin_default_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_INT][eft_INT] = { - eft_INT, (void *)dpnp_argmin_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_INT][eft_LNG] = { - eft_LNG, (void *)dpnp_argmin_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_LNG][eft_INT] = { - eft_INT, (void *)dpnp_argmin_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_LNG][eft_LNG] = { - eft_LNG, (void *)dpnp_argmin_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_FLT][eft_INT] = { - eft_INT, (void *)dpnp_argmin_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_FLT][eft_LNG] = { - eft_LNG, (void *)dpnp_argmin_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_DBL][eft_INT] = { - eft_INT, (void *)dpnp_argmin_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGMIN_EXT][eft_DBL][eft_LNG] = { - eft_LNG, (void *)dpnp_argmin_ext_c}; - return; } diff --git a/dpnp/dpnp_algo/CMakeLists.txt b/dpnp/dpnp_algo/CMakeLists.txt index e077ee85e157..442fa8e82b16 100644 --- a/dpnp/dpnp_algo/CMakeLists.txt +++ b/dpnp/dpnp_algo/CMakeLists.txt @@ -6,7 +6,6 @@ set(dpnp_algo_pyx_deps ${CMAKE_CURRENT_SOURCE_DIR}/dpnp_algo_sorting.pxi ${CMAKE_CURRENT_SOURCE_DIR}/dpnp_algo_arraycreation.pxi ${CMAKE_CURRENT_SOURCE_DIR}/dpnp_algo_mathematical.pxi - ${CMAKE_CURRENT_SOURCE_DIR}/dpnp_algo_searching.pxi ${CMAKE_CURRENT_SOURCE_DIR}/dpnp_algo_indexing.pxi ${CMAKE_CURRENT_SOURCE_DIR}/dpnp_algo_logic.pxi ${CMAKE_CURRENT_SOURCE_DIR}/dpnp_algo_special.pxi diff --git a/dpnp/dpnp_algo/dpnp_algo.pxd b/dpnp/dpnp_algo/dpnp_algo.pxd index d6d25fef1b95..2ec000ad573f 100644 --- a/dpnp/dpnp_algo/dpnp_algo.pxd +++ b/dpnp/dpnp_algo/dpnp_algo.pxd @@ -36,10 +36,6 @@ cdef extern from "dpnp_iface_fptr.hpp" namespace "DPNPFuncName": # need this na DPNP_FN_ALLCLOSE DPNP_FN_ALLCLOSE_EXT DPNP_FN_ARANGE - DPNP_FN_ARGMAX - DPNP_FN_ARGMAX_EXT - DPNP_FN_ARGMIN - DPNP_FN_ARGMIN_EXT DPNP_FN_ARGSORT DPNP_FN_ARGSORT_EXT DPNP_FN_CBRT @@ -355,12 +351,6 @@ Sorting functions cpdef dpnp_descriptor dpnp_argsort(dpnp_descriptor array1) cpdef dpnp_descriptor dpnp_sort(dpnp_descriptor array1) -""" -Searching functions -""" -cpdef dpnp_descriptor dpnp_argmax(dpnp_descriptor array1) -cpdef dpnp_descriptor dpnp_argmin(dpnp_descriptor array1) - """ Trigonometric functions """ diff --git a/dpnp/dpnp_algo/dpnp_algo.pyx b/dpnp/dpnp_algo/dpnp_algo.pyx index 74a8b8e89c57..257c502bfa0c 100644 --- a/dpnp/dpnp_algo/dpnp_algo.pyx +++ b/dpnp/dpnp_algo/dpnp_algo.pyx @@ -63,7 +63,6 @@ include "dpnp_algo_indexing.pxi" include "dpnp_algo_linearalgebra.pxi" include "dpnp_algo_logic.pxi" include "dpnp_algo_mathematical.pxi" -include "dpnp_algo_searching.pxi" include "dpnp_algo_sorting.pxi" include "dpnp_algo_special.pxi" include "dpnp_algo_statistics.pxi" diff --git a/dpnp/dpnp_algo/dpnp_algo_searching.pxi b/dpnp/dpnp_algo/dpnp_algo_searching.pxi deleted file mode 100644 index a84c918f3c23..000000000000 --- a/dpnp/dpnp_algo/dpnp_algo_searching.pxi +++ /dev/null @@ -1,119 +0,0 @@ -# cython: language_level=3 -# cython: linetrace=True -# -*- coding: utf-8 -*- -# ***************************************************************************** -# Copyright (c) 2016-2023, Intel Corporation -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# - Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# - Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -# THE POSSIBILITY OF SUCH DAMAGE. -# ***************************************************************************** - -"""Module Backend (Searching part) - -This module contains interface functions between C backend layer -and the rest of the library - -""" - -# NO IMPORTs here. All imports must be placed into main "dpnp_algo.pyx" file - -__all__ += [ - "dpnp_argmax", - "dpnp_argmin" -] - - -# C function pointer to the C library template functions -ctypedef c_dpctl.DPCTLSyclEventRef(*custom_search_1in_1out_func_ptr_t)(c_dpctl.DPCTLSyclQueueRef, - void * , void * , size_t, - const c_dpctl.DPCTLEventVectorRef) - - -cpdef utils.dpnp_descriptor dpnp_argmax(utils.dpnp_descriptor in_array1): - cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(in_array1.dtype) - cdef DPNPFuncType output_type = dpnp_dtype_to_DPNPFuncType(dpnp.int64) - - cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_ARGMAX_EXT, param1_type, output_type) - - in_array1_obj = in_array1.get_array() - - # create result array with type given by FPTR data - cdef shape_type_c result_shape = (1,) - cdef utils.dpnp_descriptor result = utils.create_output_descriptor(result_shape, - kernel_data.return_type, - None, - device=in_array1_obj.sycl_device, - usm_type=in_array1_obj.usm_type, - sycl_queue=in_array1_obj.sycl_queue) - - 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() - - cdef custom_search_1in_1out_func_ptr_t func = kernel_data.ptr - - cdef c_dpctl.DPCTLSyclEventRef event_ref = func(q_ref, - in_array1.get_data(), - result.get_data(), - in_array1.size, - 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_argmin(utils.dpnp_descriptor in_array1): - cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(in_array1.dtype) - cdef DPNPFuncType output_type = dpnp_dtype_to_DPNPFuncType(dpnp.int64) - - cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_ARGMIN_EXT, param1_type, output_type) - - in_array1_obj = in_array1.get_array() - - # create result array with type given by FPTR data - cdef shape_type_c result_shape = (1,) - cdef utils.dpnp_descriptor result = utils.create_output_descriptor(result_shape, - kernel_data.return_type, - None, - device=in_array1_obj.sycl_device, - usm_type=in_array1_obj.usm_type, - sycl_queue=in_array1_obj.sycl_queue) - - 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() - - cdef custom_search_1in_1out_func_ptr_t func = kernel_data.ptr - - cdef c_dpctl.DPCTLSyclEventRef event_ref = func(q_ref, - in_array1.get_data(), - result.get_data(), - in_array1.size, - NULL) # dep_events_ref - - with nogil: c_dpctl.DPCTLEvent_WaitAndThrow(event_ref) - c_dpctl.DPCTLEvent_Delete(event_ref) - - return result diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 2d359b4a6253..88db5d695f9d 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -486,58 +486,23 @@ def any(self, axis=None, out=None, keepdims=False, *, where=True): self, axis=axis, out=out, keepdims=keepdims, where=where ) - def argmax(self, axis=None, out=None): + def argmax(self, axis=None, out=None, *, keepdims=False): """ Returns array of indices of the maximum values along the given axis. - Parameters - ---------- - axis : {None, integer} - If None, the index is into the flattened array, otherwise along - the specified axis - out : {None, array}, optional - Array into which the result can be placed. Its type is preserved - and it must be of the right shape to hold the output. - - Returns - ------- - index_array : {integer_array} - - Examples - -------- - >>> a = np.arange(6).reshape(2,3) - >>> a.argmax() - 5 - >>> a.argmax(0) - array([1, 1, 1]) - >>> a.argmax(1) - array([2, 2]) + Refer to :obj:`dpnp.argmax` for full documentation. """ - return dpnp.argmax(self, axis, out) + return dpnp.argmax(self, axis, out, keepdims=keepdims) - def argmin(self, axis=None, out=None): + def argmin(self, axis=None, out=None, *, keepdims=False): """ Return array of indices to the minimum values along the given axis. - Parameters - ---------- - axis : {None, integer} - If None, the index is into the flattened array, otherwise along - the specified axis - out : {None, array}, optional - Array into which the result can be placed. Its type is preserved - and it must be of the right shape to hold the output. - - Returns - ------- - ndarray or scalar - If multi-dimension input, returns a new ndarray of indices to the - minimum values along the given axis. Otherwise, returns a scalar - of index to the minimum values along the given axis. + Refer to :obj:`dpnp.argmin` for full documentation. """ - return dpnp.argmin(self, axis, out) + return dpnp.argmin(self, axis, out, keepdims=keepdims) # 'argpartition', diff --git a/dpnp/dpnp_iface.py b/dpnp/dpnp_iface.py index d6d5b3a48615..e91a9b991f89 100644 --- a/dpnp/dpnp_iface.py +++ b/dpnp/dpnp_iface.py @@ -65,6 +65,7 @@ "get_dpnp_descriptor", "get_include", "get_normalized_queue_device", + "get_result_array", "get_usm_ndarray", "get_usm_ndarray_or_scalar", "is_supported_array_or_scalar", @@ -418,6 +419,51 @@ def get_normalized_queue_device(obj=None, device=None, sycl_queue=None): ) +def get_result_array(a, out=None): + """ + If `out` is provided, value of `a` array will be copied into the + `out` array according to ``safe`` casting rule. + Otherwise, the input array `a` is returned. + + Parameters + ---------- + a : {dpnp_array} + Input array. + + out : {dpnp_array, usm_ndarray} + If provided, value of `a` array will be copied into it + according to ``safe`` casting rule. + It should be of the appropriate shape. + + Returns + ------- + out : {dpnp_array} + Return `out` if provided, otherwise return `a`. + + """ + + if out is None: + return a + else: + if out.shape != a.shape: + raise ValueError( + f"Output array of shape {a.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) + ) + ) + + dpnp.copyto(out, a, casting="safe") + + return out + + def get_usm_ndarray(a): """ Return :class:`dpctl.tensor.usm_ndarray` from input array `a`. diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index 330179c2ca44..d9e0155ca118 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -2158,26 +2158,7 @@ def prod( 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) - ) - ) - - dpnp.copyto(out, result, casting="safe") - - return out + return dpnp.get_result_array(result, out) def proj( diff --git a/dpnp/dpnp_iface_searching.py b/dpnp/dpnp_iface_searching.py index 26fc1528a0fa..e74c0c1beccf 100644 --- a/dpnp/dpnp_iface_searching.py +++ b/dpnp/dpnp_iface_searching.py @@ -1,5 +1,3 @@ -# cython: language_level=3 -# distutils: language = c++ # -*- coding: utf-8 -*- # ***************************************************************************** # Copyright (c) 2016-2023, Intel Corporation @@ -51,22 +49,38 @@ __all__ = ["argmax", "argmin", "searchsorted", "where"] -def argmax(x1, axis=None, out=None): +def argmax(a, axis=None, out=None, *, keepdims=False): """ Returns the indices of the maximum values along an axis. For full documentation refer to :obj:`numpy.argmax`. - Limitations - ----------- - Input array is supported as :obj:`dpnp.ndarray`. - Otherwise the function will be executed sequentially on CPU. - Parameter `axis` is supported only with default value ``None``. - Parameter `out` is supported only with default value ``None``. - Input array data types are limited by supported DPNP :ref:`Data types`. + Parameters + ---------- + a : {dpnp_array, usm_ndarray} + Input array. + axis : int, optional + By default, the index is into the flattened array, otherwise + along the specified axis. + out : {dpnp_array, usm_ndarray}, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. + + Returns + ------- + out : dpnp.ndarray + Indices of maximum value of `a`. It has the same shape as `a.shape` + with the dimension along `axis` removed. If `keepdims` is set to True, + then the size of `axis` will be 1 with the resulting array having same + shape as `a.shape`. See Also -------- + :obj:`dpnp.ndarray.argmax` : Equivalent function. :obj:`dpnp.argmin` : Returns the indices of the minimum values along an axis. :obj:`dpnp.amax` : The maximum value along a given axis. :obj:`dpnp.unravel_index` : Convert a flat index into an index tuple. @@ -82,46 +96,71 @@ def argmax(x1, axis=None, out=None): -------- >>> import dpnp as np >>> a = np.arange(6).reshape((2, 3)) + 10 - >>> a.shape - (2, 3) - >>> [i for i in a] - [10, 11, 12, 13, 14, 15] + >>> a + array([[10, 11, 12], + [13, 14, 15]]) >>> np.argmax(a) - 5 + array(5) - """ + >>> np.argmax(a, axis=0) + array([1, 1, 1]) + >>> np.argmax(a, axis=1) + array([2, 2]) + + >>> b = np.arange(6) + >>> b[1] = 5 + >>> b + array([0, 5, 2, 3, 4, 5]) + >>> np.argmax(b) # Only the first occurrence is returned. + array(1) - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False) - if x1_desc: - if axis is not None: - pass - elif out is not None: - pass - else: - result_obj = dpnp_argmax(x1_desc).get_pyobj() - result = dpnp.convert_single_elem_array_to_scalar(result_obj) + >>> x = np.arange(24).reshape((2, 3, 4)) + >>> res = np.argmax(x, axis=1, keepdims=True) # Setting keepdims to True + >>> res.shape + (2, 1, 4) + + """ - return result + dpt_array = dpnp.get_usm_ndarray(a) + result = dpnp_array._create_from_usm_ndarray( + dpt.argmax(dpt_array, axis=axis, keepdims=keepdims) + ) - return call_origin(numpy.argmax, x1, axis, out) + return dpnp.get_result_array(result, out) -def argmin(x1, axis=None, out=None): +def argmin(a, axis=None, out=None, *, keepdims=False): """ Returns the indices of the minimum values along an axis. For full documentation refer to :obj:`numpy.argmin`. - Limitations - ----------- - Input array is supported as :obj:`dpnp.ndarray`. - Otherwise the function will be executed sequentially on CPU. - Parameter `axis` is supported only with default value ``None``. - Parameter `out` is supported only with default value ``None``. - Input array data types are limited by supported DPNP :ref:`Data types`. + Parameters + ---------- + a : {dpnp_array, usm_ndarray} + Input array. + axis : int, optional + By default, the index is into the flattened array, otherwise + along the specified axis. + out : {dpnp_array, usm_ndarray}, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the array. + + Returns + ------- + out : dpnp.ndarray + Indices of minimum value of `a`. It has the same shape as `a.shape` + with the dimension along `axis` removed. If `keepdims` is set to True, + then the size of `axis` will be 1 with the resulting array having same + shape as `a.shape`. See Also -------- + :obj:`dpnp.ndarray.argmin` : Equivalent function. :obj:`dpnp.argmax` : Returns the indices of the maximum values along an axis. :obj:`dpnp.amin` : The minimum value along a given axis. :obj:`dpnp.unravel_index` : Convert a flat index into an index tuple. @@ -137,28 +176,37 @@ def argmin(x1, axis=None, out=None): -------- >>> import dpnp as np >>> a = np.arange(6).reshape((2, 3)) + 10 - >>> a.shape - (2, 3) - >>> [i for i in a] - [10, 11, 12, 13, 14, 15] + >>> a + array([[10, 11, 12], + [13, 14, 15]]) >>> np.argmin(a) - 0 + array(0) - """ + >>> np.argmin(a, axis=0) + array([0, 0, 0]) + >>> np.argmin(a, axis=1) + array([0, 0]) + + >>> b = np.arange(6) + 10 + >>> b[4] = 10 + >>> b + array([10, 11, 12, 13, 10, 15]) + >>> np.argmin(b) # Only the first occurrence is returned. + array(0) - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False) - if x1_desc: - if axis is not None: - pass - elif out is not None: - pass - else: - result_obj = dpnp_argmin(x1_desc).get_pyobj() - result = dpnp.convert_single_elem_array_to_scalar(result_obj) + >>> x = np.arange(24).reshape((2, 3, 4)) + >>> res = np.argmin(x, axis=1, keepdims=True) # Setting keepdims to True + >>> res.shape + (2, 1, 4) + + """ - return result + dpt_array = dpnp.get_usm_ndarray(a) + result = dpnp_array._create_from_usm_ndarray( + dpt.argmin(dpt_array, axis=axis, keepdims=keepdims) + ) - return call_origin(numpy.argmin, x1, axis, out) + return dpnp.get_result_array(result, out) def searchsorted(a, v, side="left", sorter=None): diff --git a/dpnp/dpnp_iface_statistics.py b/dpnp/dpnp_iface_statistics.py index cc6f848ae9f5..cc11aeede1a5 100644 --- a/dpnp/dpnp_iface_statistics.py +++ b/dpnp/dpnp_iface_statistics.py @@ -410,43 +410,11 @@ def max(a, axis=None, out=None, keepdims=False, initial=None, where=True): ) else: dpt_array = dpnp.get_usm_ndarray(a) - if dpt_array.size == 0: - # TODO: get rid of this if condition when dpctl supports it - axis = (axis,) if isinstance(axis, int) else axis - for i in range(a.ndim): - if a.shape[i] == 0: - if axis is None or i in axis: - raise ValueError( - "reduction does not support zero-size arrays" - ) - else: - indices = [i for i in range(a.ndim) if i not in axis] - res_shape = tuple([a.shape[i] for i in indices]) - result = dpnp.empty(res_shape, dtype=a.dtype) - else: - result = dpnp_array._create_from_usm_ndarray( - dpt.max(dpt_array, axis=axis, 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) - ) - ) - - dpnp.copyto(out, result, casting="safe") + result = dpnp_array._create_from_usm_ndarray( + dpt.max(dpt_array, axis=axis, keepdims=keepdims) + ) - return out + return dpnp.get_result_array(result, out) def mean(x, /, *, axis=None, dtype=None, keepdims=False, out=None, where=True): @@ -651,46 +619,15 @@ def min(a, axis=None, out=None, keepdims=False, initial=None, where=True): ) elif where is not True: raise NotImplementedError( - "where keyword argument is only supported by its default values." + "where keyword argument is only supported by its default value." ) else: dpt_array = dpnp.get_usm_ndarray(a) - if dpt_array.size == 0: - # TODO: get rid of this if condition when dpctl supports it - for i in range(a.ndim): - if a.shape[i] == 0: - if axis is None or i in axis: - raise ValueError( - "reduction does not support zero-size arrays" - ) - else: - indices = [i for i in range(a.ndim) if i not in axis] - res_shape = tuple([a.shape[i] for i in indices]) - result = dpnp.empty(res_shape, dtype=a.dtype) - else: - result = dpnp_array._create_from_usm_ndarray( - dpt.min(dpt_array, axis=axis, 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) - ) - ) - - dpnp.copyto(out, result, casting="safe") - - return out + result = dpnp_array._create_from_usm_ndarray( + dpt.min(dpt_array, axis=axis, keepdims=keepdims) + ) + + return dpnp.get_result_array(result, out) def ptp( diff --git a/tests/skipped_tests.tbl b/tests/skipped_tests.tbl index e20654e877a4..05acfa51b740 100644 --- a/tests/skipped_tests.tbl +++ b/tests/skipped_tests.tbl @@ -816,8 +816,6 @@ tests/third_party/cupy/sorting_tests/test_search.py::TestNanArgMin::test_nanargm tests/third_party/cupy/sorting_tests/test_search.py::TestNanArgMin::test_nanargmin_zero_size_axis1 tests/third_party/cupy/sorting_tests/test_search.py::TestNonzeroZeroDimension_param_0_{array=array(0)}::test_nonzero tests/third_party/cupy/sorting_tests/test_search.py::TestNonzeroZeroDimension_param_1_{array=array(1)}::test_nonzero -tests/third_party/cupy/sorting_tests/test_search.py::TestSearch::test_argmax_zero_size -tests/third_party/cupy/sorting_tests/test_search.py::TestSearch::test_argmin_zero_size tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_0_{external=False}::test_argpartition_axis tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_0_{external=False}::test_argpartition_invalid_axis1 tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_0_{external=False}::test_argpartition_invalid_axis2 diff --git a/tests/skipped_tests_gpu.tbl b/tests/skipped_tests_gpu.tbl index cb35f0643343..77c23e454175 100644 --- a/tests/skipped_tests_gpu.tbl +++ b/tests/skipped_tests_gpu.tbl @@ -877,8 +877,6 @@ tests/third_party/cupy/sorting_tests/test_search.py::TestNanArgMin::test_nanargm tests/third_party/cupy/sorting_tests/test_search.py::TestNanArgMin::test_nanargmin_zero_size_axis1 tests/third_party/cupy/sorting_tests/test_search.py::TestNonzeroZeroDimension_param_0_{array=array(0)}::test_nonzero tests/third_party/cupy/sorting_tests/test_search.py::TestNonzeroZeroDimension_param_1_{array=array(1)}::test_nonzero -tests/third_party/cupy/sorting_tests/test_search.py::TestSearch::test_argmax_zero_size -tests/third_party/cupy/sorting_tests/test_search.py::TestSearch::test_argmin_zero_size tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_0_{external=False}::test_argpartition_axis tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_0_{external=False}::test_argpartition_invalid_axis1 tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_0_{external=False}::test_argpartition_invalid_axis2 diff --git a/tests/test_search.py b/tests/test_search.py new file mode 100644 index 000000000000..aa5a2c9915c4 --- /dev/null +++ b/tests/test_search.py @@ -0,0 +1,76 @@ +import dpctl.tensor as dpt +import numpy +import pytest +from numpy.testing import assert_allclose + +import dpnp + +from .helper import get_all_dtypes + + +@pytest.mark.parametrize("func", ["argmax", "argmin"]) +@pytest.mark.parametrize("axis", [None, 0, 1, -1, 2, -2]) +@pytest.mark.parametrize("keepdims", [False, True]) +@pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) +def test_argmax_argmin(func, axis, keepdims, dtype): + a = numpy.arange(768, dtype=dtype).reshape((4, 4, 6, 8)) + 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("func", ["argmax", "argmin"]) +@pytest.mark.parametrize("axis", [None, 0, 1, -1]) +@pytest.mark.parametrize("keepdims", [False, True]) +def test_argmax_argmin_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.parametrize("func", ["argmax", "argmin"]) +def test_argmax_argmin_out(func): + a = numpy.arange(6).reshape((2, 3)) + 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) + + +@pytest.mark.parametrize("axis", [None, 0, 1, -1]) +@pytest.mark.parametrize("keepdims", [False, True]) +def test_ndarray_argmax_argmin(axis, keepdims): + a = numpy.arange(192, dtype="f4").reshape((4, 6, 8)) + ia = dpnp.array(a) + + np_res = a.argmax(axis=axis, keepdims=keepdims) + dpnp_res = ia.argmax(axis=axis, keepdims=keepdims) + assert_allclose(dpnp_res, np_res) + + np_res = a.argmin(axis=axis, keepdims=keepdims) + dpnp_res = ia.argmin(axis=axis, keepdims=keepdims) + assert_allclose(dpnp_res, np_res) diff --git a/tests/test_sycl_queue.py b/tests/test_sycl_queue.py index 083616cbf85b..43d89a27cb8e 100644 --- a/tests/test_sycl_queue.py +++ b/tests/test_sycl_queue.py @@ -338,6 +338,8 @@ def test_meshgrid(device_x, device_y): pytest.param("arcsinh", [-5.0, -3.5, 0.0, 3.5, 5.0]), pytest.param("arctan", [-1.0, 0.0, 1.0]), pytest.param("arctanh", [-0.5, 0.0, 0.5]), + pytest.param("argmax", [1.0, 2.0, 4.0, 7.0]), + pytest.param("argmin", [1.0, 2.0, 4.0, 7.0]), pytest.param("ceil", [-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]), pytest.param("conjugate", [[1.0 + 1.0j, 0.0], [0.0, 1.0 + 1.0j]]), pytest.param("copy", [1.0, 2.0, 3.0]), diff --git a/tests/test_usm_type.py b/tests/test_usm_type.py index fd26d3e1c054..7d31cb7f25aa 100644 --- a/tests/test_usm_type.py +++ b/tests/test_usm_type.py @@ -372,6 +372,8 @@ def test_meshgrid(usm_type_x, usm_type_y): pytest.param("arcsinh", [-5.0, -3.5, 0.0, 3.5, 5.0]), pytest.param("arctan", [-1.0, 0.0, 1.0]), pytest.param("arctanh", [-0.5, 0.0, 0.5]), + pytest.param("argmax", [1.0, 2.0, 4.0, 7.0]), + pytest.param("argmin", [1.0, 2.0, 4.0, 7.0]), pytest.param("ceil", [-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]), pytest.param("conjugate", [[1.0 + 1.0j, 0.0], [0.0, 1.0 + 1.0j]]), pytest.param( diff --git a/tests/third_party/cupy/core_tests/test_ndarray_reduction.py b/tests/third_party/cupy/core_tests/test_ndarray_reduction.py index f22864bfef5b..70d654d6a694 100644 --- a/tests/third_party/cupy/core_tests/test_ndarray_reduction.py +++ b/tests/third_party/cupy/core_tests/test_ndarray_reduction.py @@ -226,6 +226,106 @@ def test_ptp_nan_imag(self, xp, dtype): ) return xp.ptp(a) + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_all(self, xp, dtype): + a = testing.shaped_random((2, 3), xp, dtype, order=self.order) + return a.argmax() + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_axis_large(self, xp, dtype): + a = testing.shaped_random((3, 1000), xp, dtype, order=self.order) + return a.argmax(axis=0) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_axis0(self, xp, dtype): + a = testing.shaped_random((2, 3, 4), xp, dtype, order=self.order) + return a.argmax(axis=0) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_axis1(self, xp, dtype): + a = testing.shaped_random((2, 3, 4), xp, dtype, order=self.order) + return a.argmax(axis=1) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_axis2(self, xp, dtype): + a = testing.shaped_random((2, 3, 4), xp, dtype, order=self.order) + return a.argmax(axis=2) + + @testing.for_float_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_nan(self, xp, dtype): + a = xp.array([float("nan"), 1, -1], dtype, order=self.order) + return a.argmax() + + @testing.for_complex_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_nan_real(self, xp, dtype): + a = xp.array([float("nan"), 1, -1], dtype, order=self.order) + return a.argmax() + + @testing.for_complex_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmax_nan_imag(self, xp, dtype): + a = xp.array( + [float("nan") * 1.0j, 1.0j, -1.0j], dtype, order=self.order + ) + return a.argmax() + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_all(self, xp, dtype): + a = testing.shaped_random((2, 3), xp, dtype, order=self.order) + return a.argmin() + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_axis_large(self, xp, dtype): + a = testing.shaped_random((3, 1000), xp, dtype, order=self.order) + return a.argmin(axis=0) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_axis0(self, xp, dtype): + a = testing.shaped_random((2, 3, 4), xp, dtype, order=self.order) + return a.argmin(axis=0) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_axis1(self, xp, dtype): + a = testing.shaped_random((2, 3, 4), xp, dtype, order=self.order) + return a.argmin(axis=1) + + @testing.for_all_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_axis2(self, xp, dtype): + a = testing.shaped_random((2, 3, 4), xp, dtype, order=self.order) + return a.argmin(axis=2) + + @testing.for_float_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_nan(self, xp, dtype): + a = xp.array([float("nan"), 1, -1], dtype, order=self.order) + return a.argmin() + + @testing.for_complex_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_nan_real(self, xp, dtype): + a = xp.array([float("nan"), 1, -1], dtype, order=self.order) + return a.argmin() + + @testing.for_complex_dtypes() + @testing.numpy_cupy_allclose(contiguous_check=False) + def test_argmin_nan_imag(self, xp, dtype): + a = xp.array( + [float("nan") * 1.0j, 1.0j, -1.0j], dtype, order=self.order + ) + return a.argmin() + @testing.parameterize( *testing.product( @@ -263,7 +363,7 @@ def test_ptp_nan_imag(self, xp, dtype): ((2, 3, 0), (0, 1, 2)), ], "order": ("C", "F"), - "func": ("min", "max"), + "func": ("min", "max", "argmin", "argmax"), } ) ) diff --git a/tests/third_party/cupy/sorting_tests/test_search.py b/tests/third_party/cupy/sorting_tests/test_search.py index 1a917367266d..edfe4ea02ed1 100644 --- a/tests/third_party/cupy/sorting_tests/test_search.py +++ b/tests/third_party/cupy/sorting_tests/test_search.py @@ -29,35 +29,30 @@ def test_argmax_nan(self, xp, dtype): a = xp.array([float("nan"), -1, 1], dtype) return a.argmax() - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmax_axis_large(self, xp, dtype): a = testing.shaped_random((3, 1000), xp, dtype) return a.argmax(axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_external_argmax_axis_large(self, xp, dtype): a = testing.shaped_random((3, 1000), xp, dtype) return xp.argmax(a, axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmax_axis0(self, xp, dtype): a = testing.shaped_random((2, 3, 4), xp, dtype) return a.argmax(axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmax_axis1(self, xp, dtype): a = testing.shaped_random((2, 3, 4), xp, dtype) return a.argmax(axis=1) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmax_axis2(self, xp, dtype): @@ -77,7 +72,6 @@ def test_argmax_zero_size(self, dtype): with pytest.raises(ValueError): a.argmax() - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) def test_argmax_zero_size_axis0(self, dtype): for xp in (numpy, cupy): @@ -85,7 +79,6 @@ def test_argmax_zero_size_axis0(self, dtype): with pytest.raises(ValueError): a.argmax(axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmax_zero_size_axis1(self, xp, dtype): @@ -98,8 +91,8 @@ def test_argmin_all(self, xp, dtype): a = testing.shaped_random((2, 3), xp, dtype) return a.argmin() - @testing.for_all_dtypes(no_complex=True) - @testing.numpy_cupy_allclose(accept_error=ValueError) + @testing.for_float_dtypes() + @testing.numpy_cupy_allclose() def test_argmin_nan(self, xp, dtype): a = xp.array([float("nan"), -1, 1], dtype) return a.argmin() @@ -110,35 +103,30 @@ def test_external_argmin_all(self, xp, dtype): a = testing.shaped_random((2, 3), xp, dtype) return xp.argmin(a) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmin_axis_large(self, xp, dtype): a = testing.shaped_random((3, 1000), xp, dtype) return a.argmin(axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_external_argmin_axis_large(self, xp, dtype): a = testing.shaped_random((3, 1000), xp, dtype) return xp.argmin(a, axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmin_axis0(self, xp, dtype): a = testing.shaped_random((2, 3, 4), xp, dtype) return a.argmin(axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmin_axis1(self, xp, dtype): a = testing.shaped_random((2, 3, 4), xp, dtype) return a.argmin(axis=1) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmin_axis2(self, xp, dtype): @@ -158,7 +146,6 @@ def test_argmin_zero_size(self, dtype): with pytest.raises(ValueError): return a.argmin() - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) def test_argmin_zero_size_axis0(self, dtype): for xp in (numpy, cupy): @@ -166,7 +153,6 @@ def test_argmin_zero_size_axis0(self, dtype): with pytest.raises(ValueError): a.argmin(axis=0) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes(no_complex=True) @testing.numpy_cupy_allclose() def test_argmin_zero_size_axis1(self, xp, dtype):