Skip to content

Commit d2001b0

Browse files
umfranzwassistant-librarian[bot]
authored andcommitted
[rocm-libraries] ROCm/rocm-libraries#3836 (commit aef23cd)
[rocThrust] Update complex power function to resolve rounding issues (#3836) ## Motivation Recently, some rounding issues in PyTorch on AMD devices were traced back to an FMA operation in rocThrusts's pow() function, when type complex<float> is used. ## Technical Details This fix (credit to @vpykhtin) adds a specialization for type complex<float> that is enabled only on AMD GPUs. It performs the calculation in a different way so that the FMA operation no longer causes rounding issues. ## Test Plan Run rocThrust and PyTorch tests. Ensure all tests pass. ## Test Result rocThrust tests pass locally. Working on running PyTorch tests. ## Submission Checklist - [ ] Look over the contributing guidelines at https://github.com/ROCm/ROCm/blob/develop/CONTRIBUTING.md#pull-requests.
1 parent 8455671 commit d2001b0

1 file changed

Lines changed: 31 additions & 0 deletions

File tree

thrust/detail/complex/cpow.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,37 @@ pow(const complex<T0>& x, const complex<T1>& y)
3333
return exp(log(complex<T>(x)) * complex<T>(y));
3434
}
3535

36+
// Specialized version for complex<float> on AMD GPUs to use FMA-based multiplication
37+
#if defined(__HIP_DEVICE_COMPILE__) && defined(__HIP_PLATFORM_AMD__)
38+
namespace detail
39+
{
40+
// FMA-aware complex multiplication for float precision on AMD GPUs.
41+
// This prevents SLP vectorizer from breaking FMA formation, which causes
42+
// numerical precision loss in complex arithmetic.
43+
// The issue occurs when vectorizer packs scalar multiplies before backend
44+
// can form FMA instructions, resulting in double rounding instead of single.
45+
THRUST_HOST_DEVICE inline thrust::complex<float> complex_mul_fma(thrust::complex<float> a, thrust::complex<float> b)
46+
{
47+
// Complex multiplication: (a.r + a.i*i) * (b.r + b.i*i)
48+
// = (a.r*b.r - a.i*b.i) + (a.r*b.i + a.i*b.r)*i
49+
// Using __builtin_fmaf ensures FMA at source level:
50+
// real: a.r*b.r + (-(a.i*b.i)) = FMA(a.r, b.r, -(a.i*b.i))
51+
// imag: a.i*b.r + a.r*b.i = FMA(a.r, b.i, a.i*b.r)
52+
float real_part = __builtin_fmaf(a.real(), b.real(), -(a.imag() * b.imag()));
53+
float imag_part = __builtin_fmaf(a.real(), b.imag(), a.imag() * b.real());
54+
return thrust::complex<float>(real_part, imag_part);
55+
}
56+
} // namespace detail
57+
58+
template <>
59+
THRUST_HOST_DEVICE inline complex<float> pow(const complex<float>& x, const complex<float>& y)
60+
{
61+
auto log_x = thrust::log(static_cast<thrust::complex<float>>(x));
62+
auto y_log_x = detail::complex_mul_fma(static_cast<thrust::complex<float>>(y), log_x);
63+
return static_cast<complex<float>>(thrust::exp(y_log_x));
64+
}
65+
#endif
66+
3667
template <typename T0, typename T1>
3768
THRUST_HOST_DEVICE complex<typename ::internal::promoted_numerical_type<T0, T1>::type>
3869
pow(const complex<T0>& x, const T1& y)

0 commit comments

Comments
 (0)