diff --git a/include/graphblas/base/io.hpp b/include/graphblas/base/io.hpp index 0c0bab768..493dfcfea 100644 --- a/include/graphblas/base/io.hpp +++ b/include/graphblas/base/io.hpp @@ -1181,13 +1181,14 @@ namespace grb { */ template< Descriptor descr = descriptors::no_operation, - typename OutputType, typename RIT, typename CIT, typename NIT, - typename InputType, + typename OutputType, typename InputType, + typename RIT1, typename CIT1, typename NIT1, + typename RIT2, typename CIT2, typename NIT2, Backend backend > RC set( - Matrix< OutputType, backend, RIT, CIT, NIT > &C, - const Matrix< InputType, backend, RIT, CIT, NIT > &A, + Matrix< OutputType, backend, RIT1, CIT1, NIT1 > &A, + const Matrix< InputType, backend, RIT2, CIT2, NIT2 > &C, const Phase &phase = EXECUTE, const typename std::enable_if< !grb::is_object< OutputType >::value && @@ -1198,8 +1199,8 @@ namespace grb { const bool should_not_call_base_matrix_set = false; assert( should_not_call_base_matrix_set ); #endif - (void) C; (void) A; + (void) C; (void) phase; return UNSUPPORTED; } @@ -1222,10 +1223,10 @@ namespace grb { * mutually exclusive for this primitive. * \endparblock * - * @tparam DataType The type of each element in the given matrix. - * @tparam MaskType The type of each element in the given mask. - * @tparam ValueType The type of the given value. Should be convertible - * to \a DataType. + * @tparam OutputType The type of each element in the given matrix. + * @tparam MaskType The type of each element in the given mask. + * @tparam ValueType The type of the given value. Should be convertible + * to \a OutputType. * @tparam RIT The integer type for encoding row indices. * @tparam CIT The integer type for encoding column indices. * @tparam NIT The integer type for encoding nonzero indices. @@ -1264,7 +1265,7 @@ namespace grb { * * When \a descr includes #grb::descriptors::no_casting then code shall not * compile if one of the following conditions are met: - * -# \a ValueType does not match \a DataType; or + * -# \a ValueType does not match \a OutputType; or * -# \a MaskType does not match bool. * * In these cases, the code shall not compile: implementations must throw @@ -1292,18 +1293,18 @@ namespace grb { */ template< Descriptor descr = descriptors::no_operation, - typename DataType, typename RIT, typename CIT, typename NIT, - typename MaskType, - typename ValueType, + typename OutputType, typename MaskType, typename ValueType, + typename RIT1, typename CIT1, typename NIT1, + typename RIT2, typename CIT2, typename NIT2, Backend backend > RC set( - Matrix< DataType, backend, RIT, CIT, NIT > &C, - const Matrix< MaskType, backend, RIT, CIT, NIT > &mask, - const ValueType& val, + Matrix< OutputType, backend, RIT1, CIT1, NIT1 > &C, + const Matrix< MaskType, backend, RIT2, CIT2, NIT2 > &mask, + const ValueType &val, const Phase &phase = EXECUTE, const typename std::enable_if< - !grb::is_object< DataType >::value && + !grb::is_object< OutputType >::value && !grb::is_object< ValueType >::value && !grb::is_object< MaskType >::value, void >::type * const = nullptr diff --git a/include/graphblas/bsp1d/io.hpp b/include/graphblas/bsp1d/io.hpp index 8f40f1dd9..edada4191 100644 --- a/include/graphblas/bsp1d/io.hpp +++ b/include/graphblas/bsp1d/io.hpp @@ -1723,7 +1723,7 @@ namespace grb { // each thread writes to a different interval of the destination array // give pointers to hint at memmove whenever possible // (StorageType should be trivially copyable) - std::copy_n( + (void) std::copy_n( local_out.data(), num_nnz_local, out.data() + first_nnz_local ); diff --git a/include/graphblas/nonblocking/io.hpp b/include/graphblas/nonblocking/io.hpp index 95147f225..0831fc564 100644 --- a/include/graphblas/nonblocking/io.hpp +++ b/include/graphblas/nonblocking/io.hpp @@ -1134,7 +1134,8 @@ namespace grb { std::cout << "Called grb::set (matrix-to-matrix, nonblocking)" << std::endl; #endif // static checks - NO_CAST_ASSERT( ( !(descr & descriptors::no_casting) || + NO_CAST_ASSERT( + ( !(descr & descriptors::no_casting) || std::is_same< InputType, OutputType >::value ), "grb::set", "called with non-matching value types" ); @@ -1170,11 +1171,12 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, typename OutputType, typename InputType1, typename InputType2, - typename RIT, typename CIT, typename NIT + typename RIT1, typename CIT1, typename NIT1, + typename RIT2, typename CIT2, typename NIT2 > RC set( - Matrix< OutputType, nonblocking, RIT, CIT, NIT > &C, - const Matrix< InputType1, nonblocking, RIT, CIT, NIT > &A, + Matrix< OutputType, nonblocking, RIT1, CIT1, NIT1 > &C, + const Matrix< InputType1, nonblocking, RIT2, CIT2, NIT2 > &A, const InputType2 &val, const Phase &phase = EXECUTE, const typename std::enable_if< diff --git a/include/graphblas/reference/compressed_storage.hpp b/include/graphblas/reference/compressed_storage.hpp index 2bb7bb183..95b67a903 100644 --- a/include/graphblas/reference/compressed_storage.hpp +++ b/include/graphblas/reference/compressed_storage.hpp @@ -23,13 +23,21 @@ #ifndef _H_GRB_REFERENCE_COMPRESSED_STORAGE #define _H_GRB_REFERENCE_COMPRESSED_STORAGE -#include //std::memcpy +#include // std::memcpy +#include // std::copy_n + +#ifdef _DEBUG + #define _DEBUG_REFERENCE_COMPRESSED_STORAGE +#endif namespace grb { namespace internal { + template< typename D, typename IND, typename SIZE > + class Compressed_Storage; + /** * Basic functionality for a compressed storage format (CRS/CSR or CCS/CSC). * @@ -144,7 +152,7 @@ namespace grb { k( 0 ), m( 0 ), n( 0 ), row( 1 ), s( 0 ), P( 1 ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Iterator default constructor (generic) called\n"; #endif nonzero.first.first = 1; @@ -159,7 +167,7 @@ namespace grb { row( other.row ), s( other.s ), P( other.P ), nonzero( other.nonzero ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Matrix< reference >::const_iterator copy-constructor " << "called\n"; #endif @@ -167,7 +175,7 @@ namespace grb { /** Move constructor. */ ConstIterator( ConstIterator &&other ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Matrix< reference >::const_iterator move-constructor " << "called\n"; #endif @@ -194,7 +202,7 @@ namespace grb { m( _m ), n( _n ), s( _s ), P( _P ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Compressed_Storage::Const_Iterator constructor called, " << "with storage " << ( &_storage ) << ", " << "m " << _m << ", n " << _n << ", and end " << end << ".\n"; @@ -212,7 +220,7 @@ namespace grb { } if( row < m ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "\tInitial pair, pre-translated at " << row << ", " << row_index[ k ] << " with value " << values[ k ] << ". " << "P = " << P << ", row = " << row << ".\n"; @@ -226,7 +234,7 @@ namespace grb { nonzero.first.second = ActiveDistribution::local_index_to_global( row_index[ k ] - col_off, n, col_pid, P ); nonzero.second = values[ k ]; -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "\tInitial pair at " << nonzero.first.first << ", " << nonzero.first.second << " with value " << nonzero.second << ". " << "P = " << P << ", row = " << row << ".\n"; @@ -236,7 +244,7 @@ namespace grb { /** Copy assignment. */ ConstIterator & operator=( const ConstIterator &other ) noexcept { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Matrix (reference) const-iterator copy-assign operator " << "called\n"; #endif @@ -255,7 +263,7 @@ namespace grb { /** Move assignment. */ ConstIterator & operator=( ConstIterator &&other ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Matrix (reference) const-iterator move-assign operator " << "called\n"; #endif @@ -274,7 +282,7 @@ namespace grb { /** Whether two iterators compare equal. */ bool operator==( const ConstIterator &other ) const noexcept { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Compressed_Storage::Const_Iterator operator== called " << "with k ( " << k << ", " << other.k << " ), " << " m ( " << m << ", " << other.m << " )\n"; @@ -300,7 +308,7 @@ namespace grb { /** Whether two iterators do not compare equal. */ bool operator!=( const ConstIterator &other ) const noexcept { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Compressed_Storage::Const_Iterator operator!= called " << "with k ( " << k << ", " << other.k << " ), " << "row ( " << row << ", " << other.row << " ), " @@ -325,7 +333,7 @@ namespace grb { /** Move to the next iterator. */ ConstIterator & operator++() noexcept { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Compressed_Storage::operator++ called\n"; #endif if( row == m ) { @@ -338,7 +346,7 @@ namespace grb { (void) ++row; } if( row < m ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "\tupdated triple, pre-translated at ( " << row << ", " << row_index[ k ] << " ): " << values[ k ] << "\n"; #endif @@ -353,7 +361,7 @@ namespace grb { nonzero.first.second = ActiveDistribution::local_index_to_global( row_index[ k ] - col_off, n, col_pid, P ); nonzero.second = values[ k ]; -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "\tupdated triple at ( " << nonzero.first.first << ", " << nonzero.first.second << " ): " << nonzero.second << "\n"; #endif @@ -516,13 +524,17 @@ namespace grb { } /** - * Copies contents from a given Compressed_Storage. + * Copies coordinates from a given #Compressed_Storage, \a other. * - * Performs no safety checking. Performs no (re-)allocations. + * Optionally, fills the values with a given identity instead of the values + * from \a other; see \a use_id. * - * @tparam use_id If set to true, use \a id instead of values in - * \a other. + * Via SFINAE, this variant applies only to \a use_id true. * + * Performs no safety checking. Performs no (re-)allocations. + * + * @tparam use_id If set to true, use \a id instead of values in + * \a other. * @param[in] other The container to copy from. * @param[in] nz The number of nonzeroes in the \a other container. * @param[in] m The index dimension of the \a other container. @@ -530,8 +542,7 @@ namespace grb { * @param[in] end The end position to copy to (exclusive). * @param[in] id A pointer to a value overriding those in \a other. * Will only be used if and only if \a use_id is set - * true. - * + * to true. * The copy range is 2nz + m + 1, i.e., * -# 0 <= start < 2nz + m + 1 * -# 0 < end <= 2nz + m + 1 @@ -541,36 +552,51 @@ namespace grb { * complete if the union of ranges spans 0 to 2nz + m + 1. */ template< - bool use_id = false, - typename InputType, typename ValueType = char + Descriptor descr, + bool useId, + typename InputType, typename InputIND, typename InputSIZE, + typename ValueType > void copyFrom( - const Compressed_Storage< InputType, IND, SIZE > &other, + const Compressed_Storage< InputType, InputIND, InputSIZE > &other, const size_t nz, const size_t m, const size_t start, size_t end, - const ValueType * __restrict__ id = nullptr + const ValueType * __restrict__ id, + const typename std::enable_if< useId, void >::type * = nullptr ) { -#ifdef _DEBUG + static_assert( std::is_convertible< ValueType, D >::value, + "internal logic error: ValueType must be convertible to D. " + "Please submit a bug report" + ); + static_assert( std::is_convertible< InputIND, IND >::value, + "internal logic error: InputIND must be convertible to IND. " + "Please submit a bug report" + ); + static_assert( std::is_convertible< InputSIZE, SIZE >::value, + "internal logic error: InputSIZE must be convertible to SIZE. " + "Please submit a bug report" + ); +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "CompressedStorage::copyFrom (cast) called with range " - << start << "--" << end; - if( use_id ) { - std::cout << ". The identity " << (*id) << " will be used.\n"; - } else { - std::cout << ". No identity will be used.\n"; - } + << start << "--" << end << ". The identity " << (*id) << " will be used " + << ".\n"; #endif assert( start <= end ); size_t k = start; if( k < nz ) { const size_t loop_end = std::min( nz, end ); assert( k <= loop_end ); - for( ; k < loop_end; ++k ) { - if( use_id ) { - values[ k ] = *id; - } else { - values[ k ] = other.getValue( k, *id ); + GRB_UTIL_IGNORE_CLASS_MEMACCESS; // by the ALP spec, D can only be a POD + // type, in which case raw memory copies + // are OK + for( size_t i = k; i < loop_end; ++i ) { + if( utils::interpretMatrixMask< descr, InputType >( + true, other.getValues(), i ) + ) { + values[ i ] = static_cast< D >( *id ); } } + GRB_UTIL_RESTORE_WARNINGS; k = 0; } else { assert( k >= nz ); @@ -580,14 +606,21 @@ namespace grb { return; } end -= nz; + if( k < nz ) { const size_t loop_end = std::min( nz, end ); assert( k <= loop_end ); - (void) std::memcpy( - row_index + k, - other.row_index + k, - (loop_end - k) * sizeof( IND ) - ); + GRB_UTIL_IGNORE_CLASS_MEMACCESS; // by the ALP spec, D can only be a POD + // type, in which case raw memory copies + // are OK + for( size_t i = k; i < loop_end; ++i ) { + if( utils::interpretMatrixMask< descr, InputType >( + true, other.getValues(), i ) + ) { + row_index[ i ] = static_cast< IND >( other.row_index[ i ] ); + } + } + GRB_UTIL_RESTORE_WARNINGS; k = 0; } else { assert( k >= nz ); @@ -597,54 +630,97 @@ namespace grb { return; } end -= nz; + if( k < m + 1 ) { const size_t loop_end = std::min( m + 1, end ); assert( k <= loop_end ); - (void) std::memcpy( - col_start + k, - other.col_start + k, - (loop_end - k) * sizeof( SIZE ) - ); + GRB_UTIL_IGNORE_CLASS_MEMACCESS; // by the ALP spec, D can only be a POD + // type, in which case raw memory copies + // are OK + for( size_t i = k; i < loop_end; ++i ) { + if( utils::interpretMatrixMask< descr, InputType >( + true, other.getValues(), i ) + ) { + col_start[ i ] = static_cast< SIZE >( other.col_start[ i ] ); + } + } + GRB_UTIL_RESTORE_WARNINGS; +#ifndef NDEBUG + for( size_t chk = k; chk < loop_end - 1; ++chk ) { + assert( other.col_start[ chk ] <= col_start[ chk + 1 ] ); + assert( col_start[ chk ] <= col_start[ chk + 1 ] ); + } +#endif } } - /** \internal Specialisation for no cast copy */ - template< bool use_id = false > + /** + * Copies contents from a given Compressed_Storage. + * + * Optionally, fills the values with a given identity instead of the values + * from \a other; see \a use_id. + * + * Via SFINAE, this variant applies only to \a use_id false. + * + * Performs no safety checking. Performs no (re-)allocations. + * + * @param[in] other The container to copy from. + * @param[in] nz The number of nonzeroes in the \a other container. + * @param[in] m The index dimension of the \a other container. + * @param[in] start The start position to copy from (inclusive). + * @param[in] end The end position to copy to (exclusive). + * + * The copy range is 2nz + m + 1, i.e., + * -# 0 <= start < 2nz + m + 1 + * -# 0 < end <= 2nz + m + 1 + * + * Concurrent calls to this function are allowed iff they consist of + * disjoint ranges \a start and \a end. The copy is guaranteed to be + * complete if the union of ranges spans 0 to 2nz + m + 1. + */ + template< + Descriptor descr, + bool useId, + typename InputType, typename InputIND, typename InputSIZE, + typename ValueType + > void copyFrom( - const Compressed_Storage< D, IND, SIZE > &other, + const Compressed_Storage< InputType, InputIND, InputSIZE > &other, const size_t nz, const size_t m, const size_t start, size_t end, - const D * __restrict__ id = nullptr + const ValueType * __restrict__ id, + const typename std::enable_if< !useId, void >::type * = nullptr ) { -#ifdef _DEBUG - std::cout << "CompressedStorage::copyFrom (no-cast) called with range " - << start << "--" << end; - if( use_id ) { - std::cout << ". The identity " << (*id) << " will be used.\n"; - } else { - std::cout << ". No identity will be used.\n"; - } + (void) id; +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE + std::cout << "CompressedStorage::copyFrom called with range " + << start << "--" << end << ". No identity will be used.\n"; #endif + // static checks + static_assert( !std::is_void< InputType >::value, + "Internal logic error: InputType must not be void. " + "Please submit a bug report." + ); + static_assert( + ( !useId && std::is_convertible< InputType, D >::value ), + "Internal logic error: InputType must be convertible to D" + "Please submit a bug report." + ); + static_assert( std::is_convertible< InputIND, IND >::value, + "Internal logic error: InputIND must be convertible to IND" + "Please submit a bug report." + ); + static_assert( std::is_convertible< InputSIZE, SIZE >::value, + "Internal logic error: InputSIZE must be convertible to SIZE" + "Please submit a bug report." + ); + + // do copy size_t k = start; if( k < nz ) { const size_t loop_end = std::min( nz, end ); -#ifdef _DEBUG - std::cout << "\t value range " << k << " -- " << loop_end << "\n"; -#endif assert( k <= loop_end ); - if( use_id ) { - std::fill( values + k, values + loop_end, *id ); - } else { - GRB_UTIL_IGNORE_CLASS_MEMACCESS // by the ALP spec, D can only be POD - // types. In this case raw memory copies - // are OK. - (void) std::memcpy( - values + k, - other.values + k, - (loop_end - k) * sizeof( D ) - ); - GRB_UTIL_RESTORE_WARNINGS - } + (void) std::copy_n( other.values + k, loop_end - k, values + k ); k = 0; } else { assert( k >= nz ); @@ -654,17 +730,11 @@ namespace grb { return; } end -= nz; + if( k < nz ) { const size_t loop_end = std::min( nz, end ); -#ifdef _DEBUG - std::cout << "\t index range " << k << " -- " << loop_end << "\n"; -#endif assert( k <= loop_end ); - (void) std::memcpy( - row_index + k, - other.row_index + k, - (loop_end - k) * sizeof( IND ) - ); + (void) std::copy_n( other.row_index + k, loop_end - k, row_index + k ); k = 0; } else { assert( k >= nz ); @@ -674,17 +744,11 @@ namespace grb { return; } end -= nz; + if( k < m + 1 ) { const size_t loop_end = std::min( m + 1, end ); -#ifdef _DEBUG - std::cout << "\t start range " << k << " -- " << loop_end << "\n"; -#endif assert( k <= loop_end ); - (void) std::memcpy( - col_start + k, - other.col_start + k, - (loop_end - k) * sizeof( SIZE ) - ); + (void) std::copy_n( other.col_start + k, loop_end - k, col_start + k ); #ifndef NDEBUG for( size_t chk = k; chk < loop_end - 1; ++chk ) { assert( other.col_start[ chk ] <= other.col_start[ chk + 1 ] ); @@ -710,7 +774,7 @@ namespace grb { void recordValue( const size_t &pos, const bool row, const fwd_it &it ) { row_index[ pos ] = row ? it.i() : it.j(); values[ pos ] = it.v(); -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "\t nonzero at position " << it.i() << " by " << it.j() << " is stored at position " << pos << " has value " << it.v() << ".\n"; #endif @@ -881,7 +945,7 @@ namespace grb { k( 0 ), m( 0 ), n( 0 ), row( 1 ), s( 0 ), P( 1 ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Iterator default constructor (pattern specialisation) " << "called\n"; nonzero.first = 1; @@ -896,7 +960,7 @@ namespace grb { row( other.row ), s( other.s ), P( other.P ), nonzero( other.nonzero ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Iterator copy constructor (pattern specialisation) " << "called\n"; #endif @@ -904,7 +968,7 @@ namespace grb { /** Move constructor. */ ConstIterator( ConstIterator &&other ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Iterator move constructor (pattern specialisation) " << "called\n"; #endif @@ -929,7 +993,7 @@ namespace grb { k( 0 ), m( _m ), n( _n ), s( _s ), P( _P ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Iterator constructor (pattern specialisation) called\n"; #endif if( _nz == 0 || _m == 0 || _n == 0 || end ) { @@ -958,7 +1022,7 @@ namespace grb { /** Copy assignment. */ ConstIterator & operator=( const ConstIterator &other ) noexcept { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Iterator copy-assign operator (pattern specialisation) " << "called\n"; #endif @@ -976,7 +1040,7 @@ namespace grb { /** Move assignment. */ ConstIterator & operator=( ConstIterator &&other ) { -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "Iterator move-assign operator (pattern specialisation) " << "called\n"; #endif @@ -1198,29 +1262,30 @@ namespace grb { * \internal copyFrom specialisation for pattern matrices. */ template< - bool use_id = false, - typename InputType, - typename UnusedType = void + Descriptor, + bool unusedValue, + typename InputType, typename InputIND, typename InputSIZE, + typename UnusedType > void copyFrom( - const Compressed_Storage< InputType, IND, SIZE > &other, + const Compressed_Storage< InputType, InputIND, InputSIZE > &other, const size_t nz, const size_t m, const size_t start, size_t end, - const UnusedType * __restrict__ = nullptr + const UnusedType * __restrict__ ) { - // the use_id template is meaningless in the case of pattern matrices, but - // is retained to keep the API the same as with the non-pattern case. - (void) use_id; -#ifdef _DEBUG + (void) unusedValue; + // the unusedValue template is meaningless in the case of + // pattern matrices, but is retained to keep the API + // the same as with the non-pattern case. + auto k = start; +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "CompressedStorage::copyFrom (void) called with range " << start << "--" << end << "\n"; #endif - size_t k = start; + if( k < nz ) { const size_t loop_end = std::min( nz, end ); - (void) std::memcpy( - row_index + k, other.row_index + k, - (loop_end - k) * sizeof( IND ) - ); + assert( k <= loop_end ); + (void) std::copy_n( other.row_index + k, loop_end - k, row_index + k ); k = 0; } else { assert( k >= nz ); @@ -1230,12 +1295,17 @@ namespace grb { return; } end -= nz; + if( k < m + 1 ) { const size_t loop_end = std::min( m + 1, end ); - (void) std::memcpy( - col_start + k, other.col_start + k, - (loop_end - k) * sizeof( SIZE ) - ); + assert( k <= loop_end ); + (void) std::copy_n( other.col_start + k, loop_end - k, col_start + k ); +#ifndef NDEBUG + for( size_t chk = k; chk < loop_end - 1; ++chk ) { + assert( other.col_start[ chk ] <= other.col_start[ chk + 1 ] ); + assert( col_start[ chk ] <= col_start[ chk + 1 ] ); + } +#endif } } @@ -1255,7 +1325,7 @@ namespace grb { void recordValue( const size_t &pos, const bool row, const fwd_it &it ) { row_index[ pos ] = row ? it.i() : it.j(); // values are ignored for pattern matrices -#ifdef _DEBUG +#ifdef _DEBUG_REFERENCE_COMPRESSED_STORAGE std::cout << "\t nonzero at position " << it.i() << " by " << it.j() << " is stored at position " << pos << ". " << "It records no nonzero value as this is a pattern matrix.\n"; diff --git a/include/graphblas/reference/io.hpp b/include/graphblas/reference/io.hpp index f6896e11b..f4f1b0709 100644 --- a/include/graphblas/reference/io.hpp +++ b/include/graphblas/reference/io.hpp @@ -925,14 +925,19 @@ namespace grb { bool A_is_mask, Descriptor descr, typename OutputType, typename InputType1, - typename InputType2 = const OutputType, + typename InputType2, typename RIT, typename CIT, typename NIT > RC set_copy( Matrix< OutputType, reference, RIT, CIT, NIT > &C, const Matrix< InputType1, reference, RIT, CIT, NIT > &A, - const InputType2 * __restrict__ id = nullptr + const InputType2 * __restrict__ id ) noexcept { +#ifndef NDEBUG + if( A_is_mask ) { + assert( id != nullptr ); + } +#endif #ifdef _DEBUG_REFERENCE_IO std::cout << "\t called grb::internal::set_copy (reference), " << "execute phase\n"; @@ -1020,30 +1025,18 @@ namespace grb { const size_t start = 0; size_t end = range; #endif - if( A_is_mask ) { - internal::getCRS( C ).template copyFrom< true >( - internal::getCRS( A ), nz, m, start, end, id - ); - } else { - internal::getCRS( C ).template copyFrom< false >( - internal::getCRS( A ), nz, m, start, end - ); - } + internal::getCRS( C ).template copyFrom< descr, A_is_mask >( + internal::getCRS( A ), nz, m, start, end, id + ); range = internal::getCCS( C ).copyFromRange( nz, n ); #ifdef _H_GRB_REFERENCE_OMP_IO config::OMP::localRange( start, end, 0, range ); #else end = range; #endif - if( A_is_mask ) { - internal::getCCS( C ).template copyFrom< true >( - internal::getCCS( A ), nz, n, start, end, id - ); - } else { - internal::getCCS( C ).template copyFrom< false >( - internal::getCCS( A ), nz, n, start, end - ); - } + internal::getCCS( C ).template copyFrom< descr, A_is_mask >( + internal::getCCS( A ), nz, n, start, end, id + ); } internal::setCurrentNonzeroes( C, nz ); @@ -1706,6 +1699,14 @@ namespace grb { #ifdef _DEBUG std::cout << "In grb::set (vector-to-value, masked)\n"; #endif + static_assert( + std::is_void< MaskType >::value || + (descr & descriptors::structural) || + std::is_convertible< MaskType, bool > ::value, + "grb::set (masked set to value): mask vector must be a " + "pattern vector, or have a data-type that is convertible to bool, " + "or use the structural descriptor" + ); // static sanity checks NO_CAST_ASSERT( ( !(descr & descriptors::no_casting) || std::is_same< DataType, T >::value ), "grb::set (Vector to scalar, masked)", @@ -1835,8 +1836,9 @@ namespace grb { !grb::is_object< InputType >::value, void >::type * const = nullptr ) noexcept { - static_assert( std::is_same< OutputType, void >::value || - !std::is_same< InputType, void >::value, + static_assert( + !std::is_void< InputType >::value || + std::is_same< OutputType, InputType >::value, "grb::set cannot interpret an input pattern matrix without a " "semiring or a monoid. This interpretation is needed for " "writing the non-pattern matrix output. Possible solutions: 1) " @@ -1846,7 +1848,8 @@ namespace grb { std::cout << "Called grb::set (matrix-to-matrix, reference)" << std::endl; #endif // static checks - NO_CAST_ASSERT( ( !(descr & descriptors::no_casting) || + NO_CAST_ASSERT( + ( !(descr & descriptors::no_casting) || std::is_same< InputType, OutputType >::value ), "grb::set", "called with non-matching value types" ); @@ -1882,18 +1885,20 @@ namespace grb { return grb::resize( C, std::max( nnz( C ), nnz( A ) ) ); } else { assert( phase == EXECUTE ); - return internal::set_copy< false, descr >( C, A ); + const OutputType * const dummy = nullptr; + return internal::set_copy< false, descr >( C, A, dummy ); } } template< Descriptor descr = descriptors::no_operation, typename OutputType, typename InputType1, typename InputType2, - typename RIT, typename CIT, typename NIT + typename RIT1, typename CIT1, typename NIT1, + typename RIT2, typename CIT2, typename NIT2 > RC set( - Matrix< OutputType, reference, RIT, CIT, NIT > &C, - const Matrix< InputType1, reference, RIT, CIT, NIT > &A, + Matrix< OutputType, reference, RIT1, CIT1, NIT1 > &C, + const Matrix< InputType1, reference, RIT2, CIT2, NIT2 > &A, const InputType2 &val, const Phase &phase = EXECUTE, const typename std::enable_if< @@ -1906,6 +1911,26 @@ namespace grb { std::cout << "Called grb::set (matrix-to-value-masked, reference)\n"; #endif // static checks + static_assert( std::is_void< OutputType >::value || + std::is_same< OutputType, InputType2 >::value || + std::is_convertible< InputType2, OutputType >::value, + "grb::set (masked set to value): non-void output type should be either a) " + "the same as the input scalar value type or b) the input scalar type should " + "be convertible to the output type" + ); + static_assert( + std::is_void< InputType1 >::value || + std::is_convertible< InputType1, bool >::value, + "grb::set (masked set to value): mask matrix must be a " + "pattern matrix or have a data-type that is convertible to bool" + ); + static_assert( !( + ( descr & descriptors::structural ) && + ( descr & descriptors::invert_mask) + ), + "grb::set (masked set to value): descriptors::structural " + "and descriptors::invert_mask cannot be combined" + ); NO_CAST_ASSERT( ( !(descr & descriptors::no_casting) || std::is_same< InputType2, OutputType >::value ), @@ -1996,13 +2021,10 @@ namespace grb { std::cout << "\t dispatching to void or non-void set_copy variant\n"; #endif assert( phase == EXECUTE ); - if( std::is_same< OutputType, void >::value ) { - return internal::set_copy< false, descr & ~(descriptors::invert_mask) >( - C, A ); - } else { - return internal::set_copy< true, descr & ~(descriptors::invert_mask) >( - C, A, &val ); - } + constexpr bool outputIsVoid = std::is_void< OutputType >::value; + return internal::set_copy< + !outputIsVoid, descr & ~(descriptors::invert_mask) + >( C, A, &val ); } } diff --git a/include/graphblas/reference/matrix.hpp b/include/graphblas/reference/matrix.hpp index a560eba92..c1b3a365d 100644 --- a/include/graphblas/reference/matrix.hpp +++ b/include/graphblas/reference/matrix.hpp @@ -2147,6 +2147,7 @@ namespace grb { #pragma omp parallel #endif { + const char * const dummy = nullptr; size_t range = CRS.copyFromRange( nz, m ); #ifdef _H_GRB_REFERENCE_OMP_MATRIX size_t start, end; @@ -2155,14 +2156,16 @@ namespace grb { const size_t start = 0; size_t end = range; #endif - CRS.copyFrom( other.CRS, nz, m, start, end ); + CRS.template copyFrom< descriptors::no_operation, false >( + other.CRS, nz, m, start, end, dummy ); range = CCS.copyFromRange( nz, n ); #ifdef _H_GRB_REFERENCE_OMP_MATRIX config::OMP::localRange( start, end, 0, range ); #else end = range; #endif - CCS.copyFrom( other.CCS, nz, n, start, end ); + CCS.template copyFrom< descriptors::no_operation, false >( + other.CCS, nz, n, start, end, dummy ); } } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 983848ae7..5cd03cf77 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -256,6 +256,10 @@ add_grb_executables( copyVoidMatrices copyVoidMatrices.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) +add_grb_executables( copyMixedDomainsMatrices copyMixedDomainsMatrices.cpp + BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking +) + add_grb_executables( masked_muladd masked_muladd.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) diff --git a/tests/unit/copyMixedDomainsMatrices.cpp b/tests/unit/copyMixedDomainsMatrices.cpp new file mode 100644 index 000000000..290223045 --- /dev/null +++ b/tests/unit/copyMixedDomainsMatrices.cpp @@ -0,0 +1,208 @@ +/* + * 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 + +using namespace grb; + + +void grb_program( const size_t &n, grb::RC &rc ) { + Matrix< int > A_int( n, n, n ); + Matrix< void > A_void( n, n, n ); + + { // Build A_int and A_void: identity matrices + std::vector< size_t > A_coords( n ); + std::iota( A_coords.begin(), A_coords.end(), 0 ); + if( SUCCESS != + buildMatrixUnique( A_void, A_coords.data(), A_coords.data(), n, PARALLEL ) + ) { + std::cerr << "\t initialisation of A_void FAILED: rc is " + << grb::toString( rc ) << "\n"; + rc = FAILED; + return; + } + + std::vector< int > A_vals( n ); + std::fill( A_vals.begin(), A_vals.end(), 1 ); + if( SUCCESS != + buildMatrixUnique( A_int, A_coords.data(), A_coords.data(), A_vals.data(), n, PARALLEL ) + ) { + std::cerr << "\t initialisation of A_int FAILED: rc is " + << grb::toString( rc ) << "\n"; + rc = FAILED; + return; + } + } + + { // Try cast to ushort (should succeed) + Matrix< ushort > M_short( n, n, 0UL ); + rc = grb::set( M_short, A_int, Phase::RESIZE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_short, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + rc = grb::set( M_short, A_int, Phase::EXECUTE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_short, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + // Cast back to compare + Matrix< int > M_int( n, n, nnz( A_int ) ); + rc = grb::set( M_int, M_short, Phase::EXECUTE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_int, M_short ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + if( !std::equal( M_int.begin(), M_int.end(), A_int.begin() ) ) { + std::cerr << "\t FAILED: M_int != A_int\n"; + rc = FAILED; + return; + } + } + { // Try (fake-)cast to int (should succeed) + Matrix< int > M_int( n, n, 0UL ); + rc = grb::set( M_int, A_int, Phase::RESIZE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_int, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + rc = grb::set( M_int, A_int, Phase::EXECUTE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_int, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + if( !std::equal( M_int.begin(), M_int.end(), A_int.begin() ) ) { + std::cerr << "\t FAILED: M_int != A_int\n"; + rc = FAILED; + return; + } + } + { // Try cast to bool (should succeed) + Matrix< bool > M_bool( n, n, 0UL ); + rc = grb::set( M_bool, A_int, Phase::RESIZE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_bool, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + rc = grb::set( M_bool, A_int, Phase::EXECUTE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_bool, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + // Cast back to compare + Matrix< int > M_int( n, n, nnz( A_int ) ); + rc = grb::set( M_int, M_bool, Phase::EXECUTE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_int, M_short ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + if( !std::equal( M_int.begin(), M_int.end(), A_int.begin() ) ) { + std::cerr << "\t FAILED: M_int != A_int\n"; + rc = FAILED; + return; + } + } + { // Try cast to void (should succeed) + Matrix< void > M_void( n, n, 0UL ); + rc = grb::set( M_void, A_int, Phase::RESIZE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_void, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + rc = grb::set( M_void, A_int, Phase::EXECUTE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_void, A_int ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + // Cast back to compare + Matrix< int > M_int( n, n, nnz( A_int ) ); + rc = grb::set( M_int, M_void, 1, Phase::EXECUTE ); + if( rc != SUCCESS ) { + std::cerr << "\t set( M_int, M_void, 1 ) FAILED: rc is " + << grb::toString( rc ) << "\n"; + return; + } + if( !std::equal( M_int.begin(), M_int.end(), A_int.begin() ) ) { + std::cerr << "\t FAILED: M_int != A_int\n"; + rc = FAILED; + return; + } + } + + rc = 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 { + // all OK + in = read; + } + } + if( printUsage ) { + std::cerr << "Usage: " << argv[ 0 ] << " [n]\n"; + std::cerr << " -n (optional, default is 100): an integer test size.\n"; + return 1; + } + + std::cout << "This is functional test " << argv[ 0 ] << "\n"; + grb::Launcher< AUTOMATIC > launcher; + grb::RC out; + if( launcher.exec( &grb_program, in, out, true ) != SUCCESS ) { + std::cerr << "Launching test FAILED\n" << std::endl; + return 255; + } + if( out != SUCCESS ) { + std::cerr << std::flush; + std::cout << "Test FAILED (" << grb::toString( out ) << ")\n" << std::endl; + } else { + std::cout << "Test OK\n" << std::endl; + } + return 0; +} + diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index a7bf23b38..0b49d2421 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -670,6 +670,13 @@ for MODE in ${MODES}; do grep 'Test OK' ${TEST_OUT_DIR}/copyVoidMatrices_${MODE}_${BACKEND}_${P}_${T} || echo "Test FAILED" echo " " + echo ">>> [x] [ ] Testing copy-constructor of square matrices with mixed-domains" + echo " of size 1003." + $runner ${TEST_BIN_DIR}/copyMixedDomainsMatrices_${MODE}_${BACKEND} 1003 &> ${TEST_OUT_DIR}/copyMixedDomainsMatrices_${MODE}_${BACKEND}_${P}_${T} + head -1 ${TEST_OUT_DIR}/copyMixedDomainsMatrices_${MODE}_${BACKEND}_${P}_${T} + grep 'Test OK' ${TEST_OUT_DIR}/copyMixedDomainsMatrices_${MODE}_${BACKEND}_${P}_${T} || echo "Test FAILED" + echo " " + if [ "$BACKEND" = "bsp1d" ] || [ "$BACKEND" = "hybrid" ]; then echo "Additional standardised unit tests not yet supported for the ${BACKEND} backend." echo