Additional operator overloads
It would be covenient to have operator overloads for:
// Comparison operators
V(X) == V(X) -> V(bool)
V(X) != V(X) -> V(bool)
V(X) < V(X) -> V(bool)
V(X) <= V(X) -> V(bool)
V(X) > V(X) -> V(bool)
V(X) >= V(X) -> V(bool)
V(X) <=> V(X) -> V(strong_ordering)
any(V(bool)) -> bool
all(V(bool)) -> bool
// Logical operators
V(bool) && V(bool) -> V(bool)
V(bool) || V(bool) -> V(bool)
!V(bool) -> V(bool)
// Arithmetic operators
V(unsigned integral) & V(unsigned integral) -> V(unsigned integral)
V(unsigned integral) | V(unsigned integral) -> V(unsigned integral)
V(unsigned integral) ~ V(unsigned integral) -> V(unsigned integral)
V(unsigned integral) << V(unsigned integral) -> V(unsigned integral)
V(unsigned integral) >> V(unsigned integral) -> V(unsigned integral)
V(unsigned integral) << unsigned integral -> V(unsigned integral)
V(unsigned integral) >> unsigned integral -> V(unsigned integral)
// Assignment operators
V(unsigned integral) &= V(unsigned integral) -> V(unsigned integral)&
V(unsigned integral) |= V(unsigned integral) -> V(unsigned integral)&
V(unsigned integral) ~= V(unsigned integral) -> V(unsigned integral)&
V(unsigned integral) <<= V(unsigned integral) -> V(unsigned integral)&
V(unsigned integral) >>= V(unsigned integral) -> V(unsigned integral)&
V(unsigned integral) <<= unsigned integral -> V(unsigned integral)&
V(unsigned integral) >>= unsigned integral -> V(unsigned integral)&
Here V can be a vector or matrix type. Having these operators would make Blaze very suitable as a HLSL/GLSL equivalent on the CPU side while still having one SIMD math library supporting arbitrary vector and matrix dimensions (<> GLM, DirectXMath).
Note that the operators involving scalars are also related to #70, #109 and #132.
Kind regards,
Matthias
Comments (13)
-
-
reporter Thank you very much for the examples!
Kind regards,
Matthias
-
Hi Matthias!
With the last push we have provided the majority of the requested operators. All arithmetic operators (i.e.
operator&()
,operator|()
,operator^()
,operator<<()
, andoperator>>()
) and logical operators (i.e.operator!()
,operator&&()
, andoperator||()
) can now be used for both dense vectors and dense matrices.We are still thinking about the relational operators and the proposed
any()
andall()
functions, though. Sinceoperator==()
already exists, there is unfortunately no way to provide anotheroperator==()
for an elementwise comparison. We'll have to figure out in which form we could provide these operations or if a user has to manually define these operations by means of themap()
function.Best regards,
Klaus!
-
-
assigned issue to
-
assigned issue to
-
- changed status to open
-
reporter Thanks Klaus! This is a great addition to the library.
Imo, the relational
<
,<=
,>
,>=
operators make mathematically only sense if performed elementwise. Unfortunately,==
and!=
make both sense if performed elementwise and if performed elementwise + collapse usingall
orany
, respectively. The latter is even more unfortunate as that eliminates the possibility of an implicit cast operator fromV(bool)
tobool .
What is the current use case of
==
? -
reporter Another interesting addition and closely associated to the given list of operators is a function to represent the ternary
?:
operator, which unfortunately could not be overloaded in C++, but naturally translates to SIMD intrinsics using a mask (with the difference that short circuiting is not possible). (Note that short circuiting is also not possible for overloads of operator&&
and||
, but that is never really a use case for an algebra library).Select(V(bool), V(X), V(X));
Pseudocode:
Result[i] = (V1[i] & mask[i])|(V2[i] & ~mask[i]);
It is possible to implement that
already usingmap
and the operator overloads orusing intrinsics:SSE: _mm_and_ps
,_mm_andnot_ps, _mm_or_ps
ARM NEON: vbslq_f32
-
Hi Matthias!
I agree with your assessment about the less-than and greater-than comparisons. The current use case for the comparison operator is a complete comparison of two vectors or matrices (i.e. “Are these two equal?”). Since this behavior has been part of Blaze since version 1.0, this is something that cannot be changed anymore.
Unfortunately it is not possible to use the
map()
function for emulating the conditional operator sincemap()
only takes two arguments. But I really like the idea of aselect()
function. I believe that in the context of this issue this would be too much, but I would appreciate another issue with a specific request for this feature.Best regards,
Klaus!
-
Hi Matthias!
For the release of Blaze 3.6 we have decided to leave the relational operators out. However, with the latest additions to Blaze they are very easily manually implemented. The following code snippet provides a blueprint for how to implement
operator<()
,operator>()
,operator<=()
andoperator>=()
for both dense vectors and dense matrices. For the implementation ofall()
andany()
we recommend to build onstd::all_of()
andstd::any_of()
since these functions exploit short circuit evaluation.
Relational Operators
template< typename VT1, typename VT2, bool TF > decltype(auto) operator<( const blaze::DenseVector<VT1,TF>& lhs, const blaze::DenseVector<VT2,TF>& rhs ) { return blaze::map( ~lhs, ~rhs, blaze::Less{} ); } template< typename VT1, typename VT2, bool TF > decltype(auto) operator>( const blaze::DenseVector<VT1,TF>& lhs, const blaze::DenseVector<VT2,TF>& rhs ) { return blaze::map( ~lhs, ~rhs, blaze::Greater{} ); } template< typename VT1, typename VT2, bool TF > decltype(auto) operator<=( const blaze::DenseVector<VT1,TF>& lhs, const blaze::DenseVector<VT2,TF>& rhs ) { return !( ~lhs > ~rhs ); } template< typename VT1, typename VT2, bool TF > decltype(auto) operator>=( const blaze::DenseVector<VT1,TF>& lhs, const blaze::DenseVector<VT2,TF>& rhs ) { return !( ~lhs < ~rhs ); } template< typename MT1, bool SO1, typename MT2, bool SO2 > decltype(auto) operator<( const blaze::DenseMatrix<MT1,SO1>& lhs, const blaze::DenseMatrix<MT2,SO2>& rhs ) { return blaze::map( ~lhs, ~rhs, blaze::Less{} ); } template< typename MT1, bool SO1, typename MT2, bool SO2 > decltype(auto) operator>( const blaze::DenseMatrix<MT1,SO1>& lhs, const blaze::DenseMatrix<MT2,SO2>& rhs ) { return blaze::map( ~lhs, ~rhs, blaze::Greater{} ); } template< typename MT1, bool SO1, typename MT2, bool SO2 > decltype(auto) operator<=( const blaze::DenseMatrix<MT1,SO1>& lhs, const blaze::DenseMatrix<MT2,SO2>& rhs ) { return !( ~lhs > ~rhs ); } template< typename MT1, bool SO1, typename MT2, bool SO2 > decltype(auto) operator>=( const blaze::DenseMatrix<MT1,SO1>& lhs, const blaze::DenseMatrix<MT2,SO2>& rhs ) { return !( ~lhs < ~rhs ); } template< typename VT, bool TF > bool any( const blaze::DenseVector<VT,TF>& dv ) { return std::any_of( begin(~dv), end(~dv), []( const auto& v ) -> bool { return v; } ); } template< typename VT, bool TF > bool all( const blaze::DenseVector<VT,TF>& dv ) { return std::all_of( begin(~dv), end(~dv), []( const auto& v ) -> bool { return v; } ); }
Thanks again for creating this issue,
Best regards,
Klaus!
-
- changed status to resolved
Bitwise Shift
Vector/Vector Shift
Via the left-shift operator (i.e.
operator<<()
) and the right-shift operator (i.e.operator>>()
) it is possible to perform an elementwise shift of a dense vector:blaze::DynamicVector<unsigned int> v1( 5UL ), v3; blaze::DynamicVector<unsigned short> v2( 5UL ); // ... Initializing the vectors v3 = v1 << v2; // Elementwise left-shift of a dense column vector v3 = v1 >> v2; // Elementwise right-shift of a dense column vector
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. Also note that it is only possible to shift vectors with the same transpose flag:
using blaze::columnVector; using blaze::rowVector; blaze::DynamicVector<unsigned int,columnVector> v1( 5UL ); blaze::DynamicVector<unsigned int,rowVector> v2( 5UL ); v1 << v2; // Compilation error: Cannot shift a column vector by a row vector v1 << trans( v2 ); // OK: Shifting a column vector by another column vector
Furthermore, it is possible to use different element types in the two vector operands, but shifting two vectors with the same element type is favorable due to possible vectorization of the operation:
blaze::DynamicVector<unsigned int> v1( 100UL ), v2( 100UL ), v3; // ... Initialization of the vectors v3 = v1 << v2; // Vectorized left-shift of an unsigned int vector
Matrix/Matrix Shift
The left-shift operator (i.e.
operator<<()
) and the right-shift operator (i.e.operator>>()
) can also be used to perform an elementwise shift of a dense matrix:using blaze::rowMajor; using blaze::columnMajor; blaze::DynamicMatrix<unsigned int,columnMajor> M1( 7UL, 3UL ); blaze::DynamicMatrix<unsigned short,rowMajor> M2( 7UL, 3UL ), M3; // ... Initializing the matrices M3 = M1 << M2; // Elementwise left-shift of a dense column-major matrix M3 = M1 >> M2; // Elementwise right-shift of a dense column-major matrix
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. It is possible to use any combination of row-major and column-major matrices. Note however that in favor of performance using two matrices with the same storage order is favorable. The same argument holds for the element type: While it is possible to use matrices with different element type, using two matrices with the same element type potentially leads to better performance due to vectorization of the operation.
blaze::DynamicMatrix<unsigned int> M1( 50UL, 70UL ), M2( 50UL, 70UL ), M3; // ... Initialization of the matrices M3 = M1 << M2; // Vectorized left-shift of an unsigned int matrix
Scalar Shift
It is also possible to uniformly shift all elements of a dense vector or dense matrix by means of a scalar, which has the same effect as shifting by means of a uniform vector or matrix (see UniformVector and UniformMatrix). In Blaze it is possible to use all built-in/fundamental data types except bool as scalar values. Examples:
blaze::DynamicVector<unsigned int> v1{ 3, 2, 5, 4, 1, 6 }; // Uniform left-shift by one bit of all elements of v1; Results in // // ( 6, 4, 10, 8, 2, 12 ) // blaze::DynamicVector<int> v2( v1 << 1U );
blaze::DynamicMatrix<unsigned int> M1{ { 3, 2, 5 }, { 4, 1, 6 } }; // Uniform left-shift by one bit of all elements of M1; Results in // // ( 6, 4, 10 ) // ( 8, 2, 12 ) // blaze::DynamicMatrix<unsigned int> M2( M1 << 1U );
Bitwise AND
Vector/Vector Bitwise AND
Via the bitwise AND operator (i.e.
operator&()
) it is possible to perform an elementwise bitwise AND with dense vectors:blaze::DynamicVector<unsigned int> v1( 5UL ), v3; blaze::DynamicVector<unsigned short> v2( 5UL ); // ... Initializing the vectors v3 = v1 & v2; // Elementwise bitwise AND of two dense column vectors of different data type
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. Also note that it is only possible to use vectors with the same transpose flag:
using blaze::columnVector; using blaze::rowVector; blaze::DynamicVector<unsigned int,columnVector> v1( 5UL ); blaze::DynamicVector<unsigned int,rowVector> v2( 5UL ); v1 & v2; // Compilation error: Cannot AND a column vector and a row vector v1 & trans( v2 ); // OK: Bitwise AND of two column vectors
Furthermore, it is possible to use different element types in the two vector operands, but a bitwise AND of two vectors with the same element type is favorable due to possible vectorization of the operation:
blaze::DynamicVector<unsigned int> v1( 100UL ), v2( 100UL ), v3; // ... Initialization of the vectors v3 = v1 & v2; // Vectorized bitwise AND of an unsigned int vector
Matrix/Matrix Bitwise AND
The bitwise AND operator (i.e.
operator&()
) can also be used to perform an elementwise bitwise AND with dense matrices:using blaze::rowMajor; using blaze::columnMajor; blaze::DynamicMatrix<unsigned int,columnMajor> M1( 7UL, 3UL ); blaze::DynamicMatrix<unsigned short,rowMajor> M2( 7UL, 3UL ), M3; // ... Initializing the matrices M3 = M1 & M2; // Elementwise bitwise AND of two dense matrices of different data type
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. It is possible to use any combination of row-major and column-major matrices. Note however that in favor of performance using two matrices with the same storage order is favorable. The same argument holds for the element type: While it is possible to use matrices with different element type, using two matrices with the same element type potentially leads to better performance due to vectorization of the operation.
blaze::DynamicMatrix<unsigned int> M1( 50UL, 70UL ), M2( 50UL, 70UL ), M3; // ... Initialization of the matrices M3 = M1 & M2; // Vectorized bitwise AND of two row-major, unsigned int dense matrices
Scalar Bitwise AND
Is is also possible to perform a bitwise AND between a dense vector or dense matrix and a scalar value, which has the same effect as performing a bitwise AND by means of a uniform vector or matrix (see UniformVector and UniformMatrix). In Blaze it is possible to use all built-in/fundamental data types except bool as scalar values. Examples:
blaze::DynamicVector<unsigned int> v1{ 3U, 2U, 5U, 4U, 1U, 6U }; // Perform a bitwise AND with all elements of v1; Results in // // ( 3, 2, 1, 0, 1, 2 ) // blaze::DynamicVector<int> v2( v1 & 3U );
blaze::DynamicMatrix<unsigned int> M1{ { 3U, 2U, 5U }, { 4U, 1U, 6U } }; // Perform a bitwise AND with all elements of M1; Results in // // ( 3, 2, 1 ) // ( 0, 1, 2 ) // blaze::DynamicMatrix<unsigned int> M2( M1 & 3U );
Bitwise OR
Vector/Vector Bitwise OR
Via the bitwise OR operator (i.e.
operator|()
) it is possible to perform an elementwise bitwise OR with dense vectors:blaze::DynamicVector<unsigned int> v1( 5UL ), v3; blaze::DynamicVector<unsigned short> v2( 5UL ); // ... Initializing the vectors v3 = v1 | v2; // Elementwise bitwise OR of two dense column vectors of different data type
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. Also note that it is only possible to use vectors with the same transpose flag:
using blaze::columnVector; using blaze::rowVector; blaze::DynamicVector<unsigned int,columnVector> v1( 5UL ); blaze::DynamicVector<unsigned int,rowVector> v2( 5UL ); v1 | v2; // Compilation error: Cannot OR a column vector and a row vector v1 | trans( v2 ); // OK: Bitwise OR of two column vectors
Furthermore, it is possible to use different element types in the two vector operands, but a bitwise OR of two vectors with the same element type is favorable due to possible vectorization of the operation:
blaze::DynamicVector<unsigned int> v1( 100UL ), v2( 100UL ), v3; // ... Initialization of the vectors v3 = v1 | v2; // Vectorized bitwise OR of an unsigned int vector
Matrix/Matrix Bitwise OR
The bitwise OR operator (i.e.
operator|()
) can also be used to perform an elementwise bitwise OR with dense matrices:using blaze::rowMajor; using blaze::columnMajor; blaze::DynamicMatrix<unsigned int,columnMajor> M1( 7UL, 3UL ); blaze::DynamicMatrix<unsigned short,rowMajor> M2( 7UL, 3UL ), M3; // ... Initializing the matrices M3 = M1 | M2; // Elementwise bitwise OR of two dense matrices of different data type
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. It is possible to use any combination of row-major and column-major matrices. Note however that in favor of performance using two matrices with the same storage order is favorable. The same argument holds for the element type: While it is possible to use matrices with different element type, using two matrices with the same element type potentially leads to better performance due to vectorization of the operation.
blaze::DynamicMatrix<unsigned int> M1( 50UL, 70UL ), M2( 50UL, 70UL ), M3; // ... Initialization of the matrices M3 = M1 | M2; // Vectorized bitwise OR of two row-major, unsigned int dense matrices
Scalar Bitwise OR
Is is also possible to perform a bitwise OR between a dense vector or dense matrix and a scalar value, which has the same effect as performing a bitwise OR by means of a uniform vector or matrix (see UniformVector and UniformMatrix). In Blaze it is possible to use all built-in/fundamental data types except bool as scalar values. Examples:
blaze::DynamicVector<unsigned int> v1{ 3U, 2U, 5U, 4U, 1U, 6U }; // Perform a bitwise OR with all elements of v1; Results in // // ( 3, 3, 7, 7, 3, 3 ) // blaze::DynamicVector<int> v2( v1 | 3U );
blaze::DynamicMatrix<unsigned int> M1{ { 3U, 2U, 5U }, { 4U, 1U, 6U } }; // Perform a bitwise OR with all elements of M1; Results in // // ( 3, 3, 7 ) // ( 7, 3, 3 ) // blaze::DynamicMatrix<unsigned int> M2( M1 | 3U );
Bitwise XOR
Vector/Vector Bitwise XOR
Via the bitwise XOR operator (i.e.
operator^()
) it is possible to perform an elementwise bitwise XOR with dense vectors:blaze::DynamicVector<unsigned int> v1( 5UL ), v3; blaze::DynamicVector<unsigned short> v2( 5UL ); // ... Initializing the vectors v3 = v1 ^ v2; // Elementwise bitwise XOR of two dense column vectors of different data type
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. Also note that it is only possible to use vectors with the same transpose flag:
using blaze::columnVector; using blaze::rowVector; blaze::DynamicVector<unsigned int,columnVector> v1( 5UL ); blaze::DynamicVector<unsigned int,rowVector> v2( 5UL ); v1 ^ v2; // Compilation error: Cannot XOR a column vector and a row vector v1 ^ trans( v2 ); // OK: Bitwise XOR of two column vectors
Furthermore, it is possible to use different element types in the two vector operands, but a bitwise XOR of two vectors with the same element type is favorable due to possible vectorization of the operation:
blaze::DynamicVector<unsigned int> v1( 100UL ), v2( 100UL ), v3; // ... Initialization of the vectors v3 = v1 ^ v2; // Vectorized bitwise XOR of an unsigned int vector
Matrix/Matrix Bitwise XOR
The bitwise XOR operator (i.e.
operator^()
) can also be used to perform an elementwise bitwise XOR with dense matrices:using blaze::rowMajor; using blaze::columnMajor; blaze::DynamicMatrix<unsigned int,columnMajor> M1( 7UL, 3UL ); blaze::DynamicMatrix<unsigned short,rowMajor> M2( 7UL, 3UL ), M3; // ... Initializing the matrices M3 = M1 ^ M2; // Elementwise bitwise XOR of two dense matrices of different data type
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. It is possible to use any combination of row-major and column-major matrices. Note however that in favor of performance using two matrices with the same storage order is favorable. The same argument holds for the element type: While it is possible to use matrices with different element type, using two matrices with the same element type potentially leads to better performance due to vectorization of the operation.
blaze::DynamicMatrix<unsigned int> M1( 50UL, 70UL ), M2( 50UL, 70UL ), M3; // ... Initialization of the matrices M3 = M1 ^ M2; // Vectorized bitwise XOR of two row-major, unsigned int dense matrices
Scalar Bitwise XOR
Is is also possible to perform a bitwise XOR between a dense vector or dense matrix and a scalar value, which has the same effect as performing a bitwise XOR by means of a uniform vector or matrix (see UniformVector and UniformMatrix). In Blaze it is possible to use all built-in/fundamental data types except bool as scalar values. Examples:
blaze::DynamicVector<unsigned int> v1{ 3U, 2U, 5U, 4U, 1U, 6U }; // Perform a bitwise XOR with all elements of v1; Results in // // ( 0, 1, 6, 7, 2, 5 ) // blaze::DynamicVector<int> v2( v1 ^ 3U );
blaze::DynamicMatrix<unsigned int> M1{ { 3U, 2U, 5U }, { 4U, 1U, 6U } }; // Perform a bitwise XOR with all elements of M1; Results in // // ( 0, 1, 6 ) // ( 7, 2, 5 ) // blaze::DynamicMatrix<unsigned int> M2( M1 ^ 3U );
Logical NOT
Vector/Vector Logical NOT
Via the logical NOT operator (i.e.
operator!()
) it is possible to compute an elementwise logical NOT of a dense vector:blaze::DynamicVector<bool> v1( 5UL ), v2; // ... Initializing the vectors v2 = !v1; // Elementwise logical NOT of a dense column vector
Matrix/Matrix Logical NOT
The logical NOT operator (i.e.
operator!()
) can also be used to compute an elementwise logical NOT with dense matrices:using blaze::rowMajor; using blaze::columnMajor; blaze::DynamicMatrix<bool,rowMajor> M1( 7UL, 3UL ), M2; // ... Initializing the matrices M2 = !M1; // Elementwise logical NOT of a dense row-major matrix
Logical AND
Vector/Vector Logical AND
Via the logical AND operator (i.e.
operator&&()
) it is possible to compute an elementwise logical AND with dense vectors:blaze::DynamicVector<bool> v1( 5UL ), v3; blaze::DynamicVector<bool> v2( 5UL ); // ... Initializing the vectors v3 = v1 && v2; // Elementwise logical AND of two dense column vectors
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. Also note that it is only possible to use vectors with the same transpose flag:
using blaze::columnVector; using blaze::rowVector; blaze::DynamicVector<bool,columnVector> v1( 5UL ); blaze::DynamicVector<bool,rowVector> v2( 5UL ); v1 && v2; // Compilation error: Cannot AND a column vector and a row vector v1 && trans( v2 ); // OK: Logical AND of two column vectors
Matrix/Matrix Logical AND
The logical AND operator (i.e.
operator&&()
) can also be used to compute an elementwise logical AND with dense matrices:using blaze::rowMajor; using blaze::columnMajor; blaze::DynamicMatrix<bool,columnMajor> M1( 7UL, 3UL ); blaze::DynamicMatrix<bool,rowMajor> M2( 7UL, 3UL ), M3; // ... Initializing the matrices M3 = M1 && M2; // Elementwise logical AND of two dense matrices
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. It is possible to use any combination of row-major and column-major matrices. Note however that in favor of performance using two matrices with the same storage order is favorable.
Logical OR
Vector/Vector Logical OR
Via the logical OR operator (i.e.
operator||()
) it is possible to perform an elementwise logical OR with dense vectors:blaze::DynamicVector<bool> v1( 5UL ), v3; blaze::DynamicVector<bool> v2( 5UL ); // ... Initializing the vectors v3 = v1 || v2; // Elementwise logical OR of two dense column vectors
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. Also note that it is only possible to use vectors with the same transpose flag:
using blaze::columnVector; using blaze::rowVector; blaze::DynamicVector<unsigned int,columnVector> v1( 5UL ); blaze::DynamicVector<unsigned int,rowVector> v2( 5UL ); v1 || v2; // Compilation error: Cannot OR a column vector and a row vector v1 || trans( v2 ); // OK: Logical OR of two column vectors
Matrix/Matrix Logical OR
The logical OR operator (i.e.
operator||()
) can also be used to perform an elementwise logical OR with dense matrices:using blaze::rowMajor; using blaze::columnMajor; blaze::DynamicMatrix<bool,columnMajor> M1( 7UL, 3UL ); blaze::DynamicMatrix<bool,rowMajor> M2( 7UL, 3UL ), M3; // ... Initializing the matrices M3 = M1 || M2; // Elementwise logical OR of two dense matrices
Note that it is necessary that both operands have exactly the same dimensions. Violating this precondition results in an exception. It is possible to use any combination of row-major and column-major matrices. Note however that in favor of performance using two matrices with the same storage order is favorable.
-
reporter Thank you Klaus for adding and documenting these features!
Wrt the documentation: shouldn’t the shift and bit-wise scalar be restricted to an unsigned integral type to eliminate the use cases of a signed integral type and a floating point type?
-
reporter I refactored the relational operator workarounds mentioned above using C++20’s concepts (something similar can be done using more verbose SFINAE in <= C++17) as this results in way less verbose (and more constraint; as it requires matching element types) template signatures and can be useful for someone else reading this far . (Note that the
noexcept
is technically only correct for the non-dynamic dense vectors.)As the vector types are passed as-is instead of using the base type, avoiding the need of
operator~
.
template< DenseVector VectorT > [[nodiscard]] inline auto Equal(const VectorT& lhs, const VectorT& rhs) noexcept { return blaze::map(lhs, rhs, [](auto l, auto r) { return l == r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Equal(const VectorT& lhs, typename VectorT::ElementType rhs) noexcept { return blaze::map(lhs, [rhs](auto l) { return l == rhs; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Equal(typename VectorT::ElementType lhs, const VectorT& rhs) noexcept { return blaze::map(rhs, [lhs](auto r) { return lhs == r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto NonEqual(const VectorT& lhs, const VectorT& rhs) noexcept { return blaze::map(lhs, rhs, [](auto l, auto r) { return l != r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto NonEqual(const VectorT& lhs, typename VectorT::ElementType rhs) noexcept { return blaze::map(lhs, [rhs](auto l) { return l != rhs; }); } template< DenseVector VectorT > [[nodiscard]] inline auto NonEqual(typename VectorT::ElementType lhs, const VectorT& rhs) noexcept { return blaze::map(rhs, [lhs](auto r) { return lhs != r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Less(const VectorT& lhs, const VectorT& rhs) noexcept { return blaze::map(lhs, rhs, [](auto l, auto r) { return l < r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Less(const VectorT& lhs, typename VectorT::ElementType rhs) noexcept { return blaze::map(lhs, [rhs](auto l) { return l < rhs; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Less(typename VectorT::ElementType lhs, const VectorT& rhs) noexcept { return blaze::map(rhs, [lhs](auto r) { return lhs < r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto LessEqual(const VectorT& lhs, const VectorT& rhs) noexcept { return blaze::map(lhs, rhs, [](auto l, auto r) { return l <= r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto LessEqual(const VectorT& lhs, typename VectorT::ElementType rhs) noexcept { return blaze::map(lhs, [rhs](auto l) { return l <= rhs; }); } template< DenseVector VectorT > [[nodiscard]] inline auto LessEqual(typename VectorT::ElementType lhs, const VectorT& rhs) noexcept { return blaze::map(rhs, [lhs](auto r) { return lhs <= r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Greater(const VectorT& lhs, const VectorT& rhs) noexcept { return blaze::map(lhs, rhs, [](auto l, auto r) { return l > r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Greater(const VectorT& lhs, typename VectorT::ElementType rhs) noexcept { return blaze::map(lhs, [rhs](auto l) { return l > rhs; }); } template< DenseVector VectorT > [[nodiscard]] inline auto Greater(typename VectorT::ElementType lhs, const VectorT& rhs) noexcept { return blaze::map(rhs, [lhs](auto r) { return lhs > r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto GreaterEqual(const VectorT& lhs, const VectorT& rhs) noexcept { return blaze::map(lhs, rhs, [](auto l, auto r) { return l >= r; }); } template< DenseVector VectorT > [[nodiscard]] inline auto GreaterEqual(const VectorT& lhs, typename VectorT::ElementType rhs) noexcept { return blaze::map(lhs, [rhs](auto l) { return l >= rhs; }); } template< DenseVector VectorT > [[nodiscard]] inline auto GreaterEqual(typename VectorT::ElementType lhs, const VectorT& rhs) noexcept { return blaze::map(rhs, [lhs](auto r) { return lhs >= r; }); } template< DenseVector VectorT > [[nodiscard]] inline bool All(const VectorT& v) noexcept { return std::all_of(blaze::begin(v), blaze::end(v), [](const auto& x) { return static_cast< bool >(x); }); } template< DenseVector VectorT > [[nodiscard]] inline bool Any(const VectorT& v) noexcept { return std::any_of(blaze::begin(v), blaze::end(v), [](const auto& x) { return static_cast< bool >(x); }); } template< DenseVector VectorT > [[nodiscard]] inline bool None(const VectorT& v) noexcept { return std::none_of(blaze::begin(v), blaze::end(v), [](const auto& x) { return static_cast< bool >(x); }); }
-
Thanks for the sample code. Once Blaze is upgraded to C++17, this will definitely be useful.
- Log in to comment
Hi Matthias!
Thanks a lot for the great proposal. We agree that this would be a great addition to Blaze and we will try to integrate this as soon as possible. Until then you can build on the
map()
function to implement these operators yourself:Logical operators
Arithmetic operators
Although this implementation works, it depends on the compiler to vectorise the operation. In order to guarantee vectorization you can implement custom operations. For instance, the following struct implements the necessary right-shift operation for the last
operator>>()
(please not that it is limited to 32-bit integrals and AVX2):This struct can be used to upgrade the
operator>>()
:Example:
Thanks again for the proposal,
Best regards,
Klaus!