/* Copyright (c) 2021 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. */ #pragma once #include "paddle/common/hostdevice.h" #include "paddle/common/macros.h" #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" #include "paddle/phi/common/type_safe_sign_math.h" #include "paddle/phi/kernels/funcs/sleef_vectorized_math.h" #ifdef PADDLE_WITH_SLEEF #include #if defined(__AVX512F__) #include #endif #endif namespace phi { namespace funcs { // Define the binary functors used in elementwise ops. // Note: InverseXxxFunctor is needed when calling ElementwiseComputeEx on CPU. // Add template struct AddFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return a + b; } }; template using InverseAddFunctor = AddFunctor; template struct MultiPrecisionAddFunctor { inline HOSTDEVICE T operator()(const T x, const Ty y) const { return x + static_cast(y); } }; // Float32Bfloat16Add template struct Float32Bfloat16AddFunctor { inline HOSTDEVICE T operator()(const T x, const phi::bfloat16 y) { return x + static_cast(y); } }; // Float32Float16Add template struct Float32Float16AddFunctor { inline HOSTDEVICE T operator()(const T x, const phi::float16 y) { return x + static_cast(y); } }; // Subtract template struct SubtractFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return a - b; } }; template struct InverseSubtractFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return b - a; } }; // Multiply template struct MultiplyFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return a * b; } }; template <> struct MultiplyFunctor { inline HOSTDEVICE bool operator()(const bool a, const bool b) const { return a && b; } }; template using InverseMultiplyFunctor = MultiplyFunctor; template struct IsZeroFunctor { HOSTDEVICE bool operator()(T x) const { return x == static_cast(0); } }; // Divide #define DIV_ERROR_INFO \ "InvalidArgumentError: Integer division by zero encountered in " \ "(floor/trunc) divide. Please check the input value." template struct DivideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return a / b; } }; template struct DivideFunctor< T, typename std::enable_if::value>::type> { inline HOSTDEVICE T operator()(const T a, const T b) const { // For int32/int64, need to check whether the division is zero. PADDLE_ENFORCE(b != 0, DIV_ERROR_INFO); return a / b; } }; template struct InverseDivideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return b / a; } }; template using ComplexType = phi::dtype::complex; // Reference: https://github.com/pytorch/pytorch/pull/92539 template struct DivideFunctor> { inline HOSTDEVICE ComplexType operator()(const ComplexType x, const ComplexType y) const { T a = x.real; T b = x.imag; T c = y.real; T d = y.imag; // (a + bi) / (c + di) = (ac + bd)/(c^2 + d^2) + (bc - ad)/(c^2 + d^2) i // the calculation below follows numpy's complex division #if defined(__GNUC__) && !defined(__clang__) // std::abs is already constexpr by gcc auto abs_c = std::abs(c); auto abs_d = std::abs(d); #else auto abs_c = c < 0 ? -c : c; auto abs_d = d < 0 ? -d : d; #endif T real_, imag_; auto rat = (abs_c >= abs_d) ? (d / c) : (c / d); auto scl = (abs_c >= abs_d) ? (T(1.0) / (c + d * rat)) : (T(1.0) / (d + c * rat)); if (abs_c >= abs_d) { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(b, rat, a) * scl; imag_ = std::fmaf(-a, rat, b) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(b, rat, a) * scl; imag_ = std::fma(-a, rat, b) * scl; } else { real_ = (a + b * rat) * scl; imag_ = (b - a * rat) * scl; } #else real_ = (a + b * rat) * scl; imag_ = (b - a * rat) * scl; #endif } else { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(a, rat, b) * scl; imag_ = std::fmaf(b, rat, -a) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(a, rat, b) * scl; imag_ = std::fma(b, rat, -a) * scl; } else { real_ = (a * rat + b) * scl; imag_ = (b * rat - a) * scl; } #else real_ = (a * rat + b) * scl; imag_ = (b * rat - a) * scl; #endif } return ComplexType(real_, imag_); } }; template struct InverseDivideFunctor> { inline HOSTDEVICE ComplexType operator()(const ComplexType x, const ComplexType y) const { T a = y.real; T b = y.imag; T c = x.real; T d = x.imag; // (a + bi) / (c + di) = (ac + bd)/(c^2 + d^2) + (bc - ad)/(c^2 + d^2) i // the calculation below follows numpy's complex division #if defined(__GNUC__) && !defined(__clang__) // std::abs is already constexpr by gcc auto abs_c = std::abs(c); auto abs_d = std::abs(d); #else auto abs_c = c < 0 ? -c : c; auto abs_d = d < 0 ? -d : d; #endif T real_, imag_; auto rat = (abs_c >= abs_d) ? (d / c) : (c / d); auto scl = (abs_c >= abs_d) ? (T(1.0) / (c + d * rat)) : (T(1.0) / (d + c * rat)); if (abs_c >= abs_d) { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(b, rat, a) * scl; imag_ = std::fmaf(-a, rat, b) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(b, rat, a) * scl; imag_ = std::fma(-a, rat, b) * scl; } else { real_ = (a + b * rat) * scl; imag_ = (b - a * rat) * scl; } #else real_ = (a + b * rat) * scl; imag_ = (b - a * rat) * scl; #endif } else { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(a, rat, b) * scl; imag_ = std::fmaf(b, rat, -a) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(a, rat, b) * scl; imag_ = std::fma(b, rat, -a) * scl; } else { real_ = (a * rat + b) * scl; imag_ = (b * rat - a) * scl; } #else real_ = (a * rat + b) * scl; imag_ = (b * rat - a) * scl; #endif } return ComplexType(real_, imag_); } }; template struct DivGradXYFunctor { inline HOSTDEVICE phi::Array operator()(const InT a, const InT b, const InT c) { // dx = dout / y // dy = - dout * out / y phi::Array outs; outs[0] = a / c; outs[1] = -a * ((b / c) / c); return outs; } }; template struct DivGradXYFunctor, ComplexType> { inline HOSTDEVICE phi::Array, 2> operator()( const ComplexType a, const ComplexType b, const ComplexType c) { phi::Array, 2> outs; ComplexType c_conj(c.real, -c.imag); ComplexType out_div_c_conj(((b / c) / c).real, -((b / c) / c).imag); outs[0] = a / c_conj; outs[1] = -a * out_div_c_conj; return outs; } }; // Float div grad template struct DivGradXFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return a / b; } }; // ComplexType div grad template struct DivGradXFunctor> { inline HOSTDEVICE ComplexType operator()(const ComplexType a, const ComplexType b) const { ComplexType b_conj(b.real, -b.imag); return a / b_conj; } }; // Float mul and div template struct DivGradYFunctor { inline HOSTDEVICE T operator()(const T a, const T b, const T c) const { return -a * ((b / c) / c); } }; // ComplexType mul and div template struct DivGradYFunctor> { inline HOSTDEVICE ComplexType operator()(const ComplexType a, const ComplexType b, const ComplexType c) const { ComplexType out_div_c_conj(((b / c) / c).real, -((b / c) / c).imag); return -a * out_div_c_conj; } }; // Floor divide template struct FloorDivideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { #ifndef PADDLE_WITH_XPU_KP PADDLE_ENFORCE(b != 0, DIV_ERROR_INFO); #endif if (phi::is_negative(a) != phi::is_negative(b)) { // Subtracts one from the results of truncation division if the // divisor and dividend have different sign(bit)s and the remainder of // the division is nonzero const auto quot = a / b; const auto rem = a % b; auto ret = rem ? quot - 1 : quot; return static_cast(ret); } return static_cast(a / b); } }; template struct FloorDivideFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T a, const T b) const { if (UNLIKELY(b == 0)) { // Divide by zero: return standard IEEE result return static_cast(a / b); } auto mod = std::fmod(a, b); auto div = (a - mod) / b; if ((mod != 0) && (b < 0) != (mod < 0)) { div -= T(1); } T floordiv; if (div != 0) { floordiv = std::floor(div); if (div - floordiv > T(0.5)) { floordiv += T(1.0); } } else { floordiv = phi::copysign(T(0), a / b); } return floordiv; } }; template <> struct FloorDivideFunctor { inline HOSTDEVICE dtype::float16 operator()(const dtype::float16 a, const dtype::float16 b) const { float b_float = static_cast(b); float a_float = static_cast(a); if (UNLIKELY(b_float == 0)) { // Divide by zero: return standard IEEE result return static_cast(a_float / b_float); } auto mod = std::fmod(a_float, b_float); auto div = (a_float - mod) / b_float; if ((mod != 0) && (b_float < 0) != (mod < 0)) { div -= static_cast(1); } float floordiv; if (div != 0) { floordiv = std::floor(div); if (div - floordiv > static_cast(0.5)) { floordiv += static_cast(1.0); } } else { floordiv = phi::copysign(static_cast(0), a_float / b_float); } return static_cast(floordiv); } }; template <> struct FloorDivideFunctor { inline HOSTDEVICE dtype::bfloat16 operator()(const dtype::bfloat16 a, const dtype::bfloat16 b) const { float b_float = static_cast(b); float a_float = static_cast(a); if (UNLIKELY(b_float == 0)) { // Divide by zero: return standard IEEE result return static_cast(a_float / b_float); } auto mod = std::fmod(a_float, b_float); auto div = (a_float - mod) / b_float; if ((mod != 0) && (b_float < 0) != (mod < 0)) { div -= static_cast(1); } float floordiv; if (div != 0) { floordiv = std::floor(div); if (div - floordiv > static_cast(0.5)) { floordiv += static_cast(1.0); } } else { floordiv = phi::copysign(static_cast(0), a_float / b_float); } return static_cast(floordiv); } }; template struct InverseFloorDivideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { #ifndef PADDLE_WITH_XPU_KP PADDLE_ENFORCE(a != 0, DIV_ERROR_INFO); #endif if (phi::is_negative(a) != phi::is_negative(b)) { // Subtracts one from the results of truncation division if the // divisor and dividend have different sign(bit)s and the remainder of // the division is nonzero const auto quot = b / a; const auto rem = b % a; auto ret = rem ? quot - 1 : quot; return static_cast(ret); } return static_cast(b / a); } }; template struct InverseFloorDivideFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T a, const T b) const { if (UNLIKELY(a == 0)) { // Divide by zero: return standard IEEE result return static_cast(b / a); } auto mod = std::fmod(b, a); auto div = (b - mod) / a; if ((mod != 0) && (a < 0) != (mod < 0)) { div -= T(1); } T floordiv; if (div != 0) { floordiv = std::floor(div); if (div - floordiv > T(0.5)) { floordiv += T(1.0); } } else { floordiv = phi::copysign(T(0), b / a); } return floordiv; } }; template <> struct InverseFloorDivideFunctor { inline HOSTDEVICE dtype::float16 operator()(const dtype::float16 a, const dtype::float16 b) const { float b_float = static_cast(a); float a_float = static_cast(b); if (UNLIKELY(b_float == 0)) { // Divide by zero: return standard IEEE result return static_cast(a_float / b_float); } auto mod = std::fmod(a_float, b_float); auto div = (a_float - mod) / b_float; if ((mod != 0) && (b_float < 0) != (mod < 0)) { div -= static_cast(1); } float floordiv; if (div != 0) { floordiv = std::floor(div); if (div - floordiv > static_cast(0.5)) { floordiv += static_cast(1.0); } } else { floordiv = phi::copysign(static_cast(0), a_float / b_float); } return static_cast(floordiv); } }; template <> struct InverseFloorDivideFunctor { inline HOSTDEVICE dtype::bfloat16 operator()(const dtype::bfloat16 a, const dtype::bfloat16 b) const { float b_float = static_cast(a); float a_float = static_cast(b); if (UNLIKELY(b_float == 0)) { // Divide by zero: return standard IEEE result return static_cast(a_float / b_float); } auto mod = std::fmod(a_float, b_float); auto div = (a_float - mod) / b_float; if ((mod != 0) && (b_float < 0) != (mod < 0)) { div -= static_cast(1); } float floordiv; if (div != 0) { floordiv = std::floor(div); if (div - floordiv > static_cast(0.5)) { floordiv += static_cast(1.0); } } else { floordiv = phi::copysign(static_cast(0), a_float / b_float); } return static_cast(floordiv); } }; // Fmin template struct FMinFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return std::fmin(a, b); } }; template <> struct FMinFunctor { inline HOSTDEVICE dtype::float16 operator()(const dtype::float16 a, const dtype::float16 b) const { float float_a = static_cast(a); float float_b = static_cast(b); auto result = std::fmin(float_a, float_b); return static_cast(result); } }; template <> struct FMinFunctor { inline HOSTDEVICE dtype::bfloat16 operator()(const dtype::bfloat16 a, const dtype::bfloat16 b) const { float float_a = static_cast(a); float float_b = static_cast(b); auto result = std::fmin(float_a, float_b); return static_cast(result); } }; template <> struct FMinFunctor { inline HOSTDEVICE int operator()(const int a, const int b) const { float float_a = static_cast(a); float float_b = static_cast(b); auto result = std::fmin(float_a, float_b); return std::lrint(result); } }; template <> struct FMinFunctor { inline HOSTDEVICE int64_t operator()(const int64_t a, const int64_t b) const { double double_a = static_cast(a); double double_b = static_cast(b); auto result = std::fmin(double_a, double_b); return std::llrint(result); } }; // Fmax template struct FMaxFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return std::fmax(a, b); } }; template <> struct FMaxFunctor { inline HOSTDEVICE dtype::float16 operator()(const dtype::float16 a, const dtype::float16 b) const { float float_a = static_cast(a); float float_b = static_cast(b); auto result = std::fmax(float_a, float_b); return static_cast(result); } }; template <> struct FMaxFunctor { inline HOSTDEVICE dtype::bfloat16 operator()(const dtype::bfloat16 a, const dtype::bfloat16 b) const { float float_a = static_cast(a); float float_b = static_cast(b); auto result = std::fmax(float_a, float_b); return static_cast(result); } }; template <> struct FMaxFunctor { inline HOSTDEVICE int operator()(const int a, const int b) const { float float_a = static_cast(a); float float_b = static_cast(b); auto result = std::fmax(float_a, float_b); return std::lrint(result); } }; template <> struct FMaxFunctor { inline HOSTDEVICE int64_t operator()(const int64_t a, const int64_t b) const { double double_a = static_cast(a); double double_b = static_cast(b); auto result = std::fmax(double_a, double_b); return std::llrint(result); } }; template struct FMaxGradDx { HOSTDEVICE T operator()(T x, T y, T out UNUSED, T dout) const { return dout * static_cast((x >= y) || isnan(y)); } }; template <> struct FMaxGradDx { HOSTDEVICE dtype::float16 operator()(dtype::float16 x, dtype::float16 y, dtype::float16 out UNUSED, dtype::float16 dout) const { return dout * static_cast((x >= y) || dtype::isnan(y)); } }; template <> struct FMaxGradDx { HOSTDEVICE int operator()(int x, int y, int out UNUSED, int dout) const { return dout * static_cast((x >= y)); } }; template <> struct FMaxGradDx { HOSTDEVICE int64_t operator()(int64_t x, int64_t y, int64_t out UNUSED, int64_t dout) const { return dout * static_cast((x >= y)); } }; template struct FMaxGradDy { HOSTDEVICE T operator()(T x, T y, T out UNUSED, T dout) const { return dout * static_cast(!((x >= y) || isnan(y))); } }; template <> struct FMaxGradDy { HOSTDEVICE dtype::float16 operator()(dtype::float16 x, dtype::float16 y, dtype::float16 out UNUSED, dtype::float16 dout) const { return dout * static_cast(!((x >= y) || dtype::isnan(y))); } }; template <> struct FMaxGradDy { HOSTDEVICE int64_t operator()(int64_t x, int64_t y, int64_t out UNUSED, int64_t dout) const { return dout * static_cast(!((x >= y))); } }; template <> struct FMaxGradDy { HOSTDEVICE int operator()(int x, int y, int out UNUSED, int dout) const { return dout * static_cast(!((x >= y))); } }; template struct FMinGradDx { HOSTDEVICE T operator()(T x, T y, T out UNUSED, T dout) const { return dout * static_cast((x <= y) || isnan(y)); } }; template <> struct FMinGradDx { HOSTDEVICE dtype::float16 operator()(dtype::float16 x, dtype::float16 y, dtype::float16 out UNUSED, dtype::float16 dout) const { return dout * static_cast((x <= y) || dtype::isnan(y)); } }; template <> struct FMinGradDx { HOSTDEVICE int operator()(int x, int y, int out UNUSED, int dout) const { return dout * static_cast((x <= y)); } }; template <> struct FMinGradDx { HOSTDEVICE int64_t operator()(int64_t x, int64_t y, int64_t out UNUSED, int64_t dout) const { return dout * static_cast((x <= y)); } }; template struct FMinGradDy { HOSTDEVICE T operator()(T x, T y, T out UNUSED, T dout) const { return dout * static_cast(!((x <= y) || isnan(y))); } }; template <> struct FMinGradDy { HOSTDEVICE dtype::float16 operator()(dtype::float16 x, dtype::float16 y, dtype::float16 out UNUSED, dtype::float16 dout) const { return dout * static_cast(!((x <= y) || dtype::isnan(y))); } }; template <> struct FMinGradDy { HOSTDEVICE int operator()(int x, int y, int out UNUSED, int dout) const { return dout * static_cast(!((x <= y))); } }; template <> struct FMinGradDy { HOSTDEVICE int64_t operator()(int64_t x, int64_t y, int64_t out UNUSED, int64_t dout) const { return dout * static_cast(!((x <= y))); } }; template struct MultiplyGradFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return a * b; } }; template struct MultiplyGradFunctor> { inline HOSTDEVICE ComplexType operator()(const ComplexType a, const ComplexType b) const { ComplexType b_conj(b.real, -b.imag); return a * b_conj; } }; template struct MultiplyGradXYFunctor { inline HOSTDEVICE phi::Array operator()(const InT a, const InT b, const InT c) { phi::Array outs; // dx = dout * y outs[0] = a * b; // dy = dout * x outs[1] = a * c; return outs; } }; template struct MultiplyGradXYFunctor, ComplexType> { inline HOSTDEVICE phi::Array, 2> operator()( const ComplexType a, const ComplexType b, const ComplexType c) { phi::Array, 2> outs; // dx = dout * y ComplexType b_conj(b.real, -b.imag); outs[0] = a * b_conj; // dy = dout * x ComplexType c_conj(c.real, -c.imag); outs[1] = a * c_conj; return outs; } }; // Maximum 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; } }; template struct MaxGradXFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { return dout * static_cast(x > y) + (dout / static_cast(2)) * static_cast(x == y); } }; template struct MaxGradYFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { return dout * static_cast(x < y) + (dout / static_cast(2)) * static_cast(x == y); } }; template struct MaxGradXYFunctor { inline HOSTDEVICE phi::Array operator()(const InT x, const InT y, const InT dout) { phi::Array outs; // dx = dout * (x > y) + dout / 2 * (x == y) outs[0] = static_cast(dout * static_cast(x > y) + (dout / static_cast(2)) * static_cast(x == y)); // dy = dout * (x < y) + dout / 2 * (x == y) outs[1] = static_cast(dout * static_cast(x < y) + (dout / static_cast(2)) * static_cast(x == y)); return outs; } }; // Minimum 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 { return dout * static_cast(x < y) + (dout / static_cast(2)) * static_cast(x == y); } }; template struct MinGradYFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { return dout * static_cast(x > y) + (dout / static_cast(2)) * static_cast(x == y); } }; template struct MinGradXYFunctor { inline HOSTDEVICE phi::Array operator()(const InT x, const InT y, const InT dout) { phi::Array outs; // dx = dout * (x < y) + dout / 2 * (x == y) outs[0] = static_cast(dout * static_cast(x < y) + (dout / static_cast(2)) * static_cast(x == y)); // dy = dout * (x > y) + dout / 2 * (x == y) outs[1] = static_cast(dout * static_cast(x > y) + (dout / static_cast(2)) * static_cast(x == y)); return outs; } }; // Modulo template struct RemainderFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { PADDLE_ENFORCE(b != 0, DIV_ERROR_INFO); T res = a % b; // According to #PR26732: in dividend % divisor // remainder shall have the same sign as divisor. if ((res != 0) && ((b ^ res) < 0)) res += b; return res; } }; template struct RemainderFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T a, const T b) const { T res = fmod(a, b); // According to #PR26732: in dividend % divisor // remainder shall have the same sign as divisor. if ((res != 0) && ((res < 0) != (b < 0))) res += b; return res; } }; template <> struct RemainderFunctor { inline HOSTDEVICE dtype::float16 operator()(const dtype::float16 a, const dtype::float16 b) const { float b_float = static_cast(b); float res = fmod(static_cast(a), b_float); // According to #PR26732: in dividend % divisor // remainder shall have the same sign as divisor. if ((res != 0.0f) && ((res < 0.0f) != (b_float < 0.0f))) res += b_float; return static_cast(res); } }; template <> struct RemainderFunctor { inline HOSTDEVICE dtype::bfloat16 operator()(const dtype::bfloat16 a, const dtype::bfloat16 b) const { float b_float = static_cast(b); float res = fmod(static_cast(a), b_float); // According to #PR26732: in dividend % divisor // remainder shall have the same sign as divisor. if ((res != 0.0f) && ((res < 0.0f) != (b_float < 0.0f))) res += b_float; return static_cast(res); } }; /** * Remainder for complex number rule * Regarding a and b is gaussian integer, then * r = mod(a, b) = a - b * round(a/b) * and a, b is complex number */ template struct RemainderFunctor> { inline HOSTDEVICE ComplexType operator()(ComplexType a, ComplexType b) const { // remainder = z1 - q_rounded * z2 T a__ = a.real; T b__ = a.imag; T c__ = b.real; T d__ = b.imag; // (a + bi) / (c + di) = (ac + bd)/(c^2 + d^2) + (bc - ad)/(c^2 + d^2) i // the calculation below follows numpy's complex division #if defined(__GNUC___) && !defined(__clang__) // std::abs is already constexpr by gcc auto abs_c = std::abs(c__); auto abs_d = std::abs(d__); #else auto abs_c = c__ < 0 ? -c__ : c__; auto abs_d = d__ < 0 ? -d__ : d__; #endif T real_, imag_; auto rat = (abs_c >= abs_d) ? (d__ / c__) : (c__ / d__); auto scl = (abs_c >= abs_d) ? (T(1.0) / (c__ + d__ * rat)) : (T(1.0) / (d__ + c__ * rat)); if (abs_c >= abs_d) { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(b__, rat, a__) * scl; imag_ = std::fmaf(-a__, rat, b__) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(b__, rat, a__) * scl; imag_ = std::fma(-a__, rat, b__) * scl; } else { real_ = (a__ + b__ * rat) * scl; imag_ = (b__ - a__ * rat) * scl; } #else real_ = (a__ + b__ * rat) * scl; imag_ = (b__ - a__ * rat) * scl; #endif } else { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(a__, rat, b__) * scl; imag_ = std::fmaf(b__, rat, -a__) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(a__, rat, b__) * scl; imag_ = std::fma(b__, rat, -a__) * scl; } else { real_ = (a__ * rat + b__) * scl; imag_ = (b__ * rat - a__) * scl; } #else real_ = (a__ * rat + b__) * scl; imag_ = (b__ * rat - a__) * scl; #endif } auto q = ComplexType(real_, imag_); #if defined(__CUDA_ARCH__) || defined(__HIPCC__) const auto& q_rounded = ComplexType(round(q.real), round(q.imag)); #else const auto& q_rounded = ComplexType(std::round(q.real), std::round(q.imag)); #endif const auto& a_ = q_rounded.real; const auto& b_ = q_rounded.imag; const auto& c = b.real; const auto& d = b.imag; const auto& t_real_ = a_ * c - b_ * d; const auto& t_imag_ = a_ * d + b_ * c; auto remainder = ComplexType(a.real - t_real_, a.imag - t_imag_); return remainder; } }; // RemainderGradXFunctor template struct RemainderGradXFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { // dx = dout return dout; } }; // RemainderGradYFunctor template struct RemainderGradYFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { // dy = -dout * (floor_div(x, y)) return -dout * static_cast((std::floor(x / y))); } }; template struct RemainderGradYFunctor< T, typename std::enable_if::value>::type> { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { using MT = typename MPTypeTrait::Type; // dy = -dout * (floor_div(x, y)) auto x_ = static_cast(x); auto y_ = static_cast(y); FloorDivideFunctor floor_div; return static_cast(-static_cast(dout) * (floor_div(x_, y_))); } }; template struct RemainderGradYFunctor< T, typename std::enable_if::value>::type> { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { // dy = -dout * (floor_div(x, y)) if (phi::is_negative(x) != phi::is_negative(y)) { // Subtracts one from the results of truncation division if the // divisor and dividend have different sign(bit)s and the remainder of // the division is nonzero const auto quot = x / y; const auto rem = x % y; auto ret = rem ? quot - 1 : quot; return -dout * static_cast(ret); } return -dout * static_cast(x / y); } }; // RemainderGradXYFunctor template struct RemainderGradXYFunctor { inline HOSTDEVICE phi::Array operator()(const InT x, const InT y, const InT dout) { phi::Array outs; // dx = dout outs[0] = static_cast(dout); // dy = -dout * (floor_div(x, y)) FloorDivideFunctor floor_div; outs[1] = static_cast(dout * static_cast(floor_div(x, y))); return outs; } }; template struct RemainderGradXYFunctor< InT, OutT, typename std::enable_if::value>::type> { inline HOSTDEVICE Array operator()(const InT x, const InT y, const InT dout) { Array outs; // dx = dout outs[0] = static_cast(dout); // dy = -dout * (x / y) using MT = typename MPTypeTrait::Type; auto x_ = static_cast(x); auto y_ = static_cast(y); FloorDivideFunctor floor_div; outs[1] = static_cast(static_cast(-dout) * floor_div(x_, y_)); return outs; } }; template struct RemainderGradXYFunctor< InT, OutT, typename std::enable_if::value>::type> { inline HOSTDEVICE Array operator()(const InT x, const InT y, const InT dout) { Array outs; // dx = dout outs[0] = static_cast(dout); // dy = -dout * (x / y) if (phi::is_negative(x) != phi::is_negative(y)) { // Subtracts one from the results of truncation division if the // divisor and dividend have different sign(bit)s and the remainder of // the division is nonzero const auto quot = x / y; const auto rem = x % y; auto ret = rem ? quot - 1 : quot; outs[1] = -static_cast(dout) * static_cast(ret); } outs[1] = -static_cast(dout) * static_cast(x / y); return outs; } }; template struct InverseRemainderFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { T res = b % a; if ((res != 0) && ((res < 0) != (a < 0))) res += a; return res; } }; template struct InverseRemainderFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T a, const T b) const { T res = fmod(b, a); if ((res != 0) && ((a < 0) != (res < 0))) res += a; return res; } }; /** * Remainder for complex number rule * Regarding a and b is gaussian integer, then * r = mod(a, b) = a - b * round(a/b) * and a, b is complex number */ template struct InverseRemainderFunctor< ComplexType, typename std::enable_if_t::value>> { inline HOSTDEVICE ComplexType operator()(ComplexType b, ComplexType a) const { // remainder = z1 - q_rounded * z2 T a__ = a.real; T b__ = a.imag; T c__ = b.real; T d__ = b.imag; // (a + bi) / (c + di) = (ac + bd)/(c^2 + d^2) + (bc - ad)/(c^2 + d^2) i // the calculation below follows numpy's complex division #if defined(__GNUC___) && !defined(__clang__) // std::abs is already constexpr by gcc auto abs_c = std::abs(c__); auto abs_d = std::abs(d__); #else auto abs_c = c__ < 0 ? -c__ : c__; auto abs_d = d__ < 0 ? -d__ : d__; #endif T real_, imag_; auto rat = (abs_c >= abs_d) ? (d__ / c__) : (c__ / d__); auto scl = (abs_c >= abs_d) ? (T(1.0) / (c__ + d__ * rat)) : (T(1.0) / (d__ + c__ * rat)); if (abs_c >= abs_d) { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(b__, rat, a__) * scl; imag_ = std::fmaf(-a__, rat, b__) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(b__, rat, a__) * scl; imag_ = std::fma(-a__, rat, b__) * scl; } else { real_ = (a__ + b__ * rat) * scl; imag_ = (b__ - a__ * rat) * scl; } #else real_ = (a__ + b__ * rat) * scl; imag_ = (b__ - a__ * rat) * scl; #endif } else { #if __cplusplus >= 201703L if constexpr (std::is_same_v) { real_ = std::fmaf(a__, rat, b__) * scl; imag_ = std::fmaf(b__, rat, -a__) * scl; } else if constexpr (std::is_same_v) { real_ = std::fma(a__, rat, b__) * scl; imag_ = std::fma(b__, rat, -a__) * scl; } else { real_ = (a__ * rat + b__) * scl; imag_ = (b__ * rat - a__) * scl; } #else real_ = (a__ * rat + b__) * scl; imag_ = (b__ * rat - a__) * scl; #endif } auto q = ComplexType(real_, imag_); #if defined(__CUDA_ARCH__) || defined(__HIPCC__) const auto& q_rounded = ComplexType(round(q.real), round(q.imag)); #else const auto& q_rounded = ComplexType(std::round(q.real), std::round(q.imag)); #endif const auto& a_ = q_rounded.real; const auto& b_ = q_rounded.imag; const auto& c = b.real; const auto& d = b.imag; const auto& t_real_ = a_ * c - b_ * d; const auto& t_imag_ = a_ * d + b_ * c; auto remainder = ComplexType(a.real - t_real_, a.imag - t_imag_); return remainder; } }; template struct ElementwiseHeavisideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return a == static_cast(0) ? b : static_cast(a > static_cast(0)); } }; template struct ElementwiseInverseHeavisideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return b == static_cast(0) ? a : static_cast(b > static_cast(0)); } }; template struct TruncDivideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { #ifndef PADDLE_WITH_XPU_KP PADDLE_ENFORCE(b != 0, DIV_ERROR_INFO); #endif return static_cast(a / b); } }; template struct TruncDivideFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T a, const T b) const { if (UNLIKELY(b == 0)) { return static_cast(a / b); } return std::trunc(a / b); } }; template <> struct TruncDivideFunctor { inline HOSTDEVICE dtype::float16 operator()(const dtype::float16 a, const dtype::float16 b) const { float a_float = static_cast(a); float b_float = static_cast(b); return static_cast(std::trunc(a_float / b_float)); } }; template <> struct TruncDivideFunctor { inline HOSTDEVICE dtype::bfloat16 operator()(const dtype::bfloat16 a, const dtype::bfloat16 b) const { float a_float = static_cast(a); float b_float = static_cast(b); return static_cast(std::trunc(a_float / b_float)); } }; template struct InverseTruncDivideFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { #ifndef PADDLE_WITH_XPU_KP PADDLE_ENFORCE(a != 0, DIV_ERROR_INFO); #endif return static_cast(b / a); } }; template struct InverseTruncDivideFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T a, const T b) const { if (UNLIKELY(a == 0)) { return static_cast(b / a); } return std::trunc(b / a); } }; template <> struct InverseTruncDivideFunctor { inline HOSTDEVICE dtype::float16 operator()(const dtype::float16 a, const dtype::float16 b) const { float a_float = static_cast(a); float b_float = static_cast(b); return static_cast(std::trunc(b_float / a_float)); } }; template <> struct InverseTruncDivideFunctor { inline HOSTDEVICE dtype::bfloat16 operator()(const dtype::bfloat16 a, const dtype::bfloat16 b) const { float a_float = static_cast(a); float b_float = static_cast(b); return static_cast(std::trunc(b_float / a_float)); } }; #ifdef PADDLE_WITH_SLEEF template inline HOSTDEVICE typename std::enable_if::value, T>::type compute_pow_sleef(const T a, const T b) { return Sleef_powf1_u10(a, b); } template inline HOSTDEVICE typename std::enable_if::value, T>::type compute_pow_sleef(const T a, const T b) { return Sleef_powd1_u10(a, b); } #if defined(__AVX512F__) inline HOSTDEVICE __m512 compute_pow_sleef_vec(__m512 a, __m512 b) { return Sleef_powf16_u10(a, b); } inline HOSTDEVICE __m512d compute_pow_sleef_vec(__m512d a, __m512d b) { return Sleef_powd8_u10(a, b); } #endif #endif #if defined(__CUDA_ARCH__) || defined(__HIPCC__) template inline HOSTDEVICE typename std::enable_if::value, T>::type compute_pow(const T a, const T b) { // TODO(wujionghao): A potential speed improvement is supporting different // types in C++. // On CUDAPlace, pow(3, 1) calls pow(float, float), and // it will return a float number like 2.99... , which floor to 2 // when cast to int by default and it is wrong. // Use llrint to cast it to the nearest integer, which is 3. T zero = static_cast(0); if (a == zero && b < zero) { return zero; } return llrint(pow(static_cast(a), static_cast(b))); } template inline HOSTDEVICE typename std::enable_if::value, T>::type compute_pow(const T a, const T b) { MT a_val = static_cast(a); MT b_val = static_cast(b); return static_cast(pow(a_val, b_val)); } #else template inline HOSTDEVICE T compute_pow(const T a, const T b) { if constexpr (std::is_integral::value) { if (a == static_cast(0) && b < static_cast(0)) { return static_cast(0); } } MT a_val = static_cast(a); MT b_val = static_cast(b); #ifdef PADDLE_WITH_XPU_KP return static_cast(pow(a_val, b_val)); #endif return static_cast(std::pow(a_val, b_val)); } #endif template struct ElementwisePowFunctor { using MT = typename MPTypeTrait::Type; inline HOSTDEVICE T operator()(const T a, const T b) const { return compute_pow(a, b); } }; template struct ElementwiseInversePowFunctor { using MT = typename MPTypeTrait::Type; inline HOSTDEVICE T operator()(const T a, const T b) const { return compute_pow(b, a); } }; template struct ElementwisePowFunctor> { inline HOSTDEVICE ComplexType operator()(const ComplexType a, const ComplexType b) const { #if defined(__CUDA_ARCH__) || defined(__HIPCC__) return pow(a, b); #else return std::pow(static_cast>(a), static_cast>(b)); #endif } }; template struct ElementwiseInversePowFunctor> { inline HOSTDEVICE ComplexType operator()(const ComplexType a, const ComplexType b) const { #if defined(__CUDA_ARCH__) || defined(__HIPCC__) return pow(b, a); #else return std::pow(static_cast>(b), static_cast>(a)); #endif } }; // copysign forward and grad functors template inline HOSTDEVICE auto copysign_func(const T& a, const T& b) { #ifdef WIN32 using U = typename std::conditional_t::value, float, T>; return static_cast(std::copysign(static_cast(a), static_cast(b))); #else return static_cast(std::copysign(a, b)); #endif } inline HOSTDEVICE phi::float16 copysign_func(phi::float16 a, phi::float16 b) { return phi::dtype::raw_uint16_to_float16((a.x & 0x7fff) | (b.x & 0x8000)); } inline HOSTDEVICE phi::bfloat16 copysign_func(phi::bfloat16 a, phi::bfloat16 b) { return phi::dtype::raw_uint16_to_bfloat16((a.x & 0x7fff) | (b.x & 0x8000)); } template struct CopySignGradXFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { if (x == static_cast(0)) return x; return dout * (funcs::copysign_func(x, y) / x); } }; template struct CopySignGradYFunctor { inline HOSTDEVICE T operator()(const T x, const T y, const T dout) const { return static_cast(0); } }; template struct CopySignGradXYFunctor { inline HOSTDEVICE phi::Array operator()(const InT x, const InT y, const InT dout) { phi::Array outs; // dx if (x == static_cast(0)) outs[0] = static_cast(0); else outs[0] = static_cast(dout * (funcs::copysign_func(x, y) / x)); // dy = 0 outs[1] = static_cast(0); return outs; } }; template struct CopySignFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return copysign_func(a, b); } }; template struct InverseCopySignFunctor { inline HOSTDEVICE T operator()(const T a, const T b) const { return copysign_func(b, a); } }; template struct NextafterFunctor { inline HOSTDEVICE T operator()(const T x, const T y) const { return static_cast( std::nextafter(static_cast(x), static_cast(y))); } }; template struct NextafterFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T x, const T y) const { return std::nextafter(x, y); } }; template struct NextafterFunctor::value>> { inline HOSTDEVICE double operator()(const T x, const T y) const { return std::nextafter(static_cast(x), static_cast(y)); } }; template struct InverseNextafterFunctor { inline HOSTDEVICE T operator()(const T x, const T y) const { return static_cast( std::nextafter(static_cast(y), static_cast(x))); } }; template struct InverseNextafterFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE T operator()(const T x, const T y) const { return std::nextafter(y, x); } }; template struct InverseNextafterFunctor< T, typename std::enable_if_t::value>> { inline HOSTDEVICE double operator()(const T x, const T y) const { return std::nextafter(static_cast(y), static_cast(x)); } }; } // namespace funcs } // namespace phi