diff --git a/paddle/phi/kernels/funcs/elementwise_functor.h b/paddle/phi/kernels/funcs/elementwise_functor.h index bf119e27309f5b..e44b8e4eed218b 100644 --- a/paddle/phi/kernels/funcs/elementwise_functor.h +++ b/paddle/phi/kernels/funcs/elementwise_functor.h @@ -22,7 +22,7 @@ limitations under the License. */ #include "paddle/phi/core/enforce.h" #if defined(__xpu__) #include - +#include #include "xpu/kernel/math_xpu2.h" // pow() #endif #include "paddle/phi/common/amp_type_traits.h" @@ -467,9 +467,40 @@ struct MultiplyGradXYFunctor, ComplexType> { }; // Maximum -template +template struct MaximumFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { + if constexpr ((std::is_floating_point_v)&&( + !(std::is_same_v || + std::is_same_v))) { +#if defined(__CUDACC__) || defined(__HIPCC__) + if (::isnan(a)) { + return a; + } + if (::isnan(b)) { + return b; + } +#else + if (std::isnan(a)) { + return a; + } + if (std::isnan(b)) { + return b; + } +#endif + } + return a > b ? a : b; + } +}; + +template +struct MaximumFunctor< + T, + typename std::enable_if || + std::is_same_v>::type> { + inline HOSTDEVICE T operator()(const T a, const T b) const { + if (phi::dtype::isnan(a)) return a; + if (phi::dtype::isnan(b)) return b; return a > b ? a : b; } }; @@ -509,12 +540,44 @@ struct MaxGradXYFunctor { }; // Minimum -template +template struct MinimumFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { + if constexpr (std::is_floating_point_v && + (!(std::is_same_v || + std::is_same_v))) { +#if defined(__CUDACC__) || defined(__HIPCC__) + if (::isnan(a)) { + return a; + } + if (::isnan(b)) { + return b; + } +#else + if (std::isnan(a)) { + return a; + } + if (std::isnan(b)) { + return b; + } +#endif + } return a < b ? a : b; } }; + +template +struct MinimumFunctor< + T, + typename std::enable_if || + std::is_same_v>::type> { + inline HOSTDEVICE T operator()(const T a, const T b) const { + if (phi::dtype::isnan(a)) return a; + if (phi::dtype::isnan(b)) return b; + return a < b ? a : b; + } +}; + template struct MinGradXFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { diff --git a/test/legacy_test/test_maximum_op.py b/test/legacy_test/test_maximum_op.py index 1388d720e1b35b..6fa1e356eedba6 100644 --- a/test/legacy_test/test_maximum_op.py +++ b/test/legacy_test/test_maximum_op.py @@ -15,6 +15,7 @@ import unittest import numpy as np +from utils import dygraph_guard, static_guard import paddle from paddle.base import core @@ -34,10 +35,22 @@ def setUp(self): self.input_b = np.array([2, np.inf, -np.inf]).astype('int64') self.input_c = np.array([4, 1, 3]).astype('int64') + self.input_nan_a = np.array([0, np.nan, np.nan]).astype('float32') + self.input_nan_b = np.array([0, 1, 2]).astype('float32') + self.np_expected1 = np.maximum(self.input_x, self.input_y) self.np_expected2 = np.maximum(self.input_x, self.input_z) self.np_expected3 = np.maximum(self.input_a, self.input_c) self.np_expected4 = np.maximum(self.input_b, self.input_c) + self.np_expected_nan_aa = np.maximum( + self.input_nan_a, self.input_nan_a + ) # maximum(Nan, Nan) + self.np_expected_nan_ab = np.maximum( + self.input_nan_a, self.input_nan_b + ) # maximum(Nan, Num) + self.np_expected_nan_ba = np.maximum( + self.input_nan_b, self.input_nan_a + ) # maximum(Num, Nan) def test_static_api(self): paddle.enable_static() @@ -164,6 +177,83 @@ def test_equal_tensors(self): 1e-2, ) + @unittest.skipIf( + core.is_compiled_with_xpu(), + "XPU need fix the bug", + ) + def test_dynamic_nan(self): + with dygraph_guard(): + nan_a = paddle.to_tensor(self.input_nan_a) + nan_b = paddle.to_tensor(self.input_nan_b) + res = paddle.maximum(nan_a, nan_a) + res = res.numpy() + np.testing.assert_allclose( + res, self.np_expected_nan_aa, rtol=1e-05, equal_nan=True + ) + + res = paddle.maximum(nan_a, nan_b) + res = res.numpy() + np.testing.assert_allclose( + res, self.np_expected_nan_ab, rtol=1e-05, equal_nan=True + ) + + res = paddle.maximum(nan_b, nan_a) + res = res.numpy() + np.testing.assert_allclose( + res, self.np_expected_nan_ba, rtol=1e-05, equal_nan=True + ) + + @unittest.skipIf( + core.is_compiled_with_xpu(), + "XPU need fix the bug", + ) + def test_static_nan(self): + with static_guard(): + with paddle.static.program_guard( + paddle.static.Program(), paddle.static.Program() + ): + data_a = paddle.static.data("a", shape=[3], dtype="float32") + data_b = paddle.static.data("b", shape=[3], dtype="float32") + result_max = paddle.maximum(data_a, data_b) + exe = paddle.static.Executor(self.place) + (res,) = exe.run( + feed={"a": self.input_nan_a, "b": self.input_nan_a}, + fetch_list=[result_max], + ) + np.testing.assert_allclose( + res, self.np_expected_nan_aa, rtol=1e-05, equal_nan=True + ) + + with paddle.static.program_guard( + paddle.static.Program(), paddle.static.Program() + ): + data_a = paddle.static.data("a", shape=[3], dtype="float32") + data_b = paddle.static.data("b", shape=[3], dtype="float32") + result_max = paddle.maximum(data_a, data_b) + exe = paddle.static.Executor(self.place) + (res,) = exe.run( + feed={"a": self.input_nan_a, "b": self.input_nan_b}, + fetch_list=[result_max], + ) + np.testing.assert_allclose( + res, self.np_expected_nan_ab, rtol=1e-05, equal_nan=True + ) + + with paddle.static.program_guard( + paddle.static.Program(), paddle.static.Program() + ): + data_a = paddle.static.data("a", shape=[3], dtype="float32") + data_b = paddle.static.data("b", shape=[3], dtype="float32") + result_max = paddle.maximum(data_a, data_b) + exe = paddle.static.Executor(self.place) + (res,) = exe.run( + feed={"a": self.input_nan_b, "b": self.input_nan_a}, + fetch_list=[result_max], + ) + np.testing.assert_allclose( + res, self.np_expected_nan_ba, rtol=1e-05, equal_nan=True + ) + def test_0size_input(self): numpy_tensor = np.ones([0, 1, 2]).astype("float32") paddle_x = paddle.to_tensor(numpy_tensor) diff --git a/test/legacy_test/test_minimum_op.py b/test/legacy_test/test_minimum_op.py index c69e3ac29f2d68..f5847a8898e72a 100644 --- a/test/legacy_test/test_minimum_op.py +++ b/test/legacy_test/test_minimum_op.py @@ -15,6 +15,7 @@ import unittest import numpy as np +from utils import dygraph_guard, static_guard import paddle from paddle.base import core @@ -34,10 +35,22 @@ def setUp(self): self.input_b = np.array([2, np.inf, -np.inf]).astype('int64') self.input_c = np.array([4, 1, 3]).astype('int64') + self.input_nan_a = np.array([0, np.nan, np.nan]).astype('float32') + self.input_nan_b = np.array([0, 1, 2]).astype('float32') + self.np_expected1 = np.minimum(self.input_x, self.input_y) self.np_expected2 = np.minimum(self.input_x, self.input_z) self.np_expected3 = np.minimum(self.input_a, self.input_c) self.np_expected4 = np.minimum(self.input_b, self.input_c) + self.np_expected_nan_aa = np.minimum( + self.input_nan_a, self.input_nan_a + ) # minimum(Nan, Nan) + self.np_expected_nan_ab = np.minimum( + self.input_nan_a, self.input_nan_b + ) # minimum(Nan, Num) + self.np_expected_nan_ba = np.minimum( + self.input_nan_b, self.input_nan_a + ) # minimum(Num, Nan) def test_static_api(self): paddle.enable_static() @@ -164,6 +177,84 @@ def test_equal_tensors(self): 1e-2, ) + @unittest.skipIf( + core.is_compiled_with_xpu(), + "XPU need fix the bug", + ) + def test_dynamic_nan(self): + with dygraph_guard(): + nan_a = paddle.to_tensor(self.input_nan_a) + nan_b = paddle.to_tensor(self.input_nan_b) + + res = paddle.minimum(nan_a, nan_a) + res = res.numpy() + np.testing.assert_allclose( + res, self.np_expected_nan_aa, rtol=1e-05, equal_nan=True + ) + + res = paddle.minimum(nan_a, nan_b) + res = res.numpy() + np.testing.assert_allclose( + res, self.np_expected_nan_ab, rtol=1e-05, equal_nan=True + ) + + res = paddle.minimum(nan_b, nan_a) + res = res.numpy() + np.testing.assert_allclose( + res, self.np_expected_nan_ba, rtol=1e-05, equal_nan=True + ) + + @unittest.skipIf( + core.is_compiled_with_xpu(), + "XPU need fix the bug", + ) + def test_static_nan(self): + with static_guard(): + with paddle.static.program_guard( + paddle.static.Program(), paddle.static.Program() + ): + data_a = paddle.static.data("a", shape=[3], dtype="float32") + data_b = paddle.static.data("b", shape=[3], dtype="float32") + result_max = paddle.minimum(data_a, data_b) + exe = paddle.static.Executor(self.place) + (res,) = exe.run( + feed={"a": self.input_nan_a, "b": self.input_nan_a}, + fetch_list=[result_max], + ) + np.testing.assert_allclose( + res, self.np_expected_nan_aa, rtol=1e-05, equal_nan=True + ) + + with paddle.static.program_guard( + paddle.static.Program(), paddle.static.Program() + ): + data_a = paddle.static.data("a", shape=[3], dtype="float32") + data_b = paddle.static.data("b", shape=[3], dtype="float32") + result_max = paddle.minimum(data_a, data_b) + exe = paddle.static.Executor(self.place) + (res,) = exe.run( + feed={"a": self.input_nan_a, "b": self.input_nan_b}, + fetch_list=[result_max], + ) + np.testing.assert_allclose( + res, self.np_expected_nan_ab, rtol=1e-05, equal_nan=True + ) + + with paddle.static.program_guard( + paddle.static.Program(), paddle.static.Program() + ): + data_a = paddle.static.data("a", shape=[3], dtype="float32") + data_b = paddle.static.data("b", shape=[3], dtype="float32") + result_max = paddle.minimum(data_a, data_b) + exe = paddle.static.Executor(self.place) + (res,) = exe.run( + feed={"a": self.input_nan_b, "b": self.input_nan_a}, + fetch_list=[result_max], + ) + np.testing.assert_allclose( + res, self.np_expected_nan_ba, rtol=1e-05, equal_nan=True + ) + def test_0size_input(self): numpy_tensor = np.ones([0, 1, 2]).astype("float32") paddle_x = paddle.to_tensor(numpy_tensor)