diff --git a/paddle/phi/ops/yaml/ops.yaml b/paddle/phi/ops/yaml/ops.yaml index b5f4d6371a82b1..e124d501d2a3b0 100644 --- a/paddle/phi/ops/yaml/ops.yaml +++ b/paddle/phi/ops/yaml/ops.yaml @@ -282,6 +282,10 @@ - op : any args : (Tensor x, int64_t[] axis={}, bool keepdim=false) + python_api: + name : [paddle.any, paddle.Tensor.any] + args_alias: + use_default_mapping : True output : Tensor(out) infer_meta : func : ReduceInferMeta diff --git a/python/paddle/_paddle_docs.py b/python/paddle/_paddle_docs.py index 29e551e4a4841e..02212c974e43e6 100644 --- a/python/paddle/_paddle_docs.py +++ b/python/paddle/_paddle_docs.py @@ -505,6 +505,88 @@ def isnan( ) # liuyi +add_doc_and_signature( + "any", + """ + Computes the ``logical or`` of tensor elements over the given dimension, and return the result. + + .. note:: + Alias Support: The parameter name ``input`` can be used as an alias for ``x``, and the parameter name ``dim`` can be used as an alias for ``axis``. + For example, ``any(input=tensor_x, dim=1)`` is equivalent to ``any(x=tensor_x, axis=1)``. + + Args: + x (Tensor): An N-D Tensor, the input data type should be 'bool', 'float32', 'float64', 'int32', 'int64', 'complex64', 'complex128'. + alias: ``input``. + axis (int|list|tuple|None, optional): The dimensions along which the ``logical or`` is compute. If + :attr:`None`, and all elements of :attr:`x` and return a + Tensor with a single element, otherwise must be in the + range :math:`[-rank(x), rank(x))`. If :math:`axis[i] < 0`, + the dimension to reduce is :math:`rank + axis[i]`. + alias: ``dim``. + keepdim (bool, optional): Whether to reserve the reduced dimension in the + output Tensor. The result Tensor will have one fewer dimension + than the :attr:`x` unless :attr:`keepdim` is true, default + value is False. + name (str|None, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`. + out (Tensor|None, optional): The output tensor. Default: None. + + Returns: + Tensor: Results the ``logical or`` on the specified axis of input Tensor `x`, it's data type is bool. + + Examples: + .. code-block:: python + + >>> import paddle + >>> # type: ignore + + >>> x = paddle.to_tensor([[1, 0], [1, 1]], dtype='int32') + >>> x = paddle.assign(x) + >>> x + Tensor(shape=[2, 2], dtype=int32, place=Place(cpu), stop_gradient=True, + [[1, 0], + [1, 1]]) + >>> x = paddle.cast(x, 'bool') + >>> # x is a bool Tensor with following elements: + >>> # [[True, False] + >>> # [True, True]] + + >>> # out1 should be True + >>> out1 = paddle.any(x) + >>> out1 + Tensor(shape=[], dtype=bool, place=Place(cpu), stop_gradient=True, + True) + + >>> # out2 should be [True, True] + >>> out2 = paddle.any(x, axis=0) + >>> out2 + Tensor(shape=[2], dtype=bool, place=Place(cpu), stop_gradient=True, + [True, True]) + + >>> # keepdim=False, out3 should be [True, True], out.shape should be (2,) + >>> out3 = paddle.any(x, axis=-1) + >>> out3 + Tensor(shape=[2], dtype=bool, place=Place(cpu), stop_gradient=True, + [True, True]) + + >>> # keepdim=True, result should be [[True], [True]], out.shape should be (2,1) + >>> out4 = paddle.any(x, axis=1, keepdim=True) + >>> out4 + Tensor(shape=[2, 1], dtype=bool, place=Place(cpu), stop_gradient=True, + [[True], + [True]]) + + """, + """ + def any( + x: Tensor, + axis: int | Sequence[int] | None = None, + keepdim: bool = False, + name: str | None = None, + *, + out: Tensor | None = None + ) -> Tensor + """, +) # shenwei diff --git a/python/paddle/tensor/manipulation.py b/python/paddle/tensor/manipulation.py index 403f48d17c2334..10d69d65793371 100644 --- a/python/paddle/tensor/manipulation.py +++ b/python/paddle/tensor/manipulation.py @@ -4536,6 +4536,7 @@ def scatter_nd( return scatter_nd_add(zeros(shape, updates.dtype), index, updates, name) +@ParamAliasDecorator({"x": ["input"], "axis": ["dim"]}) def chunk( x: Tensor, chunks: int, axis: int | Tensor = 0, name: str | None = None ) -> list[Tensor]: @@ -4555,12 +4556,18 @@ def chunk( :alt: legend of reshape API :align: center + .. note:: + Alias Support: The parameter name ``input`` can be used as an alias for ``x``, and the parameter name ``dim`` can be used as an alias for ``axis``. + For example, ``chunk(input=tensor_x, dim=1)`` is equivalent to ``chunk(x=tensor_x, axis=1)``. + Args: x (Tensor): A N-D Tensor. The data type is bool, float16, float32, float64, int32 or int64. + alias: ``input``. chunks(int): The number of tensor to be split along the certain axis. axis (int|Tensor, optional): The axis along which to split, it can be a integer or a ``0-D Tensor`` with shape [] and data type ``int32`` or ``int64``. If :math::`axis < 0`, the axis to split along is :math:`rank(x) + axis`. Default is 0. + alias: ``dim``. name (str|None, optional): The default value is None. Normally there is no need for user to set this property. For more information, please refer to :ref:`api_guide_Name` . Returns: diff --git a/python/paddle/tensor/math.py b/python/paddle/tensor/math.py index 1f84b1d6067e4f..f8b999e933d354 100644 --- a/python/paddle/tensor/math.py +++ b/python/paddle/tensor/math.py @@ -26,6 +26,7 @@ all, amax, amin, + any, isfinite, isinf, isnan, @@ -4952,109 +4953,6 @@ def increment(x: Tensor, value: float = 1.0, name: str | None = None) -> Tensor: return x -def any( - x: Tensor, - axis: int | Sequence[int] | None = None, - keepdim: bool = False, - name: str | None = None, -) -> Tensor: - """ - Computes the ``logical or`` of tensor elements over the given dimension, and return the result. - - Args: - x (Tensor): An N-D Tensor, the input data type should be 'bool', 'float32', 'float64', 'int32', 'int64', 'complex64', 'complex128'. - axis (int|list|tuple|None, optional): The dimensions along which the ``logical or`` is compute. If - :attr:`None`, and all elements of :attr:`x` and return a - Tensor with a single element, otherwise must be in the - range :math:`[-rank(x), rank(x))`. If :math:`axis[i] < 0`, - the dimension to reduce is :math:`rank + axis[i]`. - keepdim (bool, optional): Whether to reserve the reduced dimension in the - output Tensor. The result Tensor will have one fewer dimension - than the :attr:`x` unless :attr:`keepdim` is true, default - value is False. - name (str|None, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`. - - Returns: - Tensor: Results the ``logical or`` on the specified axis of input Tensor `x`, it's data type is bool. - - Examples: - .. code-block:: python - - >>> import paddle - - >>> x = paddle.to_tensor([[1, 0], [1, 1]], dtype='int32') - >>> x = paddle.assign(x) - >>> x - Tensor(shape=[2, 2], dtype=int32, place=Place(cpu), stop_gradient=True, - [[1, 0], - [1, 1]]) - >>> x = paddle.cast(x, 'bool') - >>> # x is a bool Tensor with following elements: - >>> # [[True, False] - >>> # [True, True]] - - >>> # out1 should be True - >>> out1 = paddle.any(x) - >>> out1 - Tensor(shape=[], dtype=bool, place=Place(cpu), stop_gradient=True, - True) - - >>> # out2 should be [True, True] - >>> out2 = paddle.any(x, axis=0) - >>> out2 - Tensor(shape=[2], dtype=bool, place=Place(cpu), stop_gradient=True, - [True, True]) - - >>> # keepdim=False, out3 should be [True, True], out.shape should be (2,) - >>> out3 = paddle.any(x, axis=-1) - >>> out3 - Tensor(shape=[2], dtype=bool, place=Place(cpu), stop_gradient=True, - [True, True]) - - >>> # keepdim=True, result should be [[True], [True]], out.shape should be (2,1) - >>> out4 = paddle.any(x, axis=1, keepdim=True) - >>> out4 - Tensor(shape=[2, 1], dtype=bool, place=Place(cpu), stop_gradient=True, - [[True], - [True]]) - - """ - if in_dynamic_or_pir_mode(): - return _C_ops.any(x, axis, keepdim) - else: - reduce_all, axis = _get_reduce_axis(axis, x) - attrs = { - 'dim': axis, - 'keep_dim': keepdim, - 'reduce_all': reduce_all, - } - check_variable_and_dtype( - x, - 'x', - [ - 'bool', - 'float32', - 'float64', - 'int32', - 'int64', - 'complex64', - 'complex128', - ], - 'any', - ) - check_type(axis, 'axis', (int, list, tuple, type(None)), 'any') - - helper = LayerHelper('any', **locals()) - out = helper.create_variable_for_type_inference(dtype=paddle.bool) - helper.append_op( - type='reduce_any', - inputs={'X': x}, - outputs={'Out': out}, - attrs=attrs, - ) - return out - - def broadcast_shapes(*shapes: Sequence[int]) -> list[int]: """ The function returns the shape of doing operation with broadcasting on tensors of shape list. diff --git a/python/paddle/tensor/search.py b/python/paddle/tensor/search.py index 5a40997626ba7b..f09d46216d9185 100755 --- a/python/paddle/tensor/search.py +++ b/python/paddle/tensor/search.py @@ -47,6 +47,7 @@ __all__ = [] +@ParamAliasDecorator({"x": ["input"], "axis": ["dim"]}) def argsort( x: Tensor, axis: int = -1, @@ -57,12 +58,18 @@ def argsort( """ Sorts the input along the given axis, and returns the corresponding index tensor for the sorted output values. The default sort algorithm is ascending, if you want the sort algorithm to be descending, you must set the :attr:`descending` as True. + .. note:: + Alias Support: The parameter name ``input`` can be used as an alias for ``x``, and the parameter name ``dim`` can be used as an alias for ``axis``. + For example, ``argsort(input=tensor_x, dim=1)`` is equivalent to ``(x=tensor_x, axis=1)``. + Args: x (Tensor): An input N-D Tensor with type bfloat16, float16, float32, float64, int16, int32, int64, uint8. + alias: ``input``. axis (int, optional): Axis to compute indices along. The effective range is [-R, R), where R is Rank(x). when axis<0, it works the same way as axis+R. Default is -1. + alias: ``dim``. descending (bool, optional) : Descending is a flag, if set to true, algorithm will sort by descending order, else sort by ascending order. Default is false. @@ -456,15 +463,21 @@ def index_select( @overload -def nonzero(x: Tensor, as_tuple: Literal[False] = ...) -> Tensor: ... +def nonzero( + x: Tensor, as_tuple: Literal[False] = ..., *, out: Tensor | None = None +) -> Tensor: ... @overload -def nonzero(x: Tensor, as_tuple: Literal[True] = ...) -> tuple[Tensor, ...]: ... +def nonzero( + x: Tensor, as_tuple: Literal[True] = ..., *, out: Tensor | None = None +) -> tuple[Tensor, ...]: ... @overload -def nonzero(x: Tensor, as_tuple: bool = ...) -> Tensor | tuple[Tensor, ...]: ... +def nonzero( + x: Tensor, as_tuple: bool = ..., *, out: Tensor | None = None +) -> Tensor | tuple[Tensor, ...]: ... @param_one_alias(['x', 'input']) diff --git a/test/amp/test_amp_api.py b/test/amp/test_amp_api.py index 1ce2524a10ea8f..0591af498a88e2 100644 --- a/test/amp/test_amp_api.py +++ b/test/amp/test_amp_api.py @@ -19,7 +19,6 @@ from amp_base_models import AmpTestBase import paddle -import paddle.nn.functional as F from paddle import nn from paddle.base import core from paddle.static import amp @@ -176,24 +175,6 @@ def check_results( level, ) - def test_static_amp_OD(self): - paddle.enable_static() - expected_fp16_calls = { - "conv2d": 1, - "elementwise_add": 0, - "matmul_v2": 1, - "reduce_mean": 0, - } - with paddle.pir_utils.OldIrGuard(): - self.check_results( - True, - 'float16', - 'OD', - use_promote=True, - expected_op_calls=expected_fp16_calls, - ) - paddle.disable_static() - @unittest.skipIf( not core.is_compiled_with_cuda() and not core.is_compiled_with_xpu(), @@ -308,101 +289,6 @@ def test_pir_amp_grad_scaler(self): and core.get_xpu_device_version(0) == core.XPUVersion.XPU3, "Bugs on XPU3, disable temporarily", ) -class TestFp16Guard(AmpTestBase): - def test_fp16_guard(self): - paddle.enable_static() - - def run_example_code(): - if paddle.is_compiled_with_cuda(): - place = paddle.CUDAPlace(0) - elif paddle.device.is_compiled_with_xpu(): - place = paddle.device.XPUPlace(0) - else: - raise ValueError("Only support CUDA or XPU Place.") - main_program = paddle.static.Program() - startup_program = paddle.static.Program() - - exe = paddle.static.Executor(place) - - fetch_vars = [] - # 1) Use fp16_guard to control the range of fp16 kernels used. - with paddle.static.program_guard(main_program, startup_program): - with paddle.static.amp.fp16_guard(): - data = paddle.static.data( - name='X', shape=[None, 1, 28, 28], dtype='float32' - ) - conv2d = paddle.static.nn.conv2d( - input=data, num_filters=6, filter_size=3 - ) - bn = paddle.static.nn.batch_norm(input=conv2d, act="relu") - - pool = F.max_pool2d(bn, kernel_size=2, stride=2) - hidden = paddle.static.nn.fc(pool, size=10) - loss = paddle.mean(hidden) - fetch_vars = [loss] - # 2) Create the optimizer and set `multi_precision` to True. - # Setting `multi_precision` to True can avoid the poor accuracy - # or the slow convergence in a way. - optimizer = paddle.optimizer.Momentum( - learning_rate=0.01, multi_precision=True - ) - # 3) These ops in `custom_black_list` will keep in the float32 computation type. - amp_list = paddle.static.amp.CustomOpLists( - custom_black_list=['pool2d'] - ) - # 4) The entry of Paddle AMP. - # Enable pure fp16 training by setting `use_pure_fp16` to True. - optimizer = paddle.static.amp.decorate( - optimizer, - amp_list, - init_loss_scaling=128.0, - use_dynamic_loss_scaling=True, - use_pure_fp16=True, - ) - # If you don't use the default_startup_program(), you should pass - # your defined `startup_program` into `minimize`. - optimizer.minimize(loss) - - exe.run(startup_program) - # 5) Use `amp_init` after FP32 parameters initialization(such as `exe.run(startup_program)`). - # If you want to perform the testing process, you should pass `test_program` into `amp_init`. - optimizer.amp_init(place, scope=paddle.static.global_scope()) - - x_fp32 = np.random.random(size=[1, 1, 28, 28]).astype("float32") - (loss_data,) = exe.run( - main_program, feed={"X": x_fp32}, fetch_list=[loss] - ) - - self.assertEqual( - paddle.static.global_scope() - .find_var("conv2d_0.b_0") - .get_tensor() - ._dtype(), - paddle.float16, - ) - self.assertEqual( - paddle.static.global_scope() - .find_var("fc_0.b_0") - .get_tensor() - ._dtype(), - paddle.float32, - ) - - if ( - paddle.is_compiled_with_cuda() - and len(paddle.static.cuda_places()) > 0 - ): - with paddle.pir_utils.OldIrGuard(): - run_example_code() - elif ( - paddle.is_compiled_with_xpu() - and len(paddle.static.xpu_places()) > 0 - ): - with paddle.pir_utils.OldIrGuard(): - run_example_code() - paddle.disable_static() - - class SimpleModelIncludeSetValue(nn.Layer): def __init__(self): super().__init__() diff --git a/test/legacy_test/test_argsort_op.py b/test/legacy_test/test_argsort_op.py index 0d4e75497babbb..7f047849589ec2 100644 --- a/test/legacy_test/test_argsort_op.py +++ b/test/legacy_test/test_argsort_op.py @@ -16,6 +16,7 @@ import numpy as np from op_test import OpTest, convert_float_to_uint16 +from utils import dygraph_guard, static_guard import paddle from paddle import base @@ -619,5 +620,110 @@ def init_direction(self): self.descending = True +class TestArgsortCompatibility(unittest.TestCase): + def setUp(self): + self.places = [paddle.CPUPlace()] + if paddle.base.core.is_compiled_with_cuda(): + self.places.append(paddle.CUDAPlace(0)) + self.func = paddle.argsort + self.init_data() + self.init_case() + + def init_data(self): + self.shape = [5, 6] + self.dtype = 'float32' + self.axis = 1 + self.np_input = np.random.rand(*self.shape).astype(self.dtype) + self.np_out = np.argsort(self.np_input, self.axis) + + def init_case(self): + params = [['x', 'input'], ['axis', 'dim']] # param1 # param2 + + # Generate all valid combinations + def generate_cases(param_groups, case_list): + from itertools import product + + for combo in product(*[[None, *names] for names in param_groups]): + args = ['pos' if p is None else 'kw' for p in combo] + if args == sorted(args, key=lambda x: x != 'pos'): + case_list.append(combo) + + # paddle.chunk() + self.test_cases = [] + generate_cases(params, self.test_cases) + # x.chunk() + self.tensor_test_cases = [] + generate_cases(params[1:], self.tensor_test_cases) + + def _build_args_kwargs(self, param_names, params): + args = [] + kwargs = {} + for name, param in zip(param_names, params): + if name is None: + args.append(param) + else: + kwargs[name] = param + return args, kwargs + + def test_dygraph_compatibility(self): + with dygraph_guard(): + for place in self.places: + paddle.device.set_device(place) + x = paddle.to_tensor(self.np_input) + # paddle. + for param_names in self.test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (x, self.axis) + ) + out = self.func(*args, **kwargs) + np.testing.assert_array_equal(self.np_out, out.numpy()) + # paddle.Tensor. + for param_names in self.tensor_test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (self.axis,) + ) + out = x.argsort(*args, **kwargs) + np.testing.assert_array_equal(self.np_out, out.numpy()) + + def test_static_compatibility(self): + with static_guard(): + for place in self.places: + main = paddle.static.Program() + startup = paddle.static.Program() + with base.program_guard(main, startup): + x = paddle.static.data( + name="x", shape=self.shape, dtype=self.dtype + ) + # paddle. + for param_names in self.test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (x, self.axis) + ) + out = self.func(*args, **kwargs) + + exe = base.Executor(place) + fetches = exe.run( + main, + feed={"x": self.np_input}, + fetch_list=[out], + ) + np.testing.assert_array_equal(self.np_out, fetches[0]) + # paddle.Tensor. + for param_names in self.tensor_test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (self.axis,) + ) + + out = x.argsort(*args, **kwargs) + + exe = base.Executor(place) + fetches = exe.run( + main, + feed={"x": self.np_input}, + fetch_list=[out], + ) + np.testing.assert_array_equal(self.np_out, fetches[0]) + + if __name__ == "__main__": unittest.main() diff --git a/test/legacy_test/test_chunk_op.py b/test/legacy_test/test_chunk_op.py index 07c81c4ff7dd85..64f309b8d8c307 100644 --- a/test/legacy_test/test_chunk_op.py +++ b/test/legacy_test/test_chunk_op.py @@ -15,6 +15,7 @@ import unittest import numpy as np +from utils import dygraph_guard, static_guard import paddle from paddle import base @@ -31,24 +32,16 @@ def test_axis_type(): self.assertRaises(TypeError, test_axis_type) - # The type of axis in chunk op should be int or Variable. - def test_axis_variable_type(): - x2 = paddle.static.data(shape=[4], dtype='float16', name='x9') - x3 = paddle.static.data(shape=[1], dtype='float16', name='x10') - paddle.chunk(input=x2, chunks=2, axis=x3) - - self.assertRaises(TypeError, test_axis_variable_type) - # The type of num_or_sections in chunk_op should be int, tuple or list. def test_chunks_type(): x4 = paddle.static.data(shape=[4], dtype='float16', name='x4') - paddle.chunk(input=x4, chunks=2.1, axis=3) + paddle.chunk(x=x4, chunks=2.1, axis=3) self.assertRaises(TypeError, test_chunks_type) def test_axis_type_tensor(): x5 = paddle.static.data(shape=[4], dtype='float16', name='x6') - paddle.chunk(input=x5, chunks=2, axis=3.2) + paddle.chunk(x=x5, chunks=2, axis=3.2) self.assertRaises(TypeError, test_axis_type_tensor) @@ -188,5 +181,127 @@ def test_axis_tensor_input(self): np.testing.assert_allclose(ex_x2, x2_out, rtol=1e-05) +class TestChunkCompatibility(unittest.TestCase): + def setUp(self): + self.places = [paddle.CPUPlace()] + if paddle.base.core.is_compiled_with_cuda(): + self.places.append(paddle.CUDAPlace(0)) + self.func = paddle.chunk + self.init_data() + self.init_case() + + def init_data(self): + self.shape = [6, 4] + self.dtype = 'float32' + self.np_input = np.random.random(self.shape).astype(self.dtype) + self.chunks = 2 + self.axis = 0 + self.np_out = np.array_split(self.np_input, self.chunks, axis=self.axis) + + def init_case(self): + params = [ + ['x', 'input'], # param1 + ['chunks'], # param2 + ['axis', 'dim'], # param3 + ] + + # Generate all valid combinations + def generate_cases(param_groups, case_list): + from itertools import product + + for combo in product(*[[None, *names] for names in param_groups]): + args = ['pos' if p is None else 'kw' for p in combo] + if args == sorted(args, key=lambda x: x != 'pos'): + case_list.append(combo) + + # paddle.chunk() + self.test_cases = [] + generate_cases(params, self.test_cases) + # x.chunk() + self.tensor_test_cases = [] + generate_cases(params[1:], self.tensor_test_cases) + + def _build_args_kwargs(self, param_names, params): + args = [] + kwargs = {} + for name, param in zip(param_names, params): + if name is None: + args.append(param) + else: + kwargs[name] = param + return args, kwargs + + def test_dygraph_compatibility(self): + with dygraph_guard(): + for place in self.places: + paddle.device.set_device(place) + x = paddle.to_tensor(self.np_input) + # paddle. + for param_names in self.test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (x, self.chunks, self.axis) + ) + outs = self.func(*args, **kwargs) + for out, np_out in zip(outs, self.np_out): + np.testing.assert_allclose( + np_out, out.numpy(), rtol=1e-10 + ) + # paddle.Tensor. + for param_names in self.tensor_test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (self.chunks, self.axis) + ) + outs = x.chunk(*args, **kwargs) + for out, np_out in zip(outs, self.np_out): + np.testing.assert_allclose( + np_out, out.numpy(), rtol=1e-10 + ) + + def test_static_compatibility(self): + with static_guard(): + for place in self.places: + main = paddle.static.Program() + startup = paddle.static.Program() + with base.program_guard(main, startup): + x = paddle.static.data( + name="x", shape=self.shape, dtype=self.dtype + ) + # paddle. + for param_names in self.test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (x, self.chunks, self.axis) + ) + + outs = self.func(*args, **kwargs) + + exe = base.Executor(place) + fetches = exe.run( + main, + feed={"x": self.np_input}, + fetch_list=outs, + ) + for fetch, np_out in zip(fetches, self.np_out): + np.testing.assert_allclose( + np_out, fetch, rtol=1e-10 + ) + # paddle.Tensor. + for param_names in self.tensor_test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (self.chunks, self.axis) + ) + outs = x.chunk(*args, **kwargs) + + exe = base.Executor(place) + fetches = exe.run( + main, + feed={"x": self.np_input}, + fetch_list=outs, + ) + for fetch, np_out in zip(fetches, self.np_out): + np.testing.assert_allclose( + np_out, fetch, rtol=1e-10 + ) + + if __name__ == '__main__': unittest.main() diff --git a/test/legacy_test/test_reduce_op.py b/test/legacy_test/test_reduce_op.py index 905a91712866e3..ee5d9f3b517eac 100644 --- a/test/legacy_test/test_reduce_op.py +++ b/test/legacy_test/test_reduce_op.py @@ -2600,6 +2600,172 @@ def test_zero_size(self): self._test_any(place, axis, keepdim, dtype) +class TestAnyCompatibility(unittest.TestCase): + def setUp(self): + self.places = [paddle.CPUPlace()] + if paddle.base.core.is_compiled_with_cuda(): + self.places.append(paddle.CUDAPlace(0)) + self.func = paddle.any + self.init_data() + self.init_case() + + def init_data(self): + self.shape = [5, 6] + self.dtype = 'float32' + self.axis = 1 + self.np_input = np.random.randint(0, 2, self.shape).astype(self.dtype) + self.np_out = np.any(self.np_input, self.axis, keepdims=True) + + def init_case(self): + params = [['x', 'input'], ['axis', 'dim']] # param1 # param2 + + # Generate all valid combinations + def generate_cases(param_groups, case_list): + from itertools import product + + for combo in product(*[[None, *names] for names in param_groups]): + args = ['pos' if p is None else 'kw' for p in combo] + if args == sorted(args, key=lambda x: x != 'pos'): + case_list.append(combo) + + # paddle.chunk() + self.test_cases = [] + generate_cases(params, self.test_cases) + # x.chunk() + self.tensor_test_cases = [] + generate_cases(params[1:], self.tensor_test_cases) + + def _build_args_kwargs(self, param_names, params): + args = [] + kwargs = {} + for name, param in zip(param_names, params): + if name is None: + args.append(param) + else: + kwargs[name] = param + kwargs['keepdim'] = True + return args, kwargs + + def test_dygraph_compatibility(self): + with dygraph_guard(): + for place in self.places: + paddle.device.set_device(place) + x = paddle.to_tensor(self.np_input) + # paddle. + for param_names in self.test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (x, self.axis) + ) + for out_flag in [False, True]: + if out_flag: + kwargs['out'] = paddle.empty([]) + self.func(*args, **kwargs) + out = kwargs["out"] + else: + out = self.func(*args, **kwargs) + np.testing.assert_allclose( + self.np_out, out.numpy(), rtol=1e-10 + ) + # paddle.Tensor. + for param_names in self.tensor_test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (self.axis,) + ) + out = x.any(*args, **kwargs) + np.testing.assert_allclose( + self.np_out, out.numpy(), rtol=1e-10 + ) + + def test_dygraph_out(self): + def run_any(test_type): + x = paddle.to_tensor(self.np_input) + x.stop_gradient = False + out = ( + paddle.zeros(self.np_out.shape) + if test_type in ["with_out", "both"] + else None + ) + if test_type == "return": + out = paddle.any(x, axis=self.axis, keepdim=True) + elif test_type == "with_out": + paddle.any(x, axis=self.axis, keepdim=True, out=out) + elif test_type == "both": + out = paddle.any(x, axis=self.axis, keepdim=True, out=out) + else: + raise ValueError(f"Invalid test_mode: {test_type}") + + expected = paddle._C_ops.any(x, self.axis, True) + np.testing.assert_array_equal(out.numpy(), expected.numpy()) + loss = out.sum().astype('float32') + loss.backward() + return out, x.grad + + def assert_outputs_equal(outputs, rtol: float = 1e-10): + for out in outputs[1:]: + np.testing.assert_allclose( + outputs[0].numpy(), out.numpy(), rtol=rtol + ) + + with dygraph_guard(): + for place in self.places: + paddle.device.set_device(place) + out1, grad1 = run_any("return") + out2, grad2 = run_any("with_out") + out3, grad3 = run_any("both") + + assert_outputs_equal([out1, out2, out3]) + if ( + grad1 is not None + and grad2 is not None + and grad3 is not None + ): + assert_outputs_equal([grad1, grad2, grad3]) + + def test_static_compatibility(self): + with static_guard(): + for place in self.places: + main = paddle.static.Program() + startup = paddle.static.Program() + with base.program_guard(main, startup): + x = paddle.static.data( + name="x", shape=self.shape, dtype=self.dtype + ) + # paddle. + for param_names in self.test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (x, self.axis) + ) + + out = self.func(*args, **kwargs) + + exe = base.Executor(place) + fetches = exe.run( + main, + feed={"x": self.np_input}, + fetch_list=[out], + ) + np.testing.assert_allclose( + self.np_out, fetches[0], rtol=1e-10 + ) + # paddle.Tensor. + for param_names in self.tensor_test_cases: + args, kwargs = self._build_args_kwargs( + param_names, (self.axis,) + ) + + out = x.any(*args, **kwargs) + + exe = base.Executor(place) + fetches = exe.run( + main, + feed={"x": self.np_input}, + fetch_list=[out], + ) + np.testing.assert_allclose( + self.np_out, fetches[0], rtol=1e-10 + ) + + if __name__ == '__main__': paddle.enable_static() unittest.main()