From 7eea4f5b04922e09f239b883dc17a5301c05eb46 Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Wed, 2 Aug 2023 18:35:59 +0300 Subject: [PATCH 1/9] Added nanprod to all backends, ivy array and container and also added unit test --- .../array/experimental/statistical.py | 60 ++++++++ .../container/experimental/statistical.py | 134 ++++++++++++++++++ .../backends/jax/experimental/statistical.py | 18 +++ .../numpy/experimental/statistical.py | 23 +++ .../paddle/experimental/statistical.py | 36 +++++ .../tensorflow/experimental/statistical.py | 26 ++++ .../torch/experimental/statistical.py | 29 ++++ .../ivy/experimental/statistical.py | 64 +++++++++ .../test_core/test_statistical.py | 72 +++++++++- 9 files changed, 461 insertions(+), 1 deletion(-) diff --git a/ivy/data_classes/array/experimental/statistical.py b/ivy/data_classes/array/experimental/statistical.py index b304b237162b0..4626b331cf387 100644 --- a/ivy/data_classes/array/experimental/statistical.py +++ b/ivy/data_classes/array/experimental/statistical.py @@ -180,6 +180,66 @@ def nanmean( self._data, axis=axis, keepdims=keepdims, dtype=dtype, out=out ) + def nanprod( + self: ivy.Array, + /, + *, + axis: Optional[Union[Tuple[int], int]] = None, + dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype]] = None, + out: Optional[ivy.Array] = None, + keepdims: Optional[bool] = False, + initial: Optional[Union[int, float, complex]] = None, + where: Optional[ivy.Array] = None, + ) -> ivy.Array: + """ + ivy.Array instance method variant of ivy.nanprod. This method simply wraps the + function, and so the docstring for ivy.prod also applies to this method with + minimal changes. + + Parameters + ---------- + self + Input array. + axis + Axis or axes along which the product is computed. + The default is to compute the product of the flattened array. + dtype + The desired data type of returned array. Default is None. + out + optional output array, for writing the result to. + keepdims + If this is set to True, the axes which are reduced are left in the result + as dimensions with size one. With this option, the result will broadcast + correctly against the original a. + initial + The starting value for this product. + where + Elements to include in the product + + Returns + ------- + ret + The product of array elements over a given axis treating + Not a Numbers (NaNs) as ones + + Examples + -------- + >>> a = ivy.array([[1, 2], [3, ivy.nan]]) + >>> a.nanprod(a) + 6.0 + >>> a.nanprod(a, axis=0) + ivy.array([3., 2.]) + """ + return ivy.nanprod( + self._data, + axis=axis, + keepdims=keepdims, + dtype=dtype, + out=out, + initial=initial, + where=where, + ) + def quantile( self: ivy.Array, q: Union[ivy.Array, float], diff --git a/ivy/data_classes/container/experimental/statistical.py b/ivy/data_classes/container/experimental/statistical.py index 10c6a4d954346..34e7ae90ad809 100644 --- a/ivy/data_classes/container/experimental/statistical.py +++ b/ivy/data_classes/container/experimental/statistical.py @@ -443,6 +443,140 @@ def nanmean( self, axis=axis, keepdims=keepdims, dtype=dtype, out=out ) + @staticmethod + def static_nanprod( + input: ivy.Container, + /, + *, + axis: Optional[Union[Tuple[int], int, ivy.Container]] = None, + dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype, ivy.Container]] = None, + keepdims: Optional[Union[bool, ivy.Container]] = False, + key_chains: Optional[Union[List[str], Dict[str, str], ivy.Container]] = None, + to_apply: Union[bool, ivy.Container] = True, + prune_unapplied: Union[bool, ivy.Container] = False, + map_sequences: Union[bool, ivy.Container] = False, + out: Optional[Union[ivy.Array, ivy.Container]] = None, + initial: Optional[Union[int, float, complex, ivy.Container]] = 1, + where: Optional[Union[ivy.Array, ivy.Container]] = None, + ) -> ivy.Container: + """ + ivy.Container static method variant of ivy.nanprod. This method simply wraps the + function, and so the docstring for ivy.nanprod also applies to this method with + minimal changes. + + Parameters + ---------- + input + Input container including arrays. + axis + Axis or axes along which the product is computed. + The default is to compute the product of the flattened array. + dtype + The desired data type of returned array. Default is None. + out + optional output array, for writing the result to. + keepdims + If this is set to True, the axes which are reduced are left in the result + as dimensions with size one. With this option, the result will broadcast + correctly against the original a. + initial + The starting value for this product. + where + Elements to include in the product + + Returns + ------- + ret + The product of array elements over a given axis treating + Not a Numbers (NaNs) as ones + + Examples + -------- + >>> a = ivy.Container(x=ivy.array([[1, 2], [3, ivy.nan]]),\ + y=ivy.array([[ivy.nan, 1, 2], [1, 2, 3]]) + >>> ivy.Container.static_moveaxis(a) + { + x: 12.0 + y: 12.0 + } + """ + return ContainerBase.cont_multi_map_in_function( + "nanprod", + input, + axis=axis, + keepdims=keepdims, + dtype=dtype, + key_chains=key_chains, + to_apply=to_apply, + prune_unapplied=prune_unapplied, + map_sequences=map_sequences, + out=out, + initial=initial, + where=where, + ) + + def nanprod( + self: ivy.Container, + /, + *, + axis: Optional[Union[Tuple[int], int, ivy.Container]] = None, + dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype, ivy.Container]] = None, + keepdims: Optional[Union[bool, ivy.Container]] = False, + out: Optional[ivy.Container] = None, + initial: Optional[Union[int, float, complex, ivy.Container]] = None, + where: Optional[Union[ivy.Array, ivy.Container]] = None, + ) -> ivy.Container: + """ + ivy.Container instance method variant of ivy.nanprod. This method simply wraps + the function, and so the docstring for ivy.nanprod also applies to this method + with minimal changes. + + Parameters + ---------- + self + Input container including arrays. + axis + Axis or axes along which the product is computed. + The default is to compute the product of the flattened array. + dtype + The desired data type of returned array. Default is None. + out + optional output array, for writing the result to. + keepdims + If this is set to True, the axes which are reduced are left in the result + as dimensions with size one. With this option, the result will broadcast + correctly against the original a. + initial + The starting value for this product. + where + Elements to include in the product + + Returns + ------- + ret + The product of array elements over a given axis treating + Not a Numbers (NaNs) as ones + + Examples + -------- + >>> a = ivy.Container(x=ivy.array([[1, 2], [3, ivy.nan]]),\ + y=ivy.array([[ivy.nan, 1, 2], [1, 2, 3]]) + >>> ivy.Container.static_moveaxis(a) + { + x: 12.0 + y: 12.0 + } + """ + return self.static_nanprod( + self, + axis=axis, + keepdims=keepdims, + dtype=dtype, + out=out, + initial=initial, + where=where, + ) + @staticmethod def static_quantile( a: Union[ivy.Container, ivy.Array, ivy.NativeArray], diff --git a/ivy/functional/backends/jax/experimental/statistical.py b/ivy/functional/backends/jax/experimental/statistical.py index 9b7f80cafab77..8ddb20001a831 100644 --- a/ivy/functional/backends/jax/experimental/statistical.py +++ b/ivy/functional/backends/jax/experimental/statistical.py @@ -162,6 +162,24 @@ def nanmean( return jnp.nanmean(a, axis=axis, keepdims=keepdims, dtype=dtype, out=out) +def nanprod( + a: JaxArray, + /, + *, + axis: Optional[Union[int, Sequence[int]]] = None, + dtype: Optional[jnp.dtype] = None, + keepdims: Optional[bool] = False, + out: Optional[JaxArray] = None, + initial: Optional[Union[int, float, complex]] = None, + where: Optional[JaxArray] = None, +) -> JaxArray: + dtype = ivy.as_native_dtype(dtype) + if dtype is None: + dtype = _infer_dtype(a.dtype) + axis = tuple(axis) if isinstance(axis, list) else axis + return jnp.nanprod(a, axis=axis, keepdims=keepdims, dtype=dtype, out=out) + + def quantile( a: JaxArray, q: Union[float, JaxArray], diff --git a/ivy/functional/backends/numpy/experimental/statistical.py b/ivy/functional/backends/numpy/experimental/statistical.py index a21f0d544d9d7..db78935c882bf 100644 --- a/ivy/functional/backends/numpy/experimental/statistical.py +++ b/ivy/functional/backends/numpy/experimental/statistical.py @@ -166,6 +166,29 @@ def nanmean( nanmean.support_native_out = True +def nanprod( + a: np.ndarray, + /, + *, + axis: Optional[Union[int, Sequence[int]]] = None, + dtype: Optional[np.dtype] = None, + keepdims: Optional[bool] = False, + out: Optional[np.ndarray] = None, + initial: Optional[Union[int, float, complex]] = None, + where: Optional[np.ndarray] = None, +) -> np.ndarray: + dtype = ivy.as_native_dtype(dtype) + if dtype is None: + dtype = _infer_dtype(a.dtype) + axis = tuple(axis) if isinstance(axis, list) else axis + return np.asarray( + np.nanprod(a=a, axis=axis, dtype=dtype, keepdims=keepdims, out=out) + ) + + +nanprod.support_native_out = True + + def quantile( a: np.ndarray, q: Union[float, np.ndarray], diff --git a/ivy/functional/backends/paddle/experimental/statistical.py b/ivy/functional/backends/paddle/experimental/statistical.py index a7d968e0ec90e..54caff5beab3d 100644 --- a/ivy/functional/backends/paddle/experimental/statistical.py +++ b/ivy/functional/backends/paddle/experimental/statistical.py @@ -95,6 +95,42 @@ def nanmean( return ret.astype(ret_dtype) +def _infer_dtype(dtype: paddle.dtype): + default_dtype = ivy.infer_default_dtype(dtype) + if ivy.dtype_bits(dtype) < ivy.dtype_bits(default_dtype): + return default_dtype + return dtype + + +def nanprod( + a: paddle.Tensor, + /, + *, + axis: Optional[Union[int, Tuple[int]]] = None, + keepdims: Optional[bool] = False, + dtype: Optional[paddle.dtype] = None, + out: Optional[paddle.Tensor] = None, + initial: Optional[Union[int, float, complex]] = None, + where: Optional[paddle.Tensor] = None, +) -> paddle.Tensor: + dtype = ivy.as_native_dtype(dtype) + if dtype is None: + dtype = _infer_dtype(a.dtype) + if a.dtype not in [paddle.int32, paddle.int64, paddle.float32, paddle.float64]: + a = paddle.nan_to_num(a.cast("float32"), nan=1.0) + ret = paddle.prod(a, axis=axis, keepdim=keepdims) + else: + a = paddle.nan_to_num(a, nan=1.0) + ret = paddle.prod(a, axis=axis, keepdim=keepdims) + + if isinstance(axis, Sequence): + if len(axis) == a.ndim: + axis = None + if (a.ndim == 1 or axis is None) and not keepdims: + ret = ret.squeeze() + return ret.cast(dtype) + + def _compute_quantile( x, q, axis=None, keepdim=False, ignore_nan=False, interpolation="linear" ): diff --git a/ivy/functional/backends/tensorflow/experimental/statistical.py b/ivy/functional/backends/tensorflow/experimental/statistical.py index 029dbb69480cc..e6d9c2c9fa417 100644 --- a/ivy/functional/backends/tensorflow/experimental/statistical.py +++ b/ivy/functional/backends/tensorflow/experimental/statistical.py @@ -112,6 +112,32 @@ def nanmean( return tf.experimental.numpy.nanmean(a, axis=axis, keepdims=keepdims, dtype=dtype) +def _infer_dtype(dtype: tf.DType): + default_dtype = ivy.infer_default_dtype(dtype) + if ivy.dtype_bits(dtype) < ivy.dtype_bits(default_dtype): + return default_dtype + return dtype + + +def nanprod( + a: Union[tf.Tensor, tf.Variable], + /, + *, + axis: Optional[Union[int, Sequence[int]]] = None, + dtype: Optional[tf.DType] = None, + keepdims: Optional[bool] = False, + out: Optional[Union[tf.Tensor, tf.Variable]] = None, + initial: Optional[Union[int, float, complex]] = None, + where: Optional[Union[tf.Tensor, tf.Variable]] = None, +) -> Union[tf.Tensor, tf.Variable]: + np_math_ops.enable_numpy_methods_on_tensor() + dtype = ivy.as_native_dtype(dtype) + if dtype is None: + dtype = _infer_dtype(a.dtype) + axis = tuple(axis) if isinstance(axis, list) else axis + return tf.experimental.numpy.nanprod(a, axis=axis, keepdims=keepdims, dtype=dtype) + + def quantile( a: Union[tf.Tensor, tf.Variable], q: Union[tf.Tensor, float], diff --git a/ivy/functional/backends/torch/experimental/statistical.py b/ivy/functional/backends/torch/experimental/statistical.py index 39ce2206b838a..cd947f909b3c2 100644 --- a/ivy/functional/backends/torch/experimental/statistical.py +++ b/ivy/functional/backends/torch/experimental/statistical.py @@ -184,6 +184,35 @@ def nanmean( nanmean.support_native_out = True +def nanprod( + a: torch.Tensor, + /, + *, + axis: Optional[Union[int, Sequence[int]]] = None, + dtype: Optional[torch.dtype] = None, + keepdims: Optional[bool] = False, + out: Optional[torch.Tensor] = None, + initial: Optional[Union[int, float, complex, ivy.Container]] = None, + where: Optional[torch.Tensor] = None, +) -> torch.Tensor: + a = torch.nan_to_num(a, nan=1.0) + dtype = ivy.as_native_dtype(dtype) + if dtype is None: + dtype = _infer_dtype(a.dtype) + if axis == (): + return a.type(dtype) + if axis is None: + return torch.prod(input=a, dtype=dtype, out=out) + if isinstance(axis, tuple) or isinstance(axis, list): + for i in axis: + a = torch.prod(a, dim=i, keepdim=keepdims, dtype=dtype, out=out) + return a + return torch.prod(a, dim=axis, keepdim=keepdims, dtype=dtype, out=out) + + +nanprod.support_native_out = True + + @with_unsupported_dtypes({"2.0.1 and below": ("bfloat16", "float16")}, backend_version) def quantile( a: torch.Tensor, diff --git a/ivy/functional/ivy/experimental/statistical.py b/ivy/functional/ivy/experimental/statistical.py index 8b8aa9374f533..954c479a62340 100644 --- a/ivy/functional/ivy/experimental/statistical.py +++ b/ivy/functional/ivy/experimental/statistical.py @@ -248,6 +248,70 @@ def nanmean( ) +@handle_exceptions +@handle_nestable +@handle_out_argument +@to_native_arrays_and_back +@infer_dtype +@handle_device_shifting +def nanprod( + a: ivy.Array, + /, + *, + axis: Optional[Union[Tuple[int], int]] = None, + keepdims: Optional[bool] = False, + dtype: Optional[Union[ivy.Dtype, ivy.NativeDtype]] = None, + out: Optional[ivy.Array] = None, + initial: Optional[Union[int, float, complex]] = None, + where: Optional[ivy.Array] = None, +) -> ivy.Array: + """ + Compute the product of array elements over a given axis treating Not a Numbers + (NaNs) as ones. + + Parameters + ---------- + a + Input array. + axis + Axis or axes along which the product is computed. + The default is to compute the product of the flattened array. + dtype + The desired data type of returned array. Default is None. + out + optional output array, for writing the result to. + keepdims + If this is set to True, the axes which are reduced are left in the result + as dimensions with size one. With this option, the result will broadcast + correctly against the original a. + initial + The starting value for this product. + where + Elements to include in the product + + Returns + ------- + ret + The product of array elements over a given axis treating + Not a Numbers (NaNs) as ones + + Functional Examples + ------------------- + >>> a = ivy.array([[1, ivy.nan], [3, 4]]) + >>> ivy.nanprod(a) + 12.0 + """ + return ivy.current_backend(a).nanprod( + a, + axis=axis, + keepdims=keepdims, + dtype=dtype, + out=out, + initial=initial, + where=where, + ) + + @handle_exceptions @handle_nestable @handle_out_argument diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py index cc39623a80d95..92e96f3b01c0e 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py @@ -1,5 +1,5 @@ # global -from hypothesis import strategies as st +from hypothesis import strategies as st, assume # local import numpy as np @@ -226,6 +226,76 @@ def test_nanmean( ) +@st.composite +def _get_castable_float_dtype_nan(draw, min_value=None, max_value=None): + available_dtypes = helpers.get_dtypes("float") + shape = draw(helpers.get_shape(min_num_dims=1, max_num_dims=4, max_dim_size=6)) + dtype, values = draw( + helpers.dtype_and_values( + available_dtypes=available_dtypes, + num_arrays=1, + large_abs_safety_factor=6, + small_abs_safety_factor=24, + safety_factor_scale="log", + shape=shape, + min_value=min_value, + max_value=max_value, + allow_nan=True, + ) + ) + axis = draw(helpers.get_axis(shape=shape, force_int=True)) + dtype1, values, dtype2 = draw( + helpers.get_castable_dtype(draw(available_dtypes), dtype[0], values[0]) + ) + return dtype1, [values], axis, dtype2 + + +@handle_test( + fn_tree="functional.ivy.experimental.nanprod", + dtype_x_axis_castable=_get_castable_float_dtype_nan(), + keep_dims=st.booleans(), + test_gradients=st.just(False), +) +def test_nanprod( + *, dtype_x_axis_castable, keep_dims, test_flags, backend_fw, fn_name, on_device +): + input_dtype, x, axis, castable_dtype = dtype_x_axis_castable + x = x[0] + # Added clipping because the min_value and max_value arguments + # don't seem to clip the returned array. + # Also, for really big/small numbers some backends return something like Xe300 + # and some return inf some the value checks fail + # and that's why I clipped the input. + x = np.clip(x, -5, 5) + if "torch" in backend_fw: + assume( + not ( + "torch" in str(backend_fw) + and "float16" in castable_dtype + and on_device == "cpu" + ) + ) + # This assumption is made because for some reason when + # testing with paddle and bfloat16, + # the returned and ground truth returned type were different, + # one was uint16 and the other bfloat16. + # This could be removed when this issue is resolved. + assume("bfloat16" not in castable_dtype) + helpers.test_function( + input_dtypes=[input_dtype], + test_flags=test_flags, + rtol_=1e-1, + atol_=1e-1, + backend_to_test=backend_fw, + fn_name=fn_name, + on_device=on_device, + a=x, + axis=axis, + keepdims=keep_dims, + dtype=castable_dtype, + ) + + @st.composite def _quantile_helper(draw): large_abs_safety_factor = 2 From cf5f14a290d4807a8ad6d2458e9d9b4a5eb4d7bb Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Thu, 17 Aug 2023 15:33:46 +0300 Subject: [PATCH 2/9] Fixed typos in container docstrings --- ivy/data_classes/container/experimental/statistical.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ivy/data_classes/container/experimental/statistical.py b/ivy/data_classes/container/experimental/statistical.py index 837c468f015e4..c5b3a7beb4c53 100644 --- a/ivy/data_classes/container/experimental/statistical.py +++ b/ivy/data_classes/container/experimental/statistical.py @@ -495,7 +495,7 @@ def static_nanprod( -------- >>> a = ivy.Container(x=ivy.array([[1, 2], [3, ivy.nan]]),\ y=ivy.array([[ivy.nan, 1, 2], [1, 2, 3]]) - >>> ivy.Container.static_moveaxis(a) + >>> ivy.Container.static_nanprod(a) { x: 12.0 y: 12.0 @@ -562,7 +562,7 @@ def nanprod( -------- >>> a = ivy.Container(x=ivy.array([[1, 2], [3, ivy.nan]]),\ y=ivy.array([[ivy.nan, 1, 2], [1, 2, 3]]) - >>> ivy.Container.static_moveaxis(a) + >>> a.nanprod() { x: 12.0 y: 12.0 From 5dc99bc634f6790d1ebecf878cd68b677d6726f2 Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Thu, 17 Aug 2023 15:38:18 +0300 Subject: [PATCH 3/9] Added more examples of usage in docstrings --- ivy/functional/ivy/experimental/statistical.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ivy/functional/ivy/experimental/statistical.py b/ivy/functional/ivy/experimental/statistical.py index ba9be8291281e..44613dad602cd 100644 --- a/ivy/functional/ivy/experimental/statistical.py +++ b/ivy/functional/ivy/experimental/statistical.py @@ -306,6 +306,10 @@ def nanprod( >>> a = ivy.array([[1, ivy.nan], [3, 4]]) >>> ivy.nanprod(a) 12.0 + >>> ivy.nanprod(a, axis=0) + [3. 4.] + >>> ivy.nanprod(a, axis=0, keepdims=True) + [[3. 4.]] """ return ivy.current_backend(a).nanprod( a, From 848cb3cd11dc677ce7ec0337efd74c64473bcc14 Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Thu, 17 Aug 2023 19:18:38 +0300 Subject: [PATCH 4/9] Fix for corner cases in testing bfloat16 type --- .../paddle/experimental/statistical.py | 2 +- ivy/functional/backends/paddle/general.py | 18 ++++++++++++++++++ .../test_core/test_statistical.py | 12 ------------ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/ivy/functional/backends/paddle/experimental/statistical.py b/ivy/functional/backends/paddle/experimental/statistical.py index d11dbb8998419..f597766a6ac32 100644 --- a/ivy/functional/backends/paddle/experimental/statistical.py +++ b/ivy/functional/backends/paddle/experimental/statistical.py @@ -129,7 +129,7 @@ def nanprod( if dtype is None: dtype = _infer_dtype(a.dtype) if a.dtype not in [paddle.int32, paddle.int64, paddle.float32, paddle.float64]: - a = paddle.nan_to_num(a.cast("float32"), nan=1.0) + a = paddle.nan_to_num(a.cast("float64"), nan=1.0) ret = paddle.prod(a, axis=axis, keepdim=keepdims) else: a = paddle.nan_to_num(a, nan=1.0) diff --git a/ivy/functional/backends/paddle/general.py b/ivy/functional/backends/paddle/general.py index 7cf7184234bbd..b2221f445884b 100644 --- a/ivy/functional/backends/paddle/general.py +++ b/ivy/functional/backends/paddle/general.py @@ -82,6 +82,24 @@ def to_numpy( else: return x elif paddle.is_tensor(x): + unsqueezed = False + if x.dtype == paddle.bfloat16: + if x.ndim == 0 and ivy.is_array(x): + x = paddle.unsqueeze(x, axis=0) + unsqueezed = True + default_dtype = ivy.default_float_dtype(as_native=True) + if default_dtype is paddle.bfloat16: + x = x.cast(paddle.float32) + else: + x = x.cast(default_dtype) + if copy: + if unsqueezed: + return np.squeeze(np.array(x).astype("bfloat16"), 0) + return np.array(x).astype("bfloat16") + else: + if unsqueezed: + return np.squeeze(np.asarray(x).astype("bfloat16"), 0) + return np.asarray(x).astype("bfloat16") if copy: return np.array(x) else: diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py index af3acd121d492..0273ca3e87c76 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py @@ -261,12 +261,6 @@ def test_nanprod( ): input_dtype, x, axis, castable_dtype = dtype_x_axis_castable x = x[0] - # Added clipping because the min_value and max_value arguments - # don't seem to clip the returned array. - # Also, for really big/small numbers some backends return something like Xe300 - # and some return inf some the value checks fail - # and that's why I clipped the input. - x = np.clip(x, -5, 5) if "torch" in backend_fw: assume( not ( @@ -275,12 +269,6 @@ def test_nanprod( and on_device == "cpu" ) ) - # This assumption is made because for some reason when - # testing with paddle and bfloat16, - # the returned and ground truth returned type were different, - # one was uint16 and the other bfloat16. - # This could be removed when this issue is resolved. - assume("bfloat16" not in castable_dtype) helpers.test_function( input_dtypes=[input_dtype], test_flags=test_flags, From ca0fe3b526133b145f6954755cd95c6d8114136b Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Thu, 17 Aug 2023 20:00:43 +0300 Subject: [PATCH 5/9] Fix for torch's half precision on cpu --- .../backends/torch/experimental/statistical.py | 13 ++++++++++++- .../test_experimental/test_core/test_statistical.py | 10 +--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ivy/functional/backends/torch/experimental/statistical.py b/ivy/functional/backends/torch/experimental/statistical.py index fa63fb6a97fe8..d984e6fa37796 100644 --- a/ivy/functional/backends/torch/experimental/statistical.py +++ b/ivy/functional/backends/torch/experimental/statistical.py @@ -200,14 +200,25 @@ def nanprod( dtype = ivy.as_native_dtype(dtype) if dtype is None: dtype = _infer_dtype(a.dtype) + if a.dtype == torch.float16 and a.device.type == "cpu": + a = a.to(torch.float32) if axis == (): return a.type(dtype) if axis is None: + if dtype == torch.float16: + return torch.prod(input=a, out=out).to(torch.float16) return torch.prod(input=a, dtype=dtype, out=out) if isinstance(axis, tuple) or isinstance(axis, list): for i in axis: - a = torch.prod(a, dim=i, keepdim=keepdims, dtype=dtype, out=out) + if dtype == torch.float16: + a = torch.prod(a, dim=i, keepdim=keepdims, out=out) + else: + a = torch.prod(a, dim=i, keepdim=keepdims, dtype=dtype, out=out) + if dtype == torch.float16: + return a.to(torch.float16) return a + if dtype == torch.float16: + return torch.prod(a, dim=axis, keepdim=keepdims, out=out).to(torch.float16) return torch.prod(a, dim=axis, keepdim=keepdims, dtype=dtype, out=out) diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py index 0273ca3e87c76..8a4a8cb25fe3c 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py @@ -1,5 +1,5 @@ # global -from hypothesis import strategies as st, assume +from hypothesis import strategies as st # local import numpy as np @@ -261,14 +261,6 @@ def test_nanprod( ): input_dtype, x, axis, castable_dtype = dtype_x_axis_castable x = x[0] - if "torch" in backend_fw: - assume( - not ( - "torch" in str(backend_fw) - and "float16" in castable_dtype - and on_device == "cpu" - ) - ) helpers.test_function( input_dtypes=[input_dtype], test_flags=test_flags, From 7122c01134e65edaf9a89a6a7e43d61b2deb68ed Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Sat, 19 Aug 2023 12:49:30 +0300 Subject: [PATCH 6/9] Fix for result inaccuracies in paddle and torch --- .../paddle/experimental/statistical.py | 1 + .../torch/experimental/statistical.py | 24 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/ivy/functional/backends/paddle/experimental/statistical.py b/ivy/functional/backends/paddle/experimental/statistical.py index f597766a6ac32..605258ba6b702 100644 --- a/ivy/functional/backends/paddle/experimental/statistical.py +++ b/ivy/functional/backends/paddle/experimental/statistical.py @@ -128,6 +128,7 @@ def nanprod( dtype = ivy.as_native_dtype(dtype) if dtype is None: dtype = _infer_dtype(a.dtype) + a = a.cast(dtype) if a.dtype not in [paddle.int32, paddle.int64, paddle.float32, paddle.float64]: a = paddle.nan_to_num(a.cast("float64"), nan=1.0) ret = paddle.prod(a, axis=axis, keepdim=keepdims) diff --git a/ivy/functional/backends/torch/experimental/statistical.py b/ivy/functional/backends/torch/experimental/statistical.py index d984e6fa37796..00d68c01615ec 100644 --- a/ivy/functional/backends/torch/experimental/statistical.py +++ b/ivy/functional/backends/torch/experimental/statistical.py @@ -200,26 +200,20 @@ def nanprod( dtype = ivy.as_native_dtype(dtype) if dtype is None: dtype = _infer_dtype(a.dtype) - if a.dtype == torch.float16 and a.device.type == "cpu": - a = a.to(torch.float32) + a = a.type(dtype) + if a.dtype == torch.float16: + a = a.type(torch.float32) if axis == (): return a.type(dtype) if axis is None: - if dtype == torch.float16: - return torch.prod(input=a, out=out).to(torch.float16) - return torch.prod(input=a, dtype=dtype, out=out) + return torch.prod(input=a, out=out) if isinstance(axis, tuple) or isinstance(axis, list): for i in axis: - if dtype == torch.float16: - a = torch.prod(a, dim=i, keepdim=keepdims, out=out) - else: - a = torch.prod(a, dim=i, keepdim=keepdims, dtype=dtype, out=out) - if dtype == torch.float16: - return a.to(torch.float16) - return a - if dtype == torch.float16: - return torch.prod(a, dim=axis, keepdim=keepdims, out=out).to(torch.float16) - return torch.prod(a, dim=axis, keepdim=keepdims, dtype=dtype, out=out) + a = torch.prod(a, dim=i, keepdim=keepdims, out=out).type(dtype) + if a.dtype == torch.float16: + a = a.type(torch.float32) + return a.type(dtype) + return torch.prod(a, dim=axis, keepdim=keepdims, out=out).type(dtype) nanprod.support_native_out = True From 480a1d3e26cd81691eade95e3a11c5ede3263280 Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Sat, 26 Aug 2023 12:08:52 +0300 Subject: [PATCH 7/9] Removed changes in paddle to_numpy and handled case in function testing --- ivy/functional/backends/paddle/general.py | 18 ------------------ .../backends/torch/experimental/statistical.py | 4 ++-- ivy_tests/test_ivy/helpers/function_testing.py | 7 +++++-- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/ivy/functional/backends/paddle/general.py b/ivy/functional/backends/paddle/general.py index b2221f445884b..7cf7184234bbd 100644 --- a/ivy/functional/backends/paddle/general.py +++ b/ivy/functional/backends/paddle/general.py @@ -82,24 +82,6 @@ def to_numpy( else: return x elif paddle.is_tensor(x): - unsqueezed = False - if x.dtype == paddle.bfloat16: - if x.ndim == 0 and ivy.is_array(x): - x = paddle.unsqueeze(x, axis=0) - unsqueezed = True - default_dtype = ivy.default_float_dtype(as_native=True) - if default_dtype is paddle.bfloat16: - x = x.cast(paddle.float32) - else: - x = x.cast(default_dtype) - if copy: - if unsqueezed: - return np.squeeze(np.array(x).astype("bfloat16"), 0) - return np.array(x).astype("bfloat16") - else: - if unsqueezed: - return np.squeeze(np.asarray(x).astype("bfloat16"), 0) - return np.asarray(x).astype("bfloat16") if copy: return np.array(x) else: diff --git a/ivy/functional/backends/torch/experimental/statistical.py b/ivy/functional/backends/torch/experimental/statistical.py index 00d68c01615ec..c26d79202331f 100644 --- a/ivy/functional/backends/torch/experimental/statistical.py +++ b/ivy/functional/backends/torch/experimental/statistical.py @@ -196,17 +196,17 @@ def nanprod( initial: Optional[Union[int, float, complex, ivy.Container]] = None, where: Optional[torch.Tensor] = None, ) -> torch.Tensor: - a = torch.nan_to_num(a, nan=1.0) dtype = ivy.as_native_dtype(dtype) if dtype is None: dtype = _infer_dtype(a.dtype) a = a.type(dtype) + a = torch.nan_to_num(a, nan=1.0) if a.dtype == torch.float16: a = a.type(torch.float32) if axis == (): return a.type(dtype) if axis is None: - return torch.prod(input=a, out=out) + return torch.prod(input=a, out=out).type(dtype) if isinstance(axis, tuple) or isinstance(axis, list): for i in axis: a = torch.prod(a, dim=i, keepdim=keepdims, out=out).type(dtype) diff --git a/ivy_tests/test_ivy/helpers/function_testing.py b/ivy_tests/test_ivy/helpers/function_testing.py index f95e7f83f7205..c1248d70685b4 100644 --- a/ivy_tests/test_ivy/helpers/function_testing.py +++ b/ivy_tests/test_ivy/helpers/function_testing.py @@ -1839,7 +1839,6 @@ def flatten(*, backend: str, ret): """Return a flattened numpy version of the arrays in ret.""" if not isinstance(ret, tuple): ret = (ret,) - with update_backend(backend) as ivy_backend: ret_idxs = ivy_backend.nested_argwhere(ret, ivy_backend.is_ivy_array) @@ -1884,7 +1883,11 @@ def flatten_and_to_np(*, backend: str, ret): # flatten the return ret_flat = flatten(backend=backend, ret=ret) with update_backend(backend) as ivy_backend: - return [ivy_backend.to_numpy(x) for x in ret_flat] + ret = [ivy_backend.to_numpy(x) for x in ret_flat] + for i in range(len(ret_flat)): + if str(ret_flat[i].dtype) != str(ret[i].dtype): + ret[i] = np.array(ret_flat[i]) + return ret def flatten_frontend_to_np(*, backend: str, ret, frontend_array_fn=None): From 271c0b18f8282cef84977ccfd63d0aebee626d55 Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Tue, 5 Sep 2023 13:01:27 +0300 Subject: [PATCH 8/9] Added supported dtypes decorator for paddle. Also fixed some type checks in __cummax due to error from lint --- .../backends/paddle/experimental/statistical.py | 12 ++++++++---- ivy_tests/test_ivy/helpers/function_testing.py | 3 --- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ivy/functional/backends/paddle/experimental/statistical.py b/ivy/functional/backends/paddle/experimental/statistical.py index 605258ba6b702..676d7fbd927df 100644 --- a/ivy/functional/backends/paddle/experimental/statistical.py +++ b/ivy/functional/backends/paddle/experimental/statistical.py @@ -6,7 +6,7 @@ from copy import deepcopy # local -from ivy.func_wrapper import with_unsupported_device_and_dtypes +from ivy.func_wrapper import with_unsupported_device_and_dtypes, with_supported_dtypes from ivy.utils.exceptions import IvyNotImplementedException from . import backend_version @@ -114,6 +114,10 @@ def _validate_quantile(q): return True +@with_supported_dtypes( + {"2.5.1 and below": ("float64", "float32")}, + backend_version, +) def nanprod( a: paddle.Tensor, /, @@ -584,8 +588,8 @@ def __find_cummax( isinstance(x.tolist()[0], list) and len(x[0].shape) >= 1 and ( - (type(x[0]) == paddle.Tensor) - or (type(x[0]) == ivy.data_classes.array.array.Array) + (type(x[0]) is paddle.Tensor) + or (type(x[0]) is ivy.data_classes.array.array.Array) ) ): if axis >= 1: @@ -636,7 +640,7 @@ def __find_cummax( values.append(y) indices.append(n) - if type(x) == paddle.Tensor: + if type(x) is paddle.Tensor: return paddle.to_tensor(values, dtype=x.dtype), paddle.to_tensor( indices, dtype="int64" ) diff --git a/ivy_tests/test_ivy/helpers/function_testing.py b/ivy_tests/test_ivy/helpers/function_testing.py index 21213f53b3421..46121543bbf3c 100644 --- a/ivy_tests/test_ivy/helpers/function_testing.py +++ b/ivy_tests/test_ivy/helpers/function_testing.py @@ -1939,9 +1939,6 @@ def flatten_and_to_np(*, backend: str, ret): ret_flat = flatten(backend=backend, ret=ret) with BackendHandler.update_backend(backend) as ivy_backend: ret = [ivy_backend.to_numpy(x) for x in ret_flat] - for i in range(len(ret_flat)): - if str(ret_flat[i].dtype) != str(ret[i].dtype): - ret[i] = np.array(ret_flat[i]) return ret From b0aef6fa7146ab925e13d927345885cc8e261a6d Mon Sep 17 00:00:00 2001 From: DimitrisAngelis Date: Tue, 5 Sep 2023 13:43:56 +0300 Subject: [PATCH 9/9] Added initial argument and strategy --- .../backends/jax/experimental/statistical.py | 4 +++- .../backends/numpy/experimental/statistical.py | 4 +++- .../backends/paddle/experimental/statistical.py | 6 ++++-- .../backends/tensorflow/experimental/statistical.py | 7 ++++++- .../backends/torch/experimental/statistical.py | 8 +++++--- .../test_experimental/test_core/test_statistical.py | 11 ++++++++++- 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/ivy/functional/backends/jax/experimental/statistical.py b/ivy/functional/backends/jax/experimental/statistical.py index bf6005ba62cc7..9d35e51ae6017 100644 --- a/ivy/functional/backends/jax/experimental/statistical.py +++ b/ivy/functional/backends/jax/experimental/statistical.py @@ -177,7 +177,9 @@ def nanprod( if dtype is None: dtype = _infer_dtype(a.dtype) axis = tuple(axis) if isinstance(axis, list) else axis - return jnp.nanprod(a, axis=axis, keepdims=keepdims, dtype=dtype, out=out) + return jnp.nanprod( + a, axis=axis, keepdims=keepdims, dtype=dtype, out=out, initial=initial + ) def quantile( diff --git a/ivy/functional/backends/numpy/experimental/statistical.py b/ivy/functional/backends/numpy/experimental/statistical.py index d9655124b6d00..667c6106c61a3 100644 --- a/ivy/functional/backends/numpy/experimental/statistical.py +++ b/ivy/functional/backends/numpy/experimental/statistical.py @@ -183,7 +183,9 @@ def nanprod( dtype = _infer_dtype(a.dtype) axis = tuple(axis) if isinstance(axis, list) else axis return np.asarray( - np.nanprod(a=a, axis=axis, dtype=dtype, keepdims=keepdims, out=out) + np.nanprod( + a=a, axis=axis, dtype=dtype, keepdims=keepdims, out=out, initial=initial + ) ) diff --git a/ivy/functional/backends/paddle/experimental/statistical.py b/ivy/functional/backends/paddle/experimental/statistical.py index 63676d26fad52..336fe507f94bc 100644 --- a/ivy/functional/backends/paddle/experimental/statistical.py +++ b/ivy/functional/backends/paddle/experimental/statistical.py @@ -135,12 +135,14 @@ def nanprod( if dtype is None: dtype = _infer_dtype(a.dtype) a = a.cast(dtype) + if initial is None: + initial = 1 if a.dtype not in [paddle.int32, paddle.int64, paddle.float32, paddle.float64]: a = paddle.nan_to_num(a.cast("float64"), nan=1.0) - ret = paddle.prod(a, axis=axis, keepdim=keepdims) + ret = paddle.prod(a, axis=axis, keepdim=keepdims) * initial else: a = paddle.nan_to_num(a, nan=1.0) - ret = paddle.prod(a, axis=axis, keepdim=keepdims) + ret = paddle.prod(a, axis=axis, keepdim=keepdims) * initial if isinstance(axis, Sequence): if len(axis) == a.ndim: diff --git a/ivy/functional/backends/tensorflow/experimental/statistical.py b/ivy/functional/backends/tensorflow/experimental/statistical.py index e2e4dbd393f59..7c7d45204ff3f 100644 --- a/ivy/functional/backends/tensorflow/experimental/statistical.py +++ b/ivy/functional/backends/tensorflow/experimental/statistical.py @@ -137,8 +137,13 @@ def nanprod( dtype = ivy.as_native_dtype(dtype) if dtype is None: dtype = _infer_dtype(a.dtype) + if initial is None: + initial = 1 axis = tuple(axis) if isinstance(axis, list) else axis - return tf.experimental.numpy.nanprod(a, axis=axis, keepdims=keepdims, dtype=dtype) + return ( + tf.experimental.numpy.nanprod(a, axis=axis, keepdims=keepdims, dtype=dtype) + * initial + ) def _validate_quantile(q): diff --git a/ivy/functional/backends/torch/experimental/statistical.py b/ivy/functional/backends/torch/experimental/statistical.py index 33826d0ad3441..b2aee02bcd414 100644 --- a/ivy/functional/backends/torch/experimental/statistical.py +++ b/ivy/functional/backends/torch/experimental/statistical.py @@ -199,6 +199,8 @@ def nanprod( dtype = ivy.as_native_dtype(dtype) if dtype is None: dtype = _infer_dtype(a.dtype) + if initial is None: + initial = 1 a = a.type(dtype) a = torch.nan_to_num(a, nan=1.0) if a.dtype == torch.float16: @@ -206,14 +208,14 @@ def nanprod( if axis == (): return a.type(dtype) if axis is None: - return torch.prod(input=a, out=out).type(dtype) + return torch.prod(input=a, out=out).type(dtype) * initial if isinstance(axis, tuple) or isinstance(axis, list): for i in axis: a = torch.prod(a, dim=i, keepdim=keepdims, out=out).type(dtype) if a.dtype == torch.float16: a = a.type(torch.float32) - return a.type(dtype) - return torch.prod(a, dim=axis, keepdim=keepdims, out=out).type(dtype) + return a.type(dtype) * initial + return torch.prod(a, dim=axis, keepdim=keepdims, out=out).type(dtype) * initial nanprod.support_native_out = True diff --git a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py index bdb97b63dee1e..716ff0ae7353c 100644 --- a/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py +++ b/ivy_tests/test_ivy/test_functional/test_experimental/test_core/test_statistical.py @@ -651,9 +651,17 @@ def test_nanmedian( dtype_x_axis_castable=_get_castable_float_dtype_nan(), keep_dims=st.booleans(), test_gradients=st.just(False), + initial=st.integers(min_value=-5, max_value=5), ) def test_nanprod( - *, dtype_x_axis_castable, keep_dims, test_flags, backend_fw, fn_name, on_device + *, + dtype_x_axis_castable, + keep_dims, + test_flags, + initial, + backend_fw, + fn_name, + on_device, ): input_dtype, x, axis, castable_dtype = dtype_x_axis_castable x = x[0] @@ -669,6 +677,7 @@ def test_nanprod( axis=axis, keepdims=keep_dims, dtype=castable_dtype, + initial=initial, )