From 030b9bb37cdcc526850efdf98d729229869222c5 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Fri, 12 Jan 2024 18:23:58 -0600 Subject: [PATCH 1/5] implement sort and argsort --- dpnp/backend/include/dpnp_iface_fptr.hpp | 6 +- dpnp/backend/kernels/dpnp_krnl_sorting.cpp | 34 ---- dpnp/dpnp_algo/dpnp_algo.pxd | 10 -- dpnp/dpnp_algo/dpnp_algo_sorting.pxi | 13 -- dpnp/dpnp_array.py | 69 ++++---- dpnp/dpnp_iface_mathematical.py | 4 +- dpnp/dpnp_iface_nanfunctions.py | 10 +- dpnp/dpnp_iface_sorting.py | 148 +++++++++++++----- dpnp/dpnp_iface_statistics.py | 8 +- tests/skipped_tests.tbl | 51 +----- tests/skipped_tests_gpu.tbl | 51 +----- tests/test_indexing.py | 2 - tests/test_sort.py | 125 ++++++++++++++- .../cupy/sorting_tests/test_sort.py | 20 +-- 14 files changed, 288 insertions(+), 263 deletions(-) diff --git a/dpnp/backend/include/dpnp_iface_fptr.hpp b/dpnp/backend/include/dpnp_iface_fptr.hpp index cc0cb9e32a1b..64abe7fb27b3 100644 --- a/dpnp/backend/include/dpnp_iface_fptr.hpp +++ b/dpnp/backend/include/dpnp_iface_fptr.hpp @@ -78,8 +78,6 @@ enum class DPNPFuncName : size_t DPNP_FN_ARGMAX, /**< Used in numpy.argmax() impl */ DPNP_FN_ARGMIN, /**< Used in numpy.argmin() impl */ DPNP_FN_ARGSORT, /**< Used in numpy.argsort() impl */ - DPNP_FN_ARGSORT_EXT, /**< Used in numpy.argsort() impl, requires extra - parameters */ DPNP_FN_AROUND, /**< Used in numpy.around() impl */ DPNP_FN_ASTYPE, /**< Used in numpy.astype() impl */ DPNP_FN_BITWISE_AND, /**< Used in numpy.bitwise_and() impl */ @@ -361,9 +359,7 @@ enum class DPNPFuncName : size_t DPNP_FN_SIN, /**< Used in numpy.sin() impl */ DPNP_FN_SINH, /**< Used in numpy.sinh() impl */ DPNP_FN_SORT, /**< Used in numpy.sort() impl */ - DPNP_FN_SORT_EXT, /**< Used in numpy.sort() impl, requires extra parameters - */ - DPNP_FN_SQRT, /**< Used in numpy.sqrt() impl */ + DPNP_FN_SQRT, /**< Used in numpy.sqrt() impl */ DPNP_FN_SQRT_EXT, /**< Used in numpy.sqrt() impl, requires extra parameters */ DPNP_FN_SQUARE, /**< Used in numpy.square() impl */ diff --git a/dpnp/backend/kernels/dpnp_krnl_sorting.cpp b/dpnp/backend/kernels/dpnp_krnl_sorting.cpp index ac4992466e2f..6f33c1af723a 100644 --- a/dpnp/backend/kernels/dpnp_krnl_sorting.cpp +++ b/dpnp/backend/kernels/dpnp_krnl_sorting.cpp @@ -97,14 +97,6 @@ template void (*dpnp_argsort_default_c)(void *, void *, size_t) = dpnp_argsort_c<_DataType, _idx_DataType>; -template -DPCTLSyclEventRef (*dpnp_argsort_ext_c)(DPCTLSyclQueueRef, - void *, - void *, - size_t, - const DPCTLEventVectorRef) = - dpnp_argsort_c<_DataType, _idx_DataType>; - // template void dpnp_argsort_c(void* array1_in, void* result1, // size_t size); template void dpnp_argsort_c(void* array1_in, // void* result1, size_t size); template void dpnp_argsort_c(void* @@ -471,14 +463,6 @@ void dpnp_sort_c(void *array1_in, void *result1, size_t size) template void (*dpnp_sort_default_c)(void *, void *, size_t) = dpnp_sort_c<_DataType>; -template -DPCTLSyclEventRef (*dpnp_sort_ext_c)(DPCTLSyclQueueRef, - void *, - void *, - size_t, - const DPCTLEventVectorRef) = - dpnp_sort_c<_DataType>; - void func_map_init_sorting(func_map_t &fmap) { fmap[DPNPFuncName::DPNP_FN_ARGSORT][eft_INT][eft_INT] = { @@ -490,15 +474,6 @@ void func_map_init_sorting(func_map_t &fmap) fmap[DPNPFuncName::DPNP_FN_ARGSORT][eft_DBL][eft_DBL] = { eft_LNG, (void *)dpnp_argsort_default_c}; - fmap[DPNPFuncName::DPNP_FN_ARGSORT_EXT][eft_INT][eft_INT] = { - eft_LNG, (void *)dpnp_argsort_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGSORT_EXT][eft_LNG][eft_LNG] = { - eft_LNG, (void *)dpnp_argsort_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGSORT_EXT][eft_FLT][eft_FLT] = { - eft_LNG, (void *)dpnp_argsort_ext_c}; - fmap[DPNPFuncName::DPNP_FN_ARGSORT_EXT][eft_DBL][eft_DBL] = { - eft_LNG, (void *)dpnp_argsort_ext_c}; - fmap[DPNPFuncName::DPNP_FN_PARTITION][eft_INT][eft_INT] = { eft_INT, (void *)dpnp_partition_default_c}; fmap[DPNPFuncName::DPNP_FN_PARTITION][eft_LNG][eft_LNG] = { @@ -550,14 +525,5 @@ void func_map_init_sorting(func_map_t &fmap) fmap[DPNPFuncName::DPNP_FN_SORT][eft_DBL][eft_DBL] = { eft_DBL, (void *)dpnp_sort_default_c}; - fmap[DPNPFuncName::DPNP_FN_SORT_EXT][eft_INT][eft_INT] = { - eft_INT, (void *)dpnp_sort_ext_c}; - fmap[DPNPFuncName::DPNP_FN_SORT_EXT][eft_LNG][eft_LNG] = { - eft_LNG, (void *)dpnp_sort_ext_c}; - fmap[DPNPFuncName::DPNP_FN_SORT_EXT][eft_FLT][eft_FLT] = { - eft_FLT, (void *)dpnp_sort_ext_c}; - fmap[DPNPFuncName::DPNP_FN_SORT_EXT][eft_DBL][eft_DBL] = { - eft_DBL, (void *)dpnp_sort_ext_c}; - return; } diff --git a/dpnp/dpnp_algo/dpnp_algo.pxd b/dpnp/dpnp_algo/dpnp_algo.pxd index 4d59ffcf496d..d5dd513794de 100644 --- a/dpnp/dpnp_algo/dpnp_algo.pxd +++ b/dpnp/dpnp_algo/dpnp_algo.pxd @@ -36,8 +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_ARGSORT - DPNP_FN_ARGSORT_EXT DPNP_FN_CHOLESKY DPNP_FN_CHOLESKY_EXT DPNP_FN_CHOOSE @@ -179,8 +177,6 @@ cdef extern from "dpnp_iface_fptr.hpp" namespace "DPNPFuncName": # need this na DPNP_FN_RNG_ZIPF_EXT DPNP_FN_SEARCHSORTED DPNP_FN_SEARCHSORTED_EXT - DPNP_FN_SORT - DPNP_FN_SORT_EXT DPNP_FN_SVD DPNP_FN_SVD_EXT DPNP_FN_TRACE @@ -313,12 +309,6 @@ cpdef dpnp_descriptor dpnp_fmin(dpnp_descriptor x1_obj, dpnp_descriptor x2_obj, dpnp_descriptor out=*, object where=*) -""" -Sorting functions -""" -cpdef dpnp_descriptor dpnp_argsort(dpnp_descriptor array1) -cpdef dpnp_descriptor dpnp_sort(dpnp_descriptor array1) - """ Trigonometric functions """ diff --git a/dpnp/dpnp_algo/dpnp_algo_sorting.pxi b/dpnp/dpnp_algo/dpnp_algo_sorting.pxi index c05ca603b0a8..069b5335c1ce 100644 --- a/dpnp/dpnp_algo/dpnp_algo_sorting.pxi +++ b/dpnp/dpnp_algo/dpnp_algo_sorting.pxi @@ -36,10 +36,8 @@ and the rest of the library # NO IMPORTs here. All imports must be placed into main "dpnp_algo.pyx" file __all__ += [ - "dpnp_argsort", "dpnp_partition", "dpnp_searchsorted", - "dpnp_sort" ] @@ -61,13 +59,6 @@ ctypedef c_dpctl.DPCTLSyclEventRef(*fptr_dpnp_searchsorted_t)(c_dpctl.DPCTLSyclQ const c_dpctl.DPCTLEventVectorRef) -cpdef utils.dpnp_descriptor dpnp_argsort(utils.dpnp_descriptor x1): - cdef shape_type_c result_shape = x1.shape - if result_shape == (): - result_shape = (1,) - return call_fptr_1in_1out(DPNP_FN_ARGSORT_EXT, x1, result_shape) - - cpdef utils.dpnp_descriptor dpnp_partition(utils.dpnp_descriptor arr, int kth, axis=-1, kind='introselect', order=None): cdef shape_type_c shape1 = arr.shape @@ -148,7 +139,3 @@ cpdef utils.dpnp_descriptor dpnp_searchsorted(utils.dpnp_descriptor arr, utils.d c_dpctl.DPCTLEvent_Delete(event_ref) return result - - -cpdef utils.dpnp_descriptor dpnp_sort(utils.dpnp_descriptor x1): - return call_fptr_1in_1out(DPNP_FN_SORT_EXT, x1, x1.shape) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index ec8a93c335fa..66ebe7fbcdfa 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -510,39 +510,7 @@ def argsort(self, axis=-1, kind=None, order=None): """ Return an ndarray of indices that sort the array along the specified axis. - Parameters - ---------- - axis : int, optional - Axis along which to sort. If None, the default, the flattened array - is used. - .. versionchanged:: 1.13.0 - Previously, the default was documented to be -1, but that was - in error. At some future date, the default will change to -1, as - originally intended. - Until then, the axis should be given explicitly when - ``arr.ndim > 1``, to avoid a FutureWarning. - kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional - The sorting algorithm used. - order : list, optional - When `a` is an array with fields defined, this argument specifies - which fields to compare first, second, etc. Not all fields need be - specified. - - Returns - ------- - index_array : ndarray, int - Array of indices that sort `a` along the specified axis. - In other words, ``a[index_array]`` yields a sorted `a`. - - See Also - -------- - MaskedArray.sort : Describes sorting algorithms used. - :obj:`dpnp.lexsort` : Indirect stable sort with multiple keys. - :obj:`numpy.ndarray.sort` : Inplace sort. - - Notes - ----- - See `sort` for notes on the different sorting algorithms. + Refer to :obj:`dpnp.argsort` for full documentation. """ return dpnp.argsort(self, axis, kind, order) @@ -1163,14 +1131,43 @@ def size(self): return self._array_obj.size - # 'sort', + def sort(self, axis=-1, kind=None, order=None): + """ + Sort an array in-place. + + Refer to :obj:`dpnp.sort` for full documentation. + + Note + ---- + `axis` in :obj:`dpnp.sort` could be integr or ``None``. If ``None``, + the array is flattened before sorting. However, `axis` in :obj:`dpnp.ndarray.sort` + can only be integer since it sorts an array in-place. + + Examples + -------- + >>> import dpnp as np + >>> a = np.array([[1,4],[3,1]]) + >>> a.sort(axis=1) + >>> a + array([[1, 4], + [1, 3]]) + >>> a.sort(axis=0) + >>> a + array([[1, 1], + [3, 4]]) + + """ + if axis is None: + raise TypeError( + "'NoneType' object cannot be interpreted as an integer" + ) + self[...] = dpnp.sort(self, axis=axis, kind=kind, order=order) def squeeze(self, axis=None): """ Remove single-dimensional entries from the shape of an array. - .. seealso:: - :obj:`dpnp.squeeze` for full documentation + Refer to :obj:`dpnp.squeeze` for full documentation """ diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index add9c1304ce6..c63189667fa4 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -2660,7 +2660,7 @@ def sum( Parameters ---------- - a : {dpnp.ndarray, usm_ndarray}: + a : {dpnp.ndarray, usm_ndarray} Input array. axis : int or tuple of ints, optional Axis or axes along which sums must be computed. If a tuple @@ -2713,7 +2713,7 @@ def sum( Limitations ----------- - Parameters `initial` and `where` are supported with their default values. + Parameters `initial` and `where` are only supported with their default values. Otherwise ``NotImplementedError`` exception will be raised. See Also diff --git a/dpnp/dpnp_iface_nanfunctions.py b/dpnp/dpnp_iface_nanfunctions.py index 18f4ff0e87dd..7b733b32f769 100644 --- a/dpnp/dpnp_iface_nanfunctions.py +++ b/dpnp/dpnp_iface_nanfunctions.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # ***************************************************************************** -# Copyright (c) 2016-2024, Intel Corporation +# Copyright (c) 2023-2024, Intel Corporation # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -415,7 +415,7 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=False, *, where=True): Parameters ---------- - a : {dpnp.ndarray, usm_ndarray}: + a : {dpnp.ndarray, usm_ndarray} Input array. axis : int or tuple of ints, optional Axis or axes along which the arithmetic means must be computed. If @@ -696,7 +696,7 @@ def nansum( Parameters ---------- - a : {dpnp.ndarray, usm_ndarray}: + a : {dpnp.ndarray, usm_ndarray} Input array. axis : int or tuple of ints, optional Axis or axes along which sums must be computed. If a tuple @@ -806,7 +806,7 @@ def nanstd( Parameters ---------- - a : {dpnp.ndarray, usm_ndarray}: + a : {dpnp.ndarray, usm_ndarray} Input array. axis : int or tuple of ints, optional Axis or axes along which the standard deviations must be computed. @@ -908,7 +908,7 @@ def nanvar( Parameters ---------- - a : {dpnp_array, usm_ndarray}: + a : {dpnp_array, usm_ndarray} Input array. axis : int or tuple of ints, optional axis or axes along which the variances must be computed. If a tuple diff --git a/dpnp/dpnp_iface_sorting.py b/dpnp/dpnp_iface_sorting.py index c34b97ac21c1..d98eb5b7b38f 100644 --- a/dpnp/dpnp_iface_sorting.py +++ b/dpnp/dpnp_iface_sorting.py @@ -40,33 +40,52 @@ """ +import dpctl.tensor as dpt import numpy import dpnp from dpnp.dpnp_algo import * +from dpnp.dpnp_array import dpnp_array from dpnp.dpnp_utils import * __all__ = ["argsort", "partition", "searchsorted", "sort"] -def argsort(in_array1, axis=-1, kind=None, order=None): +def argsort(a, axis=-1, kind=None, order=None): """ Returns the indices that would sort an array. For full documentation refer to :obj:`numpy.argsort`. + Parameters + ---------- + a : {dpnp.ndarray, usm_ndarray} + Array to be sorted. + axis : int or None, optional + Axis along which to sort. If ``None``, the array is flattened before + sorting. The default is -1, which sorts along the last axis. + kind : None + Default is ``None``, which is equivalent to `stable`. + Unlike in NumPy any other options are not accepted here. + + Returns + ------- + out : dpnp.ndarray, int + Array of indices that sort `a` along the specified `axis`. + If `a` is one-dimensional, ``a[index_array]`` yields a sorted `a`. + More generally, ``dpnp.take_along_axis(a, index_array, axis=axis)`` + always yields the sorted `a`, irrespective of dimensionality. + Limitations ----------- - Input array is supported as :obj:`dpnp.ndarray`. + Parameters `kind` and `order` are only supported with their default values. Otherwise the function will be executed sequentially on CPU. - Parameter `axis` is supported only with default value ``-1``. - Parameter `kind` is supported only with default value ``None``. - Parameter `order` is supported only with default value ``None``. Input array data types are limited by supported DPNP :ref:`Data types`. See Also -------- - :obj:`dpnp.sort` : Describes sorting algorithms used. + :obj:`dpnp.ndarray.argsort` : Equivalent method. + :obj:`dpnp.sort` : Return a sorted copy of an array. :obj:`dpnp.lexsort` : Indirect stable sort with multiple keys. :obj:`dpnp.argpartition` : Indirect partial sort. :obj:`dpnp.take_along_axis` : Apply ``index_array`` from argsort to @@ -76,26 +95,48 @@ def argsort(in_array1, axis=-1, kind=None, order=None): -------- >>> import dpnp as np >>> x = np.array([3, 1, 2]) - >>> out = np.argsort(x) - >>> [i for i in out] - [1, 2, 0] + >>> np.argsort(x) + array([1, 2, 0]) + + >>> x = np.array([[0, 3], [2, 2]]) + >>> x + array([[0, 3], + [2, 2]]) + + >>> ind = np.argsort(x, axis=0) # sorts along first axis + >>> ind + array([[0, 1], + [1, 0]]) + >>> np.take_along_axis(x, ind, axis=0) # same as np.sort(x, axis=0) + array([[0, 2], + [2, 3]]) + + >>> ind = np.argsort(x, axis=1) # sorts along last axis + >>> ind + array([[0, 1], + [0, 1]]) + >>> np.take_along_axis(x, ind, axis=1) # same as np.sort(x, axis=1) + array([[0, 3], + [2, 2]]) """ - x1_desc = dpnp.get_dpnp_descriptor( - in_array1, copy_when_nondefault_queue=False - ) - if x1_desc: - if axis != -1: - pass - elif kind is not None: - pass - elif order is not None: - pass - else: - return dpnp_argsort(x1_desc).get_pyobj() + if kind is not None: + pass + elif order is not None: + pass + else: + if axis is None: + dpnp.check_supported_arrays_type(a) + a = a.flatten() + axis = -1 + result = dpnp_array._create_from_usm_ndarray( + dpt.argsort(dpnp.get_usm_ndarray(a), axis=axis) + ) + result = dpnp.atleast_1d(result) if a.ndim == 0 else result + return result - return call_origin(numpy.argsort, in_array1, axis, kind, order) + return call_origin(numpy.argsort, a, axis=axis, kind=kind, order=order) def partition(x1, kth, axis=-1, kind="introselect", order=None): @@ -166,22 +207,37 @@ def searchsorted(x1, x2, side="left", sorter=None): return call_origin(numpy.searchsorted, x1, x2, side=side, sorter=sorter) -def sort(x1, **kwargs): +def sort(a, axis=-1, kind=None, order=None): """ Return a sorted copy of an array. For full documentation refer to :obj:`numpy.sort`. + Parameters + ---------- + a : {dpnp.ndarray, usm_ndarray} + Array to be sorted. + axis : int or None, optional + Axis along which to sort. If ``None``, the array is flattened before + sorting. The default is -1, which sorts along the last axis. + kind : None + Default is ``None``, which is equivalent to `stable`. + Unlike in NumPy any other options are not accepted here. + + Returns + ------- + out : dpnp.ndarray + Sorted array with the same type and shape as `a`. + Limitations ----------- - Input array is supported as :obj:`dpnp.ndarray`. - Keyword arguments ``kwargs`` are currently unsupported. - Dimension of input array is supported to be equal to ``1``. + Parameters `kind` and `order` 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`. See Also -------- + :obj:`dpnp.ndarray.sort` : Sort an array in-place. :obj:`dpnp.argsort` : Indirect sort. :obj:`dpnp.lexsort` : Indirect stable sort on multiple keys. :obj:`dpnp.searchsorted` : Find elements in a sorted array. @@ -190,18 +246,34 @@ def sort(x1, **kwargs): Examples -------- >>> import dpnp as np - >>> a = np.array([1, 4, 3, 1]) - >>> out = np.sort(a) - >>> [i for i in out] - [1, 1, 3, 4] + >>> a = np.array([[1,4],[3,1]]) + >>> np.sort(a) # sort along the last axis + array([[1, 4], + [1, 3]]) + >>> np.sort(a, axis=None) # sort the flattened array + array([1, 1, 3, 4]) + >>> np.sort(a, axis=0) # sort along the first axis + array([[1, 1], + [3, 4]]) """ - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False) - if x1_desc and not kwargs: - if x1_desc.ndim != 1: - pass - else: - return dpnp_sort(x1_desc).get_pyobj() - - return call_origin(numpy.sort, x1, **kwargs) + if kind is not None: + pass + elif order is not None: + pass + else: + dpnp.check_supported_arrays_type(a) + if a.ndim == 0: + raise numpy.AxisError( + f"axis {axis} is out of bounds for array of dimension {a.ndim}." + ) + if axis is None: + dpnp.check_supported_arrays_type(a) + a = a.flatten() + axis = -1 + return dpnp_array._create_from_usm_ndarray( + dpt.sort(dpnp.get_usm_ndarray(a), axis=axis) + ) + + return call_origin(numpy.sort, a, axis=axis, kind=kind, order=order) diff --git a/dpnp/dpnp_iface_statistics.py b/dpnp/dpnp_iface_statistics.py index cf0423de2260..0b2f69acef22 100644 --- a/dpnp/dpnp_iface_statistics.py +++ b/dpnp/dpnp_iface_statistics.py @@ -151,7 +151,7 @@ def average(a, axis=None, weights=None, returned=False, *, keepdims=False): Parameters ---------- - a : {dpnp.ndarray, usm_ndarray}: + a : {dpnp.ndarray, usm_ndarray} Input array. axis : int or tuple of ints, optional Axis or axes along which the averages must be computed. If @@ -573,7 +573,7 @@ def mean(a, /, axis=None, dtype=None, out=None, keepdims=False, *, where=True): Parameters ---------- - a : {dpnp.ndarray, usm_ndarray}: + a : {dpnp.ndarray, usm_ndarray} Input array. axis : int or tuple of ints, optional Axis or axes along which the arithmetic means must be computed. If @@ -839,7 +839,7 @@ def std( Parameters ---------- - a : {dpnp_array, usm_ndarray}: + a : {dpnp_array, usm_ndarray} Input array. axis : int or tuple of ints, optional Axis or axes along which the standard deviations must be computed. @@ -958,7 +958,7 @@ def var( Parameters ---------- - a : {dpnp_array, usm_ndarray}: + a : {dpnp_array, usm_ndarray} Input array. axis : int or tuple of ints, optional axis or axes along which the variances must be computed. If a tuple diff --git a/tests/skipped_tests.tbl b/tests/skipped_tests.tbl index 8958c7f8f217..555dbe90fd3a 100644 --- a/tests/skipped_tests.tbl +++ b/tests/skipped_tests.tbl @@ -759,6 +759,7 @@ tests/third_party/cupy/sorting_tests/test_search.py::TestFlatNonzero_param_3_{ar tests/third_party/cupy/sorting_tests/test_search.py::TestFlatNonzero_param_4_{array=array([], shape=(0, 2, 0), dtype=float64)}::test_flatnonzero 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_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 @@ -789,36 +790,6 @@ tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{ext tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{external=True}::test_argpartition_one_dim tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{external=True}::test_argpartition_sequence_kth tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{external=True}::test_argpartition_zero_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_negative_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_negative_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_multi_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_negative_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_none_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_zero_dim_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_zero_dim_invalid_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_nan1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_nan2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_negative_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_negative_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_multi_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_negative_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_none_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_zero_dim_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_zero_dim_invalid_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_nan1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_nan2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_non_contiguous -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_one_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_original_array_not_modified_multi_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_original_array_not_modified_one_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_zero_dim - tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_F_order tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_lexsort_dtype tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_lexsort_three_or_more_dim @@ -828,29 +799,11 @@ tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_nan3 tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_view tests/third_party/cupy/sorting_tests/test_sort.py::TestMsort::test_msort_multi_dim tests/third_party/cupy/sorting_tests/test_sort.py::TestMsort::test_msort_one_dim - tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_1dim tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_nan tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_ndim tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_zero_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_external_sort_zero_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan3 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan4 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_axis3 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_contiguous -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_dtype -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_negative_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_negative_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_negative_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_non_contiguous -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_two_or_more_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_zero_dim + tests/third_party/cupy/statistics_tests/test_correlation.py::TestCorrcoef::test_corrcoef tests/third_party/cupy/statistics_tests/test_correlation.py::TestCorrcoef::test_corrcoef_diag_exception tests/third_party/cupy/statistics_tests/test_correlation.py::TestCorrcoef::test_corrcoef_rowvar diff --git a/tests/skipped_tests_gpu.tbl b/tests/skipped_tests_gpu.tbl index a6a2886ad86a..54540776086f 100644 --- a/tests/skipped_tests_gpu.tbl +++ b/tests/skipped_tests_gpu.tbl @@ -820,6 +820,7 @@ tests/third_party/cupy/sorting_tests/test_search.py::TestFlatNonzero_param_3_{ar tests/third_party/cupy/sorting_tests/test_search.py::TestFlatNonzero_param_4_{array=array([], shape=(0, 2, 0), dtype=float64)}::test_flatnonzero 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_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 @@ -850,36 +851,6 @@ tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{ext tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{external=True}::test_argpartition_one_dim tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{external=True}::test_argpartition_sequence_kth tests/third_party/cupy/sorting_tests/test_sort.py::TestArgpartition_param_1_{external=True}::test_argpartition_zero_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_negative_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_invalid_negative_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_multi_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_negative_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_none_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_zero_dim_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_zero_dim_invalid_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_nan1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_nan2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_negative_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_invalid_negative_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_multi_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_negative_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_none_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_zero_dim_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_argsort_zero_dim_invalid_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_nan1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_1_{external=True}::test_nan2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_non_contiguous -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_one_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_original_array_not_modified_multi_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_original_array_not_modified_one_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestArgsort_param_0_{external=False}::test_argsort_zero_dim - tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_F_order tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_lexsort_dtype tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_lexsort_three_or_more_dim @@ -889,29 +860,11 @@ tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_nan3 tests/third_party/cupy/sorting_tests/test_sort.py::TestLexsort::test_view tests/third_party/cupy/sorting_tests/test_sort.py::TestMsort::test_msort_multi_dim tests/third_party/cupy/sorting_tests/test_sort.py::TestMsort::test_msort_one_dim - tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_1dim tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_nan tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_ndim tests/third_party/cupy/sorting_tests/test_sort.py::TestSort_complex::test_sort_complex_zero_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_external_sort_zero_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan3 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_nan4 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_axis3 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_contiguous -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_dtype -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_negative_axis1 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_invalid_negative_axis2 -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_negative_axis -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_non_contiguous -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_two_or_more_dim -tests/third_party/cupy/sorting_tests/test_sort.py::TestSort::test_sort_zero_dim + tests/third_party/cupy/statistics_tests/test_correlation.py::TestCorrcoef::test_corrcoef tests/third_party/cupy/statistics_tests/test_correlation.py::TestCorrcoef::test_corrcoef_diag_exception tests/third_party/cupy/statistics_tests/test_correlation.py::TestCorrcoef::test_corrcoef_rowvar diff --git a/tests/test_indexing.py b/tests/test_indexing.py index 01e5c33f8efa..2640eb64c49a 100644 --- a/tests/test_indexing.py +++ b/tests/test_indexing.py @@ -154,8 +154,6 @@ def test_broadcast(self, arr_dt, idx_dt): class TestTakeAlongAxis: - # TODO: remove fixture once `dpnp.sort` is fully implemented - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @pytest.mark.parametrize( "func, argfunc, kwargs", [ diff --git a/tests/test_sort.py b/tests/test_sort.py index 7ef6c23f734e..cc5cd87c58a4 100644 --- a/tests/test_sort.py +++ b/tests/test_sort.py @@ -4,7 +4,130 @@ import dpnp -from .helper import get_all_dtypes +from .helper import assert_dtype_allclose, get_all_dtypes, get_complex_dtypes + + +class TestSort: + @pytest.mark.parametrize("dtype", get_all_dtypes(no_complex=True)) + def test_sort_dtype(self, dtype): + a = numpy.random.uniform(-5, 5, 10) + np_array = numpy.array(a, dtype=dtype) + dp_array = dpnp.array(np_array) + + result = dpnp.sort(dp_array) + expected = numpy.sort(np_array) + assert_dtype_allclose(result, expected) + + @pytest.mark.parametrize("dtype", get_complex_dtypes()) + def test_sort_complex(self, dtype): + a = numpy.random.uniform(-5, 5, 10) + b = numpy.random.uniform(-5, 5, 10) + np_array = numpy.array(a + b * 1j, dtype=dtype) + dp_array = dpnp.array(np_array) + + result = dpnp.sort(dp_array) + expected = numpy.sort(np_array) + assert_dtype_allclose(result, expected) + + @pytest.mark.parametrize("axis", [None, -2, -1, 0, 1, 2]) + def test_sort_axis(self, axis): + a = numpy.random.uniform(-10, 10, 36) + np_array = numpy.array(a).reshape(3, 4, 3) + dp_array = dpnp.array(np_array) + + result = dpnp.sort(dp_array, axis=axis) + expected = numpy.sort(np_array, axis=axis) + assert_dtype_allclose(result, expected) + + @pytest.mark.parametrize("dtype", get_all_dtypes()) + @pytest.mark.parametrize("axis", [-2, -1, 0, 1]) + def test_sort_ndarray(self, dtype, axis): + a = numpy.random.uniform(-10, 10, 12) + np_array = numpy.array(a, dtype=dtype).reshape(6, 2) + dp_array = dpnp.array(np_array) + + dp_array.sort(axis=axis) + np_array.sort(axis=axis) + assert_dtype_allclose(dp_array, np_array) + + def test_sort_stable(self): + np_array = numpy.repeat(numpy.arange(10), 10) + dp_array = dpnp.array(np_array) + + result = dpnp.sort(dp_array) + expected = numpy.sort(np_array, kind="stable") + assert_dtype_allclose(result, expected) + + def test_sort_ndarray_axis_none(self): + a = numpy.random.uniform(-10, 10, 12) + dp_array = dpnp.array(a).reshape(6, 2) + with pytest.raises(TypeError): + dp_array.sort(axis=None) + + def test_sort_zero_dim(self): + dp_array = dpnp.array(2.5) + with pytest.raises(numpy.AxisError): + dpnp.sort(dp_array) + + +class TestArgsort: + @pytest.mark.parametrize("dtype", get_all_dtypes(no_complex=True)) + def test_argsort_dtype(self, dtype): + a = numpy.random.uniform(-5, 5, 10) + np_array = numpy.array(a, dtype=dtype) + dp_array = dpnp.array(np_array) + + result = dpnp.argsort(dp_array) + expected = numpy.argsort(np_array) + assert_dtype_allclose(result, expected) + + @pytest.mark.parametrize("dtype", get_complex_dtypes()) + def test_argsort_complex(self, dtype): + a = numpy.random.uniform(-5, 5, 10) + b = numpy.random.uniform(-5, 5, 10) + np_array = numpy.array(a + b * 1j, dtype=dtype) + dp_array = dpnp.array(np_array) + + result = dpnp.argsort(dp_array) + expected = numpy.argsort(np_array) + assert_dtype_allclose(result, expected) + + @pytest.mark.parametrize("axis", [None, -2, -1, 0, 1, 2]) + def test_argsort_axis(self, axis): + a = numpy.random.uniform(-10, 10, 36) + np_array = numpy.array(a).reshape(3, 4, 3) + dp_array = dpnp.array(np_array) + + result = dpnp.argsort(dp_array, axis=axis) + expected = numpy.argsort(np_array, axis=axis) + assert_dtype_allclose(result, expected) + + @pytest.mark.parametrize("dtype", get_all_dtypes()) + @pytest.mark.parametrize("axis", [None, -2, -1, 0, 1]) + def test_argsort_ndarray(self, dtype, axis): + a = numpy.random.uniform(-10, 10, 12) + np_array = numpy.array(a, dtype=dtype).reshape(6, 2) + dp_array = dpnp.array(np_array) + + result = dp_array.argsort(axis=axis) + expected = np_array.argsort(axis=axis) + assert_dtype_allclose(result, expected) + + def test_argsort_stable(self): + np_array = numpy.repeat(numpy.arange(10), 10) + dp_array = dpnp.array(np_array) + + result = dpnp.argsort(dp_array) + expected = numpy.argsort(np_array, kind="stable") + assert_dtype_allclose(result, expected) + + def test_argsort_zero_dim(self): + np_array = numpy.array(2.5) + dp_array = dpnp.array(np_array) + + result = dpnp.argsort(dp_array) + expected = numpy.argsort(np_array) + assert_dtype_allclose(result, expected) @pytest.mark.parametrize("kth", [0, 1], ids=["0", "1"]) diff --git a/tests/third_party/cupy/sorting_tests/test_sort.py b/tests/third_party/cupy/sorting_tests/test_sort.py index 7ae7911f90ee..f251b428cebf 100644 --- a/tests/third_party/cupy/sorting_tests/test_sort.py +++ b/tests/third_party/cupy/sorting_tests/test_sort.py @@ -17,7 +17,6 @@ def get_array_module(*args): cupy.get_array_module = get_array_module -@testing.gpu class TestSort(unittest.TestCase): # Test ranks @@ -33,14 +32,12 @@ def test_external_sort_zero_dim(self): with pytest.raises(numpy.AxisError): xp.sort(a) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_sort_two_or_more_dim(self, xp): a = testing.shaped_random((2, 3, 3), xp) a.sort() return a - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_external_sort_two_or_more_dim(self, xp): a = testing.shaped_random((2, 3, 3), xp) @@ -69,10 +66,11 @@ def test_sort_contiguous(self, xp): a.sort() return a - def test_sort_non_contiguous(self): - a = testing.shaped_random((10,), cupy)[::2] # Non contiguous view - with self.assertRaises(NotImplementedError): - a.sort() + @testing.numpy_cupy_array_equal() + def test_sort_non_contiguous(self, xp): + a = testing.shaped_random((10,), xp)[::2] # Non contiguous view + a.sort() + return a @testing.numpy_cupy_array_equal() def test_external_sort_contiguous(self, xp): @@ -104,7 +102,6 @@ def test_sort_axis3(self, xp): a.sort(axis=2) return a - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_external_sort_axis(self, xp): a = testing.shaped_random((2, 3, 3), xp) @@ -116,13 +113,11 @@ def test_sort_negative_axis(self, xp): a.sort(axis=-2) return a - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_external_sort_negative_axis(self, xp): a = testing.shaped_random((2, 3, 3), xp) return xp.sort(a, axis=-2) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_external_sort_none_axis(self, xp): a = testing.shaped_random((2, 3, 3), xp) @@ -139,14 +134,12 @@ def test_sort_invalid_axis2(self): with self.assertRaises(numpy.AxisError): a.sort(axis=3) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_external_sort_invalid_axis1(self): for xp in (numpy, cupy): a = testing.shaped_random((2, 3, 3), xp) with pytest.raises(numpy.AxisError): xp.sort(a, axis=3) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_external_sort_invalid_axis2(self): a = testing.shaped_random((2, 3, 3), cupy) with self.assertRaises(numpy.AxisError): @@ -163,14 +156,12 @@ def test_sort_invalid_negative_axis2(self): with self.assertRaises(numpy.AxisError): a.sort(axis=-4) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_external_sort_invalid_negative_axis1(self): for xp in (numpy, cupy): a = testing.shaped_random((2, 3, 3), xp) with pytest.raises(numpy.AxisError): xp.sort(a, axis=-4) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_external_sort_invalid_negative_axis2(self): a = testing.shaped_random((2, 3, 3), cupy) with self.assertRaises(numpy.AxisError): @@ -296,7 +287,6 @@ def test_F_order(self, xp): } ) ) -@testing.gpu class TestArgsort(unittest.TestCase): def argsort(self, a, axis=-1): if self.external: From 6338cfe1d5ab659a294bf1991e219d819071e27c Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Wed, 17 Jan 2024 13:26:45 -0600 Subject: [PATCH 2/5] add more tests --- dpnp/dpnp_iface_sorting.py | 4 ++-- tests/test_strides.py | 2 ++ tests/test_sycl_queue.py | 3 +++ tests/test_usm_type.py | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dpnp/dpnp_iface_sorting.py b/dpnp/dpnp_iface_sorting.py index d98eb5b7b38f..0d1492a0498c 100644 --- a/dpnp/dpnp_iface_sorting.py +++ b/dpnp/dpnp_iface_sorting.py @@ -64,7 +64,7 @@ def argsort(a, axis=-1, kind=None, order=None): axis : int or None, optional Axis along which to sort. If ``None``, the array is flattened before sorting. The default is -1, which sorts along the last axis. - kind : None + kind : None, optional Default is ``None``, which is equivalent to `stable`. Unlike in NumPy any other options are not accepted here. @@ -220,7 +220,7 @@ def sort(a, axis=-1, kind=None, order=None): axis : int or None, optional Axis along which to sort. If ``None``, the array is flattened before sorting. The default is -1, which sorts along the last axis. - kind : None + kind : None, optional Default is ``None``, which is equivalent to `stable`. Unlike in NumPy any other options are not accepted here. diff --git a/tests/test_strides.py b/tests/test_strides.py index 903341f10006..63938f8460d7 100644 --- a/tests/test_strides.py +++ b/tests/test_strides.py @@ -50,6 +50,7 @@ def test_strides(func_name, dtype): "arcsinh", "arctan", "arctanh", + "argsort", "cbrt", "ceil", "copy", @@ -73,6 +74,7 @@ def test_strides(func_name, dtype): "sign", "sin", "sinh", + "sort", "sqrt", "square", "tan", diff --git a/tests/test_sycl_queue.py b/tests/test_sycl_queue.py index fd09d08b5beb..674d0b9cbf17 100644 --- a/tests/test_sycl_queue.py +++ b/tests/test_sycl_queue.py @@ -341,6 +341,7 @@ def test_meshgrid(device_x, device_y): 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("argsort", [2.0, 1.0, 7.0, 4.0]), pytest.param("cbrt", [1.0, 8.0, 27.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]]), @@ -394,7 +395,9 @@ def test_meshgrid(device_x, device_y): "sin", [-dpnp.pi / 2, -dpnp.pi / 4, 0.0, dpnp.pi / 4, dpnp.pi / 2] ), pytest.param("sinh", [-5.0, -3.5, 0.0, 3.5, 5.0]), + pytest.param("sort", [2.0, 1.0, 7.0, 4.0]), pytest.param("sqrt", [1.0, 3.0, 9.0]), + pytest.param("square", [1.0, 3.0, 9.0]), pytest.param("std", [1.0, 2.0, 4.0, 7.0]), pytest.param("sum", [1.0, 2.0]), pytest.param( diff --git a/tests/test_usm_type.py b/tests/test_usm_type.py index fa4a14090b37..762730ff67c6 100644 --- a/tests/test_usm_type.py +++ b/tests/test_usm_type.py @@ -408,6 +408,7 @@ def test_meshgrid(usm_type_x, usm_type_y): 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("argsort", [2.0, 1.0, 7.0, 4.0]), pytest.param("cbrt", [1, 8, 27]), 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]]), @@ -457,8 +458,11 @@ def test_meshgrid(usm_type_x, usm_type_y): "sin", [-dp.pi / 2, -dp.pi / 4, 0.0, dp.pi / 4, dp.pi / 2] ), pytest.param("sinh", [-5.0, -3.5, 0.0, 3.5, 5.0]), + pytest.param("sort", [2.0, 1.0, 7.0, 4.0]), pytest.param("sqrt", [1.0, 3.0, 9.0]), + pytest.param("square", [1.0, 3.0, 9.0]), pytest.param("std", [1.0, 2.0, 4.0, 7.0]), + pytest.param("sum", [1.0, 2.0]), pytest.param( "tan", [-dp.pi / 2, -dp.pi / 4, 0.0, dp.pi / 4, dp.pi / 2] ), From 45e8e3e70b1726e012be42818f707425a74fb3c2 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Thu, 18 Jan 2024 11:13:43 -0600 Subject: [PATCH 3/5] update for zero dimensional arrays --- dpnp/dpnp_array.py | 1 + dpnp/dpnp_iface_sorting.py | 29 ++++++++++++------- tests/test_sort.py | 19 ++++++++++-- .../cupy/sorting_tests/test_sort.py | 6 ++-- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 66ebe7fbcdfa..cf848b506905 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -1157,6 +1157,7 @@ def sort(self, axis=-1, kind=None, order=None): [3, 4]]) """ + if axis is None: raise TypeError( "'NoneType' object cannot be interpreted as an integer" diff --git a/dpnp/dpnp_iface_sorting.py b/dpnp/dpnp_iface_sorting.py index 0d1492a0498c..e70733a1cc1d 100644 --- a/dpnp/dpnp_iface_sorting.py +++ b/dpnp/dpnp_iface_sorting.py @@ -76,11 +76,15 @@ def argsort(a, axis=-1, kind=None, order=None): More generally, ``dpnp.take_along_axis(a, index_array, axis=axis)`` always yields the sorted `a`, irrespective of dimensionality. + Notes + ----- + For zero-dimensional arrays, if `axis=None`, output is a one-dimensional + a single zero element. Otherwise, an ``AxisError`` is raised. + Limitations ----------- Parameters `kind` and `order` 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`. See Also -------- @@ -126,15 +130,17 @@ def argsort(a, axis=-1, kind=None, order=None): elif order is not None: pass else: + dpnp.check_supported_arrays_type(a) if axis is None: - dpnp.check_supported_arrays_type(a) a = a.flatten() axis = -1 - result = dpnp_array._create_from_usm_ndarray( + if a.ndim == 0: + raise numpy.AxisError( + f"axis {axis} is out of bounds for array of dimension {a.ndim}." + ) + return dpnp_array._create_from_usm_ndarray( dpt.argsort(dpnp.get_usm_ndarray(a), axis=axis) ) - result = dpnp.atleast_1d(result) if a.ndim == 0 else result - return result return call_origin(numpy.argsort, a, axis=axis, kind=kind, order=order) @@ -229,11 +235,15 @@ def sort(a, axis=-1, kind=None, order=None): out : dpnp.ndarray Sorted array with the same type and shape as `a`. + Notes + ----- + For zero-dimensional arrays, if `axis=None`, output is the input array + returned as a one-dimensional array. Otherwise, an ``AxisError`` is raised. + Limitations ----------- Parameters `kind` and `order` 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`. See Also -------- @@ -264,14 +274,13 @@ def sort(a, axis=-1, kind=None, order=None): pass else: dpnp.check_supported_arrays_type(a) + if axis is None: + a = a.flatten() + axis = -1 if a.ndim == 0: raise numpy.AxisError( f"axis {axis} is out of bounds for array of dimension {a.ndim}." ) - if axis is None: - dpnp.check_supported_arrays_type(a) - a = a.flatten() - axis = -1 return dpnp_array._create_from_usm_ndarray( dpt.sort(dpnp.get_usm_ndarray(a), axis=axis) ) diff --git a/tests/test_sort.py b/tests/test_sort.py index cc5cd87c58a4..e3b8d6b43bcd 100644 --- a/tests/test_sort.py +++ b/tests/test_sort.py @@ -65,10 +65,18 @@ def test_sort_ndarray_axis_none(self): dp_array.sort(axis=None) def test_sort_zero_dim(self): - dp_array = dpnp.array(2.5) + np_array = numpy.array(2.5) + dp_array = dpnp.array(np_array) + + # with default axis=-1 with pytest.raises(numpy.AxisError): dpnp.sort(dp_array) + # with axis = None + result = dpnp.sort(dp_array, axis=None) + expected = numpy.sort(np_array, axis=None) + assert_dtype_allclose(result, expected) + class TestArgsort: @pytest.mark.parametrize("dtype", get_all_dtypes(no_complex=True)) @@ -125,8 +133,13 @@ def test_argsort_zero_dim(self): np_array = numpy.array(2.5) dp_array = dpnp.array(np_array) - result = dpnp.argsort(dp_array) - expected = numpy.argsort(np_array) + # with default axis=-1 + with pytest.raises(numpy.AxisError): + dpnp.argsort(dp_array) + + # with axis = None + result = dpnp.argsort(dp_array, axis=None) + expected = numpy.argsort(np_array, axis=None) assert_dtype_allclose(result, expected) diff --git a/tests/third_party/cupy/sorting_tests/test_sort.py b/tests/third_party/cupy/sorting_tests/test_sort.py index f251b428cebf..647bdf201b30 100644 --- a/tests/third_party/cupy/sorting_tests/test_sort.py +++ b/tests/third_party/cupy/sorting_tests/test_sort.py @@ -301,7 +301,8 @@ def argsort(self, a, axis=-1): @testing.numpy_cupy_array_equal() def test_argsort_zero_dim(self, xp, dtype): a = testing.shaped_random((), xp, dtype) - return self.argsort(a) + axis = None if xp == cupy else -1 + return self.argsort(a, axis=axis) @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() @@ -352,7 +353,8 @@ def test_argsort_invalid_axis2(self): @testing.numpy_cupy_array_equal() def test_argsort_zero_dim_axis(self, xp): a = testing.shaped_random((), xp) - return self.argsort(a, axis=0) + axis = None if xp == cupy else 0 + return self.argsort(a, axis=axis) def test_argsort_zero_dim_invalid_axis(self): for xp in (numpy, cupy): From b9739e9d9cb49e5e77ab88ba3cc24af6fe8b05ad Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Fri, 19 Jan 2024 10:18:19 -0600 Subject: [PATCH 4/5] address comments --- dpnp/dpnp_iface_indexing.py | 1 + dpnp/dpnp_iface_sorting.py | 68 ++++++++++--------- tests/test_sort.py | 22 +++++- .../cupy/sorting_tests/test_sort.py | 20 ++++-- 4 files changed, 72 insertions(+), 39 deletions(-) diff --git a/dpnp/dpnp_iface_indexing.py b/dpnp/dpnp_iface_indexing.py index 20a4a60a5bcd..8f973ed1f1aa 100644 --- a/dpnp/dpnp_iface_indexing.py +++ b/dpnp/dpnp_iface_indexing.py @@ -851,6 +851,7 @@ def take_along_axis(a, indices, axis): -------- :obj:`dpnp.take` : Take along an axis, using the same indices for every 1d slice. :obj:`dpnp.put_along_axis` : Put values into the destination array by matching 1d index and data slices. + :obj:`dpnp.argsort` : Return the indices that would sort an array. Examples -------- diff --git a/dpnp/dpnp_iface_sorting.py b/dpnp/dpnp_iface_sorting.py index e70733a1cc1d..337a9ea5fbef 100644 --- a/dpnp/dpnp_iface_sorting.py +++ b/dpnp/dpnp_iface_sorting.py @@ -42,6 +42,7 @@ import dpctl.tensor as dpt import numpy +from numpy.core.numeric import normalize_axis_index import dpnp from dpnp.dpnp_algo import * @@ -64,27 +65,30 @@ def argsort(a, axis=-1, kind=None, order=None): axis : int or None, optional Axis along which to sort. If ``None``, the array is flattened before sorting. The default is -1, which sorts along the last axis. - kind : None, optional - Default is ``None``, which is equivalent to `stable`. + kind : {None, "stable"}, optional + Default is ``None``, which is equivalent to `"stable"`. Unlike in NumPy any other options are not accepted here. Returns ------- - out : dpnp.ndarray, int + out : dpnp.ndarray Array of indices that sort `a` along the specified `axis`. If `a` is one-dimensional, ``a[index_array]`` yields a sorted `a`. More generally, ``dpnp.take_along_axis(a, index_array, axis=axis)`` always yields the sorted `a`, irrespective of dimensionality. + The return array has default array index data type. Notes ----- For zero-dimensional arrays, if `axis=None`, output is a one-dimensional - a single zero element. Otherwise, an ``AxisError`` is raised. + array with a single zero element. Otherwise, an ``AxisError`` is raised. Limitations ----------- - Parameters `kind` and `order` are only supported with their default values. - Otherwise the function will be executed sequentially on CPU. + Parameters `order` is only supported with its default value. + Parameters `kind` can only be ``None`` or ``"stable"``which + are equivalent. + Otherwise ``NotImplementedError`` exception will be raised. See Also -------- @@ -125,25 +129,25 @@ def argsort(a, axis=-1, kind=None, order=None): """ - if kind is not None: - pass - elif order is not None: - pass + if order is not None: + raise NotImplementedError( + "order keyword argument is only supported with its default value." + ) + elif kind is not None and kind != "stable": + raise NotImplementedError( + "kind keyword argument can only be None or 'stable'." + ) else: dpnp.check_supported_arrays_type(a) if axis is None: a = a.flatten() axis = -1 - if a.ndim == 0: - raise numpy.AxisError( - f"axis {axis} is out of bounds for array of dimension {a.ndim}." - ) + + axis = normalize_axis_index(axis, ndim=a.ndim) return dpnp_array._create_from_usm_ndarray( dpt.argsort(dpnp.get_usm_ndarray(a), axis=axis) ) - return call_origin(numpy.argsort, a, axis=axis, kind=kind, order=order) - def partition(x1, kth, axis=-1, kind="introselect", order=None): """ @@ -226,8 +230,8 @@ def sort(a, axis=-1, kind=None, order=None): axis : int or None, optional Axis along which to sort. If ``None``, the array is flattened before sorting. The default is -1, which sorts along the last axis. - kind : None, optional - Default is ``None``, which is equivalent to `stable`. + kind : {None, "stable"}, optional + Default is ``None``, which is equivalent to `"stable"`. Unlike in NumPy any other options are not accepted here. Returns @@ -242,13 +246,15 @@ def sort(a, axis=-1, kind=None, order=None): Limitations ----------- - Parameters `kind` and `order` are only supported with their default values. - Otherwise the function will be executed sequentially on CPU. + Parameters `order` is only supported with its default value. + Parameters `kind` can only be ``None`` or ``"stable"``which + are equivalent. + Otherwise ``NotImplementedError`` exception will be raised. See Also -------- :obj:`dpnp.ndarray.sort` : Sort an array in-place. - :obj:`dpnp.argsort` : Indirect sort. + :obj:`dpnp.argsort` : Return the indices that would sort an array. :obj:`dpnp.lexsort` : Indirect stable sort on multiple keys. :obj:`dpnp.searchsorted` : Find elements in a sorted array. :obj:`dpnp.partition` : Partial sort. @@ -268,21 +274,21 @@ def sort(a, axis=-1, kind=None, order=None): """ - if kind is not None: - pass - elif order is not None: - pass + if order is not None: + raise NotImplementedError( + "order keyword argument is only supported with its default value." + ) + elif kind is not None and kind != "stable": + raise NotImplementedError( + "kind keyword argument can only be None or 'stable'." + ) else: dpnp.check_supported_arrays_type(a) if axis is None: a = a.flatten() axis = -1 - if a.ndim == 0: - raise numpy.AxisError( - f"axis {axis} is out of bounds for array of dimension {a.ndim}." - ) + + axis = normalize_axis_index(axis, ndim=a.ndim) return dpnp_array._create_from_usm_ndarray( dpt.sort(dpnp.get_usm_ndarray(a), axis=axis) ) - - return call_origin(numpy.sort, a, axis=axis, kind=kind, order=order) diff --git a/tests/test_sort.py b/tests/test_sort.py index e3b8d6b43bcd..768870f16329 100644 --- a/tests/test_sort.py +++ b/tests/test_sort.py @@ -54,7 +54,7 @@ def test_sort_stable(self): np_array = numpy.repeat(numpy.arange(10), 10) dp_array = dpnp.array(np_array) - result = dpnp.sort(dp_array) + result = dpnp.sort(dp_array, kind="stable") expected = numpy.sort(np_array, kind="stable") assert_dtype_allclose(result, expected) @@ -77,6 +77,15 @@ def test_sort_zero_dim(self): expected = numpy.sort(np_array, axis=None) assert_dtype_allclose(result, expected) + def test_sort_notimplemented(self): + dp_array = dpnp.arange(10) + + with pytest.raises(NotImplementedError): + dpnp.sort(dp_array, kind="quicksort") + + with pytest.raises(NotImplementedError): + dpnp.sort(dp_array, order=["age"]) + class TestArgsort: @pytest.mark.parametrize("dtype", get_all_dtypes(no_complex=True)) @@ -125,7 +134,7 @@ def test_argsort_stable(self): np_array = numpy.repeat(numpy.arange(10), 10) dp_array = dpnp.array(np_array) - result = dpnp.argsort(dp_array) + result = dpnp.argsort(dp_array, kind="stable") expected = numpy.argsort(np_array, kind="stable") assert_dtype_allclose(result, expected) @@ -142,6 +151,15 @@ def test_argsort_zero_dim(self): expected = numpy.argsort(np_array, axis=None) assert_dtype_allclose(result, expected) + def test_sort_notimplemented(self): + dp_array = dpnp.arange(10) + + with pytest.raises(NotImplementedError): + dpnp.argsort(dp_array, kind="quicksort") + + with pytest.raises(NotImplementedError): + dpnp.argsort(dp_array, order=["age"]) + @pytest.mark.parametrize("kth", [0, 1], ids=["0", "1"]) @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) diff --git a/tests/third_party/cupy/sorting_tests/test_sort.py b/tests/third_party/cupy/sorting_tests/test_sort.py index 647bdf201b30..bef8859ba7be 100644 --- a/tests/third_party/cupy/sorting_tests/test_sort.py +++ b/tests/third_party/cupy/sorting_tests/test_sort.py @@ -201,6 +201,14 @@ def test_nan4(self, xp, dtype): out = xp.sort(a, axis=2) return out + # Large case + + @testing.slow + @testing.numpy_cupy_array_equal() + def test_large(self, xp): + a = testing.shaped_random((17, 1023, 1023), xp) + return xp.sort(a, axis=-1) + @testing.gpu class TestLexsort(unittest.TestCase): @@ -301,15 +309,15 @@ def argsort(self, a, axis=-1): @testing.numpy_cupy_array_equal() def test_argsort_zero_dim(self, xp, dtype): a = testing.shaped_random((), xp, dtype) - axis = None if xp == cupy else -1 - return self.argsort(a, axis=axis) + # only numpy allows 0d array without axis=None + kwargs = {} if xp == numpy else {"axis": None} + return self.argsort(a, **kwargs) @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_argsort_one_dim(self, xp, dtype): a = testing.shaped_random((10,), xp, dtype) - res = self.argsort(a) - return a[res] + return self.argsort(a) @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() @@ -353,8 +361,8 @@ def test_argsort_invalid_axis2(self): @testing.numpy_cupy_array_equal() def test_argsort_zero_dim_axis(self, xp): a = testing.shaped_random((), xp) - axis = None if xp == cupy else 0 - return self.argsort(a, axis=axis) + # only numpy allows 0d array without axis=None + return self.argsort(a, axis=None) def test_argsort_zero_dim_invalid_axis(self): for xp in (numpy, cupy): From 3cf16154090a584a7c8d1310e36272be1949b1a8 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Fri, 19 Jan 2024 12:44:48 -0600 Subject: [PATCH 5/5] fix typo --- dpnp/dpnp_iface_sorting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpnp/dpnp_iface_sorting.py b/dpnp/dpnp_iface_sorting.py index 337a9ea5fbef..6a3db20e74cb 100644 --- a/dpnp/dpnp_iface_sorting.py +++ b/dpnp/dpnp_iface_sorting.py @@ -86,7 +86,7 @@ def argsort(a, axis=-1, kind=None, order=None): Limitations ----------- Parameters `order` is only supported with its default value. - Parameters `kind` can only be ``None`` or ``"stable"``which + Parameters `kind` can only be ``None`` or ``"stable"`` which are equivalent. Otherwise ``NotImplementedError`` exception will be raised. @@ -247,7 +247,7 @@ def sort(a, axis=-1, kind=None, order=None): Limitations ----------- Parameters `order` is only supported with its default value. - Parameters `kind` can only be ``None`` or ``"stable"``which + Parameters `kind` can only be ``None`` or ``"stable"`` which are equivalent. Otherwise ``NotImplementedError`` exception will be raised.