- edited description
Element selection with boolean vector / matrix
Hello @Klaus Iglberger ,
non-contiguous views have been introduced into Blaze 3.3 in the form of the function elements() which allows selecting elements based on a given list of indices:
- https://bitbucket.org/blaze-lib/blaze/issues/48/introduce-non-contiguous-views-into-blaze
- https://bitbucket.org/blaze-lib/blaze/wiki/Element Selections
blaze::DynamicVector<int> x{ 3, 2, 5 };
const std::initializer_list<size_t> list{ 0UL, 2UL };
auto e2 = elements( x, { 0UL, 2UL }); // Results in { 3, 5 }
auto e3 = elements( x, list ); // Results in { 3, 5 }
I am interested in selecting elements based on a boolean vector which is e.g. the result of a (custom) logical vector operation:
Would it make sense to overload elements() for this use case?
blaze::DynamicVector<bool> sel{ true, false, true };
auto e4 = elements( x, sel ); // should result in { 3, 5 }
// e.g. in combination with logical operation
blaze::DynamicVector<bool> v1{ true, true, true };
blaze::DynamicVector<bool> v2{ true, false, true };
auto e5 = elements( x, v1 && v2 ); // should result in { 3, 5 }
Or would you prefer overloading the operator[] or to create a new function instead?
auto e5 = x[sel] // should result in { 3, 5 }
This issue is related to
- https://bitbucket.org/blaze-lib/blaze/issues/267/conditional-ternary-operator
- https://bitbucket.org/blaze-lib/blaze/issues/255/additional-operator-overloads
Best regards,
~Johannes Czech
Comments (5)
-
reporter -
reporter - edited description
-
reporter - edited description
-
Hi Johannes!
Thanks for your proposal. It is indeed possible to overload the
elements()
function for booleaninitializer_list
s and vectors:template< typename VT // Type of the vector , typename... REAs > // Optional arguments inline decltype(auto) elements( VT&& vector, blaze::initializer_list<bool> selection, REAs... args ) { BLAZE_FUNCTION_TRACE; blaze::SmallArray<size_t,8UL> indices; auto begin( cbegin( selection ) ); for( size_t i=0UL; i<selection.size(); ++i, ++begin ) { if( *begin ) indices.pushBack( i ); } return elements( std::forward<VT>( vector ), indices.begin(), indices.size(), args... ); } template< typename VT // Type of the vector , typename VT2 // Type of the selection vector , bool TF2 // Transpose flag of the selection vector , typename... REAs > // Optional arguments inline decltype(auto) elements( VT&& vector, const blaze::DenseVector<VT2,TF2>& selection, REAs... args ) { BLAZE_FUNCTION_TRACE; blaze::SmallArray<size_t,8UL> indices; auto begin( cbegin( selection ) ); for( size_t i=0UL; i<(~selection).size(); ++i, ++begin ) { if( *begin ) indices.pushBack( i ); } return elements( std::forward<VT>( vector ), indices.begin(), indices.size(), args... ); }
These overloads can now be used as intended:
#include <iostream> #include <blaze/Math.h> int main() { blaze::DynamicVector<int> a{ 1, 2, 3, 4 }; blaze::DynamicVector<bool> s1{ false, true, false, false }; blaze::DynamicVector<bool> s2{ false, false, false, true }; auto e1 = elements( a, { false, true, false, true } ); // Element selection via 'initializer_list<bool>' auto e2 = elements( a, s1 || s2 ); // Element selection via logical operation std::cout << "\n e1 = " << trans(e1) << "\n"; // Prints { 2 4 } std::cout << "\n e2 = " << trans(e2) << "\n\n"; // Prints { 2 4 } }
Is this what you have in mind?
Best regards,
Klaus!
-
reporter Thank you for the reply.
That is exactly what I had in mind and this could also be useful for other people.However, I wondered if it were better to avoid building a helper list of indices in this case similar to this implementation:
template< typename VT // Type of the vector , typename VT2 // Type of the selection vector , bool TF2 // Transpose flag of the selection vector , typename... REAs > // Optional arguments inline decltype(auto) elements2( VT&& vector, const blaze::DenseVector<VT2,TF2>& selection, REAs... args ) { BLAZE_FUNCTION_TRACE; blaze::SmallArray<float, 8UL> out; auto it_selection( cbegin( selection ) ); auto it_vec( cbegin( vector ) ); for( ; it_selection != cend(selection); ++it_vec, ++it_selection ) { if( *it_selection ) out.pushBack( *it_vec ); } return out; }
Here is a small runtime comparison:
size_t it = 1e7; blaze::DynamicVector<float> a{ 1, 2, 3, 4 }; blaze::DynamicVector<bool> sel{ true, false, true, false }; std::chrono::steady_clock::time_point start_el = std::chrono::steady_clock::now(); for (size_t i = 0; i < it; ++i) { auto e1 = elements( a, sel ); } std::chrono::steady_clock::time_point end_el = std::chrono::steady_clock::now(); std::cout << "Elapsed time elements(): \t" << std::chrono::duration_cast<std::chrono::milliseconds>(end_el - start_el).count() << "ms" << std::endl; std::chrono::steady_clock::time_point start_el2 = std::chrono::steady_clock::now(); for (size_t i = 0; i < it; ++i) { auto e2 = elements2( a, sel ); } std::chrono::steady_clock::time_point end_el2 = std::chrono::steady_clock::now(); std::cout << "Elapsed time elements2():\t" << std::chrono::duration_cast<std::chrono::milliseconds>(end_el2 - start_el2).count() << "ms" << std::endl;
Unfortunately, elements2() doesn't behave exactly as elements() because it returns a deep copy instead, but it is still faster in terms of runtime in this case:
Elapsed time elements(): 134ms Elapsed time elements2(): 66ms
Best regards,
~Johannes Czech - Log in to comment