From d89b6f59741edb53cdc22a5cea0bc700a19a6c81 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 01/14] started adding sequential implementation of sort + unit test --- include/alp/reference/blas1.hpp | 28 ++++++-- tests/unit/CMakeLists.txt | 4 ++ tests/unit/dense_sort.cpp | 122 ++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 tests/unit/dense_sort.cpp diff --git a/include/alp/reference/blas1.hpp b/include/alp/reference/blas1.hpp index ec2268409..b18f47d2b 100644 --- a/include/alp/reference/blas1.hpp +++ b/include/alp/reference/blas1.hpp @@ -2218,7 +2218,8 @@ namespace alp { } /** - * Sort vectors, function available to user, e.g. to sort eigenvectors + * Compute the permutation vector needed to sort the input vector according to + * the provided \a cmp function. * * @param[in] toSort vector of indices to sort, should not be modified * @param[in] cmp function with strict weak ordering relation between indices, eg bool cmp(const Type1 &a, const Type2 &b) @@ -2226,20 +2227,37 @@ namespace alp { * * @param[out] permutation iterator over index permutations which sort toSort vector * - * Complexity should be lower than O(n*log(n)), and space complexity should be lower than \Theta(n+T+P) + * Complexity should be \Theta(n*log(n)), and space complexity should be \Theta(n+T+P) */ template< - typename IndexType, typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, + typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, typename ValueType, typename ValueStructure, typename ValueView, typename ValueImfR, typename ValueImfC, typename Compare > RC sort( - Vector< IndexType, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, + Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort, Compare cmp //PHASE &phase = EXECUTE ) noexcept { - return SUCCESS; + + internal::setInitialized( permutation, internal::getInitialized( toSort ) ); + + if( !internal::getInitialized( toSort ) ) { + return SUCCESS; + } + + RC rc = alp::eWiseLambda( + [ ]( const size_t i, size_t &val ) { + val = i; + }, + permutation + ); + + (void)cmp; + //TODO: add alp::Vector::iterator to turn to std seq. sorting + + return rc; } /** diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 907a7c609..095591393 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -269,6 +269,10 @@ add_grb_executables( dense_set dense_set.cpp BACKENDS alp_reference ) +add_grb_executables( dense_sort dense_sort.cpp + BACKENDS alp_reference +) + add_grb_executables( dense_storage_polynomials dense_storage_polynomials.cpp BACKENDS alp_reference ) diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp new file mode 100644 index 000000000..14ac7f912 --- /dev/null +++ b/tests/unit/dense_sort.cpp @@ -0,0 +1,122 @@ + +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +#include "../utils/print_alp_containers.hpp" + +using namespace alp; + +void alp_program( const size_t &n, alp::RC &rc ) { + + typedef double T; + + auto print_std_vector = [](std::vector< T > const vec) { + for (auto val : vec) { + std::cout << val << ' '; + } + std::cout << std::endl; + }; + + // Check with vector of length n randomly intitialized and shuffled + alp::Vector< size_t > perm( n ); + alp::Vector< double > v( n ); + + std::random_device rd; + std::default_random_engine rng( rd() ); + + std::vector< double > stdv( n ); + + std::iota( std::begin( stdv ), std::end( stdv ), 0. ); + std::shuffle( std::begin( stdv ), std::end( stdv ), rng ); + + alp::buildVector( v, std::begin( stdv ), std::end( stdv ) ); + + std::cout << "Original content of the std::vector:" << std::endl; + print_std_vector( stdv ); + std::cout << "Original content of the alp::Vector:" << std::endl; + print_vector("v", v); + + alp::sort( perm, v, []( T a, T b) { + return a > b; + } ); + + std::sort( std::begin( stdv ), std::end( stdv ) ); + + for ( size_t i = 0; i < n; i++ ) { + if ( stdv[i] != v[ perm[ i ] ] ) { + std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( alp::v[ perm[ " << i << " ] ] = " << v[ perm[ i ] ] << " )" << std::endl; + } + } + + rc = alp::SUCCESS; +} + +int main( int argc, char ** argv ) { + // defaults + bool printUsage = false; + size_t in = 100; + + // error checking + if( argc > 2 ) { + printUsage = true; + } + if( argc == 2 ) { + size_t read; + std::istringstream ss( argv[ 1 ] ); + if( ! ( ss >> read ) ) { + std::cerr << "Error parsing first argument\n"; + printUsage = true; + } else if( ! ss.eof() ) { + std::cerr << "Error parsing first argument\n"; + printUsage = true; + } else if( read % 2 != 0 ) { + std::cerr << "Given value for n is odd\n"; + printUsage = true; + } else { + // all OK + in = read; + } + } + if( printUsage ) { + std::cerr << "Usage: " << argv[ 0 ] << " [n]\n"; + std::cerr << " -n (optional, default is 100): an even integer, the " + "test size.\n"; + return 1; + } + + std::cout << "This is functional test " << argv[ 0 ] << "\n"; + alp::Launcher< AUTOMATIC > launcher; + alp::RC out; + if( launcher.exec( &alp_program, in, out, true ) != SUCCESS ) { + std::cerr << "Launching test FAILED\n"; + return 255; + } + if( out != SUCCESS ) { + std::cout << "Test FAILED (" << alp::toString( out ) << ")" << std::endl; + return out; + } else { + std::cout << "Test OK" << std::endl; + return 0; + } +} From a968a736d1aaeab5024f14b3fee7781c5bd18322 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 02/14] added vector iterators and internal begin/end --- include/alp/amf-based/vector.hpp | 175 ++++++++++++++++++++++++++++++- include/alp/reference/blas1.hpp | 51 +++++++-- tests/unit/dense_sort.cpp | 46 ++++++-- 3 files changed, 258 insertions(+), 14 deletions(-) diff --git a/include/alp/amf-based/vector.hpp b/include/alp/amf-based/vector.hpp index 6ae4df509..7716fe70e 100644 --- a/include/alp/amf-based/vector.hpp +++ b/include/alp/amf-based/vector.hpp @@ -59,6 +59,15 @@ namespace alp { void setInitialized( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v, bool initialized ) noexcept { setInitialized( static_cast< typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::base_type &>( v ), initialized ); } + + template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > + typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator + begin( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept; + + template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > + typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator + end( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept; + } // end namespace ``alp::internal'' /** @@ -111,6 +120,153 @@ namespace alp { typedef Matrix< T, structures::General, Density::Dense, View, ImfR, ImfC, backend > base_type; private: + class VectorIterator: + public std::iterator< std::random_access_iterator_tag, T > { + + friend VectorIterator internal::begin<>( self_type & v ) noexcept; + friend VectorIterator internal::end<>( self_type & v ) noexcept; + + private: + + typedef typename self_type::storage_index_type index_type; + + self_type * vec; + index_type position; + + VectorIterator( self_type * v ) noexcept : + vec( v ), position( 0 ) + {} + + VectorIterator( self_type * v, index_type pos ) noexcept : + vec( v ), position( pos ) + {} + + bool equal( const VectorIterator & other ) const noexcept { + return ( vec == other.vec ) && ( position == other.position ); + } + + bool lessThen( const VectorIterator & other ) const noexcept { + return ( vec == other.vec ) && ( position < other.position ); + } + + public: + typedef typename std::iterator::pointer pointer; + typedef typename std::iterator::reference reference; + typedef typename std::iterator::difference_type difference_type; + + /** Default constructor. */ + VectorIterator() noexcept : + vec( nullptr ), position( 0 ) + {} + + /** Copy constructor. */ + VectorIterator( const VectorIterator &other ) noexcept : + vec( other.vec ), + position( other.position ) + {} + + /** Move constructor. */ + VectorIterator( VectorIterator &&other ) : + vec( nullptr ), position( 0 ) + { + std::swap( vec, other.vec ); + std::swap( position, other.position ); + } + + /** Copy assignment. */ + VectorIterator& operator=( const VectorIterator &other ) noexcept { + vec = other.vec; + position = other.position; + return *this; + } + + /** Move assignment. */ + VectorIterator& operator=( VectorIterator &&other ) { + vec = nullptr; + position = 0; + std::swap( vec, other.vec ); + std::swap( position, other.position ); + return *this; + } + + reference operator*() const { + return ( *vec )[ position ]; + } + + VectorIterator& operator++() { + ++position; + return *this; + } + + VectorIterator& operator--() { + --position; + return *this; + } + + VectorIterator operator++(int) { + return VectorIterator( vec, position++ ); + } + + VectorIterator operator--(int) { + return VectorIterator( vec, position-- ); + } + + VectorIterator operator+(const difference_type& n) const { + return VectorIterator( vec, ( position + n ) ); + } + + VectorIterator& operator+=(const difference_type& n) { + position += n; + return *this; + } + + VectorIterator operator-(const difference_type& n) const { + return VectorIterator( vec, ( position - n ) ); + } + + VectorIterator& operator-=(const difference_type& n) { + position -= n; + return *this; + } + + reference operator[](const difference_type& n) const { + return ( *vec )[ position + n ]; + } + + bool operator==(const VectorIterator& other) const { + return equal( other ); + } + + bool operator!=(const VectorIterator& other) const { + return !equal( other ); + } + + bool operator<(const VectorIterator& other) const { + return lessThen( other ); + } + + bool operator>(const VectorIterator& other) const { + return !( lessThen( other ) || equal( other ) ); + } + + bool operator<=(const VectorIterator& other) const { + return lessThen( other ) || equal( other ); + } + + bool operator>=(const VectorIterator& other) const { + return !lessThen( other ); + } + + difference_type operator+(const VectorIterator& other) const { + assert( other.vec == vec ); + return position + other.position; + } + + difference_type operator-(const VectorIterator& other) const { + assert( other.vec == vec ); + return position - other.position; + } + }; /********************* Storage info friends @@ -123,8 +279,9 @@ namespace alp { return nrows( static_cast< const base_type & >( *this ) ); } - public: + + typedef VectorIterator iterator; /** @see Vector::value_type. */ using value_type = T; @@ -307,6 +464,22 @@ namespace alp { }; // class Vector with physical container + namespace internal { + + template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > + typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator + begin( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept { + return typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator( &v ); + } + + template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > + typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator + end( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept { + return typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator( &v, getLength( v ) ); + } + + } // end namespace ``alp::internal'' + /** Identifies any backend's implementation of ALP vector as an ALP vector. */ template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > struct is_vector< Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > > : std::true_type {}; diff --git a/include/alp/reference/blas1.hpp b/include/alp/reference/blas1.hpp index b18f47d2b..972c36ba3 100644 --- a/include/alp/reference/blas1.hpp +++ b/include/alp/reference/blas1.hpp @@ -2229,16 +2229,49 @@ namespace alp { * * Complexity should be \Theta(n*log(n)), and space complexity should be \Theta(n+T+P) */ + template< + typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, + typename ValueType, typename ValueStructure, typename ValueView, typename ValueImfR, typename ValueImfC + > + RC sort( + Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, + const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort + ) noexcept { + + internal::setInitialized( permutation, internal::getInitialized( toSort ) ); + + if( !internal::getInitialized( toSort ) ) { + return SUCCESS; + } + + RC rc = alp::eWiseLambda( + [ ]( const size_t i, size_t &val ) { + val = i; + }, + permutation + ); + + typedef Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > PermType; + + typename PermType::iterator it_begin = internal::begin( permutation ); + typename PermType::iterator it_end = internal::end( permutation ); + + std::sort( it_begin, it_end, [ &toSort ]( const size_t& a, const size_t& b ) { + return toSort[ a ] < toSort[ b ]; + }); + + return rc; + } + template< typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, typename ValueType, typename ValueStructure, typename ValueView, typename ValueImfR, typename ValueImfC, - typename Compare + typename CompareType > RC sort( Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort, - Compare cmp - //PHASE &phase = EXECUTE + CompareType cmp ) noexcept { internal::setInitialized( permutation, internal::getInitialized( toSort ) ); @@ -2253,10 +2286,16 @@ namespace alp { }, permutation ); - - (void)cmp; - //TODO: add alp::Vector::iterator to turn to std seq. sorting + typedef Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > PermType; + + typename PermType::iterator it_begin = internal::begin( permutation ); + typename PermType::iterator it_end = internal::end( permutation ); + + std::sort( it_begin, it_end, [ &toSort, &cmp ]( const size_t& a, const size_t& b ) { + return cmp( toSort[ a ], toSort[ b ] ); + }); + return rc; } diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp index 14ac7f912..29a738d86 100644 --- a/tests/unit/dense_sort.cpp +++ b/tests/unit/dense_sort.cpp @@ -40,12 +40,12 @@ void alp_program( const size_t &n, alp::RC &rc ) { // Check with vector of length n randomly intitialized and shuffled alp::Vector< size_t > perm( n ); - alp::Vector< double > v( n ); + alp::Vector< T > v( n ); std::random_device rd; std::default_random_engine rng( rd() ); - std::vector< double > stdv( n ); + std::vector< T > stdv( n ); std::iota( std::begin( stdv ), std::end( stdv ), 0. ); std::shuffle( std::begin( stdv ), std::end( stdv ), rng ); @@ -57,18 +57,50 @@ void alp_program( const size_t &n, alp::RC &rc ) { std::cout << "Original content of the alp::Vector:" << std::endl; print_vector("v", v); - alp::sort( perm, v, []( T a, T b) { - return a > b; - } ); + alp::sort( perm, v ); std::sort( std::begin( stdv ), std::end( stdv ) ); + auto sorted_v = alp::get_view< alp::structures::General >( v, perm ); + + // Check sorted view + for ( size_t i = 0; i < n; i++ ) { + if ( stdv[i] != sorted_v[ i ] ) { + std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << sorted_v[ i ] << " )" << std::endl; + rc = alp::FAILED; + } + } + + std::cout << "Sorted alp::Vector:" << std::endl; + print_vector("sorted_v", sorted_v); + + if (rc == alp::FAILED ) { + return; + } + + auto desc_cmp = []( const T& a, const T& b) { + return a > b; + }; + + // Check descending sorted view + alp::sort( perm, v, desc_cmp ); + + std::sort( std::begin( stdv ), std::end( stdv ), desc_cmp ); + + auto desc_sorted_v = alp::get_view< alp::structures::General >( v, perm ); + + // Check sorted view for ( size_t i = 0; i < n; i++ ) { - if ( stdv[i] != v[ perm[ i ] ] ) { - std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( alp::v[ perm[ " << i << " ] ] = " << v[ perm[ i ] ] << " )" << std::endl; + if ( stdv[i] != desc_sorted_v[ i ] ) { + std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" << std::endl; + rc = alp::FAILED; } } + std::cout << "Sorted alp::Vector in descending order:" << std::endl; + print_vector("desc_sorted_v", desc_sorted_v); + + rc = alp::SUCCESS; } From a700b7f05f93fb56637a9353fe2ed4c133fa20c7 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 03/14] begin/end logic defined privately --- include/alp/amf-based/vector.hpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/include/alp/amf-based/vector.hpp b/include/alp/amf-based/vector.hpp index 7716fe70e..9631e882e 100644 --- a/include/alp/amf-based/vector.hpp +++ b/include/alp/amf-based/vector.hpp @@ -123,8 +123,7 @@ namespace alp { class VectorIterator: public std::iterator< std::random_access_iterator_tag, T > { - friend VectorIterator internal::begin<>( self_type & v ) noexcept; - friend VectorIterator internal::end<>( self_type & v ) noexcept; + friend class Vector< T, structures::General, Density::Dense, View, ImfR, ImfC, backend >; private: @@ -133,12 +132,12 @@ namespace alp { self_type * vec; index_type position; - VectorIterator( self_type * v ) noexcept : - vec( v ), position( 0 ) + VectorIterator( self_type * vptr ) noexcept : + vec( vptr ), position( 0 ) {} - VectorIterator( self_type * v, index_type pos ) noexcept : - vec( v ), position( pos ) + VectorIterator( self_type * vptr, index_type pos ) noexcept : + vec( vptr ), position( pos ) {} bool equal( const VectorIterator & other ) const noexcept { @@ -279,10 +278,22 @@ namespace alp { return nrows( static_cast< const base_type & >( *this ) ); } + VectorIterator begin() noexcept { + return VectorIterator( this ); + } + + VectorIterator end() noexcept { + return VectorIterator( this, _length() ); + } + + public: typedef VectorIterator iterator; + friend iterator internal::begin<>( self_type & v ) noexcept; + friend iterator internal::end<>( self_type & v ) noexcept; + /** @see Vector::value_type. */ using value_type = T; @@ -469,13 +480,13 @@ namespace alp { template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator begin( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept { - return typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator( &v ); + return v.begin(); } template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator end( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept { - return typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator( &v, getLength( v ) ); + return v.end(); } } // end namespace ``alp::internal'' From 0a76d7b3e912c9bc161a7adf1af41b433d13b3a8 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 04/14] Addressing first round of reviews --- include/alp/amf-based/vector.hpp | 138 +++++++++++++++++++++---------- include/alp/reference/blas1.hpp | 6 ++ tests/unit/dense_sort.cpp | 33 ++++---- 3 files changed, 116 insertions(+), 61 deletions(-) diff --git a/include/alp/amf-based/vector.hpp b/include/alp/amf-based/vector.hpp index 9631e882e..06f502bd4 100644 --- a/include/alp/amf-based/vector.hpp +++ b/include/alp/amf-based/vector.hpp @@ -55,18 +55,37 @@ namespace alp { return getInitialized( static_cast< const typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::base_type & >( v ) ); } - template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > - void setInitialized( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v, bool initialized ) noexcept { + template< + typename T, typename Structure, typename View, + typename ImfR, typename ImfC, enum Backend backend + > + void setInitialized( + alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > &v, bool initialized + ) noexcept { setInitialized( static_cast< typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::base_type &>( v ), initialized ); } - template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > - typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator - begin( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept; - - template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > - typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator - end( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept; + template< + typename T, typename Structure, typename View, + typename ImfR, typename ImfC, enum Backend backend + > + typename alp::Vector< + T, Structure, Density::Dense, View, ImfR, ImfC, backend + >::iterator + begin( + alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > &v + ) noexcept; + + template< + typename T, typename Structure, typename View, + typename ImfR, typename ImfC, enum Backend backend + > + typename alp::Vector< + T, Structure, Density::Dense, View, ImfR, ImfC, backend + >::iterator + end( + alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > &v + ) noexcept; } // end namespace ``alp::internal'' @@ -104,47 +123,64 @@ namespace alp { * accessible via functions. * */ - template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > + template< + typename T, typename Structure, typename View, + typename ImfR, typename ImfC, enum Backend backend + > class Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > { }; /* * ALP vector with a general structure */ - template< typename T, typename View, typename ImfR, typename ImfC, enum Backend backend > - class Vector< T, structures::General, Density::Dense, View, ImfR, ImfC, backend > : - public Matrix< T, structures::General, Density::Dense, View, ImfR, ImfC, backend > { + template< + typename T, typename View, + typename ImfR, typename ImfC, enum Backend backend + > + class Vector< + T, structures::General, Density::Dense, View, ImfR, ImfC, backend + > : public Matrix< T, structures::General, Density::Dense, View, ImfR, ImfC, backend > { public: - typedef Vector< T, structures::General, Density::Dense, View, ImfR, ImfC, backend > self_type; - typedef Matrix< T, structures::General, Density::Dense, View, ImfR, ImfC, backend > base_type; + typedef Vector< + T, structures::General, Density::Dense, + View, ImfR, ImfC, backend + > self_type; + + typedef Matrix< + T, structures::General, Density::Dense, + View, ImfR, ImfC, backend + > base_type; private: class VectorIterator: public std::iterator< std::random_access_iterator_tag, T > { - friend class Vector< T, structures::General, Density::Dense, View, ImfR, ImfC, backend >; + friend class Vector< + T, structures::General, Density::Dense, + View, ImfR, ImfC, backend + >; private: typedef typename self_type::storage_index_type index_type; - self_type * vec; + self_type *vec; index_type position; - VectorIterator( self_type * vptr ) noexcept : + VectorIterator( self_type *vptr ) noexcept : vec( vptr ), position( 0 ) {} - VectorIterator( self_type * vptr, index_type pos ) noexcept : + VectorIterator( self_type *vptr, index_type pos ) noexcept : vec( vptr ), position( pos ) {} - bool equal( const VectorIterator & other ) const noexcept { + bool equal( const VectorIterator &other ) const noexcept { return ( vec == other.vec ) && ( position == other.position ); } - bool lessThen( const VectorIterator & other ) const noexcept { + bool lessThen( const VectorIterator &other ) const noexcept { return ( vec == other.vec ) && ( position < other.position ); } @@ -202,66 +238,66 @@ namespace alp { return *this; } - VectorIterator operator++(int) { + VectorIterator operator++( int ) { return VectorIterator( vec, position++ ); } - VectorIterator operator--(int) { + VectorIterator operator--( int ) { return VectorIterator( vec, position-- ); } - VectorIterator operator+(const difference_type& n) const { + VectorIterator operator+( const difference_type &n ) const { return VectorIterator( vec, ( position + n ) ); } - VectorIterator& operator+=(const difference_type& n) { + VectorIterator& operator+=( const difference_type &n ) { position += n; return *this; } - VectorIterator operator-(const difference_type& n) const { + VectorIterator operator-( const difference_type &n ) const { return VectorIterator( vec, ( position - n ) ); } - VectorIterator& operator-=(const difference_type& n) { + VectorIterator& operator-=( const difference_type &n ) { position -= n; return *this; } - reference operator[](const difference_type& n) const { + reference operator[]( const difference_type &n ) const { return ( *vec )[ position + n ]; } - bool operator==(const VectorIterator& other) const { + bool operator==( const VectorIterator &other ) const { return equal( other ); } - bool operator!=(const VectorIterator& other) const { + bool operator!=( const VectorIterator &other ) const { return !equal( other ); } - bool operator<(const VectorIterator& other) const { + bool operator<( const VectorIterator &other ) const { return lessThen( other ); } - bool operator>(const VectorIterator& other) const { + bool operator>( const VectorIterator &other ) const { return !( lessThen( other ) || equal( other ) ); } - bool operator<=(const VectorIterator& other) const { + bool operator<=( const VectorIterator &other ) const { return lessThen( other ) || equal( other ); } - bool operator>=(const VectorIterator& other) const { + bool operator>=( const VectorIterator &other ) const { return !lessThen( other ); } - difference_type operator+(const VectorIterator& other) const { + difference_type operator+( const VectorIterator &other ) const { assert( other.vec == vec ); return position + other.position; } - difference_type operator-(const VectorIterator& other) const { + difference_type operator-( const VectorIterator &other ) const { assert( other.vec == vec ); return position - other.position; } @@ -291,8 +327,8 @@ namespace alp { typedef VectorIterator iterator; - friend iterator internal::begin<>( self_type & v ) noexcept; - friend iterator internal::end<>( self_type & v ) noexcept; + friend iterator internal::begin<>( self_type &v ) noexcept; + friend iterator internal::end<>( self_type &v ) noexcept; /** @see Vector::value_type. */ using value_type = T; @@ -477,15 +513,29 @@ namespace alp { namespace internal { - template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > - typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator - begin( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept { + template< + typename T, typename Structure, typename View, + typename ImfR, typename ImfC, enum Backend backend + > + typename alp::Vector< + T, Structure, Density::Dense, View, ImfR, ImfC, backend + >::iterator + begin( + alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > &v + ) noexcept { return v.begin(); } - template< typename T, typename Structure, typename View, typename ImfR, typename ImfC, enum Backend backend > - typename alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend >::iterator - end( alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > & v ) noexcept { + template< + typename T, typename Structure, typename View, + typename ImfR, typename ImfC, enum Backend backend + > + typename alp::Vector< + T, Structure, Density::Dense, View, ImfR, ImfC, backend + >::iterator + end( + alp::Vector< T, Structure, Density::Dense, View, ImfR, ImfC, backend > &v + ) noexcept { return v.end(); } diff --git a/include/alp/reference/blas1.hpp b/include/alp/reference/blas1.hpp index 972c36ba3..2e9b4243e 100644 --- a/include/alp/reference/blas1.hpp +++ b/include/alp/reference/blas1.hpp @@ -2238,6 +2238,8 @@ namespace alp { const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort ) noexcept { + // TODO: this overload has to go. A partial order is always needed. + internal::setInitialized( permutation, internal::getInitialized( toSort ) ); if( !internal::getInitialized( toSort ) ) { @@ -2274,6 +2276,10 @@ namespace alp { CompareType cmp ) noexcept { + if ( getLength( permutation ) != getLength( toSort ) ) { + return ILLEGAL; + } + internal::setInitialized( permutation, internal::getInitialized( toSort ) ); if( !internal::getInitialized( toSort ) ) { diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp index 29a738d86..90f95ee57 100644 --- a/tests/unit/dense_sort.cpp +++ b/tests/unit/dense_sort.cpp @@ -31,12 +31,12 @@ void alp_program( const size_t &n, alp::RC &rc ) { typedef double T; - auto print_std_vector = [](std::vector< T > const vec) { - for (auto val : vec) { - std::cout << val << ' '; - } - std::cout << std::endl; - }; + auto print_std_vector = [](std::vector< T > const vec) { + for(auto val : vec) { + std::cout << val << ' '; + } + std::cout << std::endl; + }; // Check with vector of length n randomly intitialized and shuffled alp::Vector< size_t > perm( n ); @@ -52,9 +52,9 @@ void alp_program( const size_t &n, alp::RC &rc ) { alp::buildVector( v, std::begin( stdv ), std::end( stdv ) ); - std::cout << "Original content of the std::vector:" << std::endl; + std::cout << "Original content of the std::vector:" << std::endl; print_std_vector( stdv ); - std::cout << "Original content of the alp::Vector:" << std::endl; + std::cout << "Original content of the alp::Vector:" << std::endl; print_vector("v", v); alp::sort( perm, v ); @@ -64,17 +64,17 @@ void alp_program( const size_t &n, alp::RC &rc ) { auto sorted_v = alp::get_view< alp::structures::General >( v, perm ); // Check sorted view - for ( size_t i = 0; i < n; i++ ) { - if ( stdv[i] != sorted_v[ i ] ) { + for( size_t i = 0; i < n; i++ ) { + if( stdv[i] != sorted_v[ i ] ) { std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << sorted_v[ i ] << " )" << std::endl; rc = alp::FAILED; } } - std::cout << "Sorted alp::Vector:" << std::endl; + std::cout << "Sorted alp::Vector:" << std::endl; print_vector("sorted_v", sorted_v); - if (rc == alp::FAILED ) { + if( rc == alp::FAILED ) { return; } @@ -90,21 +90,20 @@ void alp_program( const size_t &n, alp::RC &rc ) { auto desc_sorted_v = alp::get_view< alp::structures::General >( v, perm ); // Check sorted view - for ( size_t i = 0; i < n; i++ ) { - if ( stdv[i] != desc_sorted_v[ i ] ) { + for( size_t i = 0; i < n; i++ ) { + if( stdv[i] != desc_sorted_v[ i ] ) { std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" << std::endl; rc = alp::FAILED; } } - std::cout << "Sorted alp::Vector in descending order:" << std::endl; + std::cout << "Sorted alp::Vector in descending order:" << std::endl; print_vector("desc_sorted_v", desc_sorted_v); - rc = alp::SUCCESS; } -int main( int argc, char ** argv ) { +int main( int argc, char **argv ) { // defaults bool printUsage = false; size_t in = 100; From af3f3681da433b46e11bce7334c06e1fc9f98836 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 05/14] Started definition of internal base classes for ALP relations. --- include/alp/base/internalrels.hpp | 531 ++++++++++++++++++++++++++++++ 1 file changed, 531 insertions(+) create mode 100644 include/alp/base/internalrels.hpp diff --git a/include/alp/base/internalrels.hpp b/include/alp/base/internalrels.hpp new file mode 100644 index 000000000..32d6d2c4d --- /dev/null +++ b/include/alp/base/internalrels.hpp @@ -0,0 +1,531 @@ + +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @author D. G. Spampinato + * @date 2nd of November, 2022 + */ + +#ifndef _H_ALP_INTERNAL_RELATIONS_BASE +#define _H_ALP_INTERNAL_RELATIONS_BASE + +#include + +#include "config.hpp" + +namespace alp { + + namespace relations { + + /** Core implementations of the standard relations in #alp::relations. */ + namespace internal { + + /** + * Standard less then or equal (le) operator. + * + * Assumes native availability of operator <= on the given data types + * or assumes that the relevant operators are properly overloaded. + * + * Assumes that the <= operator is a total order. Non-standard/non-matching + * data types or non-standard (overloaded) <= operators should + * therefore be used with caution. + * + * @tparam SET The input data type. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class le { + + public: + /** Alias to the left-hand input data type. */ + typedef SET left_type; + + /** Alias to the right-hand input data type. */ + typedef SET right_type; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a \le a \f$. + */ + static constexpr bool is_reflexive = true; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a \le a \f$. + */ + static constexpr bool is_irreflexive = false; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a \le b \f$ then \f$ b \le a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a \le b \f$ and + * \f$ b \le a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a \le b \f$ and + * \f$ b \le c \f$ then \f$ a \le c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a \le b \f$ or \f$ b \le a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a \le b \f$ or \f$ b \le a \f$. + */ + static constexpr bool is_strongly_connected = true; + + /** + * Check if a <= b. + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool test( const left_type * __restrict__ const a, + const right_type * __restrict__ const b + ) { + return *a <= *b; + } + }; + + /** + * Standard less then (lt) operator. + * + * Assumes native availability of operator < on the given data types + * or assumes that the relevant operators are properly overloaded. + * + * Assumes that the < operator is a strict total order. Non-standard/non-matching + * data types or non-standard (overloaded) < operators should + * therefore be used with caution. + * + * @tparam SET The input data type. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class lt { + + public: + /** Alias to the left-hand input data type. */ + typedef SET left_type; + + /** Alias to the right-hand input data type. */ + typedef SET right_type; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a < a \f$. + */ + static constexpr bool is_reflexive = false; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a < a \f$. + */ + static constexpr bool is_irreflexive = true; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a < b \f$ then \f$ b < a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a < b \f$ and + * \f$ b < a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = false; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a < b \f$ and + * \f$ b < c \f$ then \f$ a < c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected (or total); that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a < b \f$ or \f$ b < a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected ; + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a < b \f$ or \f$ b < a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * Check if a < b. + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool test( const left_type * __restrict__ const a, + const right_type * __restrict__ const b + ) { + return *a < *b; + } + }; + + /** + * Standard equal (eq) operator. + * + * Assumes native availability of operator == on the given data types + * or assumes that the relevant operators are properly overloaded. + * + * Assumes that the == operator is an equivalence relation. + * Non-standard/non-matching data types or non-standard (overloaded) + * == operators should therefore be used with caution. + * + * @tparam SET The input data type. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class eq { + + public: + /** Alias to the left-hand input data type. */ + typedef SET left_type; + + /** Alias to the right-hand input data type. */ + typedef SET right_type; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a = a \f$. + */ + static constexpr bool is_reflexive = true; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a = a \f$. + */ + static constexpr bool is_irreflexive = false; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a = b \f$ then \f$ b = a \f$. + */ + static constexpr bool is_symmetric = true; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a = b \f$ and + * \f$ b = a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a = b \f$ and + * \f$ b = c \f$ then \f$ a = c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a = b \f$ or \f$ b = a \f$. + */ + static constexpr bool is_connected = false; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a = b \f$ or \f$ b = a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * Check if a == b. + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool test( const left_type * __restrict__ const a, + const right_type * __restrict__ const b + ) { + return *a == *b; + } + }; + + + /** + * This class takes a generic operator implementation and exposes a more + * convenient test() function based on it. This function allows arbitrary + * data types being passed as parameters, and automatically handles any + * casting required for the raw operator. + * + * @tparam REL The generic operator implementation. + * + */ + template< typename REL, enum Backend implementation = config::default_backend > + class RelationBase { + + protected: + + /** The left-hand input domain. */ + typedef typename REL::left_type D1; + + /** The right-hand input domain. */ + typedef typename REL::right_type D2; + + public: + /** @return Whether this relation is reflexive. */ + static constexpr bool is_reflexive() { + return REL::is_reflexive; + } + + /** @return Whether this relation is irreflexive. */ + static constexpr bool is_irreflexive() { + return REL::is_irreflexive; + } + + /** @return Whether this relation is symmetric. */ + static constexpr bool is_symmetric() { + return REL::is_symmetric; + } + + /** @return Whether this relation is antisymmetric. */ + static constexpr bool is_antisymmetric() { + return REL::is_antisymmetric; + } + + /** @return Whether this relation is transitive. */ + static constexpr bool is_transitive() { + return REL::is_transitive; + } + + /** @return Whether this relation is connected. */ + static constexpr bool is_connected() { + return REL::is_connected; + } + + /** @return Whether this relation is strongly connected. */ + static constexpr bool is_strongly_connected() { + return REL::is_strongly_connected; + } + + /** + * Straightforward test of this relation. Returns if \f$ x REL y \f$. + * + * @tparam InputType1 The type of the input parameter \a x. + * @tparam InputType2 The type of the input parameter \a y. + * + * \warning If \a InputType1 does not match \a D1 \em or \a InputType2 does + * not match \a D2, then input will be cast into temporary + * variables of the correct types. + * + * \note Best performance is thus only guaranteed when all domains match. + * + * @param[in] x The left-hand side input. + * @param[in] y The right-hand side input. + */ + template< typename InputType1, typename InputType2 > + static bool test( const InputType1 & x, const InputType2 & y ) { + const D1 a = static_cast< D1 >( x ); + const D2 b = static_cast< D2 >( y ); + return REL::test( &a, &b ); + } + + /** + * This is the high-performance version of apply() in the sense that no + * casting is required. This version will be automatically caled whenever + * possible. + */ + static bool test( const D1 & x, const D2 & y ) { + return REL::test( &x, &y ); + } + }; + + /** + * TODO: Update for Relation + * This is the operator interface exposed to the GraphBLAS implementation. + * + * \warning Note that most GraphBLAS usage requires associative operators. + * While very easily possible to create non-associative operators + * using this interface, passing them to GraphBLAS functions, + * either explicitly or indirectly (by, e.g., including them in a + * alp::Monoid or alp::Semiring), will lead to undefined + * behaviour. + * + * This class wraps around a base operator of type \a OP we denote by + * \f$ \odot:\ D_1\times D_2 \to D_3 \f$. + * + * \parblock + * \par Base Operators + * + * The class \a OP is expected to define the following public function: + * - \a apply, which takes three pointers to parameters \f$ x \in D_1 \f$ + * \f$ y \in D_2 \f$, and \f$ z \in D_3 \f$ and computes + * \f$ z = x \odot y \f$. + * + * It is also expected to define the following types: + * - \a left_type, which corresponds to \f$ D_1 \f$, + * - \a right_type, which corresponds to \f$ D_2 \f$, + * - \a result_type, which corresponds to \f$ D_3 \f$. + * + * It is also expected to define the following two public boolean fields: + * - \a has_foldr + * - \a has_foldl + * + * If \a has_foldr is \a true, then the class \a OP is expected to also + * define the function + * - foldr, which takes two pointers to parameters \f$ x \in D_1 \f$ + * and \f$ z \in D_2 \subseteq D_3 \f$ and stores in \a z the result of + * \f$ x \odot z \f$. + * + * If \a has_foldl is \a true, the the class \a OP is expected to also + * define the function + * - foldl, which takes two pointers to parameters + * \f$ z \in D_1 \subseteq D_3 \f$ and \f$ y \in D_2 \f$ and stores in + * \a z the result of \f$ z \odot y \f$. + * + * For examples of these base operators, see alp::operators::internal::max + * or alp::operators::internal::mul. An example of a full implementation, + * in this case for numerical addition, is the following: + * + * \snippet internalops.hpp Example Base Operator Implementation + * + * \note GraphBLAS users should never call these functions directly. This + * documentation is provided for developers to understand or extend + * the current implementation, for example to include new operators. + * + * \warning When calling these functions directly, note that the pointers + * to the memory areas are declared using the \em restrict key + * word. One of the consequences is that all pointers given in a + * single call may never refer to the same memory area, or + * undefined behaviour is invoked. + * + * \endparblock + * + * \parblock + * \par The exposed GraphBLAS Operator Interface + * + * The Base Operators as illustrated above are wrapped by this class to + * provide a more convient API. It translates the functionality of any Base + * Operator and exposes the following interface instead: + * + * -# apply, which takes three parameters \f$ x, y, z \f$ of arbitrary + * types and computes \f$ z = x \odot y \f$ after performing any + * casting if required. + * -# foldr, which takes two parameters \f$ x, z \f$ of arbitrary types + * and computes \f$ z = x \odot z \f$ after performing any casting if + * required. + * -# foldl, which takes two parameters \f$ z, y \f$ of arbitrary types + * and computes \f$ z = z \odot y \f$ after performing any casting if + * required. + * -# eWiseApply, which takes three pointers to arrays \f$ x, y, z \f$ + * and a size \a n. The arrays can correspond to elements of any type, + * all three with length at least \a n. For every i-th element of the + * three arrays, on the values \f$ x_i, y_i, z_i \f$, \f$ z_i \f$ will + * be set to \f$ x_i \odot y_i \f$. + * -# foldrArray, which takes a pointer to an array \f$ x \f$, a + * parameter \f$ z \f$ of arbitrary type, and a size \n as parameters. + * The value \f$ z \f$ will be overwritten to \f$ x_i \odot z \f$ for + * each of the \f$ i \in \{ 0, 1, \ldots, n-1 \} \f$. The order of + * application, in the sense of which \f$ i \f$ are processed first, + * is undefined. + * -# foldlArray, which takes as parameters: \f$ z \f$ of arbitrary type, + * an array \f$ y \f$, and a size \n. The value \f$ z \f$ will be + * overwritten to \f$ z \odot y_i \f$ for each of the + * \f$ i \in \{ 0, 1, \ldots, n-1 \} \f$. The order of application, in + * the sense of which \f$ i \f$ are processed first, is undefined. + * \endparblock + * + * \note This class only allows wrapping of stateless base operators. This + * GraphBLAS implementation in principle allows for stateful + * operators, though they must be provided by a specialised class + * which directly implements the above public interface. + * + * @see OperatorBase::apply + * @see OperatorFR::foldr + * @see OperatorFL::foldl + * @see \ref OperatorNoFRFLeWiseApply + * @see Operator::foldrArray + * @see Operator::foldlArray + * + * \parblock + * \par Providing New Operators + * + * New operators are easily added to this + * GraphBLAS implementation by providing a base operator and wrapping this + * class around it, as illustrated, e.g., by alp::operators::add as follows: + * + * \snippet ops.hpp Operator Wrapping + * + * This need to be compatible with the GraphBLAS type traits, specifically, + * the #is_operator template. To ensure this, a specialisation of it must be + * privided: + * + * \snippet ops.hpp Operator Type Traits + * \endparblock + */ + template< typename REL, enum Backend implementation = config::default_backend > + class Relation : public RelationBase< REL > { + }; + + /** + * + * @tparam REL The generic homogeneous relation. + * + * @see Relation + * @see RelationBase for additional functions exposed to the final relation. + */ + template< + typename REL, + enum Backend implementation = config::default_backend, + typename std::enable_if< std::is_same< typename REL::left_type, typename REL::right_type >::value >::type + > + class HomogeneousRelation : public RelationBase< REL > { + }; + + + } // namespace internal + + } // namespace relations + +} // namespace alp + +#endif // _H_ALP_INTERNAL_OPERATORS_BASE + From cfd9030422e4550f8260504a168be4ee10c55c00 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 06/14] added first unit tests --- include/alp.hpp | 1 + include/alp/CMakeLists.txt | 1 + include/alp/base/internalrels.hpp | 61 +++++++++++++++++++++++-- include/alp/internalrels.hpp | 32 +++++++++++++ include/alp/rels.hpp | 70 +++++++++++++++++++++++++++++ include/alp/type_traits.hpp | 11 +++++ tests/unit/CMakeLists.txt | 4 ++ tests/unit/alp_relations.cpp | 74 +++++++++++++++++++++++++++++++ 8 files changed, 250 insertions(+), 4 deletions(-) create mode 100644 include/alp/internalrels.hpp create mode 100644 include/alp/rels.hpp create mode 100644 tests/unit/alp_relations.cpp diff --git a/include/alp.hpp b/include/alp.hpp index 7a3ac53e1..07e0cb0f3 100644 --- a/include/alp.hpp +++ b/include/alp.hpp @@ -152,6 +152,7 @@ // #include // #include // #include +#include // #include // #include diff --git a/include/alp/CMakeLists.txt b/include/alp/CMakeLists.txt index dd27afaae..c2256749b 100644 --- a/include/alp/CMakeLists.txt +++ b/include/alp/CMakeLists.txt @@ -48,6 +48,7 @@ set( root_files "ops.hpp" "phase.hpp" "rc.hpp" + "rels.hpp" "scalar.hpp" "semiring.hpp" "storage.hpp" diff --git a/include/alp/base/internalrels.hpp b/include/alp/base/internalrels.hpp index 32d6d2c4d..3786a9935 100644 --- a/include/alp/base/internalrels.hpp +++ b/include/alp/base/internalrels.hpp @@ -25,7 +25,7 @@ #include -#include "config.hpp" +#include namespace alp { @@ -165,7 +165,7 @@ namespace alp { * for all \a a, \a b in \a SET, if \f$ a < b \f$ and * \f$ b < a \f$ then \f$ a = b \f$. */ - static constexpr bool is_antisymmetric = false; + static constexpr bool is_antisymmetric = true; /** * Whether this relation is \em transitive; that is, @@ -503,6 +503,11 @@ namespace alp { */ template< typename REL, enum Backend implementation = config::default_backend > class Relation : public RelationBase< REL > { + + public: + typedef typename RelationBase< REL >::D1 D1; + typedef typename RelationBase< REL >::D2 D2; + }; /** @@ -515,11 +520,59 @@ namespace alp { template< typename REL, enum Backend implementation = config::default_backend, - typename std::enable_if< std::is_same< typename REL::left_type, typename REL::right_type >::value >::type + std::enable_if_t< + std::is_same< + typename REL::left_type, + typename REL::right_type + >::value + > * = nullptr > class HomogeneousRelation : public RelationBase< REL > { }; + template< + typename REL, + enum Backend implementation = config::default_backend, + std::enable_if_t< + REL::is_reflexive + && REL::is_transitive + && REL::is_antisymmetric + > * = nullptr + > + class PartialOrder : public HomogeneousRelation< REL > { + }; + + template< + typename REL, + enum Backend implementation = config::default_backend, + std::enable_if_t< + REL::is_irreflexive + && REL::is_transitive + && REL::is_antisymmetric + > * = nullptr + > + class StrictPartialOrder : public HomogeneousRelation< REL > { + }; + + template< + typename REL, + enum Backend implementation = config::default_backend, + std::enable_if_t< + REL::is_strongly_connected + > * = nullptr + > + class TotalOrder : public PartialOrder< REL > { + }; + + template< + typename REL, + enum Backend implementation = config::default_backend, + std::enable_if_t< + REL::is_connected + > * = nullptr + > + class StrictTotalOrder : public StrictPartialOrder< REL > { + }; } // namespace internal @@ -527,5 +580,5 @@ namespace alp { } // namespace alp -#endif // _H_ALP_INTERNAL_OPERATORS_BASE +#endif // _H_ALP_INTERNAL_RELATIONS_BASE diff --git a/include/alp/internalrels.hpp b/include/alp/internalrels.hpp new file mode 100644 index 000000000..7dcb5b0fa --- /dev/null +++ b/include/alp/internalrels.hpp @@ -0,0 +1,32 @@ + +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @author D. G. Spampinato + * @date 3 of November, 2022 + */ + +#ifndef _H_ALP_INTERNAL_RELATIONS +#define _H_ALP_INTERNAL_RELATIONS + +// certain backends may want to specialize these functionalities, +// e.g., for specific targets, e.g. to exploit dedicated hardware +// features +#include "base/internalrels.hpp" + +#endif + diff --git a/include/alp/rels.hpp b/include/alp/rels.hpp new file mode 100644 index 000000000..3f236bf1f --- /dev/null +++ b/include/alp/rels.hpp @@ -0,0 +1,70 @@ + +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @author D. G. Spampinato + * @date 3rd of November, 2022 + */ + +#ifndef _H_ALP_RELATIONS +#define _H_ALP_RELATIONS + +#include + +#include "internalrels.hpp" + +namespace alp { + + /** + * This namespace holds various standard operators such as #alp::relations::lt. + */ + namespace relations { + + /** + * This relation tests if two input parameters and writes it to + * the output variable. It exposes the complete interface detailed in + * alp::relations::internal::TotalOrder. + * + * \note A proper GraphBLAS program never uses the interface exposed by this + * operator directly, and instead simply passes the operator on to + * GraphBLAS functions. + * + * @tparam SET The domain and codomain of the relation. + * + * \warning This operator expects a numerical type for \a SET or types + * that have the appropriate operator<-functions available. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class lt : public internal::StrictTotalOrder< internal::lt< SET, implementation > > { + public: + lt() {} + }; + + } // namespace relations + + template< + typename IntRel, + enum Backend implementation + > + struct is_relation< relations::lt< IntRel, implementation > > { + static const constexpr bool value = true; + }; + +} // namespace alp + +#endif // end ``_H_ALP_RELATIONS'' + diff --git a/include/alp/type_traits.hpp b/include/alp/type_traits.hpp index a25163bf2..03160c843 100644 --- a/include/alp/type_traits.hpp +++ b/include/alp/type_traits.hpp @@ -118,6 +118,17 @@ namespace alp { static const constexpr bool value = false; }; + /** + * Used to inspect whether a given type is an ALP operator. + * + * @tparam T The type to inspect. + */ + template< typename T > + struct is_relation { + /** Base case: an arbitrary type is not an operator. */ + static const constexpr bool value = false; + }; + /** * Used to inspect whether a given type is an ALP operator. * diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 095591393..3674ad618 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -45,6 +45,10 @@ add_grb_executables( alp_type_traits alp_type_traits.cpp BACKENDS alp_reference ) +add_grb_executables( alp_relations alp_relations.cpp + BACKENDS alp_reference +) + add_grb_executables( alp_file_iterator alp_file_iterator.cpp BACKENDS alp_reference ) diff --git a/tests/unit/alp_relations.cpp b/tests/unit/alp_relations.cpp new file mode 100644 index 000000000..45d8cdabe --- /dev/null +++ b/tests/unit/alp_relations.cpp @@ -0,0 +1,74 @@ + +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +void alp_program( const size_t & n, alp::RC & rc ) { + + (void) n; + + /* Basic checks */ + typedef alp::relations::lt< double > dbl_lt; + + if( ! alp::is_relation< dbl_lt >::value ) { + rc = alp::FAILED; + return; + } + + if( ! dbl_lt::test(2.4, 5) ) { + rc = alp::FAILED; + return; + } + + rc = alp::SUCCESS; + +} + +int main( int argc, char ** argv ) { + // defaults + bool printUsage = false; + size_t in; + + // error checking + if( argc > 1 ) { + printUsage = true; + } + + if( printUsage ) { + std::cerr << "Usage: " << argv[ 0 ] << "\n"; + return 1; + } + + std::cout << "This is functional test " << argv[ 0 ] << "\n"; + alp::Launcher< alp::AUTOMATIC > launcher; + alp::RC out; + if( launcher.exec( &alp_program, in, out, true ) != alp::SUCCESS ) { + std::cerr << "Launching test FAILED\n"; + return 255; + } + if( out != alp::SUCCESS ) { + std::cerr << "Test FAILED (" << alp::toString( out ) << ")" << std::endl; + } else { + std::cout << "Test OK" << std::endl; + } + return 0; +} + From f6063acd9735263a61dbc3a4f4f7168a77211676 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 07/14] adapted sort to use the ALP relations' proposed draft --- include/alp/base/internalrels.hpp | 201 ++++++++++++------------------ include/alp/reference/blas1.hpp | 56 ++------- include/alp/rels.hpp | 106 +++++++++++++++- include/alp/type_traits.hpp | 4 +- tests/unit/alp_relations.cpp | 135 +++++++++++++++++++- tests/unit/dense_sort.cpp | 32 ++--- 6 files changed, 339 insertions(+), 195 deletions(-) diff --git a/include/alp/base/internalrels.hpp b/include/alp/base/internalrels.hpp index 3786a9935..686920f9c 100644 --- a/include/alp/base/internalrels.hpp +++ b/include/alp/base/internalrels.hpp @@ -35,19 +35,19 @@ namespace alp { namespace internal { /** - * Standard less then or equal (le) operator. + * Standard less then (lt) operator. * - * Assumes native availability of operator <= on the given data types + * Assumes native availability of operator < on the given data types * or assumes that the relevant operators are properly overloaded. * - * Assumes that the <= operator is a total order. Non-standard/non-matching - * data types or non-standard (overloaded) <= operators should + * Assumes that the < operator is a strict total order. Non-standard/non-matching + * data types or non-standard (overloaded) < operators should * therefore be used with caution. * * @tparam SET The input data type. */ template< typename SET, enum Backend implementation = config::default_backend > - class le { + class lt { public: /** Alias to the left-hand input data type. */ @@ -58,81 +58,81 @@ namespace alp { /** * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a \le a \f$. + * for all \a a in \a SET, \f$ a < a \f$. */ - static constexpr bool is_reflexive = true; + static constexpr bool is_reflexive = false; /** * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a \le a \f$. + * for all \a a in \a SET, not \f$ a < a \f$. */ - static constexpr bool is_irreflexive = false; + static constexpr bool is_irreflexive = true; /** * Whether this relation is \em symmetric; that is, * for all \a a, \a b in \a SET, - * if \f$ a \le b \f$ then \f$ b \le a \f$. + * if \f$ a < b \f$ then \f$ b < a \f$. */ static constexpr bool is_symmetric = false; /** * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a \le b \f$ and - * \f$ b \le a \f$ then \f$ a = b \f$. + * for all \a a, \a b in \a SET, if \f$ a < b \f$ and + * \f$ b < a \f$ then \f$ a = b \f$. */ static constexpr bool is_antisymmetric = true; /** * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a \le b \f$ and - * \f$ b \le c \f$ then \f$ a \le c \f$. + * for all \a a, \a b, \a c in \a SET, if \f$ a < b \f$ and + * \f$ b < c \f$ then \f$ a < c \f$. */ static constexpr bool is_transitive = true; /** - * Whether this relation is \em connected; that is, + * Whether this relation is \em connected (or total); that is, * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a \le b \f$ or \f$ b \le a \f$. + * either \f$ a < b \f$ or \f$ b < a \f$. */ static constexpr bool is_connected = true; /** - * Whether this relation is strongly connected (or total); + * Whether this relation is strongly connected ; * that is, * for all \a a, \a b in \a SET, - * either \f$ a \le b \f$ or \f$ b \le a \f$. + * either \f$ a < b \f$ or \f$ b < a \f$. */ - static constexpr bool is_strongly_connected = true; + static constexpr bool is_strongly_connected = false; /** - * Check if a <= b. + * This function checks if a < b . * * @param[in] a The left-hand side input. Must be pre-allocated and initialised. * @param[in] b The right-hand side input. Must be pre-allocated and initialised. * * \warning Passing invalid pointers will result in UB. */ - static bool test( const left_type * __restrict__ const a, + static bool check( const left_type * __restrict__ const a, const right_type * __restrict__ const b ) { - return *a <= *b; + return *a < *b; } }; /** - * Standard less then (lt) operator. + * Standard equal (eq) operator. * - * Assumes native availability of operator < on the given data types + * Assumes native availability of operator == on the given data types * or assumes that the relevant operators are properly overloaded. * - * Assumes that the < operator is a strict total order. Non-standard/non-matching - * data types or non-standard (overloaded) < operators should - * therefore be used with caution. + * Assumes that the == operator is an equivalence relation. + * Non-standard/non-matching data types or non-standard (overloaded) + * == operators should therefore be used with caution. * * @tparam SET The input data type. */ template< typename SET, enum Backend implementation = config::default_backend > - class lt { + class eq { public: /** Alias to the left-hand input data type. */ @@ -143,81 +143,81 @@ namespace alp { /** * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a < a \f$. + * for all \a a in \a SET, \f$ a = a \f$. */ - static constexpr bool is_reflexive = false; + static constexpr bool is_reflexive = true; /** * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a < a \f$. + * for all \a a in \a SET, not \f$ a = a \f$. */ - static constexpr bool is_irreflexive = true; + static constexpr bool is_irreflexive = false; /** * Whether this relation is \em symmetric; that is, * for all \a a, \a b in \a SET, - * if \f$ a < b \f$ then \f$ b < a \f$. + * if \f$ a = b \f$ then \f$ b = a \f$. */ - static constexpr bool is_symmetric = false; + static constexpr bool is_symmetric = true; /** * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a < b \f$ and - * \f$ b < a \f$ then \f$ a = b \f$. + * for all \a a, \a b in \a SET, if \f$ a = b \f$ and + * \f$ b = a \f$ then \f$ a = b \f$. */ static constexpr bool is_antisymmetric = true; /** * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a < b \f$ and - * \f$ b < c \f$ then \f$ a < c \f$. + * for all \a a, \a b, \a c in \a SET, if \f$ a = b \f$ and + * \f$ b = c \f$ then \f$ a = c \f$. */ static constexpr bool is_transitive = true; /** - * Whether this relation is \em connected (or total); that is, + * Whether this relation is \em connected; that is, * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a < b \f$ or \f$ b < a \f$. + * either \f$ a = b \f$ or \f$ b = a \f$. */ - static constexpr bool is_connected = true; + static constexpr bool is_connected = false; /** - * Whether this relation is strongly connected ; + * Whether this relation is strongly connected (or total); * that is, * for all \a a, \a b in \a SET, - * either \f$ a < b \f$ or \f$ b < a \f$. + * either \f$ a = b \f$ or \f$ b = a \f$. */ static constexpr bool is_strongly_connected = false; /** - * Check if a < b. + * This function checks if a == b . * * @param[in] a The left-hand side input. Must be pre-allocated and initialised. * @param[in] b The right-hand side input. Must be pre-allocated and initialised. * * \warning Passing invalid pointers will result in UB. */ - static bool test( const left_type * __restrict__ const a, + static bool check( const left_type * __restrict__ const a, const right_type * __restrict__ const b ) { - return *a < *b; + return *a == *b; } }; /** - * Standard equal (eq) operator. + * Standard less then or equal (le) operator. * - * Assumes native availability of operator == on the given data types + * Assumes native availability of operator <= on the given data types * or assumes that the relevant operators are properly overloaded. * - * Assumes that the == operator is an equivalence relation. - * Non-standard/non-matching data types or non-standard (overloaded) - * == operators should therefore be used with caution. + * Assumes that the <= operator is a total order. Non-standard/non-matching + * data types or non-standard (overloaded) <= operators should + * therefore be used with caution. * * @tparam SET The input data type. */ template< typename SET, enum Backend implementation = config::default_backend > - class eq { + class le { public: /** Alias to the left-hand input data type. */ @@ -228,68 +228,67 @@ namespace alp { /** * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a = a \f$. + * for all \a a in \a SET, \f$ a \le a \f$. */ static constexpr bool is_reflexive = true; /** * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a = a \f$. + * for all \a a in \a SET, not \f$ a \le a \f$. */ static constexpr bool is_irreflexive = false; /** * Whether this relation is \em symmetric; that is, * for all \a a, \a b in \a SET, - * if \f$ a = b \f$ then \f$ b = a \f$. + * if \f$ a \le b \f$ then \f$ b \le a \f$. */ - static constexpr bool is_symmetric = true; + static constexpr bool is_symmetric = false; /** * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a = b \f$ and - * \f$ b = a \f$ then \f$ a = b \f$. + * for all \a a, \a b in \a SET, if \f$ a \le b \f$ and + * \f$ b \le a \f$ then \f$ a = b \f$. */ static constexpr bool is_antisymmetric = true; /** * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a = b \f$ and - * \f$ b = c \f$ then \f$ a = c \f$. + * for all \a a, \a b, \a c in \a SET, if \f$ a \le b \f$ and + * \f$ b \le c \f$ then \f$ a \le c \f$. */ static constexpr bool is_transitive = true; /** * Whether this relation is \em connected; that is, * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a = b \f$ or \f$ b = a \f$. + * either \f$ a \le b \f$ or \f$ b \le a \f$. */ - static constexpr bool is_connected = false; + static constexpr bool is_connected = true; /** * Whether this relation is strongly connected (or total); * that is, * for all \a a, \a b in \a SET, - * either \f$ a = b \f$ or \f$ b = a \f$. + * either \f$ a \le b \f$ or \f$ b \le a \f$. */ - static constexpr bool is_strongly_connected = false; + static constexpr bool is_strongly_connected = true; /** - * Check if a == b. + * This function checks if a <= b . * * @param[in] a The left-hand side input. Must be pre-allocated and initialised. * @param[in] b The right-hand side input. Must be pre-allocated and initialised. * * \warning Passing invalid pointers will result in UB. */ - static bool test( const left_type * __restrict__ const a, + static bool check( const left_type * __restrict__ const a, const right_type * __restrict__ const b ) { - return *a == *b; + return *a <= *b; } }; - /** * This class takes a generic operator implementation and exposes a more * convenient test() function based on it. This function allows arbitrary @@ -347,7 +346,7 @@ namespace alp { } /** - * Straightforward test of this relation. Returns if \f$ x REL y \f$. + * This function checks if \f$ x REL y \f$. * * @tparam InputType1 The type of the input parameter \a x. * @tparam InputType2 The type of the input parameter \a y. @@ -362,19 +361,19 @@ namespace alp { * @param[in] y The right-hand side input. */ template< typename InputType1, typename InputType2 > - static bool test( const InputType1 & x, const InputType2 & y ) { + static bool check( const InputType1 & x, const InputType2 & y ) { const D1 a = static_cast< D1 >( x ); const D2 b = static_cast< D2 >( y ); - return REL::test( &a, &b ); + return REL::check( &a, &b ); } /** - * This is the high-performance version of apply() in the sense that no - * casting is required. This version will be automatically caled whenever + * This is the high-performance version of check() in the sense that no + * casting is required. This version will be automatically called whenever * possible. */ - static bool test( const D1 & x, const D2 & y ) { - return REL::test( &x, &y ); + static bool check( const D1 & x, const D2 & y ) { + return REL::check( &x, &y ); } }; @@ -502,11 +501,11 @@ namespace alp { * \endparblock */ template< typename REL, enum Backend implementation = config::default_backend > - class Relation : public RelationBase< REL > { + class Relation : public RelationBase< REL, implementation > { public: - typedef typename RelationBase< REL >::D1 D1; - typedef typename RelationBase< REL >::D2 D2; + typedef typename RelationBase< REL, implementation >::D1 D1; + typedef typename RelationBase< REL, implementation >::D2 D2; }; @@ -527,51 +526,7 @@ namespace alp { >::value > * = nullptr > - class HomogeneousRelation : public RelationBase< REL > { - }; - - template< - typename REL, - enum Backend implementation = config::default_backend, - std::enable_if_t< - REL::is_reflexive - && REL::is_transitive - && REL::is_antisymmetric - > * = nullptr - > - class PartialOrder : public HomogeneousRelation< REL > { - }; - - template< - typename REL, - enum Backend implementation = config::default_backend, - std::enable_if_t< - REL::is_irreflexive - && REL::is_transitive - && REL::is_antisymmetric - > * = nullptr - > - class StrictPartialOrder : public HomogeneousRelation< REL > { - }; - - template< - typename REL, - enum Backend implementation = config::default_backend, - std::enable_if_t< - REL::is_strongly_connected - > * = nullptr - > - class TotalOrder : public PartialOrder< REL > { - }; - - template< - typename REL, - enum Backend implementation = config::default_backend, - std::enable_if_t< - REL::is_connected - > * = nullptr - > - class StrictTotalOrder : public StrictPartialOrder< REL > { + class HomogeneousRelation : public Relation< REL, implementation > { }; } // namespace internal diff --git a/include/alp/reference/blas1.hpp b/include/alp/reference/blas1.hpp index 2e9b4243e..f74fe8f76 100644 --- a/include/alp/reference/blas1.hpp +++ b/include/alp/reference/blas1.hpp @@ -26,8 +26,11 @@ #include #include #include -#include #include +#include +#include +#include +#include #include "scalar.hpp" #include "matrix.hpp" #include "vector.hpp" @@ -2229,57 +2232,20 @@ namespace alp { * * Complexity should be \Theta(n*log(n)), and space complexity should be \Theta(n+T+P) */ - template< - typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, - typename ValueType, typename ValueStructure, typename ValueView, typename ValueImfR, typename ValueImfC - > - RC sort( - Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, - const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort - ) noexcept { - - // TODO: this overload has to go. A partial order is always needed. - - internal::setInitialized( permutation, internal::getInitialized( toSort ) ); - - if( !internal::getInitialized( toSort ) ) { - return SUCCESS; - } - - RC rc = alp::eWiseLambda( - [ ]( const size_t i, size_t &val ) { - val = i; - }, - permutation - ); - - typedef Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > PermType; - - typename PermType::iterator it_begin = internal::begin( permutation ); - typename PermType::iterator it_end = internal::end( permutation ); - - std::sort( it_begin, it_end, [ &toSort ]( const size_t& a, const size_t& b ) { - return toSort[ a ] < toSort[ b ]; - }); - - return rc; - } - template< typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, typename ValueType, typename ValueStructure, typename ValueView, typename ValueImfR, typename ValueImfC, - typename CompareType + typename Relation = relations::lt< ValueType, reference > > RC sort( Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort, - CompareType cmp + const Relation &rel = Relation(), + const std::enable_if_t< + ! alp::is_object< ValueType >::value && alp::is_relation< Relation >::value + > * const = nullptr ) noexcept { - if ( getLength( permutation ) != getLength( toSort ) ) { - return ILLEGAL; - } - internal::setInitialized( permutation, internal::getInitialized( toSort ) ); if( !internal::getInitialized( toSort ) ) { @@ -2298,8 +2264,8 @@ namespace alp { typename PermType::iterator it_begin = internal::begin( permutation ); typename PermType::iterator it_end = internal::end( permutation ); - std::sort( it_begin, it_end, [ &toSort, &cmp ]( const size_t& a, const size_t& b ) { - return cmp( toSort[ a ], toSort[ b ] ); + std::sort( it_begin, it_end, [ &toSort, &rel ]( const size_t& a, const size_t& b ) { + return rel.check( toSort[ a ], toSort[ b ] ); }); return rc; diff --git a/include/alp/rels.hpp b/include/alp/rels.hpp index 3f236bf1f..8bb3eb7de 100644 --- a/include/alp/rels.hpp +++ b/include/alp/rels.hpp @@ -35,12 +35,12 @@ namespace alp { namespace relations { /** - * This relation tests if two input parameters and writes it to - * the output variable. It exposes the complete interface detailed in - * alp::relations::internal::TotalOrder. + * This class implements the less-then relation. + * It exposes the complete interface detailed in + * \a alp::relations::internal::HomogeneousRelation. * * \note A proper GraphBLAS program never uses the interface exposed by this - * operator directly, and instead simply passes the operator on to + * relation directly, and instead simply passes the relation on to * GraphBLAS functions. * * @tparam SET The domain and codomain of the relation. @@ -49,14 +49,54 @@ namespace alp { * that have the appropriate operator<-functions available. */ template< typename SET, enum Backend implementation = config::default_backend > - class lt : public internal::StrictTotalOrder< internal::lt< SET, implementation > > { + class lt : public internal::HomogeneousRelation< internal::lt< SET, implementation > > { public: lt() {} }; + /** + * This class implements the equality relation. + * It exposes the complete interface detailed in + * \a alp::relations::internal::HomogeneousRelation. + * + * \note A proper GraphBLAS program never uses the interface exposed by this + * operator directly, and instead simply passes the operator on to + * GraphBLAS functions. + * + * @tparam SET The domain and codomain of the relation. + * + * \warning This operator expects a numerical type for \a SET or types + * that have the appropriate operator==-functions available. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class eq : public internal::HomogeneousRelation< internal::eq< SET, implementation > > { + public: + eq() {} + }; + + /** + * This class implements the less-then-or-equal relation. + * It exposes the complete interface detailed in + * \a alp::relations::internal::HomogeneousRelation. + * + * \note A proper GraphBLAS program never uses the interface exposed by this + * operator directly, and instead simply passes the operator on to + * GraphBLAS functions. + * + * @tparam SET The domain and codomain of the relation. + * + * \warning This operator expects a numerical type for \a SET or types + * that have the appropriate operator<=-functions available. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class le : public internal::HomogeneousRelation< internal::le< SET, implementation > > { + public: + le() {} + }; + } // namespace relations - template< + template< typename IntRel, enum Backend implementation > @@ -64,6 +104,60 @@ namespace alp { static const constexpr bool value = true; }; + template< + typename IntRel, + enum Backend implementation + > + struct is_relation< relations::eq< IntRel, implementation > > { + static const constexpr bool value = true; + }; + + template< + typename IntRel, + enum Backend implementation + > + struct is_relation< relations::le< IntRel, implementation > > { + static const constexpr bool value = true; + }; + + template< typename Rel > + struct is_partial_order { + static const constexpr bool value = is_relation< Rel >::value + and Rel::is_reflexive() + and Rel::is_transitive() + and Rel::is_antisymmetric(); + }; + + template< typename Rel > + struct is_strict_partial_order { + static const constexpr bool value = is_relation< Rel >::value + and Rel::is_irreflexive() + and Rel::is_transitive() + and Rel::is_antisymmetric(); + }; + + template< typename Rel > + struct is_total_order { + static const constexpr bool value = is_relation< Rel >::value + and is_partial_order< Rel >::value + and Rel::is_strongly_connected(); + }; + + template< typename Rel > + struct is_strict_total_order { + static const constexpr bool value = is_relation< Rel >::value + and is_strict_partial_order< Rel >::value + and Rel::is_connected(); + }; + + template< typename Rel > + struct is_equivalence_relation { + static const constexpr bool value = is_relation< Rel >::value + and Rel::is_reflexive() + and Rel::is_transitive() + and Rel::is_symmetric(); + }; + } // namespace alp #endif // end ``_H_ALP_RELATIONS'' diff --git a/include/alp/type_traits.hpp b/include/alp/type_traits.hpp index 03160c843..69c59881d 100644 --- a/include/alp/type_traits.hpp +++ b/include/alp/type_traits.hpp @@ -119,13 +119,13 @@ namespace alp { }; /** - * Used to inspect whether a given type is an ALP operator. + * Used to inspect whether a given type is an ALP relation. * * @tparam T The type to inspect. */ template< typename T > struct is_relation { - /** Base case: an arbitrary type is not an operator. */ + /** Base case: an arbitrary type is not a relation. */ static const constexpr bool value = false; }; diff --git a/tests/unit/alp_relations.cpp b/tests/unit/alp_relations.cpp index 45d8cdabe..fce8518be 100644 --- a/tests/unit/alp_relations.cpp +++ b/tests/unit/alp_relations.cpp @@ -25,15 +25,144 @@ void alp_program( const size_t & n, alp::RC & rc ) { (void) n; - /* Basic checks */ + /** + * Basic checks on the less-then relation (lt). + * lt is a strict total order and therefore also a strict partial order. + */ typedef alp::relations::lt< double > dbl_lt; - if( ! alp::is_relation< dbl_lt >::value ) { + static_assert( alp::is_relation< dbl_lt >::value ); + + static_assert( ! alp::is_partial_order< dbl_lt >::value ); + + static_assert( alp::is_strict_partial_order< dbl_lt >::value ); + + static_assert( ! alp::is_total_order< dbl_lt >::value ); + + static_assert( alp::is_strict_total_order< dbl_lt >::value ); + + static_assert( ! alp::is_equivalence_relation< dbl_lt >::value ); + + if( ! dbl_lt::check(2.4, 5) ) { +#ifndef NDEBUG + std::cerr << "dbl_lt::test(2.4, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( dbl_lt::check(5, 2.4) ) { +#ifndef NDEBUG + std::cerr << "dbl_lt::check(5, 2.4) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( dbl_lt::check(5, 5) ) { +#ifndef NDEBUG + std::cerr << "dbl_lt::test(5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + /** + * Basic checks on the equality relation (eq). + * eq is both an equivalence relation and a partial order. + */ + typedef alp::relations::eq< int > int_eq; + + static_assert( alp::is_relation< int_eq >::value ); + + static_assert( alp::is_partial_order< int_eq >::value ); + + static_assert( ! alp::is_strict_partial_order< int_eq >::value ); + + static_assert( ! alp::is_total_order< int_eq >::value ); + + static_assert( ! alp::is_strict_total_order< int_eq >::value ); + + static_assert( alp::is_equivalence_relation< int_eq >::value ); + + if( int_eq::check(2.4, 5) ) { +#ifndef NDEBUG + std::cerr << "int_eq::test(2.4, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( int_eq::check(5, 2.4) ) { +#ifndef NDEBUG + std::cerr << "int_eq::check(5, 2.4) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! int_eq::check(5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_eq::test(5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! int_eq::check(5.5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_eq::test(5.5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + /** + * Basic checks on the less-then-or-equal relation (le). + * le is both a total order and therefore also a partial order. + */ + typedef alp::relations::le< int > int_le; + + static_assert( alp::is_relation< int_le >::value ); + + static_assert( alp::is_partial_order< int_le >::value ); + + static_assert( ! alp::is_strict_partial_order< int_le >::value ); + + static_assert( alp::is_total_order< int_le >::value ); + + static_assert( ! alp::is_strict_total_order< int_le >::value ); + + static_assert( ! alp::is_equivalence_relation< int_le >::value ); + + if( ! int_le::check(2.4, 5) ) { +#ifndef NDEBUG + std::cerr << "int_le::test(2.4, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( int_le::check(5, 2.4) ) { +#ifndef NDEBUG + std::cerr << "int_le::check(5, 2.4) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! int_le::check(5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_le::test(5, 5) failed." << std::endl; +#endif rc = alp::FAILED; return; } - if( ! dbl_lt::test(2.4, 5) ) { + if( ! int_le::check(5.5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_le::test(5.5, 5) failed." << std::endl; +#endif rc = alp::FAILED; return; } diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp index 90f95ee57..f04a31a8f 100644 --- a/tests/unit/dense_sort.cpp +++ b/tests/unit/dense_sort.cpp @@ -78,27 +78,27 @@ void alp_program( const size_t &n, alp::RC &rc ) { return; } - auto desc_cmp = []( const T& a, const T& b) { - return a > b; - }; + // // Check descending sorted view + // alp::sort( perm, v, alp::relations:: ); - // Check descending sorted view - alp::sort( perm, v, desc_cmp ); + // auto desc_cmp = []( const T& a, const T& b) { + // return a > b; + // }; - std::sort( std::begin( stdv ), std::end( stdv ), desc_cmp ); + // std::sort( std::begin( stdv ), std::end( stdv ), desc_cmp ); - auto desc_sorted_v = alp::get_view< alp::structures::General >( v, perm ); + // auto desc_sorted_v = alp::get_view< alp::structures::General >( v, perm ); - // Check sorted view - for( size_t i = 0; i < n; i++ ) { - if( stdv[i] != desc_sorted_v[ i ] ) { - std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" << std::endl; - rc = alp::FAILED; - } - } + // // Check sorted view + // for( size_t i = 0; i < n; i++ ) { + // if( stdv[i] != desc_sorted_v[ i ] ) { + // std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" << std::endl; + // rc = alp::FAILED; + // } + // } - std::cout << "Sorted alp::Vector in descending order:" << std::endl; - print_vector("desc_sorted_v", desc_sorted_v); + // std::cout << "Sorted alp::Vector in descending order:" << std::endl; + // print_vector("desc_sorted_v", desc_sorted_v); rc = alp::SUCCESS; } From d624b56100130733a107237a59568ae30f0a8aec Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 08/14] Added missing basic relations and respective unit tests. --- include/alp/base/internalrels.hpp | 281 ++++++++++++++++++++++++++++-- include/alp/rels.hpp | 88 +++++++++- tests/unit/alp_relations.cpp | 146 +++++++++++++++- tests/unit/dense_sort.cpp | 52 ++++-- 4 files changed, 534 insertions(+), 33 deletions(-) diff --git a/include/alp/base/internalrels.hpp b/include/alp/base/internalrels.hpp index 686920f9c..480206751 100644 --- a/include/alp/base/internalrels.hpp +++ b/include/alp/base/internalrels.hpp @@ -35,13 +35,13 @@ namespace alp { namespace internal { /** - * Standard less then (lt) operator. + * Standard less-than (\a lt) operator. * - * Assumes native availability of operator < on the given data types + * Assumes native availability of operator< on the given data types * or assumes that the relevant operators are properly overloaded. * - * Assumes that the < operator is a strict total order. Non-standard/non-matching - * data types or non-standard (overloaded) < operators should + * Assumes that \a lt is a strict total order. Non-standard/non-matching + * data types or non-standard (overloaded) \a operator< should * therefore be used with caution. * * @tparam SET The input data type. @@ -120,14 +120,99 @@ namespace alp { }; /** - * Standard equal (eq) operator. + * Standard greater-than (\a gt) operator. * - * Assumes native availability of operator == on the given data types + * Assumes native availability of \a operator> on the given data types * or assumes that the relevant operators are properly overloaded. * - * Assumes that the == operator is an equivalence relation. + * Assumes that \a gt is a strict total order. Non-standard/non-matching + * data types or non-standard (overloaded) \a operator> should + * therefore be used with caution. + * + * @tparam SET The input data type. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class gt { + + public: + /** Alias to the left-hand input data type. */ + typedef SET left_type; + + /** Alias to the right-hand input data type. */ + typedef SET right_type; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a > a \f$. + */ + static constexpr bool is_reflexive = false; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a > a \f$. + */ + static constexpr bool is_irreflexive = true; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a > b \f$ then \f$ b > a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a > b \f$ and + * \f$ b > a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a > b \f$ and + * \f$ b > c \f$ then \f$ a > c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected (or total); that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a > b \f$ or \f$ b > a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected ; + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a > b \f$ or \f$ b > a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * This function checks if a > b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const left_type * __restrict__ const a, + const right_type * __restrict__ const b + ) { + return *a > *b; + } + }; + + /** + * Standard equal (\a eq) relation. + * + * Assumes native availability of \a operator== on the given data types + * or assumes that the relevant operators are properly overloaded. + * + * Assumes that \a eq is an equivalence relation. * Non-standard/non-matching data types or non-standard (overloaded) - * == operators should therefore be used with caution. + * \a operator== should therefore be used with caution. * * @tparam SET The input data type. */ @@ -205,13 +290,100 @@ namespace alp { }; /** - * Standard less then or equal (le) operator. + * Standard not-equal (\a neq) operator. * - * Assumes native availability of operator <= on the given data types + * Assumes native availability of \a operator!= on the given data types * or assumes that the relevant operators are properly overloaded. * - * Assumes that the <= operator is a total order. Non-standard/non-matching - * data types or non-standard (overloaded) <= operators should + * While \a neq does not require two values to be members of + * an ordered set, the relation is still assumed to be irreflexive, + * symmetric and connected. + * Non-standard/non-matching data types or non-standard (overloaded) + * \a operator!= should therefore be used with caution. + * + * @tparam SET The input data type. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class neq { + + public: + /** Alias to the left-hand input data type. */ + typedef SET left_type; + + /** Alias to the right-hand input data type. */ + typedef SET right_type; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a \neq a \f$. + */ + static constexpr bool is_reflexive = false; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a \neq a \f$. + */ + static constexpr bool is_irreflexive = true; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a \neq b \f$ then \f$ b \neq a \f$. + */ + static constexpr bool is_symmetric = true; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ and + * \f$ b \neq a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = false; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a \neq b \f$ and + * \f$ b \neq c \f$ then \f$ a \neq c \f$. + */ + static constexpr bool is_transitive = false; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a \neq b \f$ or \f$ b \neq a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a \neq b \f$ or \f$ b \neq a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * This function checks if a != b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const left_type * __restrict__ const a, + const right_type * __restrict__ const b + ) { + return *a != *b; + } + }; + + /** + * Standard less-than-or-equal (\a le) operator. + * + * Assumes native availability of \a operator<= on the given data types + * or assumes that the relevant operators are properly overloaded. + * + * Assumes that \a le is a total order. Non-standard/non-matching + * data types or non-standard (overloaded) \a operator<= should * therefore be used with caution. * * @tparam SET The input data type. @@ -289,6 +461,91 @@ namespace alp { } }; + /** + * Standard greater-than-or-equal (\a ge) operator. + * + * Assumes native availability of \a operator>= on the given data types + * or assumes that the relevant operators are properly overloaded. + * + * Assumes that \a ge is a total order. Non-standard/non-matching + * data types or non-standard (overloaded) \a operator>= should + * therefore be used with caution. + * + * @tparam SET The input data type. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class ge { + + public: + /** Alias to the left-hand input data type. */ + typedef SET left_type; + + /** Alias to the right-hand input data type. */ + typedef SET right_type; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a \ge a \f$. + */ + static constexpr bool is_reflexive = true; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a \ge a \f$. + */ + static constexpr bool is_irreflexive = false; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a \ge b \f$ then \f$ b \ge a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a \ge b \f$ and + * \f$ b \ge a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a \ge b \f$ and + * \f$ b \ge c \f$ then \f$ a \ge c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a \ge b \f$ or \f$ b \ge a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a \ge b \f$ or \f$ b \ge a \f$. + */ + static constexpr bool is_strongly_connected = true; + + /** + * This function checks if a >= b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const left_type * __restrict__ const a, + const right_type * __restrict__ const b + ) { + return *a >= *b; + } + }; + /** * This class takes a generic operator implementation and exposes a more * convenient test() function based on it. This function allows arbitrary diff --git a/include/alp/rels.hpp b/include/alp/rels.hpp index 8bb3eb7de..23a480d2a 100644 --- a/include/alp/rels.hpp +++ b/include/alp/rels.hpp @@ -35,7 +35,7 @@ namespace alp { namespace relations { /** - * This class implements the less-then relation. + * This class implements the less-than relation. * It exposes the complete interface detailed in * \a alp::relations::internal::HomogeneousRelation. * @@ -54,6 +54,26 @@ namespace alp { lt() {} }; + /** + * This class implements the greater-than relation. + * It exposes the complete interface detailed in + * \a alp::relations::internal::HomogeneousRelation. + * + * \note A proper GraphBLAS program never uses the interface exposed by this + * relation directly, and instead simply passes the relation on to + * GraphBLAS functions. + * + * @tparam SET The domain and codomain of the relation. + * + * \warning This operator expects a numerical type for \a SET or types + * that have the appropriate operator>-functions available. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class gt : public internal::HomogeneousRelation< internal::gt< SET, implementation > > { + public: + gt() {} + }; + /** * This class implements the equality relation. * It exposes the complete interface detailed in @@ -75,7 +95,27 @@ namespace alp { }; /** - * This class implements the less-then-or-equal relation. + * This class implements the not-equal relation. + * It exposes the complete interface detailed in + * \a alp::relations::internal::HomogeneousRelation. + * + * \note A proper GraphBLAS program never uses the interface exposed by this + * operator directly, and instead simply passes the operator on to + * GraphBLAS functions. + * + * @tparam SET The domain and codomain of the relation. + * + * \warning This operator expects a numerical type for \a SET or types + * that have the appropriate operator==-functions available. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class neq : public internal::HomogeneousRelation< internal::neq< SET, implementation > > { + public: + neq() {} + }; + + /** + * This class implements the less-than-or-equal relation. * It exposes the complete interface detailed in * \a alp::relations::internal::HomogeneousRelation. * @@ -94,6 +134,26 @@ namespace alp { le() {} }; + /** + * This class implements the greater-than-or-equal relation. + * It exposes the complete interface detailed in + * \a alp::relations::internal::HomogeneousRelation. + * + * \note A proper GraphBLAS program never uses the interface exposed by this + * operator directly, and instead simply passes the operator on to + * GraphBLAS functions. + * + * @tparam SET The domain and codomain of the relation. + * + * \warning This operator expects a numerical type for \a SET or types + * that have the appropriate operator<=-functions available. + */ + template< typename SET, enum Backend implementation = config::default_backend > + class ge : public internal::HomogeneousRelation< internal::ge< SET, implementation > > { + public: + ge() {} + }; + } // namespace relations template< @@ -104,6 +164,14 @@ namespace alp { static const constexpr bool value = true; }; + template< + typename IntRel, + enum Backend implementation + > + struct is_relation< relations::gt< IntRel, implementation > > { + static const constexpr bool value = true; + }; + template< typename IntRel, enum Backend implementation @@ -112,6 +180,14 @@ namespace alp { static const constexpr bool value = true; }; + template< + typename IntRel, + enum Backend implementation + > + struct is_relation< relations::neq< IntRel, implementation > > { + static const constexpr bool value = true; + }; + template< typename IntRel, enum Backend implementation @@ -120,6 +196,14 @@ namespace alp { static const constexpr bool value = true; }; + template< + typename IntRel, + enum Backend implementation + > + struct is_relation< relations::ge< IntRel, implementation > > { + static const constexpr bool value = true; + }; + template< typename Rel > struct is_partial_order { static const constexpr bool value = is_relation< Rel >::value diff --git a/tests/unit/alp_relations.cpp b/tests/unit/alp_relations.cpp index fce8518be..b89174075 100644 --- a/tests/unit/alp_relations.cpp +++ b/tests/unit/alp_relations.cpp @@ -26,7 +26,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { (void) n; /** - * Basic checks on the less-then relation (lt). + * Basic checks on the less-than relation (lt). * lt is a strict total order and therefore also a strict partial order. */ typedef alp::relations::lt< double > dbl_lt; @@ -67,6 +67,48 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } + /** + * Basic checks on the greater-than relation (gt). + * lt is a strict total order and therefore also a strict partial order. + */ + typedef alp::relations::gt< double > dbl_gt; + + static_assert( alp::is_relation< dbl_gt >::value ); + + static_assert( ! alp::is_partial_order< dbl_gt >::value ); + + static_assert( alp::is_strict_partial_order< dbl_gt >::value ); + + static_assert( ! alp::is_total_order< dbl_gt >::value ); + + static_assert( alp::is_strict_total_order< dbl_gt >::value ); + + static_assert( ! alp::is_equivalence_relation< dbl_gt >::value ); + + if( dbl_gt::check(2.4, 5) ) { +#ifndef NDEBUG + std::cerr << "dbl_gt::test(2.4, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! dbl_gt::check(5, 2.4) ) { +#ifndef NDEBUG + std::cerr << "dbl_gt::check(5, 2.4) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( dbl_gt::check(5, 5) ) { +#ifndef NDEBUG + std::cerr << "dbl_gt::test(5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + /** * Basic checks on the equality relation (eq). * eq is both an equivalence relation and a partial order. @@ -118,7 +160,57 @@ void alp_program( const size_t & n, alp::RC & rc ) { } /** - * Basic checks on the less-then-or-equal relation (le). + * Basic checks on the not-equal relation (neq). + * neq is neither an order nor an equivalence relation. + */ + typedef alp::relations::neq< int > int_neq; + + static_assert( alp::is_relation< int_neq >::value ); + + static_assert( ! alp::is_partial_order< int_neq >::value ); + + static_assert( ! alp::is_strict_partial_order< int_neq >::value ); + + static_assert( ! alp::is_total_order< int_neq >::value ); + + static_assert( ! alp::is_strict_total_order< int_neq >::value ); + + static_assert( ! alp::is_equivalence_relation< int_neq >::value ); + + if( ! int_neq::check(2.4, 5) ) { +#ifndef NDEBUG + std::cerr << "int_neq::test(2.4, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! int_neq::check(5, 2.4) ) { +#ifndef NDEBUG + std::cerr << "int_neq::check(5, 2.4) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( int_neq::check(5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_neq::test(5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( int_neq::check(5.5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_neq::test(5.5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + /** + * Basic checks on the less-than-or-equal relation (le). * le is both a total order and therefore also a partial order. */ typedef alp::relations::le< int > int_le; @@ -167,6 +259,56 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } + /** + * Basic checks on the greater-than-or-equal relation (ge). + * le is both a total order and therefore also a partial order. + */ + typedef alp::relations::ge< int > int_ge; + + static_assert( alp::is_relation< int_ge >::value ); + + static_assert( alp::is_partial_order< int_ge >::value ); + + static_assert( ! alp::is_strict_partial_order< int_ge >::value ); + + static_assert( alp::is_total_order< int_ge >::value ); + + static_assert( ! alp::is_strict_total_order< int_ge >::value ); + + static_assert( ! alp::is_equivalence_relation< int_ge >::value ); + + if( int_ge::check(2.4, 5) ) { +#ifndef NDEBUG + std::cerr << "int_ge::test(2.4, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! int_ge::check(5, 2.4) ) { +#ifndef NDEBUG + std::cerr << "int_ge::check(5, 2.4) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! int_ge::check(5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_ge::test(5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + + if( ! int_ge::check(5.5, 5) ) { +#ifndef NDEBUG + std::cerr << "int_ge::test(5.5, 5) failed." << std::endl; +#endif + rc = alp::FAILED; + return; + } + rc = alp::SUCCESS; } diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp index f04a31a8f..cf19d8a13 100644 --- a/tests/unit/dense_sort.cpp +++ b/tests/unit/dense_sort.cpp @@ -31,12 +31,14 @@ void alp_program( const size_t &n, alp::RC &rc ) { typedef double T; +#ifndef NDEBUG auto print_std_vector = [](std::vector< T > const vec) { for(auto val : vec) { std::cout << val << ' '; } std::cout << std::endl; }; +#endif // Check with vector of length n randomly intitialized and shuffled alp::Vector< size_t > perm( n ); @@ -52,10 +54,12 @@ void alp_program( const size_t &n, alp::RC &rc ) { alp::buildVector( v, std::begin( stdv ), std::end( stdv ) ); +#ifndef NDEBUG std::cout << "Original content of the std::vector:" << std::endl; print_std_vector( stdv ); std::cout << "Original content of the alp::Vector:" << std::endl; print_vector("v", v); +#endif alp::sort( perm, v ); @@ -66,39 +70,53 @@ void alp_program( const size_t &n, alp::RC &rc ) { // Check sorted view for( size_t i = 0; i < n; i++ ) { if( stdv[i] != sorted_v[ i ] ) { - std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << sorted_v[ i ] << " )" << std::endl; +#ifndef NDEBUG + std::cerr << "Error: ( std_v[ " << i << " ] = " << stdv[i] << " ) != " << " ( alp_sorted_v[ " << i << " ] = " << sorted_v[ i ] << " )" << std::endl; +#endif rc = alp::FAILED; +#ifdef NDEBUG + return; +#endif } } +#ifndef NDEBUG std::cout << "Sorted alp::Vector:" << std::endl; print_vector("sorted_v", sorted_v); if( rc == alp::FAILED ) { return; } +#endif - // // Check descending sorted view - // alp::sort( perm, v, alp::relations:: ); + // Check descending sorted view + alp::sort( perm, v, alp::relations::gt< T >() ); - // auto desc_cmp = []( const T& a, const T& b) { - // return a > b; - // }; + auto desc_cmp = []( const T& a, const T& b) { + return a > b; + }; - // std::sort( std::begin( stdv ), std::end( stdv ), desc_cmp ); + std::sort( std::begin( stdv ), std::end( stdv ), desc_cmp ); - // auto desc_sorted_v = alp::get_view< alp::structures::General >( v, perm ); + auto desc_sorted_v = alp::get_view< alp::structures::General >( v, perm ); - // // Check sorted view - // for( size_t i = 0; i < n; i++ ) { - // if( stdv[i] != desc_sorted_v[ i ] ) { - // std::cerr << "Error: ( std::v[ " << i << " ] = " << stdv[i] << " ) != " << " ( sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" << std::endl; - // rc = alp::FAILED; - // } - // } + // Check sorted view + for( size_t i = 0; i < n; i++ ) { + if( stdv[i] != desc_sorted_v[ i ] ) { +#ifndef NDEBUG + std::cerr << "Error: ( std_v[ " << i << " ] = " << stdv[i] << " ) != " << " ( alp_sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" << std::endl; +#endif + rc = alp::FAILED; +#ifdef NDEBUG + return; +#endif + } + } - // std::cout << "Sorted alp::Vector in descending order:" << std::endl; - // print_vector("desc_sorted_v", desc_sorted_v); +#ifndef NDEBUG + std::cout << "Sorted alp::Vector in descending order:" << std::endl; + print_vector("desc_sorted_v", desc_sorted_v); +#endif rc = alp::SUCCESS; } From 4b4032adf9ed066647f729a86e43116c54ceb57d Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 09/14] setting (strict) poset requirement for sort's relation --- include/alp/reference/blas1.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/alp/reference/blas1.hpp b/include/alp/reference/blas1.hpp index f74fe8f76..b5e163e53 100644 --- a/include/alp/reference/blas1.hpp +++ b/include/alp/reference/blas1.hpp @@ -2242,7 +2242,8 @@ namespace alp { const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort, const Relation &rel = Relation(), const std::enable_if_t< - ! alp::is_object< ValueType >::value && alp::is_relation< Relation >::value + ! alp::is_object< ValueType >::value + && ( alp::is_partial_order< Relation >::value || alp::is_strict_partial_order< Relation >::value ) > * const = nullptr ) noexcept { From 0d4095b08147ffe06513d58c811577feda7ec362 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 10/14] Addressing reviews. --- include/alp/amf-based/vector.hpp | 9 +- include/alp/base/internalrels.hpp | 1188 +++++++++++++++-------------- include/alp/reference/blas1.hpp | 22 +- include/alp/rels.hpp | 79 +- tests/unit/alp_relations.cpp | 64 +- tests/unit/dense_sort.cpp | 13 +- 6 files changed, 699 insertions(+), 676 deletions(-) diff --git a/include/alp/amf-based/vector.hpp b/include/alp/amf-based/vector.hpp index 06f502bd4..65d460619 100644 --- a/include/alp/amf-based/vector.hpp +++ b/include/alp/amf-based/vector.hpp @@ -164,6 +164,9 @@ namespace alp { private: typedef typename self_type::storage_index_type index_type; + typedef std::iterator< + std::random_access_iterator_tag, T + > std_base_class; self_type *vec; index_type position; @@ -185,9 +188,9 @@ namespace alp { } public: - typedef typename std::iterator::pointer pointer; - typedef typename std::iterator::reference reference; - typedef typename std::iterator::difference_type difference_type; + typedef typename std_base_class::pointer pointer; + typedef typename std_base_class::reference reference; + typedef typename std_base_class::difference_type difference_type; /** Default constructor. */ VectorIterator() noexcept : diff --git a/include/alp/base/internalrels.hpp b/include/alp/base/internalrels.hpp index 480206751..50dec813d 100644 --- a/include/alp/base/internalrels.hpp +++ b/include/alp/base/internalrels.hpp @@ -26,6 +26,8 @@ #include #include +#include + namespace alp { @@ -49,74 +51,74 @@ namespace alp { template< typename SET, enum Backend implementation = config::default_backend > class lt { - public: - /** Alias to the left-hand input data type. */ - typedef SET left_type; - - /** Alias to the right-hand input data type. */ - typedef SET right_type; - - /** - * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a < a \f$. - */ - static constexpr bool is_reflexive = false; - - /** - * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a < a \f$. - */ - static constexpr bool is_irreflexive = true; - - /** - * Whether this relation is \em symmetric; that is, - * for all \a a, \a b in \a SET, - * if \f$ a < b \f$ then \f$ b < a \f$. - */ - static constexpr bool is_symmetric = false; - - /** - * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a < b \f$ and - * \f$ b < a \f$ then \f$ a = b \f$. - */ - static constexpr bool is_antisymmetric = true; - - /** - * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a < b \f$ and - * \f$ b < c \f$ then \f$ a < c \f$. - */ - static constexpr bool is_transitive = true; - - /** - * Whether this relation is \em connected (or total); that is, - * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a < b \f$ or \f$ b < a \f$. - */ - static constexpr bool is_connected = true; - - /** - * Whether this relation is strongly connected ; - * that is, - * for all \a a, \a b in \a SET, - * either \f$ a < b \f$ or \f$ b < a \f$. - */ - static constexpr bool is_strongly_connected = false; - - /** - * This function checks if a < b . - * - * @param[in] a The left-hand side input. Must be pre-allocated and initialised. - * @param[in] b The right-hand side input. Must be pre-allocated and initialised. - * - * \warning Passing invalid pointers will result in UB. - */ - static bool check( const left_type * __restrict__ const a, - const right_type * __restrict__ const b - ) { - return *a < *b; - } + public: + /** Alias to the domain data type. */ + typedef SET domain; + + /** Alias to the codomain data type. */ + typedef SET codomain; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a < a \f$. + */ + static constexpr bool is_reflexive = false; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a < a \f$. + */ + static constexpr bool is_irreflexive = true; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a < b \f$ then \f$ b < a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a < b \f$ and + * \f$ b < a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a < b \f$ and + * \f$ b < c \f$ then \f$ a < c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected (or total); that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a < b \f$ or \f$ b < a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected ; + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a < b \f$ or \f$ b < a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * This function checks if a < b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const domain * const a, + const codomain * const b + ) { + return *a < *b; + } }; /** @@ -134,74 +136,74 @@ namespace alp { template< typename SET, enum Backend implementation = config::default_backend > class gt { - public: - /** Alias to the left-hand input data type. */ - typedef SET left_type; - - /** Alias to the right-hand input data type. */ - typedef SET right_type; - - /** - * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a > a \f$. - */ - static constexpr bool is_reflexive = false; - - /** - * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a > a \f$. - */ - static constexpr bool is_irreflexive = true; - - /** - * Whether this relation is \em symmetric; that is, - * for all \a a, \a b in \a SET, - * if \f$ a > b \f$ then \f$ b > a \f$. - */ - static constexpr bool is_symmetric = false; - - /** - * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a > b \f$ and - * \f$ b > a \f$ then \f$ a = b \f$. - */ - static constexpr bool is_antisymmetric = true; - - /** - * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a > b \f$ and - * \f$ b > c \f$ then \f$ a > c \f$. - */ - static constexpr bool is_transitive = true; - - /** - * Whether this relation is \em connected (or total); that is, - * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a > b \f$ or \f$ b > a \f$. - */ - static constexpr bool is_connected = true; - - /** - * Whether this relation is strongly connected ; - * that is, - * for all \a a, \a b in \a SET, - * either \f$ a > b \f$ or \f$ b > a \f$. - */ - static constexpr bool is_strongly_connected = false; - - /** - * This function checks if a > b . - * - * @param[in] a The left-hand side input. Must be pre-allocated and initialised. - * @param[in] b The right-hand side input. Must be pre-allocated and initialised. - * - * \warning Passing invalid pointers will result in UB. - */ - static bool check( const left_type * __restrict__ const a, - const right_type * __restrict__ const b - ) { - return *a > *b; - } + public: + /** Alias to the domain data type. */ + typedef SET domain; + + /** Alias to the codomain data type. */ + typedef SET codomain; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a > a \f$. + */ + static constexpr bool is_reflexive = false; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a > a \f$. + */ + static constexpr bool is_irreflexive = true; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a > b \f$ then \f$ b > a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a > b \f$ and + * \f$ b > a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a > b \f$ and + * \f$ b > c \f$ then \f$ a > c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected (or total); that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a > b \f$ or \f$ b > a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected ; + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a > b \f$ or \f$ b > a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * This function checks if a > b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const domain * const a, + const codomain * const b + ) { + return *a > *b; + } }; /** @@ -219,74 +221,74 @@ namespace alp { template< typename SET, enum Backend implementation = config::default_backend > class eq { - public: - /** Alias to the left-hand input data type. */ - typedef SET left_type; - - /** Alias to the right-hand input data type. */ - typedef SET right_type; - - /** - * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a = a \f$. - */ - static constexpr bool is_reflexive = true; - - /** - * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a = a \f$. - */ - static constexpr bool is_irreflexive = false; - - /** - * Whether this relation is \em symmetric; that is, - * for all \a a, \a b in \a SET, - * if \f$ a = b \f$ then \f$ b = a \f$. - */ - static constexpr bool is_symmetric = true; - - /** - * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a = b \f$ and - * \f$ b = a \f$ then \f$ a = b \f$. - */ - static constexpr bool is_antisymmetric = true; - - /** - * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a = b \f$ and - * \f$ b = c \f$ then \f$ a = c \f$. - */ - static constexpr bool is_transitive = true; - - /** - * Whether this relation is \em connected; that is, - * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a = b \f$ or \f$ b = a \f$. - */ - static constexpr bool is_connected = false; - - /** - * Whether this relation is strongly connected (or total); - * that is, - * for all \a a, \a b in \a SET, - * either \f$ a = b \f$ or \f$ b = a \f$. - */ - static constexpr bool is_strongly_connected = false; - - /** - * This function checks if a == b . - * - * @param[in] a The left-hand side input. Must be pre-allocated and initialised. - * @param[in] b The right-hand side input. Must be pre-allocated and initialised. - * - * \warning Passing invalid pointers will result in UB. - */ - static bool check( const left_type * __restrict__ const a, - const right_type * __restrict__ const b - ) { - return *a == *b; - } + public: + /** Alias to the domain data type. */ + typedef SET domain; + + /** Alias to the codomain data type. */ + typedef SET codomain; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a = a \f$. + */ + static constexpr bool is_reflexive = true; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a = a \f$. + */ + static constexpr bool is_irreflexive = false; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a = b \f$ then \f$ b = a \f$. + */ + static constexpr bool is_symmetric = true; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a = b \f$ and + * \f$ b = a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a = b \f$ and + * \f$ b = c \f$ then \f$ a = c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a = b \f$ or \f$ b = a \f$. + */ + static constexpr bool is_connected = false; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a = b \f$ or \f$ b = a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * This function checks if a == b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const domain * const a, + const codomain * const b + ) { + return *a == *b; + } }; /** @@ -306,74 +308,74 @@ namespace alp { template< typename SET, enum Backend implementation = config::default_backend > class neq { - public: - /** Alias to the left-hand input data type. */ - typedef SET left_type; - - /** Alias to the right-hand input data type. */ - typedef SET right_type; - - /** - * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a \neq a \f$. - */ - static constexpr bool is_reflexive = false; - - /** - * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a \neq a \f$. - */ - static constexpr bool is_irreflexive = true; - - /** - * Whether this relation is \em symmetric; that is, - * for all \a a, \a b in \a SET, - * if \f$ a \neq b \f$ then \f$ b \neq a \f$. - */ - static constexpr bool is_symmetric = true; - - /** - * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ and - * \f$ b \neq a \f$ then \f$ a = b \f$. - */ - static constexpr bool is_antisymmetric = false; - - /** - * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a \neq b \f$ and - * \f$ b \neq c \f$ then \f$ a \neq c \f$. - */ - static constexpr bool is_transitive = false; - - /** - * Whether this relation is \em connected; that is, - * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a \neq b \f$ or \f$ b \neq a \f$. - */ - static constexpr bool is_connected = true; - - /** - * Whether this relation is strongly connected (or total); - * that is, - * for all \a a, \a b in \a SET, - * either \f$ a \neq b \f$ or \f$ b \neq a \f$. - */ - static constexpr bool is_strongly_connected = false; - - /** - * This function checks if a != b . - * - * @param[in] a The left-hand side input. Must be pre-allocated and initialised. - * @param[in] b The right-hand side input. Must be pre-allocated and initialised. - * - * \warning Passing invalid pointers will result in UB. - */ - static bool check( const left_type * __restrict__ const a, - const right_type * __restrict__ const b - ) { - return *a != *b; - } + public: + /** Alias to the domain data type. */ + typedef SET domain; + + /** Alias to the codomain data type. */ + typedef SET codomain; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a \neq a \f$. + */ + static constexpr bool is_reflexive = false; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a \neq a \f$. + */ + static constexpr bool is_irreflexive = true; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a \neq b \f$ then \f$ b \neq a \f$. + */ + static constexpr bool is_symmetric = true; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ and + * \f$ b \neq a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = false; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a \neq b \f$ and + * \f$ b \neq c \f$ then \f$ a \neq c \f$. + */ + static constexpr bool is_transitive = false; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a \neq b \f$ or \f$ b \neq a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a \neq b \f$ or \f$ b \neq a \f$. + */ + static constexpr bool is_strongly_connected = false; + + /** + * This function checks if a != b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const domain * const a, + const codomain * const b + ) { + return *a != *b; + } }; /** @@ -391,74 +393,74 @@ namespace alp { template< typename SET, enum Backend implementation = config::default_backend > class le { - public: - /** Alias to the left-hand input data type. */ - typedef SET left_type; - - /** Alias to the right-hand input data type. */ - typedef SET right_type; - - /** - * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a \le a \f$. - */ - static constexpr bool is_reflexive = true; - - /** - * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a \le a \f$. - */ - static constexpr bool is_irreflexive = false; - - /** - * Whether this relation is \em symmetric; that is, - * for all \a a, \a b in \a SET, - * if \f$ a \le b \f$ then \f$ b \le a \f$. - */ - static constexpr bool is_symmetric = false; - - /** - * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a \le b \f$ and - * \f$ b \le a \f$ then \f$ a = b \f$. - */ - static constexpr bool is_antisymmetric = true; - - /** - * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a \le b \f$ and - * \f$ b \le c \f$ then \f$ a \le c \f$. - */ - static constexpr bool is_transitive = true; - - /** - * Whether this relation is \em connected; that is, - * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a \le b \f$ or \f$ b \le a \f$. - */ - static constexpr bool is_connected = true; - - /** - * Whether this relation is strongly connected (or total); - * that is, - * for all \a a, \a b in \a SET, - * either \f$ a \le b \f$ or \f$ b \le a \f$. - */ - static constexpr bool is_strongly_connected = true; - - /** - * This function checks if a <= b . - * - * @param[in] a The left-hand side input. Must be pre-allocated and initialised. - * @param[in] b The right-hand side input. Must be pre-allocated and initialised. - * - * \warning Passing invalid pointers will result in UB. - */ - static bool check( const left_type * __restrict__ const a, - const right_type * __restrict__ const b - ) { - return *a <= *b; - } + public: + /** Alias to the domain data type. */ + typedef SET domain; + + /** Alias to the codomain data type. */ + typedef SET codomain; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a \le a \f$. + */ + static constexpr bool is_reflexive = true; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a \le a \f$. + */ + static constexpr bool is_irreflexive = false; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a \le b \f$ then \f$ b \le a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a \le b \f$ and + * \f$ b \le a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a \le b \f$ and + * \f$ b \le c \f$ then \f$ a \le c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a \le b \f$ or \f$ b \le a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a \le b \f$ or \f$ b \le a \f$. + */ + static constexpr bool is_strongly_connected = true; + + /** + * This function checks if a <= b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const domain * const a, + const codomain * const b + ) { + return *a <= *b; + } }; /** @@ -476,74 +478,74 @@ namespace alp { template< typename SET, enum Backend implementation = config::default_backend > class ge { - public: - /** Alias to the left-hand input data type. */ - typedef SET left_type; - - /** Alias to the right-hand input data type. */ - typedef SET right_type; - - /** - * Whether this relation is \em reflexive; that is, - * for all \a a in \a SET, \f$ a \ge a \f$. - */ - static constexpr bool is_reflexive = true; - - /** - * Whether this relation is \em irreflexive; that is, - * for all \a a in \a SET, not \f$ a \ge a \f$. - */ - static constexpr bool is_irreflexive = false; - - /** - * Whether this relation is \em symmetric; that is, - * for all \a a, \a b in \a SET, - * if \f$ a \ge b \f$ then \f$ b \ge a \f$. - */ - static constexpr bool is_symmetric = false; - - /** - * Whether this relation is \em antisymmetric; that is, - * for all \a a, \a b in \a SET, if \f$ a \ge b \f$ and - * \f$ b \ge a \f$ then \f$ a = b \f$. - */ - static constexpr bool is_antisymmetric = true; - - /** - * Whether this relation is \em transitive; that is, - * for all \a a, \a b, \a c in \a SET, if \f$ a \ge b \f$ and - * \f$ b \ge c \f$ then \f$ a \ge c \f$. - */ - static constexpr bool is_transitive = true; - - /** - * Whether this relation is \em connected; that is, - * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then - * either \f$ a \ge b \f$ or \f$ b \ge a \f$. - */ - static constexpr bool is_connected = true; - - /** - * Whether this relation is strongly connected (or total); - * that is, - * for all \a a, \a b in \a SET, - * either \f$ a \ge b \f$ or \f$ b \ge a \f$. - */ - static constexpr bool is_strongly_connected = true; - - /** - * This function checks if a >= b . - * - * @param[in] a The left-hand side input. Must be pre-allocated and initialised. - * @param[in] b The right-hand side input. Must be pre-allocated and initialised. - * - * \warning Passing invalid pointers will result in UB. - */ - static bool check( const left_type * __restrict__ const a, - const right_type * __restrict__ const b - ) { - return *a >= *b; - } + public: + /** Alias to the domain data type. */ + typedef SET domain; + + /** Alias to the codomain data type. */ + typedef SET codomain; + + /** + * Whether this relation is \em reflexive; that is, + * for all \a a in \a SET, \f$ a \ge a \f$. + */ + static constexpr bool is_reflexive = true; + + /** + * Whether this relation is \em irreflexive; that is, + * for all \a a in \a SET, not \f$ a \ge a \f$. + */ + static constexpr bool is_irreflexive = false; + + /** + * Whether this relation is \em symmetric; that is, + * for all \a a, \a b in \a SET, + * if \f$ a \ge b \f$ then \f$ b \ge a \f$. + */ + static constexpr bool is_symmetric = false; + + /** + * Whether this relation is \em antisymmetric; that is, + * for all \a a, \a b in \a SET, if \f$ a \ge b \f$ and + * \f$ b \ge a \f$ then \f$ a = b \f$. + */ + static constexpr bool is_antisymmetric = true; + + /** + * Whether this relation is \em transitive; that is, + * for all \a a, \a b, \a c in \a SET, if \f$ a \ge b \f$ and + * \f$ b \ge c \f$ then \f$ a \ge c \f$. + */ + static constexpr bool is_transitive = true; + + /** + * Whether this relation is \em connected; that is, + * for all \a a, \a b in \a SET, if \f$ a \neq b \f$ then + * either \f$ a \ge b \f$ or \f$ b \ge a \f$. + */ + static constexpr bool is_connected = true; + + /** + * Whether this relation is strongly connected (or total); + * that is, + * for all \a a, \a b in \a SET, + * either \f$ a \ge b \f$ or \f$ b \ge a \f$. + */ + static constexpr bool is_strongly_connected = true; + + /** + * This function checks if a >= b . + * + * @param[in] a The left-hand side input. Must be pre-allocated and initialised. + * @param[in] b The right-hand side input. Must be pre-allocated and initialised. + * + * \warning Passing invalid pointers will result in UB. + */ + static bool check( const domain * const a, + const codomain * const b + ) { + return *a >= *b; + } }; /** @@ -558,211 +560,157 @@ namespace alp { template< typename REL, enum Backend implementation = config::default_backend > class RelationBase { - protected: - - /** The left-hand input domain. */ - typedef typename REL::left_type D1; - - /** The right-hand input domain. */ - typedef typename REL::right_type D2; - - public: - /** @return Whether this relation is reflexive. */ - static constexpr bool is_reflexive() { - return REL::is_reflexive; - } - - /** @return Whether this relation is irreflexive. */ - static constexpr bool is_irreflexive() { - return REL::is_irreflexive; - } - - /** @return Whether this relation is symmetric. */ - static constexpr bool is_symmetric() { - return REL::is_symmetric; - } - - /** @return Whether this relation is antisymmetric. */ - static constexpr bool is_antisymmetric() { - return REL::is_antisymmetric; - } - - /** @return Whether this relation is transitive. */ - static constexpr bool is_transitive() { - return REL::is_transitive; - } - - /** @return Whether this relation is connected. */ - static constexpr bool is_connected() { - return REL::is_connected; - } - - /** @return Whether this relation is strongly connected. */ - static constexpr bool is_strongly_connected() { - return REL::is_strongly_connected; - } - - /** - * This function checks if \f$ x REL y \f$. - * - * @tparam InputType1 The type of the input parameter \a x. - * @tparam InputType2 The type of the input parameter \a y. - * - * \warning If \a InputType1 does not match \a D1 \em or \a InputType2 does - * not match \a D2, then input will be cast into temporary - * variables of the correct types. - * - * \note Best performance is thus only guaranteed when all domains match. - * - * @param[in] x The left-hand side input. - * @param[in] y The right-hand side input. - */ - template< typename InputType1, typename InputType2 > - static bool check( const InputType1 & x, const InputType2 & y ) { - const D1 a = static_cast< D1 >( x ); - const D2 b = static_cast< D2 >( y ); - return REL::check( &a, &b ); - } - - /** - * This is the high-performance version of check() in the sense that no - * casting is required. This version will be automatically called whenever - * possible. - */ - static bool check( const D1 & x, const D2 & y ) { - return REL::check( &x, &y ); - } + public: + + /** The domain type. */ + typedef typename REL::domain D1; + + /** The codomain type. */ + typedef typename REL::codomain D2; + + /** @return Whether this relation is reflexive. */ + static constexpr bool is_reflexive() { + return REL::is_reflexive; + } + + /** @return Whether this relation is irreflexive. */ + static constexpr bool is_irreflexive() { + return REL::is_irreflexive; + } + + /** @return Whether this relation is symmetric. */ + static constexpr bool is_symmetric() { + return REL::is_symmetric; + } + + /** @return Whether this relation is antisymmetric. */ + static constexpr bool is_antisymmetric() { + return REL::is_antisymmetric; + } + + /** @return Whether this relation is transitive. */ + static constexpr bool is_transitive() { + return REL::is_transitive; + } + + /** @return Whether this relation is connected. */ + static constexpr bool is_connected() { + return REL::is_connected; + } + + /** @return Whether this relation is strongly connected. */ + static constexpr bool is_strongly_connected() { + return REL::is_strongly_connected; + } + + /** + * This function checks if \f$ x REL y \f$. + * + * @tparam InputType1 The type of the input parameter \a x. + * @tparam InputType2 The type of the input parameter \a y. + * + * \warning If \a InputType1 does not match \a D1 \em or \a InputType2 does + * not match \a D2, then input will be cast into temporary + * variables of the correct types. + * + * \note Best performance is thus only guaranteed when all domains match. + * + * @param[in] x The left-hand side input. + * @param[in] y The right-hand side input. + */ + template< typename InputType1, typename InputType2 > + static bool check( const InputType1 & x, const InputType2 & y ) { + const D1 a = static_cast< D1 >( x ); + const D2 b = static_cast< D2 >( y ); + return REL::check( &a, &b ); + } + + /** + * This is the high-performance version of check() in the sense that no + * casting is required. This version will be automatically called whenever + * possible. + */ + static bool check( const D1 & x, const D2 & y ) { + return REL::check( &x, &y ); + } }; /** - * TODO: Update for Relation - * This is the operator interface exposed to the GraphBLAS implementation. - * - * \warning Note that most GraphBLAS usage requires associative operators. - * While very easily possible to create non-associative operators - * using this interface, passing them to GraphBLAS functions, - * either explicitly or indirectly (by, e.g., including them in a - * alp::Monoid or alp::Semiring), will lead to undefined - * behaviour. + * This is the relation interface exposed to the ALP implementation. * - * This class wraps around a base operator of type \a OP we denote by - * \f$ \odot:\ D_1\times D_2 \to D_3 \f$. + * This class wraps around a base relation of type \a REL we denote by + * \f$ REL \subseteq D_1\times D_2 \f$. * * \parblock * \par Base Operators * - * The class \a OP is expected to define the following public function: - * - \a apply, which takes three pointers to parameters \f$ x \in D_1 \f$ - * \f$ y \in D_2 \f$, and \f$ z \in D_3 \f$ and computes - * \f$ z = x \odot y \f$. + * The class \a REL is expected to define the following public function: + * - \a check, which takes two pointers to parameters \f$ a \in D_1 \f$ + * and \f$ b \in D_2 \f$ and checks if + * \f$ a REL b \f$. * * It is also expected to define the following types: - * - \a left_type, which corresponds to \f$ D_1 \f$, - * - \a right_type, which corresponds to \f$ D_2 \f$, - * - \a result_type, which corresponds to \f$ D_3 \f$. - * - * It is also expected to define the following two public boolean fields: - * - \a has_foldr - * - \a has_foldl - * - * If \a has_foldr is \a true, then the class \a OP is expected to also - * define the function - * - foldr, which takes two pointers to parameters \f$ x \in D_1 \f$ - * and \f$ z \in D_2 \subseteq D_3 \f$ and stores in \a z the result of - * \f$ x \odot z \f$. + * - \a domain, which corresponds to \f$ D_1 \f$, + * - \a codomain, which corresponds to \f$ D_2 \f$. * - * If \a has_foldl is \a true, the the class \a OP is expected to also - * define the function - * - foldl, which takes two pointers to parameters - * \f$ z \in D_1 \subseteq D_3 \f$ and \f$ y \in D_2 \f$ and stores in - * \a z the result of \f$ z \odot y \f$. + * It is also expected to define the following public boolean fields: + * - \a is_reflexive + * - \a is_irreflexive + * - \a is_symmetric + * - \a is_antisymmetric + * - \a is_transitive + * - \a is_connected + * - \a is_strongly_connected * - * For examples of these base operators, see alp::operators::internal::max - * or alp::operators::internal::mul. An example of a full implementation, - * in this case for numerical addition, is the following: + * For an example of base relation, see alp::relations::internal::lt. * - * \snippet internalops.hpp Example Base Operator Implementation - * - * \note GraphBLAS users should never call these functions directly. This + * \note ALP users should never access these classes directly. This * documentation is provided for developers to understand or extend - * the current implementation, for example to include new operators. - * - * \warning When calling these functions directly, note that the pointers - * to the memory areas are declared using the \em restrict key - * word. One of the consequences is that all pointers given in a - * single call may never refer to the same memory area, or - * undefined behaviour is invoked. + * the current implementation, for example to include new relations. * * \endparblock * * \parblock - * \par The exposed GraphBLAS Operator Interface + * \par The exposed GraphBLAS Relation Interface * - * The Base Operators as illustrated above are wrapped by this class to + * The Base Relations as illustrated above are wrapped by this class to * provide a more convient API. It translates the functionality of any Base - * Operator and exposes the following interface instead: + * Relation and exposes the following interface instead: * - * -# apply, which takes three parameters \f$ x, y, z \f$ of arbitrary - * types and computes \f$ z = x \odot y \f$ after performing any + * -# check, which takes two parameters \f$ a, b \f$ of arbitrary + * types and checks \f$ a REL b \f$ after performing any * casting if required. - * -# foldr, which takes two parameters \f$ x, z \f$ of arbitrary types - * and computes \f$ z = x \odot z \f$ after performing any casting if - * required. - * -# foldl, which takes two parameters \f$ z, y \f$ of arbitrary types - * and computes \f$ z = z \odot y \f$ after performing any casting if - * required. - * -# eWiseApply, which takes three pointers to arrays \f$ x, y, z \f$ - * and a size \a n. The arrays can correspond to elements of any type, - * all three with length at least \a n. For every i-th element of the - * three arrays, on the values \f$ x_i, y_i, z_i \f$, \f$ z_i \f$ will - * be set to \f$ x_i \odot y_i \f$. - * -# foldrArray, which takes a pointer to an array \f$ x \f$, a - * parameter \f$ z \f$ of arbitrary type, and a size \n as parameters. - * The value \f$ z \f$ will be overwritten to \f$ x_i \odot z \f$ for - * each of the \f$ i \in \{ 0, 1, \ldots, n-1 \} \f$. The order of - * application, in the sense of which \f$ i \f$ are processed first, - * is undefined. - * -# foldlArray, which takes as parameters: \f$ z \f$ of arbitrary type, - * an array \f$ y \f$, and a size \n. The value \f$ z \f$ will be - * overwritten to \f$ z \odot y_i \f$ for each of the - * \f$ i \in \{ 0, 1, \ldots, n-1 \} \f$. The order of application, in - * the sense of which \f$ i \f$ are processed first, is undefined. * \endparblock * - * \note This class only allows wrapping of stateless base operators. This - * GraphBLAS implementation in principle allows for stateful - * operators, though they must be provided by a specialised class + * \note This class only allows wrapping of stateless base relations. This + * ALP implementation in principle allows for stateful + * relations, though they must be provided by a specialised class * which directly implements the above public interface. * - * @see OperatorBase::apply - * @see OperatorFR::foldr - * @see OperatorFL::foldl - * @see \ref OperatorNoFRFLeWiseApply - * @see Operator::foldrArray - * @see Operator::foldlArray + * @see RelationBase::check * * \parblock - * \par Providing New Operators + * \par Providing New Relations * - * New operators are easily added to this - * GraphBLAS implementation by providing a base operator and wrapping this - * class around it, as illustrated, e.g., by alp::operators::add as follows: + * New relations are easily added to this + * ALP implementation by providing a base relation and wrapping this + * class around it, as illustrated, e.g., by alp::relations::lt as follows: * - * \snippet ops.hpp Operator Wrapping + * \snippet rels.hpp Relation Wrapping * - * This need to be compatible with the GraphBLAS type traits, specifically, - * the #is_operator template. To ensure this, a specialisation of it must be + * This need to be compatible with the ALP type traits, specifically, + * the #is_relation template. To ensure this, a specialisation of it must be * privided: * - * \snippet ops.hpp Operator Type Traits + * \snippet rels.hpp Relation Type Traits * \endparblock */ template< typename REL, enum Backend implementation = config::default_backend > class Relation : public RelationBase< REL, implementation > { - public: - typedef typename RelationBase< REL, implementation >::D1 D1; - typedef typename RelationBase< REL, implementation >::D2 D2; + // public: + // typedef typename RelationBase< REL, implementation >::D1 D1; + // typedef typename RelationBase< REL, implementation >::D2 D2; }; @@ -778,8 +726,8 @@ namespace alp { enum Backend implementation = config::default_backend, std::enable_if_t< std::is_same< - typename REL::left_type, - typename REL::right_type + typename REL::domain, + typename REL::codomain >::value > * = nullptr > @@ -790,6 +738,92 @@ namespace alp { } // namespace relations + template< typename Rel > + struct is_homogeneous_relation { + static const constexpr bool value = is_relation< Rel >::value + and std::is_same< typename Rel::D1, typename Rel::D2 >::value; + }; + + template< typename Rel > + struct is_reflexive { + static const constexpr bool value = is_homogeneous_relation< Rel >::value + and Rel::is_reflexive(); + }; + + template< typename Rel > + struct is_irreflexive { + static const constexpr bool value = is_homogeneous_relation< Rel >::value + and Rel::is_irreflexive(); + }; + + template< typename Rel > + struct is_symmetric { + static const constexpr bool value = is_homogeneous_relation< Rel >::value + and Rel::is_symmetric(); + }; + + template< typename Rel > + struct is_antisymmetric { + static const constexpr bool value = is_homogeneous_relation< Rel >::value + and Rel::is_antisymmetric(); + }; + + template< typename Rel > + struct is_transitive { + static const constexpr bool value = is_homogeneous_relation< Rel >::value + and Rel::is_transitive(); + }; + + template< typename Rel > + struct is_connected { + static const constexpr bool value = is_homogeneous_relation< Rel >::value + and Rel::is_connected(); + }; + + template< typename Rel > + struct is_strongly_connected { + static const constexpr bool value = is_homogeneous_relation< Rel >::value + and Rel::is_strongly_connected(); + }; + + template< typename Rel > + struct is_asymmetric { + static const constexpr bool value = is_irreflexive< Rel >::value + and is_antisymmetric< Rel >::value; + }; + + template< typename Rel > + struct is_partial_order { + static const constexpr bool value = is_reflexive< Rel >::value + and is_antisymmetric< Rel >::value + and is_transitive< Rel >::value; + }; + + template< typename Rel > + struct is_strict_partial_order { + static const constexpr bool value = is_asymmetric< Rel >::value + and is_transitive< Rel >::value; + }; + + template< typename Rel > + struct is_total_order { + static const constexpr bool value = is_partial_order< Rel >::value + and is_strongly_connected< Rel >::value; + }; + + template< typename Rel > + struct is_strict_total_order { + static const constexpr bool value = is_strict_partial_order< Rel >::value + and is_connected< Rel >::value; + }; + + template< typename Rel > + struct is_equivalence_relation { + static const constexpr bool value = is_reflexive< Rel >::value + and is_symmetric< Rel >::value + and is_transitive< Rel >::value; + }; + } // namespace alp #endif // _H_ALP_INTERNAL_RELATIONS_BASE diff --git a/include/alp/reference/blas1.hpp b/include/alp/reference/blas1.hpp index b5e163e53..28f2e2887 100644 --- a/include/alp/reference/blas1.hpp +++ b/include/alp/reference/blas1.hpp @@ -2222,27 +2222,26 @@ namespace alp { /** * Compute the permutation vector needed to sort the input vector according to - * the provided \a cmp function. + * the provided \a cmp relation. * * @param[in] toSort vector of indices to sort, should not be modified - * @param[in] cmp function with strict weak ordering relation between indices, eg bool cmp(const Type1 &a, const Type2 &b) - * cmp must not modify the objects passed to it + * @param[in] cmp an ALP (strict) partial order * * @param[out] permutation iterator over index permutations which sort toSort vector * * Complexity should be \Theta(n*log(n)), and space complexity should be \Theta(n+T+P) */ template< - typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, + typename IndexType, typename IndexStructure, typename IndexView, typename IndexImfR, typename IndexImfC, typename ValueType, typename ValueStructure, typename ValueView, typename ValueImfR, typename ValueImfC, - typename Relation = relations::lt< ValueType, reference > + typename Relation > RC sort( - Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, + Vector< IndexType, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > &permutation, const Vector< ValueType, ValueStructure, Density::Dense, ValueView, ValueImfR, ValueImfC, reference > &toSort, const Relation &rel = Relation(), const std::enable_if_t< - ! alp::is_object< ValueType >::value + !( alp::is_object< ValueType >::value ) && ( alp::is_partial_order< Relation >::value || alp::is_strict_partial_order< Relation >::value ) > * const = nullptr ) noexcept { @@ -2254,18 +2253,21 @@ namespace alp { } RC rc = alp::eWiseLambda( - [ ]( const size_t i, size_t &val ) { + [ ]( const size_t i, IndexType &val ) { val = i; }, permutation ); - typedef Vector< size_t, IndexStructure, Density::Dense, IndexView, IndexImfR, IndexImfC, reference > PermType; + typedef Vector< + IndexType, IndexStructure, Density::Dense, + IndexView, IndexImfR, IndexImfC, reference + > PermType; typename PermType::iterator it_begin = internal::begin( permutation ); typename PermType::iterator it_end = internal::end( permutation ); - std::sort( it_begin, it_end, [ &toSort, &rel ]( const size_t& a, const size_t& b ) { + std::sort( it_begin, it_end, [ &toSort, &rel ]( const IndexType& a, const IndexType& b ) { return rel.check( toSort[ a ], toSort[ b ] ); }); diff --git a/include/alp/rels.hpp b/include/alp/rels.hpp index 23a480d2a..5871c972a 100644 --- a/include/alp/rels.hpp +++ b/include/alp/rels.hpp @@ -25,6 +25,7 @@ #include +#include "type_traits.hpp" #include "internalrels.hpp" namespace alp { @@ -48,11 +49,15 @@ namespace alp { * \warning This operator expects a numerical type for \a SET or types * that have the appropriate operator<-functions available. */ + // [Relation Wrapping] template< typename SET, enum Backend implementation = config::default_backend > class lt : public internal::HomogeneousRelation< internal::lt< SET, implementation > > { - public: - lt() {} + + public: + + lt() {} }; + // [Relation Wrapping] /** * This class implements the greater-than relation. @@ -70,8 +75,10 @@ namespace alp { */ template< typename SET, enum Backend implementation = config::default_backend > class gt : public internal::HomogeneousRelation< internal::gt< SET, implementation > > { - public: - gt() {} + + public: + + gt() {} }; /** @@ -90,8 +97,10 @@ namespace alp { */ template< typename SET, enum Backend implementation = config::default_backend > class eq : public internal::HomogeneousRelation< internal::eq< SET, implementation > > { - public: - eq() {} + + public: + + eq() {} }; /** @@ -110,8 +119,10 @@ namespace alp { */ template< typename SET, enum Backend implementation = config::default_backend > class neq : public internal::HomogeneousRelation< internal::neq< SET, implementation > > { - public: - neq() {} + + public: + + neq() {} }; /** @@ -130,8 +141,10 @@ namespace alp { */ template< typename SET, enum Backend implementation = config::default_backend > class le : public internal::HomogeneousRelation< internal::le< SET, implementation > > { - public: - le() {} + + public: + + le() {} }; /** @@ -150,12 +163,15 @@ namespace alp { */ template< typename SET, enum Backend implementation = config::default_backend > class ge : public internal::HomogeneousRelation< internal::ge< SET, implementation > > { - public: - ge() {} + + public: + + ge() {} }; } // namespace relations + // [Relation Type Traits] template< typename IntRel, enum Backend implementation @@ -163,6 +179,7 @@ namespace alp { struct is_relation< relations::lt< IntRel, implementation > > { static const constexpr bool value = true; }; + // [Relation Type Traits] template< typename IntRel, @@ -204,44 +221,6 @@ namespace alp { static const constexpr bool value = true; }; - template< typename Rel > - struct is_partial_order { - static const constexpr bool value = is_relation< Rel >::value - and Rel::is_reflexive() - and Rel::is_transitive() - and Rel::is_antisymmetric(); - }; - - template< typename Rel > - struct is_strict_partial_order { - static const constexpr bool value = is_relation< Rel >::value - and Rel::is_irreflexive() - and Rel::is_transitive() - and Rel::is_antisymmetric(); - }; - - template< typename Rel > - struct is_total_order { - static const constexpr bool value = is_relation< Rel >::value - and is_partial_order< Rel >::value - and Rel::is_strongly_connected(); - }; - - template< typename Rel > - struct is_strict_total_order { - static const constexpr bool value = is_relation< Rel >::value - and is_strict_partial_order< Rel >::value - and Rel::is_connected(); - }; - - template< typename Rel > - struct is_equivalence_relation { - static const constexpr bool value = is_relation< Rel >::value - and Rel::is_reflexive() - and Rel::is_transitive() - and Rel::is_symmetric(); - }; - } // namespace alp #endif // end ``_H_ALP_RELATIONS'' diff --git a/tests/unit/alp_relations.cpp b/tests/unit/alp_relations.cpp index b89174075..8fdaa0cec 100644 --- a/tests/unit/alp_relations.cpp +++ b/tests/unit/alp_relations.cpp @@ -33,17 +33,17 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( alp::is_relation< dbl_lt >::value ); - static_assert( ! alp::is_partial_order< dbl_lt >::value ); + static_assert( !( alp::is_partial_order< dbl_lt >::value ) ); static_assert( alp::is_strict_partial_order< dbl_lt >::value ); - static_assert( ! alp::is_total_order< dbl_lt >::value ); + static_assert( !( alp::is_total_order< dbl_lt >::value ) ); static_assert( alp::is_strict_total_order< dbl_lt >::value ); - static_assert( ! alp::is_equivalence_relation< dbl_lt >::value ); + static_assert( !( alp::is_equivalence_relation< dbl_lt >::value ) ); - if( ! dbl_lt::check(2.4, 5) ) { + if( !( dbl_lt::check(2.4, 5) ) ) { #ifndef NDEBUG std::cerr << "dbl_lt::test(2.4, 5) failed." << std::endl; #endif @@ -75,15 +75,15 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( alp::is_relation< dbl_gt >::value ); - static_assert( ! alp::is_partial_order< dbl_gt >::value ); + static_assert( !( alp::is_partial_order< dbl_gt >::value ) ); static_assert( alp::is_strict_partial_order< dbl_gt >::value ); - static_assert( ! alp::is_total_order< dbl_gt >::value ); + static_assert( !( alp::is_total_order< dbl_gt >::value ) ); static_assert( alp::is_strict_total_order< dbl_gt >::value ); - static_assert( ! alp::is_equivalence_relation< dbl_gt >::value ); + static_assert( !( alp::is_equivalence_relation< dbl_gt >::value ) ); if( dbl_gt::check(2.4, 5) ) { #ifndef NDEBUG @@ -93,7 +93,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! dbl_gt::check(5, 2.4) ) { + if( !( dbl_gt::check(5, 2.4) ) ) { #ifndef NDEBUG std::cerr << "dbl_gt::check(5, 2.4) failed." << std::endl; #endif @@ -119,11 +119,11 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( alp::is_partial_order< int_eq >::value ); - static_assert( ! alp::is_strict_partial_order< int_eq >::value ); + static_assert( !( alp::is_strict_partial_order< int_eq >::value ) ); - static_assert( ! alp::is_total_order< int_eq >::value ); + static_assert( !( alp::is_total_order< int_eq >::value ) ); - static_assert( ! alp::is_strict_total_order< int_eq >::value ); + static_assert( !( alp::is_strict_total_order< int_eq >::value ) ); static_assert( alp::is_equivalence_relation< int_eq >::value ); @@ -143,7 +143,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_eq::check(5, 5) ) { + if( !( int_eq::check(5, 5) ) ) { #ifndef NDEBUG std::cerr << "int_eq::test(5, 5) failed." << std::endl; #endif @@ -151,7 +151,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_eq::check(5.5, 5) ) { + if( !( int_eq::check(5.5, 5) ) ) { #ifndef NDEBUG std::cerr << "int_eq::test(5.5, 5) failed." << std::endl; #endif @@ -167,17 +167,17 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( alp::is_relation< int_neq >::value ); - static_assert( ! alp::is_partial_order< int_neq >::value ); + static_assert( !( alp::is_partial_order< int_neq >::value ) ); - static_assert( ! alp::is_strict_partial_order< int_neq >::value ); + static_assert( !( alp::is_strict_partial_order< int_neq >::value ) ); - static_assert( ! alp::is_total_order< int_neq >::value ); + static_assert( !( alp::is_total_order< int_neq >::value ) ); - static_assert( ! alp::is_strict_total_order< int_neq >::value ); + static_assert( !( alp::is_strict_total_order< int_neq >::value ) ); - static_assert( ! alp::is_equivalence_relation< int_neq >::value ); + static_assert( !( alp::is_equivalence_relation< int_neq >::value ) ); - if( ! int_neq::check(2.4, 5) ) { + if( !( int_neq::check(2.4, 5) ) ) { #ifndef NDEBUG std::cerr << "int_neq::test(2.4, 5) failed." << std::endl; #endif @@ -185,7 +185,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_neq::check(5, 2.4) ) { + if( !( int_neq::check(5, 2.4) ) ) { #ifndef NDEBUG std::cerr << "int_neq::check(5, 2.4) failed." << std::endl; #endif @@ -219,15 +219,15 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( alp::is_partial_order< int_le >::value ); - static_assert( ! alp::is_strict_partial_order< int_le >::value ); + static_assert( !( alp::is_strict_partial_order< int_le >::value ) ); static_assert( alp::is_total_order< int_le >::value ); - static_assert( ! alp::is_strict_total_order< int_le >::value ); + static_assert( !( alp::is_strict_total_order< int_le >::value ) ); - static_assert( ! alp::is_equivalence_relation< int_le >::value ); + static_assert( !( alp::is_equivalence_relation< int_le >::value ) ); - if( ! int_le::check(2.4, 5) ) { + if( !( int_le::check(2.4, 5) ) ) { #ifndef NDEBUG std::cerr << "int_le::test(2.4, 5) failed." << std::endl; #endif @@ -243,7 +243,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_le::check(5, 5) ) { + if( !( int_le::check(5, 5) ) ) { #ifndef NDEBUG std::cerr << "int_le::test(5, 5) failed." << std::endl; #endif @@ -251,7 +251,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_le::check(5.5, 5) ) { + if( !( int_le::check(5.5, 5) ) ) { #ifndef NDEBUG std::cerr << "int_le::test(5.5, 5) failed." << std::endl; #endif @@ -269,13 +269,13 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( alp::is_partial_order< int_ge >::value ); - static_assert( ! alp::is_strict_partial_order< int_ge >::value ); + static_assert( !( alp::is_strict_partial_order< int_ge >::value ) ); static_assert( alp::is_total_order< int_ge >::value ); - static_assert( ! alp::is_strict_total_order< int_ge >::value ); + static_assert( !( alp::is_strict_total_order< int_ge >::value ) ); - static_assert( ! alp::is_equivalence_relation< int_ge >::value ); + static_assert( !( alp::is_equivalence_relation< int_ge >::value ) ); if( int_ge::check(2.4, 5) ) { #ifndef NDEBUG @@ -285,7 +285,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_ge::check(5, 2.4) ) { + if( !( int_ge::check(5, 2.4) ) ) { #ifndef NDEBUG std::cerr << "int_ge::check(5, 2.4) failed." << std::endl; #endif @@ -293,7 +293,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_ge::check(5, 5) ) { + if( !( int_ge::check(5, 5) ) ) { #ifndef NDEBUG std::cerr << "int_ge::test(5, 5) failed." << std::endl; #endif @@ -301,7 +301,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { return; } - if( ! int_ge::check(5.5, 5) ) { + if( !( int_ge::check(5.5, 5) ) ) { #ifndef NDEBUG std::cerr << "int_ge::test(5.5, 5) failed." << std::endl; #endif diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp index cf19d8a13..b83afb45f 100644 --- a/tests/unit/dense_sort.cpp +++ b/tests/unit/dense_sort.cpp @@ -61,7 +61,7 @@ void alp_program( const size_t &n, alp::RC &rc ) { print_vector("v", v); #endif - alp::sort( perm, v ); + alp::sort( perm, v, relations::lt< T >() ); std::sort( std::begin( stdv ), std::end( stdv ) ); @@ -71,7 +71,9 @@ void alp_program( const size_t &n, alp::RC &rc ) { for( size_t i = 0; i < n; i++ ) { if( stdv[i] != sorted_v[ i ] ) { #ifndef NDEBUG - std::cerr << "Error: ( std_v[ " << i << " ] = " << stdv[i] << " ) != " << " ( alp_sorted_v[ " << i << " ] = " << sorted_v[ i ] << " )" << std::endl; + std::cerr << "Error: ( std_v[ " << i << " ] = " << stdv[i] << " ) != " + << " ( alp_sorted_v[ " << i << " ] = " << sorted_v[ i ] << " )" + << std::endl; #endif rc = alp::FAILED; #ifdef NDEBUG @@ -104,7 +106,9 @@ void alp_program( const size_t &n, alp::RC &rc ) { for( size_t i = 0; i < n; i++ ) { if( stdv[i] != desc_sorted_v[ i ] ) { #ifndef NDEBUG - std::cerr << "Error: ( std_v[ " << i << " ] = " << stdv[i] << " ) != " << " ( alp_sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" << std::endl; + std::cerr << "Error: ( std_v[ " << i << " ] = " << stdv[i] << " ) != " + << " ( alp_sorted_v[ " << i << " ] = " << desc_sorted_v[ i ] << " )" + << std::endl; #endif rc = alp::FAILED; #ifdef NDEBUG @@ -150,7 +154,7 @@ int main( int argc, char **argv ) { if( printUsage ) { std::cerr << "Usage: " << argv[ 0 ] << " [n]\n"; std::cerr << " -n (optional, default is 100): an even integer, the " - "test size.\n"; + "test size.\n"; return 1; } @@ -169,3 +173,4 @@ int main( int argc, char **argv ) { return 0; } } + From 131e88d1c9066505a15ad05634f1efc1f73d63bd Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 11/14] Addressing additional reviews: style fix, added grb-like set< use_index >, use alp::sort in symm_tridiag_dac_eigensolver algorithm. --- .../algorithms/symm_tridiag_eigensolver.hpp | 20 +---- include/alp/base/internalrels.hpp | 4 +- include/alp/blas0.hpp | 74 +++++++++++++++++++ include/alp/reference/blas1.hpp | 7 +- include/alp/reference/io.hpp | 13 +++- tests/unit/alp_relations.cpp | 2 +- 6 files changed, 94 insertions(+), 26 deletions(-) diff --git a/include/alp/algorithms/symm_tridiag_eigensolver.hpp b/include/alp/algorithms/symm_tridiag_eigensolver.hpp index 7ee17fb1c..331c44b1b 100644 --- a/include/alp/algorithms/symm_tridiag_eigensolver.hpp +++ b/include/alp/algorithms/symm_tridiag_eigensolver.hpp @@ -484,24 +484,12 @@ namespace alp { print_vector( " z ", z ); #endif - // permutations which sort dtmp - std::vector< size_t > isort_dtmp( n, 0 ); - std::vector< size_t > no_permute_data( n, 0 ); - for( size_t i = 0; i < n; ++i ) { - isort_dtmp[ i ] = i; - no_permute_data[ i ] = i; - } - std::sort( - isort_dtmp.begin(), - isort_dtmp.end(), - [ &dtmp ]( const size_t &a, const size_t &b ) { - return ( dtmp[ a ] < dtmp[ b ] ); - } - ); + // permutation that sorts dtmp alp::Vector< size_t > permutation_vec( n ); + rc = rc ? rc : alp::sort(permutation_vec, dtmp, alp::relations::lt< D >() ); + alp::Vector< size_t > no_permutation_vec( n ); - alp::buildVector( permutation_vec, isort_dtmp.begin(), isort_dtmp.end() ); - alp::buildVector( no_permutation_vec, no_permute_data.begin(), no_permute_data.end() ); + rc = rc ? rc : alp::set< alp::descriptors::use_index >( no_permutation_vec, alp::Scalar< size_t >( 0 ) ); auto dtmp2 = alp::get_view< alp::structures::General >( dtmp, diff --git a/include/alp/base/internalrels.hpp b/include/alp/base/internalrels.hpp index 50dec813d..3453f9a59 100644 --- a/include/alp/base/internalrels.hpp +++ b/include/alp/base/internalrels.hpp @@ -619,7 +619,7 @@ namespace alp { * @param[in] y The right-hand side input. */ template< typename InputType1, typename InputType2 > - static bool check( const InputType1 & x, const InputType2 & y ) { + static bool check( const InputType1 &x, const InputType2 &y ) { const D1 a = static_cast< D1 >( x ); const D2 b = static_cast< D2 >( y ); return REL::check( &a, &b ); @@ -630,7 +630,7 @@ namespace alp { * casting is required. This version will be automatically called whenever * possible. */ - static bool check( const D1 & x, const D2 & y ) { + static bool check( const D1 &x, const D2 &y ) { return REL::check( &x, &y ); } }; diff --git a/include/alp/blas0.hpp b/include/alp/blas0.hpp index d72ab2943..9e05c8af8 100644 --- a/include/alp/blas0.hpp +++ b/include/alp/blas0.hpp @@ -24,11 +24,85 @@ #define _H_ALP_BLAS0 #include "base/blas0.hpp" +#include "descriptors.hpp" // now include all specialisations contained in the backend directories: #ifdef _ALP_WITH_REFERENCE #include #endif +namespace alp { + + namespace internal { + + /** + * Helper class that, depending on a given descriptor, either returns a + * nonzero value from a vector, or its corresponding coordinate. + * + * This class hence makes the use of the following descriptor(s) transparent: + * -# #alp::descriptors::use_index + * + * @tparam descr The descriptor under which to write back either the value or + * the index. + * @tparam OutputType The type of the output to return. + * @tparam D The type of the input. + * @tparam Enabled Controls, through SFINAE, whether the use of the + * #use_index descriptor is allowed at all. + */ + template< alp::Descriptor descr, typename OutputType, typename D, typename Enabled = void > + class ValueOrIndex; + + /* Version where use_index is allowed. */ + template< alp::Descriptor descr, typename OutputType, typename D > + class ValueOrIndex< + descr, OutputType, D, + typename std::enable_if< std::is_arithmetic< OutputType >::value + && ! std::is_same< D, void >::value >::type + > { + private: + static constexpr const bool use_index = descr & alp::descriptors::use_index; + static_assert( + use_index + || std::is_convertible< D, OutputType >::value, "Cannot convert to the requested output type" + ); + + public: + + static OutputType getFromScalar( const D &x, const size_t index ) noexcept { + if( use_index ) { + return static_cast< OutputType >( index ); + } else { + return static_cast< OutputType >( x ); + } + } + + }; + + /* Version where use_index is not allowed. */ + template< alp::Descriptor descr, typename OutputType, typename D > + class ValueOrIndex< + descr, OutputType, D, + typename std::enable_if< ! std::is_arithmetic< OutputType >::value + && ! std::is_same< OutputType, void >::value >::type + > { + static_assert( + !( descr & descriptors::use_index ), + "use_index descriptor given while output type is not numeric" + ); + static_assert( + std::is_convertible< D, OutputType >::value, + "Cannot convert input to the given output type" + ); + + public: + + static OutputType getFromScalar( const D &x, const size_t ) noexcept { + return static_cast< OutputType >( x ); + } + }; + + } // namespace internal + +} // namespace alp #endif // end ``_H_ALP_BLAS0'' diff --git a/include/alp/reference/blas1.hpp b/include/alp/reference/blas1.hpp index 28f2e2887..d11c53ce9 100644 --- a/include/alp/reference/blas1.hpp +++ b/include/alp/reference/blas1.hpp @@ -2252,12 +2252,7 @@ namespace alp { return SUCCESS; } - RC rc = alp::eWiseLambda( - [ ]( const size_t i, IndexType &val ) { - val = i; - }, - permutation - ); + RC rc = alp::set< alp::descriptors::use_index >( permutation, alp::Scalar< IndexType >( 0 ) ); typedef Vector< IndexType, IndexStructure, Density::Dense, diff --git a/include/alp/reference/io.hpp b/include/alp/reference/io.hpp index 536a5b86a..f9bc431d8 100644 --- a/include/alp/reference/io.hpp +++ b/include/alp/reference/io.hpp @@ -23,6 +23,8 @@ #ifndef _H_ALP_REFERENCE_IO #define _H_ALP_REFERENCE_IO +#include + #include #include "matrix.hpp" @@ -451,7 +453,16 @@ namespace alp { // foldl requires left-hand side to be initialized prior to the call internal::setInitialized( x, true ); - return foldl( x, val, alp::operators::right_assign< DataType >() ); + + // return foldl( x, val, alp::operators::right_assign< DataType >() ); + const size_t n = size( x ); + for ( size_t i = 0; i < n; ++i ) { + x[ i ] = internal::template ValueOrIndex< descr, DataType, T >:: + getFromScalar( *val, i ); + } + + return SUCCESS; + } /** diff --git a/tests/unit/alp_relations.cpp b/tests/unit/alp_relations.cpp index 8fdaa0cec..9e0e65831 100644 --- a/tests/unit/alp_relations.cpp +++ b/tests/unit/alp_relations.cpp @@ -313,7 +313,7 @@ void alp_program( const size_t & n, alp::RC & rc ) { } -int main( int argc, char ** argv ) { +int main( int argc, char **argv ) { // defaults bool printUsage = false; size_t in; From 968ecb21fe05f80a5d311dd5064cf53d0a1d96f4 Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 12/14] Code style fix --- .../algorithms/symm_tridiag_eigensolver.hpp | 3 +- include/alp/blas0.hpp | 47 +++++----- include/alp/reference/blas0.hpp | 1 + include/alp/reference/blas3.hpp | 1 + include/alp/rels.hpp | 1 + include/alp/type_traits.hpp | 1 + tests/unit/alp_relations.cpp | 91 ++++++++++--------- tests/unit/dense_mxm.cpp | 3 +- tests/unit/dense_sort.cpp | 1 + 9 files changed, 78 insertions(+), 71 deletions(-) diff --git a/include/alp/algorithms/symm_tridiag_eigensolver.hpp b/include/alp/algorithms/symm_tridiag_eigensolver.hpp index 331c44b1b..e9e29d2ea 100644 --- a/include/alp/algorithms/symm_tridiag_eigensolver.hpp +++ b/include/alp/algorithms/symm_tridiag_eigensolver.hpp @@ -26,6 +26,7 @@ // TEMPDISABLE should be removed in the final version #define TEMPDISABLE + namespace alp { namespace algorithms { @@ -486,7 +487,7 @@ namespace alp { // permutation that sorts dtmp alp::Vector< size_t > permutation_vec( n ); - rc = rc ? rc : alp::sort(permutation_vec, dtmp, alp::relations::lt< D >() ); + rc = rc ? rc : alp::sort( permutation_vec, dtmp, alp::relations::lt< D >() ); alp::Vector< size_t > no_permutation_vec( n ); rc = rc ? rc : alp::set< alp::descriptors::use_index >( no_permutation_vec, alp::Scalar< size_t >( 0 ) ); diff --git a/include/alp/blas0.hpp b/include/alp/blas0.hpp index 9e05c8af8..dc1e39eb5 100644 --- a/include/alp/blas0.hpp +++ b/include/alp/blas0.hpp @@ -31,9 +31,10 @@ #include #endif + namespace alp { - namespace internal { + namespace internal { /** * Helper class that, depending on a given descriptor, either returns a @@ -55,16 +56,16 @@ namespace alp { /* Version where use_index is allowed. */ template< alp::Descriptor descr, typename OutputType, typename D > class ValueOrIndex< - descr, OutputType, D, - typename std::enable_if< std::is_arithmetic< OutputType >::value - && ! std::is_same< D, void >::value >::type - > { + descr, OutputType, D, + typename std::enable_if< std::is_arithmetic< OutputType >::value + && ! std::is_same< D, void >::value >::type + > { private: static constexpr const bool use_index = descr & alp::descriptors::use_index; - static_assert( - use_index - || std::is_convertible< D, OutputType >::value, "Cannot convert to the requested output type" - ); + static_assert( + use_index + || std::is_convertible< D, OutputType >::value, "Cannot convert to the requested output type" + ); public: @@ -80,19 +81,19 @@ namespace alp { /* Version where use_index is not allowed. */ template< alp::Descriptor descr, typename OutputType, typename D > - class ValueOrIndex< - descr, OutputType, D, - typename std::enable_if< ! std::is_arithmetic< OutputType >::value - && ! std::is_same< OutputType, void >::value >::type - > { - static_assert( - !( descr & descriptors::use_index ), - "use_index descriptor given while output type is not numeric" - ); - static_assert( - std::is_convertible< D, OutputType >::value, - "Cannot convert input to the given output type" - ); + class ValueOrIndex< + descr, OutputType, D, + typename std::enable_if< ! std::is_arithmetic< OutputType >::value + && ! std::is_same< OutputType, void >::value >::type + > { + static_assert( + !( descr & descriptors::use_index ), + "use_index descriptor given while output type is not numeric" + ); + static_assert( + std::is_convertible< D, OutputType >::value, + "Cannot convert input to the given output type" + ); public: @@ -101,7 +102,7 @@ namespace alp { } }; - } // namespace internal + } // namespace internal } // namespace alp diff --git a/include/alp/reference/blas0.hpp b/include/alp/reference/blas0.hpp index 8d2728af1..058d7faa2 100644 --- a/include/alp/reference/blas0.hpp +++ b/include/alp/reference/blas0.hpp @@ -49,6 +49,7 @@ "********************************************************************" \ "******************************\n" ); + namespace alp { namespace internal { diff --git a/include/alp/reference/blas3.hpp b/include/alp/reference/blas3.hpp index c8352d97b..705c5e02e 100644 --- a/include/alp/reference/blas3.hpp +++ b/include/alp/reference/blas3.hpp @@ -82,6 +82,7 @@ "********************************************************************" \ "******************************\n" ); + namespace alp { namespace internal { diff --git a/include/alp/rels.hpp b/include/alp/rels.hpp index 5871c972a..66a3dfbd1 100644 --- a/include/alp/rels.hpp +++ b/include/alp/rels.hpp @@ -28,6 +28,7 @@ #include "type_traits.hpp" #include "internalrels.hpp" + namespace alp { /** diff --git a/include/alp/type_traits.hpp b/include/alp/type_traits.hpp index 69c59881d..39f3ebf6a 100644 --- a/include/alp/type_traits.hpp +++ b/include/alp/type_traits.hpp @@ -29,6 +29,7 @@ #include #include + namespace alp { /** diff --git a/tests/unit/alp_relations.cpp b/tests/unit/alp_relations.cpp index 9e0e65831..9863298c0 100644 --- a/tests/unit/alp_relations.cpp +++ b/tests/unit/alp_relations.cpp @@ -21,7 +21,8 @@ #include -void alp_program( const size_t & n, alp::RC & rc ) { + +void alp_program( const size_t &n, alp::RC &rc ) { (void) n; @@ -43,25 +44,25 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( !( alp::is_equivalence_relation< dbl_lt >::value ) ); - if( !( dbl_lt::check(2.4, 5) ) ) { + if( !( dbl_lt::check( 2.4, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "dbl_lt::test(2.4, 5) failed." << std::endl; + std::cerr << "dbl_lt::test( 2.4, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( dbl_lt::check(5, 2.4) ) { + if( dbl_lt::check( 5, 2.4 ) ) { #ifndef NDEBUG - std::cerr << "dbl_lt::check(5, 2.4) failed." << std::endl; + std::cerr << "dbl_lt::check( 5, 2.4 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( dbl_lt::check(5, 5) ) { + if( dbl_lt::check( 5, 5 ) ) { #ifndef NDEBUG - std::cerr << "dbl_lt::test(5, 5) failed." << std::endl; + std::cerr << "dbl_lt::test( 5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; @@ -85,25 +86,25 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( !( alp::is_equivalence_relation< dbl_gt >::value ) ); - if( dbl_gt::check(2.4, 5) ) { + if( dbl_gt::check( 2.4, 5 ) ) { #ifndef NDEBUG - std::cerr << "dbl_gt::test(2.4, 5) failed." << std::endl; + std::cerr << "dbl_gt::test( 2.4, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( dbl_gt::check(5, 2.4) ) ) { + if( !( dbl_gt::check( 5, 2.4 ) ) ) { #ifndef NDEBUG - std::cerr << "dbl_gt::check(5, 2.4) failed." << std::endl; + std::cerr << "dbl_gt::check( 5, 2.4 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( dbl_gt::check(5, 5) ) { + if( dbl_gt::check( 5, 5 ) ) { #ifndef NDEBUG - std::cerr << "dbl_gt::test(5, 5) failed." << std::endl; + std::cerr << "dbl_gt::test( 5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; @@ -127,33 +128,33 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( alp::is_equivalence_relation< int_eq >::value ); - if( int_eq::check(2.4, 5) ) { + if( int_eq::check( 2.4, 5 ) ) { #ifndef NDEBUG - std::cerr << "int_eq::test(2.4, 5) failed." << std::endl; + std::cerr << "int_eq::test( 2.4, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( int_eq::check(5, 2.4) ) { + if( int_eq::check( 5, 2.4 ) ) { #ifndef NDEBUG - std::cerr << "int_eq::check(5, 2.4) failed." << std::endl; + std::cerr << "int_eq::check( 5, 2.4 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_eq::check(5, 5) ) ) { + if( !( int_eq::check( 5, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_eq::test(5, 5) failed." << std::endl; + std::cerr << "int_eq::test( 5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_eq::check(5.5, 5) ) ) { + if( !( int_eq::check( 5.5, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_eq::test(5.5, 5) failed." << std::endl; + std::cerr << "int_eq::test( 5.5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; @@ -177,33 +178,33 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( !( alp::is_equivalence_relation< int_neq >::value ) ); - if( !( int_neq::check(2.4, 5) ) ) { + if( !( int_neq::check( 2.4, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_neq::test(2.4, 5) failed." << std::endl; + std::cerr << "int_neq::test( 2.4, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_neq::check(5, 2.4) ) ) { + if( !( int_neq::check( 5, 2.4 ) ) ) { #ifndef NDEBUG - std::cerr << "int_neq::check(5, 2.4) failed." << std::endl; + std::cerr << "int_neq::check( 5, 2.4 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( int_neq::check(5, 5) ) { + if( int_neq::check( 5, 5 ) ) { #ifndef NDEBUG - std::cerr << "int_neq::test(5, 5) failed." << std::endl; + std::cerr << "int_neq::test( 5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( int_neq::check(5.5, 5) ) { + if( int_neq::check( 5.5, 5 ) ) { #ifndef NDEBUG - std::cerr << "int_neq::test(5.5, 5) failed." << std::endl; + std::cerr << "int_neq::test( 5.5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; @@ -227,33 +228,33 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( !( alp::is_equivalence_relation< int_le >::value ) ); - if( !( int_le::check(2.4, 5) ) ) { + if( !( int_le::check( 2.4, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_le::test(2.4, 5) failed." << std::endl; + std::cerr << "int_le::test( 2.4, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( int_le::check(5, 2.4) ) { + if( int_le::check( 5, 2.4 ) ) { #ifndef NDEBUG - std::cerr << "int_le::check(5, 2.4) failed." << std::endl; + std::cerr << "int_le::check( 5, 2.4 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_le::check(5, 5) ) ) { + if( !( int_le::check( 5, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_le::test(5, 5) failed." << std::endl; + std::cerr << "int_le::test( 5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_le::check(5.5, 5) ) ) { + if( !( int_le::check( 5.5, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_le::test(5.5, 5) failed." << std::endl; + std::cerr << "int_le::test( 5.5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; @@ -277,33 +278,33 @@ void alp_program( const size_t & n, alp::RC & rc ) { static_assert( !( alp::is_equivalence_relation< int_ge >::value ) ); - if( int_ge::check(2.4, 5) ) { + if( int_ge::check( 2.4, 5 ) ) { #ifndef NDEBUG - std::cerr << "int_ge::test(2.4, 5) failed." << std::endl; + std::cerr << "int_ge::test( 2.4, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_ge::check(5, 2.4) ) ) { + if( !( int_ge::check( 5, 2.4 ) ) ) { #ifndef NDEBUG - std::cerr << "int_ge::check(5, 2.4) failed." << std::endl; + std::cerr << "int_ge::check( 5, 2.4 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_ge::check(5, 5) ) ) { + if( !( int_ge::check( 5, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_ge::test(5, 5) failed." << std::endl; + std::cerr << "int_ge::test( 5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; } - if( !( int_ge::check(5.5, 5) ) ) { + if( !( int_ge::check( 5.5, 5 ) ) ) { #ifndef NDEBUG - std::cerr << "int_ge::test(5.5, 5) failed." << std::endl; + std::cerr << "int_ge::test( 5.5, 5 ) failed." << std::endl; #endif rc = alp::FAILED; return; diff --git a/tests/unit/dense_mxm.cpp b/tests/unit/dense_mxm.cpp index ff524c34b..0f2549243 100644 --- a/tests/unit/dense_mxm.cpp +++ b/tests/unit/dense_mxm.cpp @@ -24,6 +24,7 @@ using namespace alp; + template< typename T > void print_stdvec_as_matrix( std::string name, const std::vector< T > & vA, const size_t m, const size_t n, const size_t lda ) { @@ -187,8 +188,6 @@ void diff_stdvec_matrix( const std::vector< T > & vA, const size_t m, const size } - - void alp_program( const size_t & n, alp::RC & rc ) { typedef double T; diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp index b83afb45f..37c032e7f 100644 --- a/tests/unit/dense_sort.cpp +++ b/tests/unit/dense_sort.cpp @@ -27,6 +27,7 @@ using namespace alp; + void alp_program( const size_t &n, alp::RC &rc ) { typedef double T; From 4595c481685014a3b4f62dc1fe9452c3166169dc Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:06:21 +0100 Subject: [PATCH 13/14] Added review note in comment. --- include/alp/base/internalops.hpp | 1 - tests/unit/dense_sort.cpp | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/alp/base/internalops.hpp b/include/alp/base/internalops.hpp index f44c7ad4d..35b961d59 100644 --- a/include/alp/base/internalops.hpp +++ b/include/alp/base/internalops.hpp @@ -1449,7 +1449,6 @@ namespace alp { * At the end of the operation, \f$ c = \min\{a,b\} \f$. */ static void apply( const left_type * __restrict__ const a, const right_type * __restrict__ const b, result_type * __restrict__ const c ) { - printf( "Hello from mul\n" ); if( *a || *b ) { *c = static_cast< OUT >( true ); } else { diff --git a/tests/unit/dense_sort.cpp b/tests/unit/dense_sort.cpp index 37c032e7f..b93f4d183 100644 --- a/tests/unit/dense_sort.cpp +++ b/tests/unit/dense_sort.cpp @@ -33,6 +33,13 @@ void alp_program( const size_t &n, alp::RC &rc ) { typedef double T; #ifndef NDEBUG + /** + * Note: in case of a distributed backend, the resulting printout + * may appear interleaved across processes. In ALP/GraphBLAS, there is + * the if( spmd<>::pid() == k ) guard to print for process k only, + * and the statement can of course be looped over k=0 + * to spmd<>::nprocs() and separated by spmd<>::barrier(). + */ auto print_std_vector = [](std::vector< T > const vec) { for(auto val : vec) { std::cout << val << ' '; From fdc7f4852a6d04809965e7d2894f8e4dff9976ee Mon Sep 17 00:00:00 2001 From: "Daniele G. Spampinato" Date: Tue, 15 Nov 2022 11:59:18 +0100 Subject: [PATCH 14/14] Basing internal relation checks on internal ops apply (equal and not_equal only for now) --- include/alp/base/internalrels.hpp | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/include/alp/base/internalrels.hpp b/include/alp/base/internalrels.hpp index 3453f9a59..4ca19a996 100644 --- a/include/alp/base/internalrels.hpp +++ b/include/alp/base/internalrels.hpp @@ -28,6 +28,8 @@ #include #include +#include "internalops.hpp" + namespace alp { @@ -209,12 +211,9 @@ namespace alp { /** * Standard equal (\a eq) relation. * - * Assumes native availability of \a operator== on the given data types - * or assumes that the relevant operators are properly overloaded. - * - * Assumes that \a eq is an equivalence relation. - * Non-standard/non-matching data types or non-standard (overloaded) - * \a operator== should therefore be used with caution. + * Assumes native availability of ALP internal operator \a less_than + * forming an equivalence relation on SET. Non-standard/non-matching + * data types should therefore be used with caution. * * @tparam SET The input data type. */ @@ -287,21 +286,23 @@ namespace alp { static bool check( const domain * const a, const codomain * const b ) { - return *a == *b; + bool check; + operators::internal::template equal< + SET, SET, bool, implementation + >::apply( a, b, &check ); + return check; } }; /** * Standard not-equal (\a neq) operator. * - * Assumes native availability of \a operator!= on the given data types - * or assumes that the relevant operators are properly overloaded. + * Assumes availability of ALP internal operator \a not_equal. * - * While \a neq does not require two values to be members of - * an ordered set, the relation is still assumed to be irreflexive, - * symmetric and connected. - * Non-standard/non-matching data types or non-standard (overloaded) - * \a operator!= should therefore be used with caution. + * While \a not_equal does not require to form an order or an + * equivalence relation on SET, the formed relation is still assumed + * to be irreflexive, symmetric, and connected. Non-standard/non-matching + * data types should therefore be used with caution. * * @tparam SET The input data type. */ @@ -374,7 +375,11 @@ namespace alp { static bool check( const domain * const a, const codomain * const b ) { - return *a != *b; + bool check; + operators::internal::template not_equal< + SET, SET, bool, implementation + >::apply( a, b, &check ); + return check; } };