From 1e2af54cda5023cc98fe5bf108e8fb15a0aec7e8 Mon Sep 17 00:00:00 2001 From: Zheng_Bicheng <58363586+Zheng-Bicheng@users.noreply.github.com> Date: Thu, 29 Sep 2022 14:15:43 +0800 Subject: [PATCH] =?UTF-8?q?[Hackathon=20No.18]=20=E4=B8=BA=20Paddle=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20frexp=20API=20(#46401)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 之前的pr合并了大量错误代码,重新提交一份 * 之前的pr合并了大量错误代码,重新提交一份 * 修正格式问题 * 改回原来的格式 * 按照要求修改 * 按照要求修改格式 * 修复注释的问题 * 更新格式 * 测试自动格式化 * 修正英文注释 * fix docs build error * pre-commit * for docs build * for docs build * 修复mantissa计算错误的bug * 修复误判exponent可能存在负数,导致计算量增加的情况 Co-authored-by: Ligoml <39876205+Ligoml@users.noreply.github.com> --- python/paddle/__init__.py | 3 +- .../fluid/tests/unittests/test_frexp_api.py | 94 +++++++++++++++++++ python/paddle/tensor/__init__.py | 2 + python/paddle/tensor/math.py | 49 ++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 python/paddle/fluid/tests/unittests/test_frexp_api.py diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py index 4e983f43723..b9801428201 100755 --- a/python/paddle/__init__.py +++ b/python/paddle/__init__.py @@ -286,6 +286,7 @@ from .tensor.math import heaviside # noqa: F401 from .tensor.math import frac # noqa: F401 from .tensor.math import sgn # noqa: F401 from .tensor.math import take # noqa: F401 +from .tensor.math import frexp # noqa: F401 from .tensor.random import bernoulli # noqa: F401 from .tensor.random import poisson # noqa: F401 @@ -386,7 +387,6 @@ if is_compiled_with_cinn(): os.environ.setdefault('runtime_include_dir', runtime_include_dir) disable_static() - __all__ = [ # noqa 'iinfo', 'dtype', @@ -667,4 +667,5 @@ __all__ = [ # noqa 'sgn', 'triu_indices', 'take', + 'frexp', ] diff --git a/python/paddle/fluid/tests/unittests/test_frexp_api.py b/python/paddle/fluid/tests/unittests/test_frexp_api.py new file mode 100644 index 00000000000..a7c4fa6e2f7 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/test_frexp_api.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import unittest +import numpy as np +import paddle +import paddle.fluid + + +class TestFrexpAPI(unittest.TestCase): + + def setUp(self): + np.random.seed(1024) + self.rtol = 1e-5 + self.atol = 1e-8 + self.place = paddle.CUDAPlace(0) if paddle.is_compiled_with_cuda() \ + else paddle.CPUPlace() + self.set_input() + + def set_input(self): + self.x_np = np.random.uniform(-3, 3, [10, 12]).astype('float32') + + # 静态图单测 + def test_static_api(self): + # 开启静态图模式 + paddle.enable_static() + with paddle.static.program_guard(paddle.static.Program()): + input_data = paddle.fluid.data('X', self.x_np.shape, + self.x_np.dtype) + out = paddle.frexp(input_data) + # 计算静态图结果 + exe = paddle.static.Executor(self.place) + res = exe.run(feed={'X': self.x_np}, fetch_list=[out]) + + out_ref = np.frexp(self.x_np) + # 对比静态图与 numpy 实现函数计算结果是否相同 + for n, p in zip(out_ref, res): + np.testing.assert_allclose(n, p, rtol=self.rtol, atol=self.atol) + + # 动态图单测 + def test_dygraph_api(self): + # 关闭静态图模式 + paddle.disable_static(self.place) + input_num = paddle.to_tensor(self.x_np) + # 测试动态图 tensor.frexp 和 paddle.tensor.math.frexp 计算结果 + out1 = np.frexp(self.x_np) + out2 = paddle.frexp(input_num) + np.testing.assert_allclose(out1, out2, rtol=1e-05) + + out1 = np.frexp(self.x_np) + out2 = input_num.frexp() + np.testing.assert_allclose(out1, out2, rtol=1e-05) + paddle.enable_static() + + +class TestSplitsFloat32Case1(TestFrexpAPI): + """ + Test num_or_sections which is an integer and data type is float32. + """ + + def set_input(self): + self.x_np = np.random.uniform(-1, 1, [4, 5, 2]).astype('float32') + + +class TestSplitsFloat64Case1(TestFrexpAPI): + """ + Test num_or_sections which is an integer and data type is float64. + """ + + def set_input(self): + self.x_np = np.random.uniform(-3, 3, [10, 12]).astype('float64') + + +class TestSplitsFloat64Case2(TestFrexpAPI): + """ + Test num_or_sections which is an integer and data type is float64. + """ + + def set_input(self): + self.x_np = np.random.uniform(-1, 1, [4, 5, 2]).astype('float64') + + +if __name__ == "__main__": + unittest.main() diff --git a/python/paddle/tensor/__init__.py b/python/paddle/tensor/__init__.py index ba7dd5d0cec..f08d339c049 100755 --- a/python/paddle/tensor/__init__.py +++ b/python/paddle/tensor/__init__.py @@ -239,6 +239,7 @@ from .math import heaviside # noqa: F401 from .math import frac # noqa: F401 from .math import sgn # noqa: F401 from .math import take # noqa: F401 +from .math import frexp # noqa: F401 from .random import multinomial # noqa: F401 from .random import standard_normal # noqa: F401 @@ -517,6 +518,7 @@ tensor_method_func = [ # noqa 'take', 'bucketize', 'sgn', + 'frexp', ] # this list used in math_op_patch.py for magic_method bind diff --git a/python/paddle/tensor/math.py b/python/paddle/tensor/math.py index f472ded257f..b046404d83a 100644 --- a/python/paddle/tensor/math.py +++ b/python/paddle/tensor/math.py @@ -5108,3 +5108,52 @@ def take(x, index, mode='raise', name=None): out = input_1d.index_select(index_1d).reshape(index.shape) return out + + +def frexp(x, name=None): + """ + The function used to decompose a floating point number into mantissa and exponent. + + Args: + x (Tensor): The input tensor, it's data type should be float32, float64. + name (str, optional): For details, please refer to :ref:`api_guide_Name`. Generally, no setting is required. Default: None. + Returns: + + - mantissa (Tensor), A mantissa Tensor. The shape and data type of mantissa tensor and exponential tensor are + the same as those of input. + + - exponent (Tensor), A exponent Tensor. The shape and data type of mantissa tensor and exponential tensor are + the same as those of input. + + Examples: + .. code-block:: python + + import paddle + + x = paddle.to_tensor([[1, 2, 3, 4]], dtype="float32") + print(paddle.tensor.math.frexp(x)) + # (Tensor(shape=[1, 4], dtype=float32, place=Place(cpu), stop_gradient=True,[[0.50000000, 0.50000000, 0.75000000, 0.50000000]]), + # Tensor(shape=[1, 4], dtype=float32, place=Place(cpu), stop_gradient=True,[[1., 2., 2., 3.]])) + """ + if x.dtype not in [paddle.float32, paddle.float64]: + raise TypeError( + "The data type of input must be one of ['float32', 'float64'], but got {}" + .format(x.dtype)) + input_x = paddle.abs(x) + exponent = paddle.floor(paddle.log2(input_x)) + exponent = paddle.where(paddle.isinf(exponent), + paddle.full_like(exponent, 0), exponent) + + # 0填充 + mantissa = paddle.divide(input_x, 2**exponent) + # 计算exponent + exponent = paddle.where((mantissa >= 1), + paddle.add(exponent, paddle.ones_like(exponent)), + exponent) + mantissa = paddle.where((mantissa >= 1), + paddle.divide(mantissa, + 2**paddle.ones_like(exponent)), + mantissa) + + mantissa = paddle.where((x < 0), mantissa * -1, mantissa) + return mantissa, exponent -- GitLab