本文最后更新于 2024-12-11,文章内容可能已经过时。

copysign

copysign-API文档-PaddlePaddle深度学习平台

问题复现

ARRAY_API_TESTS_MODULE=array_api_compat.paddle ARRAY_API_TESTS_SKIP_DTYPES=int8,int16,uint16,uint8,uint32,uint64 ARRAY_API_TESTS_VERSION="2023.12" python -m pytest -s -vvv 'array_api_tests/test_operators_and_elementwise_functions.py::test_copysign'

执行结果如下:

============================================= FAILURES =============================================
__________________________________________ test_copysign ___________________________________________
  + Exception Group Traceback (most recent call last):
  |   File "/usr/local/lib/python3.9/dist-packages/_pytest/runner.py", line 341, in from_call
  |     result: Optional[TResult] = func()
  |   File "/usr/local/lib/python3.9/dist-packages/_pytest/runner.py", line 262, in <lambda>
  |     lambda: ihook(item=item, **kwds), when=when, reraise=reraise
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_hooks.py", line 493, in __call__
  |     return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_manager.py", line 115, in _hookexec
  |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_callers.py", line 152, in _multicall
  |     return outcome.get_result()
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_result.py", line 114, in get_result
  |     raise exc.with_traceback(exc.__traceback__)
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_callers.py", line 77, in _multicall
  |     res = hook_impl.function(*args)
  |   File "/usr/local/lib/python3.9/dist-packages/_pytest/runner.py", line 177, in pytest_runtest_call
  |     raise e
  |   File "/usr/local/lib/python3.9/dist-packages/_pytest/runner.py", line 169, in pytest_runtest_call
  |     item.runtest()
  |   File "/usr/local/lib/python3.9/dist-packages/_pytest/python.py", line 1792, in runtest
  |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_hooks.py", line 493, in __call__
  |     return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_manager.py", line 115, in _hookexec
  |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_callers.py", line 113, in _multicall
  |     raise exception.with_traceback(exception.__traceback__)
  |   File "/usr/local/lib/python3.9/dist-packages/pluggy/_callers.py", line 77, in _multicall
  |     res = hook_impl.function(*args)
  |   File "/usr/local/lib/python3.9/dist-packages/_pytest/python.py", line 194, in pytest_pyfunc_call
  |     result = testfunction(**testargs)
  |   File "/home/Paddle/array-api-tests/array_api_tests/test_operators_and_elementwise_functions.py", line 1064, in test_copysign
  |     @given(*hh.two_mutual_arrays(dh.real_float_dtypes))
  |   File "/usr/local/lib/python3.9/dist-packages/hypothesis/core.py", line 1722, in wrapped_test
  |     raise the_error_hypothesis_found
  | exceptiongroup.ExceptionGroup: Hypothesis found 2 distinct failures. (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/Paddle/array-api-tests/array_api_tests/test_operators_and_elementwise_functions.py", line 1066, in test_copysign
    |     out = xp.copysign(x1, x2)
    |   File "/home/Paddle/array-api-compat/array_api_compat/paddle/_aliases.py", line 82, in _f
    |     return f(x1, x2, **kwargs)
    |   File "/usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py", line 8007, in copysign
    |     return _C_ops.copysign(x, y)
    | ValueError: (InvalidArgument) The type of data we are trying to retrieve (float64) does not match the type of data (float32) currently contained in the container.
    |   [Hint: Expected dtype() == phi::CppTypeToDataType<T>::Type(), but received dtype():10 != phi::CppTypeToDataType<T>::Type():11.] (at /home/Paddle/paddle/phi/core/dense_tensor.cc:153)
    |
    | Falsifying example: test_copysign(
    |     x1=Tensor(shape=[], dtype=float32, place=Place(cpu), stop_gradient=True,
    |            0.),
    |     x2=Tensor(shape=[], dtype=float64, place=Place(cpu), stop_gradient=True,
    |            0.),  # or any other generated value
    | )
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File "/home/Paddle/array-api-tests/array_api_tests/test_operators_and_elementwise_functions.py", line 1066, in test_copysign
    |     out = xp.copysign(x1, x2)
    |   File "/home/Paddle/array-api-compat/array_api_compat/paddle/_aliases.py", line 82, in _f
    |     return f(x1, x2, **kwargs)
    |   File "/usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py", line 8007, in copysign
    |     return _C_ops.copysign(x, y)
    | RuntimeError: (PreconditionNotMet) The meta data must be valid when call the mutable data function.
    |   [Hint: Expected valid() == true, but received valid():0 != true:1.] (at /home/Paddle/paddle/phi/core/dense_tensor.cc:113)
    |
    | Falsifying example: test_copysign(
    |     x1=Tensor(shape=[], dtype=float32, place=Place(cpu), stop_gradient=True,
    |            0.),
    |     x2=Tensor(shape=[0], dtype=float32, place=Place(cpu), stop_gradient=True,
    |            []),
    | )
    | Explanation:
    |     These lines were always and only run by failing examples:
    |         /home/Paddle/array-api-compat/array_api_compat/paddle/_aliases.py:865
    |         /usr/local/lib/python3.9/dist-packages/paddle/base/dygraph/tensor_patch_methods.py:584
    |         /usr/local/lib/python3.9/dist-packages/paddle/base/dygraph/tensor_patch_methods.py:781
    |         /usr/local/lib/python3.9/dist-packages/paddle/base/dygraph/tensor_patch_methods.py:790
    |         /usr/local/lib/python3.9/dist-packages/paddle/base/framework.py:1630
    |         (and 4 more with settings.verbosity >= verbose)
    +------------------------------------
========================================= warnings summary =========================================
array_api_tests/__init__.py:83
  /home/Paddle/array-api-tests/array_api_tests/__init__.py:83: HypothesisWarning: Could not determine whether module array_api_compat.paddle is an Array API library
    xps = array_api.make_strategies_namespace(xp, api_version=api_version)

array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
  /usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py:8002: UserWarning: The shape of broadcast output [1, 1, 1, -1, 3] is different from the input tensor x with shape: [0, 3], please make sure you are using copysign api correctly.
    warnings.warn(

array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
  /usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py:8002: UserWarning: The shape of broadcast output [1, 1, 3, 1, 1] is different from the input tensor x with shape: [1, 1, 1, 1, 1], please make sure you are using copysign api correctly.
    warnings.warn(

array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
  /usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py:8002: UserWarning: The shape of broadcast output [-1, 3] is different from the input tensor x with shape: [0, 3], please make sure you are using copysign api correctly.
    warnings.warn(

array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
  /usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py:8002: UserWarning: The shape of broadcast output [3] is different from the input tensor x with shape: [], please make sure you are using copysign api correctly.
    warnings.warn(

array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
  /usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py:8002: UserWarning: The shape of broadcast output [1] is different from the input tensor x with shape: [], please make sure you are using copysign api correctly.
    warnings.warn(

array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
array_api_tests/test_operators_and_elementwise_functions.py::test_copysign
  /usr/local/lib/python3.9/dist-packages/paddle/tensor/math.py:8002: UserWarning: The shape of broadcast output [-1] is different from the input tensor x with shape: [], please make sure you are using copysign api correctly.
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
===================================== short test summary info ======================================
FAILED array_api_tests/test_operators_and_elementwise_functions.py::test_copysign - exceptiongroup.ExceptionGroup: Hypothesis found 2 distinct failures. (2 sub-exceptions)
================================= 1 failed, 21 warnings in 32.94s ==================================

问题1:api-test源码问题

测试时没有指定两个input类型一致

只需要在测试代码中指定类型一致即可

array_api_tests/test_operators_and_elementwise_functions.py

@pytest.mark.min_version("2023.12")
@given(*hh.two_mutual_arrays(dh.real_float_dtypes))
def test_copysign(x1, x2):
    x1 = x1.astype('float64')
    x2 = x2.astype('float64')
    out = xp.copysign(x1, x2)
    ph.assert_dtype("copysign", in_dtype=[x1.dtype, x2.dtype], out_dtype=out.dtype)
    ph.assert_result_shape("copysign", in_shapes=[x1.shape, x2.shape], out_shape=tuple(out.shape))
    binary_assert_against_refimpl("copysign", x1, x2, out, math.copysign)

问题2:需要支持0-size

这个不需要修改kernel,只需在python层修改

python/paddle/tensor/math.py

def copysign(x: Tensor, y: Tensor | float, name: str | None = None) -> Tensor:
    r"""
    Create a new floating-point tensor with the magnitude of input ``x`` and the sign of ``y``, elementwise.

    Equation:
        .. math::

            copysign(x_{i},y_{i})=\left\{\begin{matrix}
            & -|x_{i}| & if \space y_{i} <= -0.0\\
            & |x_{i}| & if \space y_{i} >= 0.0
            \end{matrix}\right.

    Args:
        x (Tensor): The input Tensor, magnitudes, the data type is bool, uint8, int8, int16, int32, int64, bfloat16, float16, float32, float64.
        y (Tensor|float): contains value(s) whose signbit(s) are applied to the magnitudes in input.
        name (str|None, optional): Name for the operation (optional, default is None). For more information, please refer to :ref:`api_guide_Name`.

    Returns:
        out (Tensor), the output tensor. The data type is the same as the input tensor.

    Examples:
        .. code-block:: python
            :name: example1

            >>> import paddle
            >>> x = paddle.to_tensor([1, 2, 3], dtype='float64')
            >>> y = paddle.to_tensor([-1, 1, -1], dtype='float64')
            >>> out = paddle.copysign(x, y)
            >>> print(out)
            Tensor(shape=[3], dtype=float64, place=Place(gpu:0), stop_gradient=True,
                   [-1.,  2., -3.])

        .. code-block:: python
            :name: example2

            >>> import paddle
            >>> x = paddle.to_tensor([1, 2, 3], dtype='float64')
            >>> y = paddle.to_tensor([-2], dtype='float64')
            >>> res = paddle.copysign(x, y)
            >>> print(res)
            Tensor(shape=[3], dtype=float64, place=Place(gpu:0), stop_gradient=True,
                   [-1.,  -2.,  -3.])

        .. code-block:: python
            :name: example_zero1

            >>> import paddle
            >>> x = paddle.to_tensor([1, 2, 3], dtype='float64')
            >>> y = paddle.to_tensor([0.0], dtype='float64')
            >>> out = paddle.copysign(x, y)
            >>> print(out)
            Tensor(shape=[3], dtype=float64, place=Place(gpu:0), stop_gradient=True,
                [1., 2., 3.])

        .. code-block:: python
            :name: example_zero2

            >>> import paddle
            >>> x = paddle.to_tensor([1, 2, 3], dtype='float64')
            >>> y = paddle.to_tensor([-0.0], dtype='float64')
            >>> out = paddle.copysign(x, y)
            >>> print(out)
            Tensor(shape=[3], dtype=float64, place=Place(gpu:0), stop_gradient=True,
                [-1., -2., -3.])
    """
    if isinstance(y, (float, int)):
        y = paddle.to_tensor(y, dtype=x.dtype)
    if x.numel() == 0 or y.numel() == 0:
        out_shape = paddle.broadcast_shape(x.shape, y.shape)
        out_shape = [builtins.max(0, dim) for dim in out_shape]
        return paddle.empty(shape=out_shape, dtype=x.dtype)
    out_shape = broadcast_shape(x.shape, y.shape)
    if out_shape != list(x.shape):
        warnings.warn(
            f"The shape of broadcast output {out_shape} is different from the input tensor x with shape: {x.shape}, please make sure you are using copysign api correctly."
        )

    if in_dynamic_or_pir_mode():
        return _C_ops.copysign(x, y)
    else:
        helper = LayerHelper("copysign", **locals())
        out = helper.create_variable_for_type_inference(dtype=x.dtype)
        helper.append_op(
            type='copysign', inputs={'x': x, 'y': y}, outputs={'out': out}
        )
        return out

增加了对x和y的形状判断,如果其中一个为0-size则返回0-size tensor

编译测试

cd /paddle/build
time cmake .. -DPY_VERSION=3.10 -DWITH_GPU=OFF -DWITH_TESTING=ON
time make -j$(nproc)
cd /paddle/build/python/dist
pip install -U paddlepaddle-0.0.0-cp310-cp310-linux_x86_64.whl --force-reinstall  -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple

添加单测

/test/legacy/test_copysign_op.py

暂时没有想明白静态图怎么在python里实现检测

class TestCopySignDygrapgZeroDimCase(unittest.TestCase):
    def setUp(self):
        self.input_init()
        self.place_init()

    def input_init(self):
        dtype = np.float16
        self.x = np.array(1.23, dtype=dtype)
        self.y = np.empty([0], dtype=dtype)

    def place_init(self):
        self.place = (
            paddle.CUDAPlace(0)
            if paddle.is_compiled_with_cuda()
            else paddle.CPUPlace()
        )

    def test_dygraph_api(self):
        paddle.disable_static()
        x = paddle.to_tensor(self.x)
        y = paddle.to_tensor(self.y)
        out = paddle.copysign(x, y)
        out_ref = ref_copysign(self.x, self.y)
        np.testing.assert_allclose(out_ref, out.numpy())
        out_ref_dtype = out_ref.dtype
        np.testing.assert_equal((out_ref_dtype == out.numpy().dtype), True)
        paddle.enable_static()

pre-commit通过