From 37a434645efe44f4770be3664d9bbdd63e18a016 Mon Sep 17 00:00:00 2001 From: Alexander-Makaryev Date: Wed, 16 Sep 2020 07:09:13 -0500 Subject: [PATCH 1/3] argmin, argmax, refactoring of sorting funcs --- dpnp/backend.pxd | 9 ++ dpnp/backend.pyx | 1 + dpnp/backend/backend_iface.hpp | 28 ++++++ dpnp/backend/custom_kernels.cpp | 89 ----------------- dpnp/backend/custom_kernels_searching.cpp | 96 ++++++++++++++++++ dpnp/backend/custom_kernels_sorting.cpp | 115 ++++++++++++++++++++++ dpnp/backend_searching.pyx | 85 ++++++++++++++++ dpnp/dparray.pyx | 18 ++++ dpnp/dpnp_iface.py | 3 + dpnp/dpnp_iface_searching.py | 98 ++++++++++++++++++ dpnp/dpnp_iface_sorting.py | 2 +- setup.py | 2 + 12 files changed, 456 insertions(+), 90 deletions(-) create mode 100644 dpnp/backend/custom_kernels_searching.cpp create mode 100644 dpnp/backend/custom_kernels_sorting.cpp create mode 100644 dpnp/backend_searching.pyx create mode 100644 dpnp/dpnp_iface_searching.py diff --git a/dpnp/backend.pxd b/dpnp/backend.pxd index f3281171eb68..a99bb3ddbbad 100644 --- a/dpnp/backend.pxd +++ b/dpnp/backend.pxd @@ -122,6 +122,9 @@ cdef extern from "backend/backend_iface.hpp": void custom_argsort_c[_DataType, _idx_DataType](void * array, void * result, size_t size) void custom_sort_c[_DataType](void * array, void * result, size_t size) + # Sorting routines + void custom_argmax_c[_DataType, _idx_DataType](void * array, void * result, size_t size) + void custom_argmin_c[_DataType, _idx_DataType](void * array, void * result, size_t size) cpdef dparray dpnp_remainder(dparray array1, int scalar) cpdef dparray dpnp_astype(dparray array1, dtype_target) @@ -198,3 +201,9 @@ Sorting functions """ cpdef dparray dpnp_argsort(dparray array1) cpdef dparray dpnp_sort(dparray array1) + +""" +Searching functions +""" +cpdef dparray dpnp_argmax(dparray array1) +cpdef dparray dpnp_argmin(dparray array1) diff --git a/dpnp/backend.pyx b/dpnp/backend.pyx index 3ab3dc94b621..93904842f32a 100644 --- a/dpnp/backend.pyx +++ b/dpnp/backend.pyx @@ -56,6 +56,7 @@ __all__ = [ include "backend_logic.pyx" include "backend_manipulation.pyx" include "backend_mathematical.pyx" +include "backend_searching.pyx" include "backend_sorting.pyx" include "backend_statistics.pyx" include "backend_trigonometric.pyx" diff --git a/dpnp/backend/backend_iface.hpp b/dpnp/backend/backend_iface.hpp index 0ad7d8fd10cb..30e85e65984a 100644 --- a/dpnp/backend/backend_iface.hpp +++ b/dpnp/backend/backend_iface.hpp @@ -198,6 +198,34 @@ INP_DLLEXPORT void custom_sort_c(void* array, void* result, size_t size); template INP_DLLEXPORT void custom_cov_c(void* array1_in, void* result1, const std::vector& input_shape); +/** + * @ingroup BACKEND_API + * @brief MKL implementation of argmax function + * + * @param [in] array Input array with data. + * + * @param [out] result Output array with indeces. + * + * @param [in] size Number of elements in input array. + * + */ +template +INP_DLLEXPORT void custom_argmax_c(void* array, void* result, size_t size); + +/** + * @ingroup BACKEND_API + * @brief MKL implementation of argmin function + * + * @param [in] array Input array with data. + * + * @param [out] result Output array with indeces. + * + * @param [in] size Number of elements in input array. + * + */ +template +INP_DLLEXPORT void custom_argmin_c(void* array, void* result, size_t size); + #if 0 // Example for OpenCL kernel template void custom_dgemm_c_opencl(void* array_1, void* array_2, void* result_1, size_t size); diff --git a/dpnp/backend/custom_kernels.cpp b/dpnp/backend/custom_kernels.cpp index 0b93637e87fc..3247b097301f 100644 --- a/dpnp/backend/custom_kernels.cpp +++ b/dpnp/backend/custom_kernels.cpp @@ -120,95 +120,6 @@ void custom_blas_dot_c(void* array1_in, void* array2_in, void* result1, size_t s template void custom_blas_dot_c(void* array1_in, void* array2_in, void* result1, size_t size); template void custom_blas_dot_c(void* array1_in, void* array2_in, void* result1, size_t size); -template -struct _argsort_less -{ - _argsort_less(_DataType* data_ptr) - { - _data_ptr = data_ptr; - } - - inline bool operator()(const _idx_DataType& idx1, const _idx_DataType& idx2) - { - return (_data_ptr[idx1] < _data_ptr[idx2]); - } - -private: - _DataType* _data_ptr = nullptr; -}; - -template -class custom_argsort_c_kernel; - -template -void custom_argsort_c(void* array1_in, void* result1, size_t size) -{ - _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); - _idx_DataType* result = reinterpret_cast<_idx_DataType*>(result1); - - for (size_t i = 0; i < size; ++i) - { - result[i] = i; - } - - auto queue = DPNP_QUEUE; - - auto policy = - oneapi::dpl::execution::make_device_policy>(queue); - - std::sort(policy, result, result + size, _argsort_less<_DataType, _idx_DataType>(array_1)); - - queue.wait_and_throw(); // looks like it is necessary to sync after call of pstl -} - -template void custom_argsort_c(void* array1_in, void* result1, size_t size); -template void custom_argsort_c(void* array1_in, void* result1, size_t size); -template void custom_argsort_c(void* array1_in, void* result1, size_t size); -template void custom_argsort_c(void* array1_in, void* result1, size_t size); -template void custom_argsort_c(void* array1_in, void* result1, size_t size); -template void custom_argsort_c(void* array1_in, void* result1, size_t size); -template void custom_argsort_c(void* array1_in, void* result1, size_t size); -template void custom_argsort_c(void* array1_in, void* result1, size_t size); - -template -struct _sort_less -{ - inline bool operator()(const _DataType& val1, const _DataType& val2) - { - return (val1 < val2); - } -}; - -template -class custom_sort_c_kernel; - -template -void custom_sort_c(void* array1_in, void* result1, size_t size) -{ - _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); - _DataType* result = reinterpret_cast<_DataType*>(result1); - - for (size_t i = 0; i < size; ++i) - { - result[i] = array_1[i]; - } - - auto queue = DPNP_QUEUE; - - auto policy = oneapi::dpl::execution::make_device_policy>(queue); - - // fails without explicitly specifying of comparator or with std::less during kernels compilation - // affects other kernels - std::sort(policy, result, result + size, _sort_less<_DataType>()); - - queue.wait_and_throw(); // looks like it is necessary to sync after call of pstl -} - -template void custom_sort_c(void* array1_in, void* result1, size_t size); -template void custom_sort_c(void* array1_in, void* result1, size_t size); -template void custom_sort_c(void* array1_in, void* result1, size_t size); -template void custom_sort_c(void* array1_in, void* result1, size_t size); - #if 0 // Example for OpenCL kernel #include #include diff --git a/dpnp/backend/custom_kernels_searching.cpp b/dpnp/backend/custom_kernels_searching.cpp new file mode 100644 index 000000000000..7bf3c9ec613b --- /dev/null +++ b/dpnp/backend/custom_kernels_searching.cpp @@ -0,0 +1,96 @@ +//***************************************************************************** +// Copyright (c) 2016-2020, 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. +//***************************************************************************** + +#include + +#include +#include "backend_pstl.hpp" +#include "queue_sycl.hpp" + +template +class custom_argmax_c_kernel; + +template +void custom_argmax_c(void* array1_in, void* result1, size_t size) +{ + _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); + _idx_DataType* result = reinterpret_cast<_idx_DataType*>(result1); + + auto queue = DPNP_QUEUE; + + auto policy = + oneapi::dpl::execution::make_device_policy>(DPNP_QUEUE); + + _DataType* res = std::max_element(policy, array_1, array_1 + size); + policy.queue().wait(); + + result[0] = std::distance(array_1, res); + +#if 0 + std::cout << "result " << result[0] << "\n"; +#endif +} + +template void custom_argmax_c(void* array1_in, void* result1, size_t size); +template void custom_argmax_c(void* array1_in, void* result1, size_t size); +template void custom_argmax_c(void* array1_in, void* result1, size_t size); +template void custom_argmax_c(void* array1_in, void* result1, size_t size); +template void custom_argmax_c(void* array1_in, void* result1, size_t size); +template void custom_argmax_c(void* array1_in, void* result1, size_t size); +template void custom_argmax_c(void* array1_in, void* result1, size_t size); +template void custom_argmax_c(void* array1_in, void* result1, size_t size); + +template +class custom_argmin_c_kernel; + +template +void custom_argmin_c(void* array1_in, void* result1, size_t size) +{ + _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); + _idx_DataType* result = reinterpret_cast<_idx_DataType*>(result1); + + auto queue = DPNP_QUEUE; + + auto policy = + oneapi::dpl::execution::make_device_policy>(DPNP_QUEUE); + + _DataType* res = std::min_element(policy, array_1, array_1 + size); + policy.queue().wait(); + + result[0] = std::distance(array_1, res); + +#if 0 + std::cout << "result " << result[0] << "\n"; +#endif +} + +template void custom_argmin_c(void* array1_in, void* result1, size_t size); +template void custom_argmin_c(void* array1_in, void* result1, size_t size); +template void custom_argmin_c(void* array1_in, void* result1, size_t size); +template void custom_argmin_c(void* array1_in, void* result1, size_t size); +template void custom_argmin_c(void* array1_in, void* result1, size_t size); +template void custom_argmin_c(void* array1_in, void* result1, size_t size); +template void custom_argmin_c(void* array1_in, void* result1, size_t size); +template void custom_argmin_c(void* array1_in, void* result1, size_t size); diff --git a/dpnp/backend/custom_kernels_sorting.cpp b/dpnp/backend/custom_kernels_sorting.cpp new file mode 100644 index 000000000000..754c8af0d640 --- /dev/null +++ b/dpnp/backend/custom_kernels_sorting.cpp @@ -0,0 +1,115 @@ +//***************************************************************************** +// Copyright (c) 2016-2020, 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. +//***************************************************************************** + +#include + +#include +#include "backend_pstl.hpp" +#include "queue_sycl.hpp" + +template +struct _argsort_less +{ + _argsort_less(_DataType* data_ptr) + { + _data_ptr = data_ptr; + } + + inline bool operator()(const _idx_DataType& idx1, const _idx_DataType& idx2) + { + return (_data_ptr[idx1] < _data_ptr[idx2]); + } + +private: + _DataType* _data_ptr = nullptr; +}; + +template +class custom_argsort_c_kernel; + +template +void custom_argsort_c(void* array1_in, void* result1, size_t size) +{ + _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); + _idx_DataType* result = reinterpret_cast<_idx_DataType*>(result1); + + for (size_t i = 0; i < size; ++i) + { + result[i] = i; + } + + auto policy = + oneapi::dpl::execution::make_device_policy>(DPNP_QUEUE); + + std::sort(policy, result, result + size, _argsort_less<_DataType, _idx_DataType>(array_1)); + + policy.queue().wait(); +} + +template void custom_argsort_c(void* array1_in, void* result1, size_t size); +template void custom_argsort_c(void* array1_in, void* result1, size_t size); +template void custom_argsort_c(void* array1_in, void* result1, size_t size); +template void custom_argsort_c(void* array1_in, void* result1, size_t size); +template void custom_argsort_c(void* array1_in, void* result1, size_t size); +template void custom_argsort_c(void* array1_in, void* result1, size_t size); +template void custom_argsort_c(void* array1_in, void* result1, size_t size); +template void custom_argsort_c(void* array1_in, void* result1, size_t size); + +template +struct _sort_less +{ + inline bool operator()(const _DataType& val1, const _DataType& val2) + { + return (val1 < val2); + } +}; + +template +class custom_sort_c_kernel; + +template +void custom_sort_c(void* array1_in, void* result1, size_t size) +{ + _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); + _DataType* result = reinterpret_cast<_DataType*>(result1); + + for (size_t i = 0; i < size; ++i) + { + result[i] = array_1[i]; + } + + auto policy = oneapi::dpl::execution::make_device_policy>(DPNP_QUEUE); + + // fails without explicitly specifying of comparator or with std::less during kernels compilation + // affects other kernels + std::sort(policy, result, result + size, _sort_less<_DataType>()); + + policy.queue().wait(); +} + +template void custom_sort_c(void* array1_in, void* result1, size_t size); +template void custom_sort_c(void* array1_in, void* result1, size_t size); +template void custom_sort_c(void* array1_in, void* result1, size_t size); +template void custom_sort_c(void* array1_in, void* result1, size_t size); diff --git a/dpnp/backend_searching.pyx b/dpnp/backend_searching.pyx new file mode 100644 index 000000000000..79c00ec4f89b --- /dev/null +++ b/dpnp/backend_searching.pyx @@ -0,0 +1,85 @@ +# cython: language_level=3 +# -*- coding: utf-8 -*- +# ***************************************************************************** +# Copyright (c) 2016-2020, 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 + +""" + + +import numpy +from dpnp.dpnp_utils cimport checker_throw_type_error, normalize_axis + + +__all__ += [ + "dpnp_argmax", + "dpnp_argmin" +] + + +cpdef dparray dpnp_argmax(dparray in_array1): + call_type = in_array1.dtype + + cdef dparray result = dparray((1,), dtype=numpy.int64) + + cdef size_t size = in_array1.size + + if call_type == numpy.float64: + custom_argmax_c[double, long](in_array1.get_data(), result.get_data(), size) + elif call_type == numpy.float32: + custom_argmax_c[float, long](in_array1.get_data(), result.get_data(), size) + elif call_type == numpy.int64: + custom_argmax_c[long, long](in_array1.get_data(), result.get_data(), size) + elif call_type == numpy.int32: + custom_argmax_c[int, long](in_array1.get_data(), result.get_data(), size) + else: + checker_throw_type_error("dpnp_argmax", call_type) + + return result + + +cpdef dparray dpnp_argmin(dparray in_array1): + call_type = in_array1.dtype + + cdef dparray result = dparray((1,), dtype=numpy.int64) + + cdef size_t size = in_array1.size + + if call_type == numpy.float64: + custom_argmin_c[double, long](in_array1.get_data(), result.get_data(), size) + elif call_type == numpy.float32: + custom_argmin_c[float, long](in_array1.get_data(), result.get_data(), size) + elif call_type == numpy.int64: + custom_argmin_c[long, long](in_array1.get_data(), result.get_data(), size) + elif call_type == numpy.int32: + custom_argmin_c[int, long](in_array1.get_data(), result.get_data(), size) + else: + checker_throw_type_error("dpnp_argmin", call_type) + + return result diff --git a/dpnp/dparray.pyx b/dpnp/dparray.pyx index 2ac5b245f0da..6c2934ea9705 100644 --- a/dpnp/dparray.pyx +++ b/dpnp/dparray.pyx @@ -800,6 +800,24 @@ cdef class dparray: return sort(self, axis, kind, order) + """ + ------------------------------------------------------------------------- + Searching + ------------------------------------------------------------------------- + """ + + def argmax(self, axis=None, out=None): + """ + """ + + return argmax(self, axis, out) + + def argmin(self, axis=None, out=None): + """ + """ + + return argmin(self, axis, out) + """ ------------------------------------------------------------------------- Other attributes diff --git a/dpnp/dpnp_iface.py b/dpnp/dpnp_iface.py index a2b3cb99edcf..7e78b902fa05 100644 --- a/dpnp/dpnp_iface.py +++ b/dpnp/dpnp_iface.py @@ -68,6 +68,7 @@ from dpnp.dpnp_iface_logic import * from dpnp.dpnp_iface_manipulation import * from dpnp.dpnp_iface_mathematical import * +from dpnp.dpnp_iface_searching import * from dpnp.dpnp_iface_sorting import * from dpnp.dpnp_iface_statistics import * from dpnp.dpnp_iface_trigonometric import * @@ -77,6 +78,7 @@ from dpnp.dpnp_iface_logic import __all__ as __all__logic from dpnp.dpnp_iface_manipulation import __all__ as __all__manipulation from dpnp.dpnp_iface_mathematical import __all__ as __all__mathematical +from dpnp.dpnp_iface_searching import __all__ as __all__searching from dpnp.dpnp_iface_sorting import __all__ as __all__sorting from dpnp.dpnp_iface_statistics import __all__ as __all__statistics from dpnp.dpnp_iface_trigonometric import __all__ as __all__trigonometric @@ -86,6 +88,7 @@ __all__ += __all__logic __all__ += __all__manipulation __all__ += __all__mathematical +__all__ += __all__searching __all__ += __all__sorting __all__ += __all__statistics __all__ += __all__trigonometric diff --git a/dpnp/dpnp_iface_searching.py b/dpnp/dpnp_iface_searching.py new file mode 100644 index 000000000000..0726c8d3e020 --- /dev/null +++ b/dpnp/dpnp_iface_searching.py @@ -0,0 +1,98 @@ +# cython: language_level=3 +# distutils: language = c++ +# -*- coding: utf-8 -*- +# ***************************************************************************** +# Copyright (c) 2016-2020, 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. +# ***************************************************************************** + +""" +Interface of the searching function of the dpnp + +Notes +----- +This module is a face or public interface file for the library +it contains: + - Interface functions + - documentation for the functions + - The functions parameters check + +""" + + +import numpy + +from dpnp.backend import * +from dpnp.dparray import dparray +from dpnp.dpnp_utils import checker_throw_value_error, use_origin_backend + +__all__ = [ + 'argmax', + 'argmin' +] + + +def argmax(in_array1, axis=None, out=None): + """ + """ + + is_dparray1 = isinstance(in_array1, dparray) + + if (not use_origin_backend(in_array1) and is_dparray1): + if axis is not None: + checker_throw_value_error("argmax", "axis", type(axis), None) + if out is not None: + checker_throw_value_error("argmax", "out", type(out), None) + + result = dpnp_argmax(in_array1) + + # scalar returned + if result.shape == (1,): + return result.dtype.type(result[0]) + + return result + + return numpy.argmax(in_array1, axis, out) + + +def argmin(in_array1, axis=None, out=None): + """ + """ + + is_dparray1 = isinstance(in_array1, dparray) + + if (not use_origin_backend(in_array1) and is_dparray1): + if axis is not None: + checker_throw_value_error("argmin", "axis", type(axis), None) + if out is not None: + checker_throw_value_error("argmin", "out", type(out), None) + + result = dpnp_argmin(in_array1) + + # scalar returned + if result.shape == (1,): + return result.dtype.type(result[0]) + + return result + + return numpy.argmin(in_array1, axis, out) diff --git a/dpnp/dpnp_iface_sorting.py b/dpnp/dpnp_iface_sorting.py index 5aef9009fb7d..a3d21910d06a 100644 --- a/dpnp/dpnp_iface_sorting.py +++ b/dpnp/dpnp_iface_sorting.py @@ -27,7 +27,7 @@ # ***************************************************************************** """ -Interface of the statistics function of the Intel NumPy +Interface of the sorting function of the dpnp Notes ----- diff --git a/setup.py b/setup.py index 99d31e221a09..589b13f704b0 100644 --- a/setup.py +++ b/setup.py @@ -228,6 +228,8 @@ "dpnp/backend/custom_kernels_elemwise.cpp", "dpnp/backend/custom_kernels_manipulation.cpp", "dpnp/backend/custom_kernels_reduction.cpp", + "dpnp/backend/custom_kernels_searching.cpp", + "dpnp/backend/custom_kernels_sorting.cpp", "dpnp/backend/custom_kernels_statistics.cpp", "dpnp/backend/memory_sycl.cpp", "dpnp/backend/mkl_wrap_blas1.cpp", From 011e536528468d9cea6d6646d4f9383964151f4b Mon Sep 17 00:00:00 2001 From: Alexander-Makaryev Date: Thu, 17 Sep 2020 06:23:08 -0500 Subject: [PATCH 2/3] iota, copy, docs --- dpnp/backend/custom_kernels_sorting.cpp | 10 +- dpnp/dparray.pyx | 120 ++++++++++++++++++++++++ dpnp/dpnp_iface_searching.py | 120 ++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 8 deletions(-) diff --git a/dpnp/backend/custom_kernels_sorting.cpp b/dpnp/backend/custom_kernels_sorting.cpp index 754c8af0d640..00fb10eabfc3 100644 --- a/dpnp/backend/custom_kernels_sorting.cpp +++ b/dpnp/backend/custom_kernels_sorting.cpp @@ -55,10 +55,7 @@ void custom_argsort_c(void* array1_in, void* result1, size_t size) _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); _idx_DataType* result = reinterpret_cast<_idx_DataType*>(result1); - for (size_t i = 0; i < size; ++i) - { - result[i] = i; - } + std::iota(result, result + size, 0); auto policy = oneapi::dpl::execution::make_device_policy>(DPNP_QUEUE); @@ -95,10 +92,7 @@ void custom_sort_c(void* array1_in, void* result1, size_t size) _DataType* array_1 = reinterpret_cast<_DataType*>(array1_in); _DataType* result = reinterpret_cast<_DataType*>(result1); - for (size_t i = 0; i < size; ++i) - { - result[i] = array_1[i]; - } + std::copy(array_1, array_1 + size, result); auto policy = oneapi::dpl::execution::make_device_policy>(DPNP_QUEUE); diff --git a/dpnp/dparray.pyx b/dpnp/dparray.pyx index 6c2934ea9705..401588b9fbb5 100644 --- a/dpnp/dparray.pyx +++ b/dpnp/dparray.pyx @@ -808,12 +808,132 @@ cdef class dparray: def argmax(self, axis=None, out=None): """ + Returns the indices of the maximum values along an axis. + Parameters + ---------- + a : array_like + Input array. + axis : int, optional + By default, the index is into the flattened array, otherwise + along the specified axis. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + Returns + ------- + index_array : ndarray of ints + Array of indices into the array. It has the same shape as `a.shape` + with the dimension along `axis` removed. + See Also + -------- + ndarray.argmax, argmin + amax : The maximum value along a given axis. + unravel_index : Convert a flat index into an index tuple. + take_along_axis : Apply ``np.expand_dims(index_array, axis)`` + from argmax to an array as if by calling max. + Notes + ----- + In case of multiple occurrences of the maximum values, the indices + corresponding to the first occurrence are returned. + Examples + -------- + >>> a = np.arange(6).reshape(2,3) + 10 + >>> a + array([[10, 11, 12], + [13, 14, 15]]) + >>> np.argmax(a) + 5 + >>> np.argmax(a, axis=0) + array([1, 1, 1]) + >>> np.argmax(a, axis=1) + array([2, 2]) + Indexes of the maximal elements of a N-dimensional array: + >>> ind = np.unravel_index(np.argmax(a, axis=None), a.shape) + >>> ind + (1, 2) + >>> a[ind] + 15 + >>> b = np.arange(6) + >>> b[1] = 5 + >>> b + array([0, 5, 2, 3, 4, 5]) + >>> np.argmax(b) # Only the first occurrence is returned. + 1 + >>> x = np.array([[4,2,3], [1,0,3]]) + >>> index_array = np.argmax(x, axis=-1) + >>> # Same as np.max(x, axis=-1, keepdims=True) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1) + array([[4], + [3]]) + >>> # Same as np.max(x, axis=-1) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1) + array([4, 3]) """ return argmax(self, axis, out) def argmin(self, axis=None, out=None): """ + Returns the indices of the minimum values along an axis. + Parameters + ---------- + a : array_like + Input array. + axis : int, optional + By default, the index is into the flattened array, otherwise + along the specified axis. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + Returns + ------- + index_array : ndarray of ints + Array of indices into the array. It has the same shape as `a.shape` + with the dimension along `axis` removed. + See Also + -------- + ndarray.argmin, argmax + amin : The minimum value along a given axis. + unravel_index : Convert a flat index into an index tuple. + take_along_axis : Apply ``np.expand_dims(index_array, axis)`` + from argmin to an array as if by calling min. + Notes + ----- + In case of multiple occurrences of the minimum values, the indices + corresponding to the first occurrence are returned. + Examples + -------- + >>> a = np.arange(6).reshape(2,3) + 10 + >>> a + array([[10, 11, 12], + [13, 14, 15]]) + >>> np.argmin(a) + 0 + >>> np.argmin(a, axis=0) + array([0, 0, 0]) + >>> np.argmin(a, axis=1) + array([0, 0]) + Indices of the minimum elements of a N-dimensional array: + >>> ind = np.unravel_index(np.argmin(a, axis=None), a.shape) + >>> ind + (0, 0) + >>> a[ind] + 10 + >>> 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. + 0 + >>> x = np.array([[4,2,3], [1,0,3]]) + >>> index_array = np.argmin(x, axis=-1) + >>> # Same as np.min(x, axis=-1, keepdims=True) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1) + array([[2], + [0]]) + >>> # Same as np.max(x, axis=-1) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1) + array([2, 0]) """ return argmin(self, axis, out) diff --git a/dpnp/dpnp_iface_searching.py b/dpnp/dpnp_iface_searching.py index 0726c8d3e020..5382c1d278eb 100644 --- a/dpnp/dpnp_iface_searching.py +++ b/dpnp/dpnp_iface_searching.py @@ -54,6 +54,66 @@ def argmax(in_array1, axis=None, out=None): """ + Returns the indices of the maximum values along an axis. + Parameters + ---------- + a : array_like + Input array. + axis : int, optional + By default, the index is into the flattened array, otherwise + along the specified axis. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + Returns + ------- + index_array : ndarray of ints + Array of indices into the array. It has the same shape as `a.shape` + with the dimension along `axis` removed. + See Also + -------- + ndarray.argmax, argmin + amax : The maximum value along a given axis. + unravel_index : Convert a flat index into an index tuple. + take_along_axis : Apply ``np.expand_dims(index_array, axis)`` + from argmax to an array as if by calling max. + Notes + ----- + In case of multiple occurrences of the maximum values, the indices + corresponding to the first occurrence are returned. + Examples + -------- + >>> a = np.arange(6).reshape(2,3) + 10 + >>> a + array([[10, 11, 12], + [13, 14, 15]]) + >>> np.argmax(a) + 5 + >>> np.argmax(a, axis=0) + array([1, 1, 1]) + >>> np.argmax(a, axis=1) + array([2, 2]) + Indexes of the maximal elements of a N-dimensional array: + >>> ind = np.unravel_index(np.argmax(a, axis=None), a.shape) + >>> ind + (1, 2) + >>> a[ind] + 15 + >>> b = np.arange(6) + >>> b[1] = 5 + >>> b + array([0, 5, 2, 3, 4, 5]) + >>> np.argmax(b) # Only the first occurrence is returned. + 1 + >>> x = np.array([[4,2,3], [1,0,3]]) + >>> index_array = np.argmax(x, axis=-1) + >>> # Same as np.max(x, axis=-1, keepdims=True) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1) + array([[4], + [3]]) + >>> # Same as np.max(x, axis=-1) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1) + array([4, 3]) """ is_dparray1 = isinstance(in_array1, dparray) @@ -77,6 +137,66 @@ def argmax(in_array1, axis=None, out=None): def argmin(in_array1, axis=None, out=None): """ + Returns the indices of the minimum values along an axis. + Parameters + ---------- + a : array_like + Input array. + axis : int, optional + By default, the index is into the flattened array, otherwise + along the specified axis. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + Returns + ------- + index_array : ndarray of ints + Array of indices into the array. It has the same shape as `a.shape` + with the dimension along `axis` removed. + See Also + -------- + ndarray.argmin, argmax + amin : The minimum value along a given axis. + unravel_index : Convert a flat index into an index tuple. + take_along_axis : Apply ``np.expand_dims(index_array, axis)`` + from argmin to an array as if by calling min. + Notes + ----- + In case of multiple occurrences of the minimum values, the indices + corresponding to the first occurrence are returned. + Examples + -------- + >>> a = np.arange(6).reshape(2,3) + 10 + >>> a + array([[10, 11, 12], + [13, 14, 15]]) + >>> np.argmin(a) + 0 + >>> np.argmin(a, axis=0) + array([0, 0, 0]) + >>> np.argmin(a, axis=1) + array([0, 0]) + Indices of the minimum elements of a N-dimensional array: + >>> ind = np.unravel_index(np.argmin(a, axis=None), a.shape) + >>> ind + (0, 0) + >>> a[ind] + 10 + >>> 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. + 0 + >>> x = np.array([[4,2,3], [1,0,3]]) + >>> index_array = np.argmin(x, axis=-1) + >>> # Same as np.min(x, axis=-1, keepdims=True) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1) + array([[2], + [0]]) + >>> # Same as np.max(x, axis=-1) + >>> np.take_along_axis(x, np.expand_dims(index_array, axis=-1), axis=-1).squeeze(axis=-1) + array([2, 0]) """ is_dparray1 = isinstance(in_array1, dparray) From 55f80171ebadba54d81e0ed982021caa7bbed79a Mon Sep 17 00:00:00 2001 From: Alexander-Makaryev Date: Thu, 17 Sep 2020 06:51:56 -0500 Subject: [PATCH 3/3] FPTR functions map --- dpnp/backend.pxd | 2 + dpnp/backend/backend_iface_fptr.cpp | 56 +++++++++++++++++++++++++++ dpnp/backend/backend_iface_fptr.hpp | 10 +++-- dpnp/backend_searching.pyx | 59 ++++++++++++++--------------- 4 files changed, 93 insertions(+), 34 deletions(-) diff --git a/dpnp/backend.pxd b/dpnp/backend.pxd index a99bb3ddbbad..2861eac6e758 100644 --- a/dpnp/backend.pxd +++ b/dpnp/backend.pxd @@ -32,6 +32,8 @@ from dpnp.dparray cimport dparray, dparray_shape_type cdef extern from "backend/backend_iface_fptr.hpp" namespace "DPNPFuncName": # need this namespace for Enum import cdef enum DPNPFuncName "DPNPFuncName": DPNP_FN_ADD + DPNP_FN_ARGMAX + DPNP_FN_ARGMIN DPNP_FN_DOT cdef extern from "backend/backend_iface_fptr.hpp" namespace "DPNPFuncType": # need this namespace for Enum import diff --git a/dpnp/backend/backend_iface_fptr.cpp b/dpnp/backend/backend_iface_fptr.cpp index 2c5f7f25f520..0fc6b6a26921 100644 --- a/dpnp/backend/backend_iface_fptr.cpp +++ b/dpnp/backend/backend_iface_fptr.cpp @@ -81,6 +81,62 @@ func_map_t func_map = } } }, + {DPNPFuncName::DPNP_FN_ARGMAX, + { // T1. First template parameter + {DPNPFuncType::DPNP_FT_INT, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmax_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmax_c}} + } + }, + {DPNPFuncType::DPNP_FT_LONG, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmax_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmax_c}} + } + }, + {DPNPFuncType::DPNP_FT_FLOAT, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmax_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmax_c}} + } + }, + {DPNPFuncType::DPNP_FT_DOUBLE, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmax_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmax_c}} + } + } + } + }, + {DPNPFuncName::DPNP_FN_ARGMIN, + { // T1. First template parameter + {DPNPFuncType::DPNP_FT_INT, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmin_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmin_c}} + } + }, + {DPNPFuncType::DPNP_FT_LONG, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmin_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmin_c}} + } + }, + {DPNPFuncType::DPNP_FT_FLOAT, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmin_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmin_c}} + } + }, + {DPNPFuncType::DPNP_FT_DOUBLE, + { // T2. Second template parameter + {DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_argmin_c}}, + {DPNPFuncType::DPNP_FT_LONG, {DPNPFuncType::DPNP_FT_LONG, (void*)custom_argmin_c}} + } + } + } + }, {DPNPFuncName::DPNP_FN_DOT, { {DPNPFuncType::DPNP_FT_INT, {{DPNPFuncType::DPNP_FT_INT, {DPNPFuncType::DPNP_FT_INT, (void*)custom_blas_dot_c}}}}, diff --git a/dpnp/backend/backend_iface_fptr.hpp b/dpnp/backend/backend_iface_fptr.hpp index f3cc14942c8f..f3e16c0c59bc 100644 --- a/dpnp/backend/backend_iface_fptr.hpp +++ b/dpnp/backend/backend_iface_fptr.hpp @@ -59,10 +59,12 @@ */ enum class DPNPFuncName : size_t { - DPNP_FN_NONE, /**< Very first element of the enumeration */ - DPNP_FN_ADD, /**< Used in numpy.add() implementation */ - DPNP_FN_DOT, /**< Used in numpy.dot() implementation */ - DPNP_FN_LAST /**< The latest element of the enumeration */ + DPNP_FN_NONE, /**< Very first element of the enumeration */ + DPNP_FN_ADD, /**< Used in numpy.add() implementation */ + DPNP_FN_ARGMAX, /**< Used in numpy.argmax() implementation */ + DPNP_FN_ARGMIN, /**< Used in numpy.argmin() implementation */ + DPNP_FN_DOT, /**< Used in numpy.dot() implementation */ + DPNP_FN_LAST /**< The latest element of the enumeration */ }; /** diff --git a/dpnp/backend_searching.pyx b/dpnp/backend_searching.pyx index 79c00ec4f89b..7ffae89cf6cd 100644 --- a/dpnp/backend_searching.pyx +++ b/dpnp/backend_searching.pyx @@ -32,7 +32,7 @@ and the rest of the library """ - +import cython import numpy from dpnp.dpnp_utils cimport checker_throw_type_error, normalize_axis @@ -43,43 +43,42 @@ __all__ += [ ] +# C function pointer to the C library template functions +ctypedef void (*custom_math_1in_1out_func_ptr_t) (void * , void * , size_t) + + +cdef struct custom_math_2in_1out: + string return_type # return type identifier which expected by the `ptr` function + custom_math_1in_1out_func_ptr_t ptr # C function pointer + + cpdef dparray dpnp_argmax(dparray in_array1): - call_type = in_array1.dtype + cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(in_array1.dtype) + cdef DPNPFuncType output_type = dpnp_dtype_to_DPNPFuncType(numpy.int64) - cdef dparray result = dparray((1,), dtype=numpy.int64) + cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_ARGMAX, param1_type, output_type) - cdef size_t size = in_array1.size + result_type = dpnp_DPNPFuncType_to_dtype( < size_t > kernel_data.return_type) + cdef dparray result = dparray((1,), dtype=result_type) - if call_type == numpy.float64: - custom_argmax_c[double, long](in_array1.get_data(), result.get_data(), size) - elif call_type == numpy.float32: - custom_argmax_c[float, long](in_array1.get_data(), result.get_data(), size) - elif call_type == numpy.int64: - custom_argmax_c[long, long](in_array1.get_data(), result.get_data(), size) - elif call_type == numpy.int32: - custom_argmax_c[int, long](in_array1.get_data(), result.get_data(), size) - else: - checker_throw_type_error("dpnp_argmax", call_type) + cdef custom_math_1in_1out_func_ptr_t func = kernel_data.ptr + + func(in_array1.get_data(), result.get_data(), in_array1.size) return result cpdef dparray dpnp_argmin(dparray in_array1): - call_type = in_array1.dtype - - cdef dparray result = dparray((1,), dtype=numpy.int64) - - cdef size_t size = in_array1.size - - if call_type == numpy.float64: - custom_argmin_c[double, long](in_array1.get_data(), result.get_data(), size) - elif call_type == numpy.float32: - custom_argmin_c[float, long](in_array1.get_data(), result.get_data(), size) - elif call_type == numpy.int64: - custom_argmin_c[long, long](in_array1.get_data(), result.get_data(), size) - elif call_type == numpy.int32: - custom_argmin_c[int, long](in_array1.get_data(), result.get_data(), size) - else: - checker_throw_type_error("dpnp_argmin", call_type) + cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(in_array1.dtype) + cdef DPNPFuncType output_type = dpnp_dtype_to_DPNPFuncType(numpy.int64) + + cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_ARGMIN, param1_type, output_type) + + result_type = dpnp_DPNPFuncType_to_dtype( < size_t > kernel_data.return_type) + cdef dparray result = dparray((1,), dtype=result_type) + + cdef custom_math_1in_1out_func_ptr_t func = kernel_data.ptr + + func(in_array1.get_data(), result.get_data(), in_array1.size) return result