Skip to content

Commit 95a24d6

Browse files
Implementation of dpnp.mean() (#1431)
* Reuse dpctl.tensor.sun for dpnp.sum * Update tests for dpnp.sum * Fix remarks * Implementation of dpnp.mean * Update logic for dpnp.mean function * add normalize_axis_tuple * Additional tests for dpnp.mean * Fix minor remarks * Add inplace support of divide * Use inplace divide only for dpnp.inexact types * Update tests for dpnp.mean * Skip test_sample.py::TestRandint2::test_bound_float1 * Remove unused import * Update dtype check * Update dpnp/dpnp_iface_statistics.py * Return deleted skips --------- Co-authored-by: Anton Volkov <[email protected]> Co-authored-by: Anton <[email protected]>
1 parent 28376c5 commit 95a24d6

File tree

10 files changed

+105
-194
lines changed

10 files changed

+105
-194
lines changed

dpnp/backend/include/dpnp_iface_fptr.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ enum class DPNPFuncName : size_t
218218
DPNP_FN_MAXIMUM, /**< Used in numpy.maximum() impl */
219219
DPNP_FN_MAXIMUM_EXT, /**< Used in numpy.maximum() impl , requires extra parameters */
220220
DPNP_FN_MEAN, /**< Used in numpy.mean() impl */
221-
DPNP_FN_MEAN_EXT, /**< Used in numpy.mean() impl, requires extra parameters */
222221
DPNP_FN_MEDIAN, /**< Used in numpy.median() impl */
223222
DPNP_FN_MEDIAN_EXT, /**< Used in numpy.median() impl, requires extra parameters */
224223
DPNP_FN_MIN, /**< Used in numpy.min() impl */

dpnp/backend/kernels/dpnp_krnl_statistics.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -650,15 +650,6 @@ void (*dpnp_mean_default_c)(void*,
650650
const shape_elem_type*,
651651
size_t) = dpnp_mean_c<_DataType, _ResultType>;
652652

653-
template <typename _DataType, typename _ResultType>
654-
DPCTLSyclEventRef (*dpnp_mean_ext_c)(DPCTLSyclQueueRef,
655-
void*,
656-
void*,
657-
const shape_elem_type*,
658-
const size_t,
659-
const shape_elem_type*,
660-
const size_t,
661-
const DPCTLEventVectorRef) = dpnp_mean_c<_DataType, _ResultType>;
662653

663654
template <typename _DataType, typename _ResultType>
664655
DPCTLSyclEventRef dpnp_median_c(DPCTLSyclQueueRef q_ref,
@@ -1402,11 +1393,6 @@ void func_map_init_statistics(func_map_t& fmap)
14021393
fmap[DPNPFuncName::DPNP_FN_MEAN][eft_FLT][eft_FLT] = {eft_FLT, (void*)dpnp_mean_default_c<float, float>};
14031394
fmap[DPNPFuncName::DPNP_FN_MEAN][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_mean_default_c<double, double>};
14041395

1405-
fmap[DPNPFuncName::DPNP_FN_MEAN_EXT][eft_INT][eft_INT] = {eft_DBL, (void*)dpnp_mean_ext_c<int32_t, double>};
1406-
fmap[DPNPFuncName::DPNP_FN_MEAN_EXT][eft_LNG][eft_LNG] = {eft_DBL, (void*)dpnp_mean_ext_c<int64_t, double>};
1407-
fmap[DPNPFuncName::DPNP_FN_MEAN_EXT][eft_FLT][eft_FLT] = {eft_FLT, (void*)dpnp_mean_ext_c<float, float>};
1408-
fmap[DPNPFuncName::DPNP_FN_MEAN_EXT][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_mean_ext_c<double, double>};
1409-
14101396
fmap[DPNPFuncName::DPNP_FN_MEDIAN][eft_INT][eft_INT] = {eft_DBL, (void*)dpnp_median_default_c<int32_t, double>};
14111397
fmap[DPNPFuncName::DPNP_FN_MEDIAN][eft_LNG][eft_LNG] = {eft_DBL, (void*)dpnp_median_default_c<int64_t, double>};
14121398
fmap[DPNPFuncName::DPNP_FN_MEDIAN][eft_FLT][eft_FLT] = {eft_FLT, (void*)dpnp_median_default_c<float, float>};

dpnp/dpnp_algo/dpnp_algo.pxd

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,6 @@ cdef extern from "dpnp_iface_fptr.hpp" namespace "DPNPFuncName": # need this na
190190
DPNP_FN_MAX_EXT
191191
DPNP_FN_MAXIMUM
192192
DPNP_FN_MAXIMUM_EXT
193-
DPNP_FN_MEAN
194-
DPNP_FN_MEAN_EXT
195193
DPNP_FN_MEDIAN
196194
DPNP_FN_MEDIAN_EXT
197195
DPNP_FN_MIN
@@ -541,7 +539,6 @@ cpdef dpnp_descriptor dpnp_repeat(dpnp_descriptor array1, repeats, axes=*)
541539
Statistics functions
542540
"""
543541
cpdef dpnp_descriptor dpnp_cov(dpnp_descriptor array1)
544-
cpdef object dpnp_mean(dpnp_descriptor a, axis)
545542
cpdef dpnp_descriptor dpnp_min(dpnp_descriptor a, axis)
546543

547544

dpnp/dpnp_algo/dpnp_algo_statistics.pxi

Lines changed: 0 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ __all__ += [
4040
"dpnp_correlate",
4141
"dpnp_cov",
4242
"dpnp_max",
43-
"dpnp_mean",
4443
"dpnp_median",
4544
"dpnp_min",
4645
"dpnp_nanvar",
@@ -302,152 +301,6 @@ cpdef utils.dpnp_descriptor dpnp_max(utils.dpnp_descriptor x1, axis):
302301

303302
return _dpnp_max(x1, axis_, output_shape)
304303

305-
306-
cpdef utils.dpnp_descriptor _dpnp_mean(utils.dpnp_descriptor x1):
307-
cdef shape_type_c x1_shape = x1.shape
308-
cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(x1.dtype)
309-
310-
cdef DPNPFuncData kernel_data = get_dpnp_function_ptr(DPNP_FN_MEAN_EXT, param1_type, param1_type)
311-
312-
x1_obj = x1.get_array()
313-
314-
cdef utils.dpnp_descriptor result = utils.create_output_descriptor((1,),
315-
kernel_data.return_type,
316-
None,
317-
device=x1_obj.sycl_device,
318-
usm_type=x1_obj.usm_type,
319-
sycl_queue=x1_obj.sycl_queue)
320-
321-
result_sycl_queue = result.get_array().sycl_queue
322-
323-
cdef c_dpctl.SyclQueue q = <c_dpctl.SyclQueue> result_sycl_queue
324-
cdef c_dpctl.DPCTLSyclQueueRef q_ref = q.get_queue_ref()
325-
326-
cdef custom_statistic_1in_1out_func_ptr_t func = <custom_statistic_1in_1out_func_ptr_t > kernel_data.ptr
327-
328-
# stub for interface support
329-
cdef shape_type_c axis
330-
cdef Py_ssize_t axis_size = 0
331-
332-
cdef c_dpctl.DPCTLSyclEventRef event_ref = func(q_ref,
333-
x1.get_data(),
334-
result.get_data(),
335-
x1_shape.data(),
336-
x1.ndim,
337-
axis.data(),
338-
axis_size,
339-
NULL) # dep_events_ref
340-
341-
with nogil: c_dpctl.DPCTLEvent_WaitAndThrow(event_ref)
342-
c_dpctl.DPCTLEvent_Delete(event_ref)
343-
344-
return result
345-
346-
347-
cpdef object dpnp_mean(utils.dpnp_descriptor x1, axis):
348-
cdef shape_type_c output_shape
349-
350-
if axis is None:
351-
return _dpnp_mean(x1).get_pyobj()
352-
353-
cdef long x1_size = x1.size
354-
cdef shape_type_c x1_shape = x1.shape
355-
356-
if x1.dtype == dpnp.float32:
357-
res_type = dpnp.float32
358-
else:
359-
res_type = dpnp.float64
360-
361-
if x1_size == 0:
362-
return dpnp.array([dpnp.nan], dtype=res_type)
363-
364-
if isinstance(axis, int):
365-
axis_ = tuple([axis])
366-
else:
367-
axis_ = axis
368-
369-
if axis_ is None:
370-
output_shape.push_back(1)
371-
else:
372-
output_shape = (0, ) * (len(x1_shape) - len(axis_))
373-
ind = 0
374-
for id, shape_axis in enumerate(x1_shape):
375-
if id not in axis_:
376-
output_shape[ind] = shape_axis
377-
ind += 1
378-
379-
cdef long prod = 1
380-
for i in range(len(output_shape)):
381-
if output_shape[i] != 0:
382-
prod *= output_shape[i]
383-
384-
result_array = [None] * prod
385-
input_shape_offsets = [None] * len(x1_shape)
386-
acc = 1
387-
388-
for i in range(len(x1_shape)):
389-
ind = len(x1_shape) - 1 - i
390-
input_shape_offsets[ind] = acc
391-
acc *= x1_shape[ind]
392-
393-
output_shape_offsets = [None] * len(x1_shape)
394-
acc = 1
395-
396-
if axis_ is not None:
397-
for i in range(len(output_shape)):
398-
ind = len(output_shape) - 1 - i
399-
output_shape_offsets[ind] = acc
400-
acc *= output_shape[ind]
401-
result_offsets = input_shape_offsets[:] # need copy. not a reference
402-
for i in axis_:
403-
result_offsets[i] = 0
404-
405-
for source_idx in range(x1_size):
406-
407-
# reconstruct x,y,z from linear source_idx
408-
xyz = []
409-
remainder = source_idx
410-
for i in input_shape_offsets:
411-
quotient, remainder = divmod(remainder, i)
412-
xyz.append(quotient)
413-
414-
# extract result axis
415-
result_axis = []
416-
if axis_ is None:
417-
result_axis = xyz
418-
else:
419-
for idx, offset in enumerate(xyz):
420-
if idx not in axis_:
421-
result_axis.append(offset)
422-
423-
# Construct result offset
424-
result_offset = 0
425-
if axis_ is not None:
426-
for i, result_axis_val in enumerate(result_axis):
427-
result_offset += (output_shape_offsets[i] * result_axis_val)
428-
429-
input_elem = input.get_pyobj().item(source_idx)
430-
if axis_ is None:
431-
if result_array[0] is None:
432-
result_array[0] = input_elem
433-
else:
434-
result_array[0] += input_elem
435-
else:
436-
if result_array[result_offset] is None:
437-
result_array[result_offset] = input_elem
438-
else:
439-
result_array[result_offset] += input_elem
440-
441-
del_ = x1_size
442-
if axis_ is not None:
443-
for i in range(len(x1_shape)):
444-
if i not in axis_:
445-
del_ = del_ / x1_shape[i]
446-
dpnp_array = dpnp.array(result_array, dtype=input.dtype)
447-
dpnp_result_array = dpnp.reshape(dpnp_array, output_shape)
448-
return dpnp_result_array / del_
449-
450-
451304
cpdef utils.dpnp_descriptor dpnp_median(utils.dpnp_descriptor array1):
452305
cdef shape_type_c x1_shape = array1.shape
453306
cdef DPNPFuncType param1_type = dpnp_dtype_to_DPNPFuncType(array1.dtype)

dpnp/dpnp_array.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -794,12 +794,12 @@ def max(self, axis=None, out=None, keepdims=numpy._NoValue, initial=numpy._NoVal
794794

795795
return dpnp.max(self, axis, out, keepdims, initial, where)
796796

797-
def mean(self, axis=None):
797+
def mean(self, axis=None, **kwargs):
798798
"""
799799
Returns the average of the array elements.
800800
"""
801801

802-
return dpnp.mean(self, axis)
802+
return dpnp.mean(self, axis=axis, **kwargs)
803803

804804
def min(self, axis=None, out=None, keepdims=numpy._NoValue, initial=numpy._NoValue, where=numpy._NoValue):
805805
"""
@@ -1050,7 +1050,7 @@ def transpose(self, *axes):
10501050
array([1, 2, 3, 4])
10511051
>>> a.transpose()
10521052
array([1, 2, 3, 4])
1053-
1053+
10541054
"""
10551055

10561056
ndim = self.ndim

dpnp/dpnp_iface_statistics.py

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import numpy
4444
import dpctl.tensor as dpt
45+
from numpy.core.numeric import normalize_axis_tuple
4546
from dpnp.dpnp_algo import *
4647
from dpnp.dpnp_utils import *
4748
from dpnp.dpnp_array import dpnp_array
@@ -394,18 +395,23 @@ def max(x1, axis=None, out=None, keepdims=False, initial=None, where=True):
394395
return call_origin(numpy.max, x1, axis, out, keepdims, initial, where)
395396

396397

397-
def mean(x1, axis=None, **kwargs):
398+
def mean(x, /, *, axis=None, dtype=None, keepdims=False, out=None, where=True):
398399
"""
399400
Compute the arithmetic mean along the specified axis.
400401
401402
For full documentation refer to :obj:`numpy.mean`.
402403
404+
Returns
405+
-------
406+
y : dpnp.ndarray
407+
an array containing the mean values of the elements along the specified axis(axes).
408+
If the input array is empty, an array containing a single NaN value is returned.
409+
403410
Limitations
404411
-----------
405-
Input array is supported as :obj:`dpnp.ndarray`.
406-
Prameters ``axis`` is supported only with default value ``None``.
407-
Keyword arguments ``kwargs`` are currently unsupported.
408-
Size of input array is limited by ``x1.size > 0``.
412+
Parameters `x` is supported as either :class:`dpnp.ndarray`
413+
or :class:`dpctl.tensor.usm_ndarray`.
414+
Parameters `keepdims`, `out` and `where` are supported with their default values.
409415
Otherwise the function will be executed sequentially on CPU.
410416
Input array data types are limited by supported DPNP :ref:`Data types`.
411417
@@ -426,23 +432,52 @@ def mean(x1, axis=None, **kwargs):
426432
>>> import dpnp as np
427433
>>> a = np.array([[1, 2], [3, 4]])
428434
>>> np.mean(a)
429-
2.5
430-
435+
array(2.5)
436+
>>> np.mean(a, axis=0)
437+
array([2., 3.])
438+
>>> np.mean(a, axis=1)
439+
array([1.5, 3.5])
431440
"""
432441

433-
x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_nondefault_queue=False)
434-
if x1_desc and not kwargs:
435-
if x1_desc.size == 0:
436-
pass
437-
elif axis is not None:
438-
pass
442+
if keepdims is not False:
443+
pass
444+
elif out is not None:
445+
pass
446+
elif where is not True:
447+
pass
448+
else:
449+
if dtype is None and dpnp.issubdtype(x.dtype, dpnp.inexact):
450+
dtype = x.dtype
451+
452+
if axis is None:
453+
if x.size == 0:
454+
return dpnp.array(dpnp.nan, dtype=dtype)
455+
else:
456+
result = dpnp.sum(x, dtype=dtype) / x.size
457+
return result.astype(dtype) if result.dtype != dtype else result
458+
459+
if not isinstance(axis,(tuple,list)):
460+
axis = (axis,)
461+
462+
axis = normalize_axis_tuple(axis, x.ndim, "axis")
463+
res_sum = dpnp.sum(x, axis=axis, dtype=dtype)
464+
465+
del_ = 1.0
466+
for axis_value in axis:
467+
del_ *= x.shape[axis_value]
468+
469+
#performing an inplace operation on arrays of bool or integer types
470+
#is not possible due to incompatible data types because
471+
#it returns a floating value
472+
if dpnp.issubdtype(res_sum.dtype, dpnp.inexact):
473+
res_sum /= del_
439474
else:
440-
result_obj = dpnp_mean(x1_desc, axis)
441-
result = dpnp.convert_single_elem_array_to_scalar(result_obj)
475+
new_res_sum = res_sum / del_
476+
return new_res_sum.astype(dtype) if new_res_sum.dtype != dtype else new_res_sum
442477

443-
return result
478+
return res_sum.astype(dtype) if res_sum.dtype != dtype else res_sum
444479

445-
return call_origin(numpy.mean, x1, axis=axis, **kwargs)
480+
return call_origin(numpy.mean, x, axis=axis, dtype=dtype, out=out, keepdims=keepdims, where=where)
446481

447482

448483
def median(x1, axis=None, out=None, overwrite_input=False, keepdims=False):

tests/skipped_tests.tbl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,7 @@ tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_1_{siz
989989
tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_2_{size=4}::test_multinomial
990990
tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_3_{size=(0,)}::test_multinomial
991991
tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_4_{size=(1, 0)}::test_multinomial
992+
tests/third_party/cupy/random_tests/test_sample.py::TestRandint2::test_bound_float1
992993
tests/third_party/cupy/random_tests/test_sample.py::TestRandint2::test_goodness_of_fit
993994
tests/third_party/cupy/random_tests/test_sample.py::TestRandint2::test_goodness_of_fit_2
994995

@@ -1237,7 +1238,6 @@ tests/third_party/cupy/statistics_tests/test_histogram.py::TestHistogram::test_h
12371238
tests/third_party/cupy/statistics_tests/test_histogram.py::TestHistogram::test_histogram_same_value
12381239

12391240
tests/third_party/cupy/statistics_tests/test_histogram.py::TestHistogram::test_histogram_weights_mismatch
1240-
tests/third_party/cupy/statistics_tests/test_meanvar.py::TestMeanVar::test_external_mean_axis
12411241
tests/third_party/cupy/statistics_tests/test_meanvar.py::TestNanMeanAdditional::test_nanmean_all_nan
12421242
tests/third_party/cupy/statistics_tests/test_meanvar.py::TestNanMeanAdditional::test_nanmean_float16
12431243
tests/third_party/cupy/statistics_tests/test_meanvar.py::TestNanMeanAdditional::test_nanmean_huge

tests/skipped_tests_gpu.tbl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticBinary2_para
8484
tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticBinary2_param_535_{arg1=array([[1, 2, 3], [4, 5, 6]], dtype=int64), arg2=array([[0, 1, 2], [3, 4, 5]]), dtype=float64, name='floor_divide', use_dtype=False}::test_binary
8585
tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticBinary2_param_543_{arg1=array([[1, 2, 3], [4, 5, 6]], dtype=int64), arg2=array([[0, 1, 2], [3, 4, 5]], dtype=int64), dtype=float64, name='floor_divide', use_dtype=False}::test_binary
8686

87+
tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_external_prod_all
88+
tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_external_prod_axis
89+
tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_prod_all
90+
tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_prod_axis
8791
tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_sum_out
8892
tests/third_party/cupy/math_tests/test_sumprod.py::TestSumprod::test_sum_out_wrong_shape
8993
tests/third_party/cupy/math_tests/test_sumprod.py::TestCumprod::test_cumprod_1dim
@@ -250,7 +254,6 @@ tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMult
250254
tests/third_party/cupy/random_tests/test_distributions.py::TestDistributionsMultivariateNormal_param_3_{d=4, shape=(3, 2)}::test_normal
251255

252256
tests/third_party/cupy/statistics_tests/test_correlation.py::TestCov::test_cov_empty
253-
tests/third_party/cupy/statistics_tests/test_meanvar.py::TestMeanVar::test_external_mean_axis
254257

255258
tests/third_party/intel/test_zero_copy_test1.py::test_dpnp_interaction_with_dpctl_memory
256259
tests/test_arraymanipulation.py::TestHstack::test_generator
@@ -1075,6 +1078,7 @@ tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_1_{siz
10751078
tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_2_{size=4}::test_multinomial
10761079
tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_3_{size=(0,)}::test_multinomial
10771080
tests/third_party/cupy/random_tests/test_sample.py::TestMultinomial_param_4_{size=(1, 0)}::test_multinomial
1081+
tests/third_party/cupy/random_tests/test_sample.py::TestRandint2::test_bound_float1
10781082
tests/third_party/cupy/random_tests/test_sample.py::TestRandint2::test_goodness_of_fit
10791083
tests/third_party/cupy/random_tests/test_sample.py::TestRandint2::test_goodness_of_fit_2
10801084
tests/third_party/cupy/random_tests/test_sample.py::TestRandomIntegers2::test_bound_1

0 commit comments

Comments
 (0)