Operator Properties#
Defined in the header <cuda/functional>.
The operator properties traits provide compile-time information about algebraic properties of binary operators. These traits are useful for generic algorithms that can apply optimizations based on operator properties, such as parallel reductions that can reorder operations for associative operators.
Associativity#
namespace cuda {
template <class Op, class T, class Enable = void>
inline constexpr bool is_associative_v;
} // namespace cuda
Determines whether a binary operator Op is associative for type T, meaning op(op(a, b), c) == op(a, op(b, c))
for all values a, b, c of type T. This allows the implementation to reorder operations.
Supported operators and types:
Operator |
Integer Types |
Floating-Point Types |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
|
|
|
|
Note
In the strictest sense of the term, the operations of plus and multiplication for integral values may result in undefined behaviour due to overflow. However, in the context of parallel algorithms, they are considered to be associative.
Commutativity#
namespace cuda {
template <class Op, class T, class Enable = void>
inline constexpr bool is_commutative_v;
} // namespace cuda
Determines whether a binary operator Op is commutative for type T, meaning op(a, b) == op(b, a)
for all values a, b of type T.
Supported operators and types:
Operator |
Integer Types |
Floating-Point Types |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
|
|
|
|
Identity Element#
namespace cuda {
template <class Op, class T>
__host__ __device__
constexpr auto identity_element() noexcept;
template <class Op, class T, class Enable = void>
inline constexpr bool has_identity_element_v;
} // namespace cuda
Provides the identity element for operator Op and type T. The identity element e satisfies
op(e, x) == op(x, e) == x for all values x of type T.
The function identity_element<Op, T>() returns the identity element at compile time. If no identity element
exists for the given operator and type combination, it returns an internal sentinel type.
has_identity_element_v evaluates to true if an identity element is defined for the given operator and type.
Identity elements by operator:
Operator |
Integer Types |
Floating-Point Types |
|---|---|---|
|
|
|
|
|
|
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
|
|
|
|
Note
For floating-point plus, the identity element is negative zero (-0.0) rather than positive zero.
This preserves the sign when adding: -0.0 + (-0.0) == -0.0.
Note
cuda::std::minus, cuda::std::divides, and cuda::std::modulus do not have identity elements.
Absorbing Element#
namespace cuda {
template <class Op, class T>
__host__ __device__
constexpr auto absorbing_element() noexcept;
template <class Op, class T, class Enable = void>
inline constexpr bool has_absorbing_element_v;
} // namespace cuda
Provides the absorbing (annihilating) element for operator Op and type T. The absorbing element z satisfies
op(z, x) == op(x, z) == z for all values x of type T.
The function absorbing_element<Op, T>() returns the absorbing element at compile time. If no absorbing element
exists for the given operator and type combination, it returns an internal sentinel type.
has_absorbing_element_v evaluates to true if an absorbing element is defined for the given operator and type.
Absorbing elements by operator:
Operator |
Integer Types |
Floating-Point Types |
|---|---|---|
|
|
N/A (see note) |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
|
|
|
|
Note
Floating-point multiplies does not have an absorbing element because:
0 * NaN = NaN(not0)0 * infinity = NaN(not0)(-1) * (+0) = -0(not+0)
Note
cuda::std::plus, cuda::std::minus, cuda::std::divides, cuda::std::modulus, and cuda::std::bit_xor
do not have absorbing elements.
Supported Types#
The functionality supports all integer and floating-point types, including extended floating-point types.
Example#
#include <cuda/functional>
#include <cuda/std/cstdio>
template <class Op, class T>
__host__ __device__ void print_properties() {
printf("Associative: %s\n", cuda::is_associative_v<Op, T> ? "yes" : "no");
printf("Commutative: %s\n", cuda::is_commutative_v<Op, T> ? "yes" : "no");
printf("Has identity: %s\n", cuda::has_identity_element_v<Op, T> ? "yes" : "no");
printf("Has absorbing: %s\n", cuda::has_absorbing_element_v<Op, T> ? "yes" : "no");
if constexpr (cuda::has_identity_element_v<Op, T>) {
printf("Identity element exists\n");
}
if constexpr (cuda::has_absorbing_element_v<Op, T>) {
printf("Absorbing element exists\n");
}
}
__global__ void example_kernel() {
// Integer plus: associative, commutative, identity=0, no absorbing
print_properties<cuda::std::plus<int>, int>();
// Integer plus: NOT associative, commutative, identity=-0.0f, no absorbing
print_properties<cuda::std::plus<float>, float>();
// identity element
constexpr int sum_identity = cuda::identity_element<cuda::std::plus<int>, int>(); // 0
constexpr int mul_identity = cuda::identity_element<cuda::std::multiplies<int>, int>(); // 1
}
Customization#
Users can extend the operator properties traits to support custom operators or custom types by specializing the internal variable templates. The following specializations are available:
template <>
inline constexpr bool cuda::is_associative_v<MyOperator, MyType> = true;
template <>
inline constexpr bool cuda::is_commutative_v<MyOperator, MyType> = true;
template <>
__host__ __device__
constexpr auto cuda::identity_element<MyOperator, MyType>() noexcept {
return /* MyType identity element */;
}
template <>
__host__ __device__
constexpr auto cuda::absorbing_element<MyOperator, MyType>() noexcept {
return /* MyType absorbing element */;
}