- Notifications
You must be signed in to change notification settings - Fork 31
Use experimental complex extension for all complex arithmetic #2069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
cb75d56 to 2dd0a72 Compare | View rendered docs @ https://intelpython.github.io/dpctl/pulls/2069/index.html |
| Array API standard conformance tests for dpctl=0.20.0dev0=py310h93fe807_183 ran successfully. |
converts to experimental sycl complex values, then performs math operations
* Move sycl_complex.hpp to utils * No longer use exprm_ns defined by header, define on per-file basis * Include alias to type sycl_complex_t<T> under sycl_utils namespace * Use identical include macro where inclusion of sycl_complex would be impossible
2dd0a72 to 52bb73e Compare | Array API standard conformance tests for dpctl=0.20.0dev0=py310h93fe807_183 ran successfully. |
| #ifndef SYCL_EXT_ONEAPI_COMPLEX | ||
| #define SYCL_EXT_ONEAPI_COMPLEX 1 | ||
| #endif | ||
| #if __has_include(<sycl/ext/oneapi/experimental/sycl_complex.hpp>) | ||
| #include <sycl/ext/oneapi/experimental/sycl_complex.hpp> | ||
| #else | ||
| #include <sycl/ext/oneapi/experimental/complex/complex.hpp> | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to include #include "sycl_complex.hpp" here instead or it was intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. It was originally intentional, because instead of sycl_complex.hpp, I had made the include part of sycl_utils.hpp, but decided it was too complicated. I'll change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I don't think sycl_utils.hpp is actually using sycl_complex.hpp anymore, so it should be removed there.
| } | ||
| } | ||
| | ||
| template <typename T> T plus_complex(const T &x1, const T &x2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need here something like below?
| template <typename T> T plus_complex(const T &x1, const T &x2) | |
| template <typename T, typename = std::enable_if_t<is_complex_v<T>> T plus_complex(const T &x1, const T &x2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems applicable to many declaration here expecting complex template type only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this would be a good idea, good suggestion
| { | ||
| T operator()(const T &x, const T &y) const | ||
| { | ||
| if constexpr (detail::IsComplex<T>::value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can reuse is_complex_v here:
| if constexpr (detail::IsComplex<T>::value) { | |
| if constexpr (dpctl::tensor::type_utils::is_complex_v<T>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type_utils.hpp isn't included in this header. Maybe it could be though.
| } | ||
| } | ||
| | ||
| template <typename T> T plus_complex(const T &x1, const T &x2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to use inline here explicitly?
As I can see compiler is actively uses inline __attribute__((__visibility__("hidden"), __always_inline__)) in $CONDA_PREFIX/include/sycl/ext/oneapi/experimental/complex/detail/complex.hpp header and I wonder if we should do the same or similar. Or it will be well optimized and inlined implicitly anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could, but I'm not sure it would do much good: function is only really used in sycl_utils.hpp for Plus struct, which is just our own implementation of sycl::plus.
It's arguable whether this function is needed at all. It only really avoids (directly) including sycl_complex.hpp in sycl_utils.hpp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of such _complex functions provide a set of helper wrappers providing extension over sycl experimental complex f/w. That is a kind of complete i/f where we avoid including sycl_complex.hpp and improving usability of complex f/w.
Also, in case when some wrapper might not be not needed by dpctl, we will add it directly in dpnp, but having that in one place in dpctl math utils header sounds like a good place probably.
| using sycl_complexT = exprm_ns::complex<realT>; | ||
| sycl_complexT z1 = sycl_complexT(x1); | ||
| sycl_complexT z2 = sycl_complexT(x2); | ||
| realT real1 = exprm_ns::real(z1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general it can be simplified by:
| realT real1 = exprm_ns::real(z1); | |
| realT real1 = z1.real(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably it is not a bad idea to implement the wrappers for real and imag functions also, since anyway it will be declared with theconstexpr:
template<typename Tp> constexpr Tp real_complex(const std::complex<Tp> &z) { return sycl_complex_t<Tp>(z).real(); }There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, we can stop using function calls to real and imag and just use the methods everywhere, which I think is pretty sensible. The methods also don't seem to trip up HIP compiler.
Then again, the std::real and std::imag functions actually didn't either, I just changed them for consistency. I vote to just use methods everywhere, they look cleaner anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do see your point though, the wrapper would be good anywhere we don't want actually use the result of sycl_complex_t<Tp>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, in general, we can have both alternatives implemented and to use either method or wrapper function depending on the use case.
| namespace sycl_utils | ||
| { | ||
| | ||
| template <typename T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| template <typename T> | |
| template <typename T, typename std::enable_if_t<std::is_floating_point_v<T>> |
With current nightly compiler, dpctl will fail to link when built for HIP backend due to undefined symbols (i.e.,
__muldc3).Since compiler does not support arithmetic with
std::complexon HIP backend, this PR takes the steps to refactor complex arithmetic throughout tensor librarylibtensor/include/kernels/elementwise_functions/sycl_complex.hppis now moved tolibtensor/include/utils/sycl_complex.hppand refactored to definedsycl_complex_t<T>, aliasing the complex type defined in the extensionsycl_complex.hppstill indirectly setsSYCL_EXT_ONEAPI_COMPLEXand includes the header for experimental extension, but no longer definesexprm_nsnamespace alias. This is now left to individual files.std::real,std::imag, etc. have been removedsycl_utils.hppnow defines new custom functors a laMaximumandMinimum,PlusandMultiplies. These structs are used by accumulation operations (sum,cumulative_sum, GEMM, etc.). They perform casting ofstd::complexinputs to SYCL equivalent, perform operations, and then return asstd::complex