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