Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement dpnp.nancumsum() through dpnp.cumsum call #1781

Merged
merged 4 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions doc/reference/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,17 @@ Sums, products, differences

dpnp.prod
dpnp.sum
dpnp.nanprod
dpnp.nansum
dpnp.cumprod
dpnp.cumsum
dpnp.nancumprod
dpnp.nancumsum
dpnp.nansum
dpnp.nanprod
dpnp.cross
dpnp.diff
dpnp.ediff1d
dpnp.gradient
dpnp.trapz
dpnp.cross


Exponents and logarithms
Expand Down
2 changes: 0 additions & 2 deletions dpnp/backend/include/dpnp_iface_fptr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ enum class DPNPFuncName : size_t
DPNP_FN_CUMPROD_EXT, /**< Used in numpy.cumprod() impl, requires extra
parameters */
DPNP_FN_CUMSUM, /**< Used in numpy.cumsum() impl */
DPNP_FN_CUMSUM_EXT, /**< Used in numpy.cumsum() impl, requires extra
parameters */
DPNP_FN_DEGREES, /**< Used in numpy.degrees() impl */
DPNP_FN_DEGREES_EXT, /**< Used in numpy.degrees() impl, requires extra
parameters */
Expand Down
17 changes: 0 additions & 17 deletions dpnp/backend/kernels/dpnp_krnl_mathematical.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,14 +425,6 @@ template <typename _DataType_input, typename _DataType_output>
void (*dpnp_cumsum_default_c)(void *, void *, size_t) =
dpnp_cumsum_c<_DataType_input, _DataType_output>;

template <typename _DataType_input, typename _DataType_output>
DPCTLSyclEventRef (*dpnp_cumsum_ext_c)(DPCTLSyclQueueRef,
void *,
void *,
size_t,
const DPCTLEventVectorRef) =
dpnp_cumsum_c<_DataType_input, _DataType_output>;

template <typename _KernelNameSpecialization1,
typename _KernelNameSpecialization2>
class dpnp_ediff1d_c_kernel;
Expand Down Expand Up @@ -1179,15 +1171,6 @@ void func_map_init_mathematical(func_map_t &fmap)
fmap[DPNPFuncName::DPNP_FN_CUMSUM][eft_DBL][eft_DBL] = {
eft_DBL, (void *)dpnp_cumsum_default_c<double, double>};

fmap[DPNPFuncName::DPNP_FN_CUMSUM_EXT][eft_INT][eft_INT] = {
eft_LNG, (void *)dpnp_cumsum_ext_c<int32_t, int64_t>};
fmap[DPNPFuncName::DPNP_FN_CUMSUM_EXT][eft_LNG][eft_LNG] = {
eft_LNG, (void *)dpnp_cumsum_ext_c<int64_t, int64_t>};
fmap[DPNPFuncName::DPNP_FN_CUMSUM_EXT][eft_FLT][eft_FLT] = {
eft_FLT, (void *)dpnp_cumsum_ext_c<float, float>};
fmap[DPNPFuncName::DPNP_FN_CUMSUM_EXT][eft_DBL][eft_DBL] = {
eft_DBL, (void *)dpnp_cumsum_ext_c<double, double>};

fmap[DPNPFuncName::DPNP_FN_EDIFF1D][eft_INT][eft_INT] = {
eft_LNG, (void *)dpnp_ediff1d_default_c<int32_t, int64_t>};
fmap[DPNPFuncName::DPNP_FN_EDIFF1D][eft_LNG][eft_LNG] = {
Expand Down
1 change: 0 additions & 1 deletion dpnp/dpnp_algo/dpnp_algo.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ cdef extern from "dpnp_iface_fptr.hpp" namespace "DPNPFuncName": # need this na
DPNP_FN_COPY_EXT
DPNP_FN_CORRELATE_EXT
DPNP_FN_CUMPROD_EXT
DPNP_FN_CUMSUM_EXT
DPNP_FN_DEGREES_EXT
DPNP_FN_DIAG_INDICES_EXT
DPNP_FN_DIAGONAL_EXT
Expand Down
26 changes: 0 additions & 26 deletions dpnp/dpnp_algo/dpnp_algo_mathematical.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ __all__ += [
"dpnp_fmin",
"dpnp_modf",
"dpnp_nancumprod",
"dpnp_nancumsum",
"dpnp_trapz",
]

Expand All @@ -70,18 +69,6 @@ cpdef utils.dpnp_descriptor dpnp_cumprod(utils.dpnp_descriptor x1):
return call_fptr_1in_1out(DPNP_FN_CUMPROD_EXT, x1, (x1.size,))


cpdef utils.dpnp_descriptor dpnp_cumsum(utils.dpnp_descriptor x1):
# instead of x1.shape, (x1.size, ) is passed to the function
# due to the following:
# >>> import numpy
# >>> a = numpy.array([[1, 2], [2, 3]])
# >>> res = numpy.cumsum(a)
# >>> res.shape
# (4,)

return call_fptr_1in_1out(DPNP_FN_CUMSUM_EXT, x1, (x1.size,))


cpdef utils.dpnp_descriptor dpnp_ediff1d(utils.dpnp_descriptor x1):

if x1.size <= 1:
Expand Down Expand Up @@ -253,19 +240,6 @@ cpdef utils.dpnp_descriptor dpnp_nancumprod(utils.dpnp_descriptor x1):
return dpnp_cumprod(x1_desc)


cpdef utils.dpnp_descriptor dpnp_nancumsum(utils.dpnp_descriptor x1):
cur_x1 = x1.get_pyobj().copy()

cur_x1_flatiter = cur_x1.flat

for i in range(cur_x1.size):
if dpnp.isnan(cur_x1_flatiter[i]):
cur_x1_flatiter[i] = 0

x1_desc = dpnp.get_dpnp_descriptor(cur_x1, copy_when_nondefault_queue=False)
return dpnp_cumsum(x1_desc)


cpdef utils.dpnp_descriptor dpnp_trapz(utils.dpnp_descriptor y1, utils.dpnp_descriptor x1, double dx):

cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(y1.dtype)
Expand Down
72 changes: 48 additions & 24 deletions dpnp/dpnp_iface_nanfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
# pylint: disable=no-name-in-module
from .dpnp_algo import (
dpnp_nancumprod,
dpnp_nancumsum,
)
from .dpnp_utils import (
call_origin,
Expand Down Expand Up @@ -292,43 +291,68 @@ def nancumprod(x1, **kwargs):
return call_origin(numpy.nancumprod, x1, **kwargs)


def nancumsum(x1, **kwargs):
def nancumsum(a, axis=None, dtype=None, out=None):
"""
Return the cumulative sum of the elements along a given axis.
Return the cumulative sum of array elements over a given axis treating
Not a Numbers (NaNs) as zero. The cumulative sum does not change when NaNs
are encountered and leading NaNs are replaced by zeros.

For full documentation refer to :obj:`numpy.nancumsum`.

Limitations
-----------
Parameter `x` is supported as :class:`dpnp.ndarray`.
Keyword argument `kwargs` is currently unsupported.
Otherwise the function will be executed sequentially on CPU.
Input array data types are limited by supported DPNP :ref:`Data types`.
Parameters
----------
a : {dpnp.ndarray, usm_ndarray}
Input array.
axis : int, optional
Axis along which the cumulative sum is computed. The default (``None``)
is to compute the cumsum over the flattened array.
dtype : dtype, optional
Type of the returned array and of the accumulator in which the elements
are summed. If `dtype` is not specified, it defaults to the dtype of
`a`, unless `a` has an integer dtype with a precision less than that of
the default platform integer. In that case, the default platform
integer is used.
out : {dpnp.ndarray, usm_ndarray}, optional
Alternative output array in which to place the result. It must have the
same shape and buffer length as the expected output but the type will
be cast if necessary.

Returns
-------
out : dpnp.ndarray
A new array holding the result is returned unless `out` is specified as
:class:`dpnp.ndarray`, in which case a reference to `out` is returned.
The result has the same size as `a`, and the same shape as `a` if `axis`
is not ``None`` or `a` is a 1-d array.

See Also
--------
:obj:`dpnp.cumsum` : Return the cumulative sum of the elements
along a given axis.
:obj:`dpnp.cumsum` : Cumulative sum across array propagating NaNs.
:obj:`dpnp.isnan` : Show which elements are NaN.

Examples
--------
>>> import dpnp as np
>>> a = np.array([1., np.nan])
>>> result = np.nancumsum(a)
>>> [x for x in result]
[1.0, 1.0]
>>> b = np.array([[1., 2., np.nan], [4., np.nan, 6.]])
>>> result = np.nancumprod(b)
>>> [x for x in result]
[1.0, 3.0, 3.0, 7.0, 7.0, 13.0]
>>> np.nancumsum(np.array(1))
array([1])
>>> np.nancumsum(np.array([1]))
array([1])
>>> np.nancumsum(np.array([1, np.nan]))
array([1., 1.])
>>> a = np.array([[1, 2], [3, np.nan]])
>>> np.nancumsum(a)
array([1., 3., 6., 6.])
>>> np.nancumsum(a, axis=0)
array([[1., 2.],
[4., 2.]])
>>> np.nancumsum(a, axis=1)
array([[1., 3.],
[3., 3.]])

"""

x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False)
if x1_desc and not kwargs:
return dpnp_nancumsum(x1_desc).get_pyobj()

return call_origin(numpy.nancumsum, x1, **kwargs)
a, _ = _replace_nan(a, 0)
return dpnp.cumsum(a, axis=axis, dtype=dtype, out=out)


def nanmax(a, axis=None, out=None, keepdims=False, initial=None, where=True):
Expand Down
101 changes: 0 additions & 101 deletions tests/test_mathematical.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,42 +720,6 @@ def test_power_scalar(shape, dtype):
assert_allclose(result, expected, rtol=1e-6)


@pytest.mark.usefixtures("allow_fall_back_on_numpy")
@pytest.mark.parametrize(
"array",
[
[1, 2, 3, 4, 5],
[1, 2, numpy.nan, 4, 5],
[[1, 2, numpy.nan], [3, -4, -5]],
],
)
def test_nancumprod(array):
np_a = numpy.array(array)
dpnp_a = dpnp.array(np_a)

result = dpnp.nancumprod(dpnp_a)
expected = numpy.nancumprod(np_a)
assert_array_equal(expected, result)


@pytest.mark.usefixtures("allow_fall_back_on_numpy")
@pytest.mark.parametrize(
"array",
[
[1, 2, 3, 4, 5],
[1, 2, numpy.nan, 4, 5],
[[1, 2, numpy.nan], [3, -4, -5]],
],
)
def test_nancumsum(array):
np_a = numpy.array(array)
dpnp_a = dpnp.array(np_a)

result = dpnp.nancumsum(dpnp_a)
expected = numpy.nancumsum(np_a)
assert_array_equal(expected, result)


@pytest.mark.parametrize(
"data",
[[[1.0, -1.0], [0.1, -0.1]], [-2, -1, 0, 1, 2]],
Expand Down Expand Up @@ -2520,71 +2484,6 @@ def test_sum(shape, dtype_in, dtype_out, transpose, keepdims, order):
assert_array_equal(numpy_res, dpnp_res.asnumpy())


class TestNanSum:
@pytest.mark.parametrize("dtype", get_float_complex_dtypes())
@pytest.mark.parametrize("axis", [None, 0, 1, (0, 1)])
@pytest.mark.parametrize("keepdims", [True, False])
def test_nansum(self, dtype, axis, keepdims):
dp_array = dpnp.array([[dpnp.nan, 1, 2], [3, dpnp.nan, 0]], dtype=dtype)
np_array = dpnp.asnumpy(dp_array)

expected = numpy.nansum(np_array, axis=axis, keepdims=keepdims)
result = dpnp.nansum(dp_array, axis=axis, keepdims=keepdims)
assert_allclose(result, expected)

@pytest.mark.parametrize("dtype", get_complex_dtypes())
def test_nansum_complex(self, dtype):
x1 = numpy.random.rand(10)
x2 = numpy.random.rand(10)
a = numpy.array(x1 + 1j * x2, dtype=dtype)
a[::3] = numpy.nan
ia = dpnp.array(a)

expected = numpy.nansum(a)
result = dpnp.nansum(ia)

# use only type kinds check when dpnp handles complex64 arrays
# since `dpnp.sum()` and `numpy.sum()` return different dtypes
assert_dtype_allclose(
result, expected, check_only_type_kind=(dtype == dpnp.complex64)
)

@pytest.mark.parametrize("dtype", get_float_complex_dtypes())
@pytest.mark.parametrize("axis", [0, 1])
def test_nansum_out(self, dtype, axis):
dp_array = dpnp.array([[dpnp.nan, 1, 2], [3, dpnp.nan, 0]], dtype=dtype)
np_array = dpnp.asnumpy(dp_array)

expected = numpy.nansum(np_array, axis=axis)
out = dpnp.empty_like(dpnp.asarray(expected))
result = dpnp.nansum(dp_array, axis=axis, out=out)
assert out is result
assert_dtype_allclose(result, expected)

@pytest.mark.parametrize("dtype", get_float_complex_dtypes())
def test_nansum_dtype(self, dtype):
dp_array = dpnp.array([[dpnp.nan, 1, 2], [3, dpnp.nan, 0]])
np_array = dpnp.asnumpy(dp_array)

expected = numpy.nansum(np_array, dtype=dtype)
result = dpnp.nansum(dp_array, dtype=dtype)
assert_dtype_allclose(result, expected)

@pytest.mark.parametrize("dtype", get_float_complex_dtypes())
def test_nansum_strided(self, dtype):
dp_array = dpnp.arange(20, dtype=dtype)
dp_array[::3] = dpnp.nan
np_array = dpnp.asnumpy(dp_array)

result = dpnp.nansum(dp_array[::-1])
expected = numpy.nansum(np_array[::-1])
assert_allclose(result, expected)

result = dpnp.nansum(dp_array[::2])
expected = numpy.nansum(np_array[::2])
assert_allclose(result, expected)


@pytest.mark.parametrize(
"dtype", get_all_dtypes(no_bool=True, no_none=True, no_complex=True)
)
Expand Down
Loading
Loading