From f2c99b05d16f14a4cb18a2f83588b4b2f7e5b04e Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Jun 2023 16:24:33 +0200 Subject: [PATCH 01/11] Declaration in base backend --- include/graphblas/base/blas3.hpp | 192 +++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/include/graphblas/base/blas3.hpp b/include/graphblas/base/blas3.hpp index 425f7bc7a..974d7bead 100644 --- a/include/graphblas/base/blas3.hpp +++ b/include/graphblas/base/blas3.hpp @@ -442,6 +442,198 @@ namespace grb { return ret == SUCCESS ? UNSUPPORTED : ret; } + /** + * Scales, or \em folds, a matrix into a matrix, using a constant + * matrix to perform element-wise operations against. + * + * @tparam descr The descriptor to be used (descriptors::no_operation if + * left unspecified). + * @tparam Monoid The monoid to use. + * @tparam IOType The type of the elements in the input and output + * ALP/GraphBLAS matrix \a A. + * @tparam MaskType The type of the elements in the supplied ALP/GraphBLAS + * matrix \a mask. + * @tparam InputType The type of the input matrix \a B. + * + * @param[in,out] A Any ALP/GraphBLAS matrix, which will be scaled. + * Prior value will be considered. + * @param[in] mask Any ALP/GraphBLAS matrix. + * @param[in] B Any ALP/GraphBLAS matrix, which will be used to + * scale \a A. + * @param[in] monoid The operator under which to perform the element-wise + * scaling. + * + * @return grb::SUCCESS When the call completed successfully. + * @return grb::MISMATCH If A, mask and B don't have the same dimensions. + * + * @see grb::foldr provides similar in-place functionality. + * @see grb::eWiseApply provides out-of-place semantics. + * + * \parblock + * \par Valid descriptors + * - descriptors::no_operation: the default descriptor. + * - descriptors::no_casting: the first domain of + * \a op must match \a IOType, the second domain of \a op + * match \a InputType, the third domain must match \a IOType. + * - descriptors::transpose_left: mask^T will be considered + * instead of \a mask. + * - descriptors::transpose_right: B^T will be considered + * instead of \a B. + * - descriptors::invert_mask: Not supported yet. + * + * \note Invalid descriptors will be ignored. + * + * \endparblock + * + * \par Performance semantics + * Each backend must define performance semantics for this primitive. + * + * @see perfSemantics + */ + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B, + Backend backend + > + RC foldl( + Matrix< IOType, backend, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, backend, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, backend, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_masked_matrix_matrix_foldl = false; + assert( should_not_call_base_masked_matrix_matrix_foldl ); +#endif + (void) A; + (void) mask; + (void) B; + (void) monoid; + return UNSUPPORTED; + } + + /** + * Scales, or \em folds, a matrix into a matrix, using a constant + * matrix to perform element-wise operations against. + * + * Left-to-right unmasked variant. + * + * Please see the masked grb::foldl variant for a full description. + */ + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B, + Backend backend + > + RC foldl( + Matrix< IOType, backend, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, backend, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_unmasked_matrix_matrix_foldl = false; + assert( should_not_call_base_unmasked_matrix_matrix_foldl ); +#endif + (void) A; + (void) B; + (void) monoid; + return UNSUPPORTED; + } + + /** + * Scales, or \em folds, a matrix into a matrix, using a constant + * matrix to perform element-wise operations against. + * + * Right-to-left masked variant. + * + * Please see the masked grb::foldl variant for a full description. + */ + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B, + Backend backend + > + RC foldr( + Matrix< IOType, backend, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, backend, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, backend, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_masked_matrix_matrix_foldr = false; + assert( should_not_call_base_masked_matrix_matrix_foldr ); +#endif + (void) A; + (void) mask; + (void) B; + (void) monoid; + return UNSUPPORTED; + } + + /** + * Scales, or \em folds, a matrix into a matrix, using a constant + * matrix to perform element-wise operations against. + * + * Right-to-left unmasked variant. + * + * Please see the masked grb::foldl variant for a full description. + */ + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B, + Backend backend + > + RC foldr( + Matrix< IOType, backend, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, backend, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_unmasked_matrix_matrix_foldr = false; + assert( should_not_call_base_unmasked_matrix_matrix_foldr ); +#endif + (void) A; + (void) B; + (void) monoid; + return UNSUPPORTED; + } + /** * @} */ From 81f52a85ade88a705928858bf7cc770da9e93603 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Jun 2023 17:19:26 +0200 Subject: [PATCH 02/11] Add unit-test --- tests/unit/CMakeLists.txt | 4 + tests/unit/fold_matrix_to_matrix.cpp | 266 +++++++++++++++++++++++++++ tests/unit/unittests.sh | 9 + 3 files changed, 279 insertions(+) create mode 100644 tests/unit/fold_matrix_to_matrix.cpp diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 16999fd42..4ef2d1ab3 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -145,6 +145,10 @@ add_grb_executables( eWiseMul eWiseMul.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) +add_grb_executables( fold_matrix_to_matrix fold_matrix_to_matrix.cpp + BACKENDS reference reference_omp hyperdags bsp1d hybrid +) + add_grb_executables( muladd muladd.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) diff --git a/tests/unit/fold_matrix_to_matrix.cpp b/tests/unit/fold_matrix_to_matrix.cpp new file mode 100644 index 000000000..42c77a4dc --- /dev/null +++ b/tests/unit/fold_matrix_to_matrix.cpp @@ -0,0 +1,266 @@ + +/* + * 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. + */ + +/* + * Tests for the foldl+r( Matrix[in,out], T[in], Operator ) API call + * + * @author Benjamin Lozes + * @date 27/05/2023 + * + * Tests whether the foldl and foldr API calls produce the expected results. + * + * The test cases are focused on the following aspects: + * * The types of the result, the matrix values and the operator + * * The initial value of the reduction result + * * The order of the operands (foldr, foldl) + */ + +#include +#include +#include +#include +#include +#include + +#include + +using namespace grb; + +constexpr bool SKIP_FOLDL = false; +constexpr bool SKIP_FOLDR = false; +constexpr bool SKIP_UNMASKED = false; +constexpr bool SKIP_MASKED = true; // Not implemented yet + +#define _DEBUG + +template< class Iterator > +void printSparseMatrixIterator( size_t rows, size_t cols, Iterator begin, Iterator end, const std::string & name = "", std::ostream & os = std::cout ) { +#ifndef _DEBUG + return; +#endif + std::cout << "Matrix \"" << name << "\" (" << rows << "x" << cols << "):" << std::endl << "[" << std::endl; + if( rows > 50 || cols > 50 ) { + os << " Matrix too large to print" << std::endl; + } else { + // os.precision( 3 ); + for( size_t y = 0; y < rows; y++ ) { + os << std::string( 3, ' ' ); + for( size_t x = 0; x < cols; x++ ) { + auto nnz_val = std::find_if( begin, end, [ y, x ]( const typename std::iterator_traits< Iterator >::value_type & a ) { + return a.first.first == y && a.first.second == x; + } ); + if( nnz_val != end ) + os << std::fixed << ( *nnz_val ).second; + else + os << '_'; + os << " "; + } + os << std::endl; + } + } + os << "]" << std::endl; + std::flush( os ); +} + +template< typename D > +void printSparseMatrix( const grb::Matrix< D > & mat, const std::string & name = "", std::ostream & os = std::cout ) { + grb::wait( mat ); + printSparseMatrixIterator( grb::nrows( mat ), grb::ncols( mat ), mat.cbegin(), mat.cend(), name, os ); +} + +template< typename D > +bool are_matrices_equals( const grb::Matrix< D > & A, const grb::Matrix< D > & B ) { + if( grb::nrows( A ) != grb::nrows( B ) || grb::ncols( A ) != grb::ncols( B ) ) + return false; + grb::wait( A ); + grb::wait( B ); + std::vector< std::pair< std::pair< size_t, size_t >, D > > A_vec( A.cbegin(), A.cend() ); + std::vector< std::pair< std::pair< size_t, size_t >, D > > B_vec( B.cbegin(), B.cend() ); + return std::is_permutation( A_vec.cbegin(), A_vec.cend(), B_vec.cbegin() ); +} + +/** + * Structure for testing + * + */ +template< typename T, typename M, typename S, class MonoidFoldl, class MonoidFoldr > +struct input { + const char * test_label; + const char * test_description; + const grb::Matrix< T > & initial; + const grb::Matrix< M > & mask; + const grb::Matrix< T > & B; + const grb::Matrix< T > & expected; + const bool skip_masked, skip_unmasked; + const MonoidFoldl & monoidFoldl; + const MonoidFoldr & monoidFoldr; + + input( const char * test_label = "", + const char * test_description = "", + const grb::Matrix< T > & initial = { 0, 0 }, + const grb::Matrix< M > & mask = { 0, 0 }, + const grb::Matrix< T > & B = { 0, 0 }, + const grb::Matrix< T > & expected = { 0, 0 }, + bool skip_masked = false, + bool skip_unmasked = false, + const MonoidFoldl & monoidFoldl = MonoidFoldl(), + const MonoidFoldr & monoidFoldr = MonoidFoldr() ) : + test_label( test_label ), + test_description( test_description ), initial( initial ), mask( mask ), B( B ), expected( expected ), skip_masked( skip_masked ), skip_unmasked( skip_unmasked ), monoidFoldl( monoidFoldl ), + monoidFoldr( monoidFoldr ) {} +}; + +template< typename T, typename M, typename S, class MonoidFoldl, class MonoidFoldr > +void grb_program( const input< T, M, S, MonoidFoldl, MonoidFoldr > & in, grb::RC & rc ) { + rc = RC::SUCCESS; + + printSparseMatrix( in.initial, "initial" ); + printSparseMatrix( in.expected, "expected" ); + + if( not in.skip_unmasked && not SKIP_FOLDL && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldl + grb::Matrix< T > result = in.initial; + foldl( result, in.B, in.monoidFoldl ); + std::cout << "foldl (unmasked) \"" << in.test_label << "\": "; + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); + if( rc == RC::SUCCESS ) + std::cout << "OK" << std::flush << std::endl; + else + std::cerr << "Failed" << std::endl << in.test_description << std::endl; + printSparseMatrix( result, "foldl (unmasked) result" ); + } + + if( not in.skip_masked && not SKIP_FOLDL && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldl + grb::Matrix< T > result = in.initial; + foldl( result, in.mask, in.B, in.monoidFoldl ); + std::cout << "foldl (masked) \"" << in.test_label << "\": "; + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); + if( rc == RC::SUCCESS ) + std::cout << "OK" << std::endl; + else + std::cerr << "Failed" << std::endl << in.test_description << std::endl; + printSparseMatrix( result, "foldl (masked) result" ); + } + + if( not in.skip_unmasked && not SKIP_FOLDR && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldr + grb::Matrix< T > result = in.initial; + foldr( result, in.B, in.monoidFoldr ); + std::cout << "foldr (unmasked) \"" << in.test_label << "\": "; + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); + if( rc == RC::SUCCESS ) + std::cout << "OK" << std::endl; + else + std::cerr << "Failed" << std::endl << in.test_description << std::endl; + printSparseMatrix( result, "foldr (unmasked) result" ); + } + + if( not in.skip_masked && not SKIP_FOLDR && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldr + grb::Matrix< T > result = in.initial; + foldr( result, in.mask, in.B, in.monoidFoldr ); + std::cout << "foldr (masked) \"" << in.test_label << "\": "; + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); + if( rc == RC::SUCCESS ) + std::cout << "OK" << std::endl; + else + std::cerr << "Failed" << std::endl << in.test_description << std::endl; + printSparseMatrix( result, "foldr (masked) result" ); + } +} + +int main( int argc, char ** argv ) { + // defaults + bool printUsage = false; + size_t n = 10; + + // error checking + if( argc > 2 ) { + printUsage = true; + } + if( argc == 2 ) { + n = std::atol( argv[ 1 ] ); + } + if( printUsage ) { + std::cerr << "Usage: " << argv[ 0 ] << " [n]\n"; + std::cerr << " -n (optional, default is 10): an even integer, the test " + << "size.\n"; + return 1; + } + + std::cout << "This is functional test " << argv[ 0 ] << "\n"; + grb::Launcher< AUTOMATIC > launcher; + grb::RC rc = RC::SUCCESS; + + // Identity matrix: I + Matrix< int > I( n, n ); + std::vector< size_t > I_coords( n ); + std::vector< int > I_vals( n, 1 ); + std::iota( I_coords.begin(), I_coords.end(), 0 ); + buildMatrixUnique( I, I_coords.data(), I_coords.data(), I_vals.data(), I_vals.size(), SEQUENTIAL ); + + { // Test 01: I *. I -> I + const std::string label( "Test 01" ); + const std::string description( "A: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Mask: Identity void matrix (matching the input).\n" + "B: Identity int [" + + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Operator: mul\n" + "Expected: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]" ); + // Mask: Pattern identity + Matrix< void > mask( n, n ); + buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ); + // B: Identity + Matrix< int > B = I; + // Expected matrix: Identity + Matrix< int > expected = I; + // Run test + std::cout << "-- Running " << label << " --" << std::endl; + input< int, void, int, grb::Monoid< grb::operators::mul< int >, grb::identities::one >, grb::Monoid< grb::operators::mul< int >, grb::identities::one > > in { label.c_str(), + description.c_str(), I, mask, B, expected }; + if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { + std::cerr << "Launching " << label << " failed" << std::endl; + return 255; + } + std::cout << std::endl << std::flush; + } + + { // Test 01: I +. I -> 2 * I + const std::string label( "Test 01" ); + const std::string description( "A: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Mask: Identity void matrix (matching the input).\n" + "B: Identity int [" + + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Operator: add\n" + "Expected: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" ); + // Mask: Pattern identity + Matrix< void > mask( n, n ); + buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ); + // B: Identity + Matrix< int > B = I; + // Expected matrix: Identity * 2 + Matrix< int > expected( n, n ); + std::vector< int > expected_vals( n, 2 ); + buildMatrixUnique( expected, I_coords.data(), I_coords.data(), expected_vals.data(), expected_vals.size(), SEQUENTIAL ); + // Run test + std::cout << "-- Running " << label << " --" << std::endl; + input< int, void, int, grb::Monoid< grb::operators::add< int >, grb::identities::zero >, grb::Monoid< grb::operators::add< int >, grb::identities::zero > > in { label.c_str(), + description.c_str(), I, mask, B, expected }; + if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { + std::cerr << "Launching " << label << " failed" << std::endl; + return 255; + } + std::cout << std::endl << std::flush; + } + + if( rc != SUCCESS ) { + std::cout << "Test FAILED (" << grb::toString( rc ) << ")" << std::endl; + return rc; + } else { + std::cout << "Test OK" << std::endl; + return 0; + } +} diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index 3817164c8..e957cfec6 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -222,6 +222,15 @@ for MODE in ${MODES}; do grep 'Test OK' ${TEST_OUT_DIR}/ewiseapply_large_${MODE}_${BACKEND}_${P}_${T} || echo "Test FAILED" echo " " + if [ "$BACKEND" != "nonblocking" ]; then + echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing sparse" + echo " matrix in-place using an operator." + $runner ${TEST_BIN_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND} ${P} &> ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" + echo " " + fi + echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing dense" echo " vectors into scalars using operators and monoids." $runner ${TEST_BIN_DIR}/fold_to_scalar_${MODE}_${BACKEND} ${P} &> ${TEST_OUT_DIR}/fold_to_scalar_${MODE}_${BACKEND}_${P}_${T}.log From e6a42ede0db14fd960ceb97b51095adf13627c2a Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Jun 2023 17:19:37 +0200 Subject: [PATCH 03/11] Implementation in reference --- include/graphblas/reference/blas3.hpp | 323 ++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index f3f918734..adf791b55 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -34,6 +34,25 @@ #include #endif + +#define OMP_CRITICAL _Pragma("omp critical") + +#ifndef _DEBUG_THREADESAFE_PRINT + #ifndef _DEBUG + #define _DEBUG_THREADESAFE_PRINT( msg ) + #else + #ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #define _DEBUG_THREADESAFE_PRINT( msg ) \ + OMP_CRITICAL \ + { \ + std::cout << "[T" << omp_get_thread_num() << "] - " << msg << std::flush; \ + } + #else + #define _DEBUG_THREADESAFE_PRINT( msg ) std::cout << msg << std::flush; + #endif + #endif +#endif + #define NO_CAST_ASSERT( x, y, z ) \ static_assert( x, \ "\n\n" \ @@ -1204,6 +1223,122 @@ namespace grb { return SUCCESS; } + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename InputType, typename IOType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC fold_matrix_matrix_unmasked_generic( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid() + ) { + _DEBUG_THREADESAFE_PRINT( "In grb::internal::fold_matrix_matrix_unmasked_generic( reference )\n" ); + RC rc = SUCCESS; + + if( grb::nnz(B) == 0 || grb::nnz(A) == 0 ) { + return rc; + } + + const auto &A_crs_raw = internal::getCRS( A ); + const auto &A_ccs_raw = internal::getCCS( A ); + const auto &B_raw = descr & grb::descriptors::transpose_right ? + internal::getCCS( B ) : internal::getCRS( B ); + const size_t m = nrows( A ); + const size_t n = ncols( A ); + const size_t m_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? + ncols( B ) : nrows( B ); + const size_t n_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? + nrows( B ) : ncols( B ); + + // Check mask dimensions + if( m != m_B || n != n_B ) { + _DEBUG_THREADESAFE_PRINT( "Mask dimensions do not match input matrix dimensions\n" ); + return MISMATCH; + } + + RC local_rc = rc; + const auto& op = monoid.getOperator(); + +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, B_raw, rc, std::cout) firstprivate(local_rc, m, op) +#endif + { + size_t start_row = 0; + size_t end_row = m; +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + config::OMP::localRange( start_row, end_row, 0, m ); +#endif + for( auto i = start_row; i < end_row; ++i ) { + auto B_k = B_raw.col_start[ i ]; + for( auto k = A_crs_raw.col_start[ i ]; k < A_crs_raw.col_start[ i + 1 ]; ++k ) { + auto k_col = A_crs_raw.row_index[ k ]; + + // Increment the mask pointer until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) + while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > k_col ) { + _DEBUG_THREADESAFE_PRINT( "NEquals B coordinate: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); + B_k++; + } + + if( B_raw.row_index[ B_k ] < k_col ) { + B_k++; + _DEBUG_THREADESAFE_PRINT( "Skip B value at: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); + continue; + } + + const auto B_val = B_raw.values[ B_k ]; + + _DEBUG_THREADESAFE_PRINT( "B( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " ) = " + std::to_string( B_val ) + "\n" ); + // Get A value + const auto a_val_before = A_crs_raw.values[ k ]; + _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( k_col ) + " ) = " + std::to_string( a_val_before ) + "\n" ); + // Compute the fold for this coordinate + local_rc = local_rc ? local_rc : grb::apply< descr >( A_crs_raw.values[ k ], a_val_before, B_val, op ); + local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, B_val, op ); + _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( a_val_before ) + ") = " + std::to_string( A_ccs_raw.values[ k ] ) + "\n" ); + } + } + +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp critical +#endif + { // Reduction with the global return code + rc = rc ? rc : local_rc; + } + } + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename InputType, typename MaskType, typename IOType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC fold_matrix_matrix_masked_generic( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid() + ) { + _DEBUG_THREADESAFE_PRINT( "In grb::internal::fold_matrix_matrix_masked_generic( reference )\n" ); + RC rc = UNSUPPORTED; + (void) A; + (void) mask; + (void) B; + (void) monoid; + + if( grb::nnz(mask) == 0 || grb::nnz(A) == 0 ) { + return rc; + } + + return rc; + } + } // namespace internal /** @@ -1326,6 +1461,194 @@ namespace grb { ); } + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + // static checks + static_assert( !std::is_same< IOType, void >::value, + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "the operator version of foldl cannot be used if the " + "scalar is of type void" + ); + static_assert( (std::is_same< typename Monoid::D1, IOType >::value), + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "called with a prefactor input type that does not match the first domain of the given operator" + ); + static_assert( (std::is_same< typename Monoid::D2, InputType >::value), + "grb::foldr ( reference, IOType <- op( IOType, InputType ): " + "called with a prefactor input type that does not match the second domain of the given operator" + ); + static_assert( (std::is_same< typename Monoid::D3, IOType >::value), + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "called with an output type that does not match the output domain of the given operator" + ); + +#ifdef _DEBUG + std::cout << "In grb::foldl( reference, matrix, mask, matrix, monoid )\n"; +#endif + + return internal::fold_matrix_matrix_masked_generic< descr, Monoid >( + A, mask, B, monoid + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + // static checks + static_assert( !std::is_same< IOType, void >::value, + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "the operator version of foldl cannot be used if the " + "scalar is of type void" + ); + static_assert( (std::is_same< typename Monoid::D1, IOType >::value), + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "called with a prefactor input type that does not match the first domain of the given operator" + ); + static_assert( + (std::is_same< typename Monoid::D2, InputType >::value), + "grb::foldr ( reference, IOType <- op( IOType, InputType ): " + "called with a prefactor input type that does not match the second domain of the given operator" + ); + static_assert( (std::is_same< typename Monoid::D3, IOType >::value), + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "called with an output type that does not match the output domain of the given operator" + ); + +#ifdef _DEBUG + std::cout << "In grb::foldl( reference, matrix, matrix, monoid )\n"; +#endif + + return internal::fold_matrix_matrix_unmasked_generic< descr, Monoid >( + A, B, monoid + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + // static checks + static_assert( !std::is_same< IOType, void >::value, + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "the operator version of foldr cannot be used if the " + "input matrix is a pattern matrix (of type void)" + ); + static_assert( (std::is_same< typename Monoid::D1, InputType >::value), + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "called with a prefactor input type that does not match the first domain of the given operator" + ); + static_assert( (std::is_same< typename Monoid::D2, IOType >::value), + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "called with a postfactor input type that does not match the second domain of the given operator" + ); + static_assert( (std::is_same< typename Monoid::D3, IOType >::value), + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "called with an output type that does not match the output domain of the given operator" + ); + +#ifdef _DEBUG + std::cout << "In grb::foldr( reference, matrix, mask, matrix, monoid )\n"; +#endif + + return internal::fold_matrix_matrix_masked_generic< descr, Monoid >( + A, mask, B, monoid + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + // static checks + static_assert( !std::is_same< IOType, void >::value, + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "the operator version of foldr cannot be used if the " + "input matrix is a pattern matrix (of type void)" + ); + static_assert( (std::is_same< typename Monoid::D1, InputType >::value), + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "called with a prefactor input type that does not match the first domain of the given operator" + ); + static_assert( (std::is_same< typename Monoid::D2, IOType >::value), + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "called with a postfactor input type that does not match the second domain of the given operator" + ); + static_assert( (std::is_same< typename Monoid::D3, IOType >::value), + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "called with an output type that does not match the output domain of the given operator" + ); + +#ifdef _DEBUG + std::cout << "In grb::foldr( reference, matrix, matrix, monoid )\n"; +#endif + + return internal::fold_matrix_matrix_unmasked_generic< descr, Monoid >( + A, B, monoid + ); + } + + } // namespace grb #undef NO_CAST_ASSERT From 1a48c65c767f55c6151f09fba08f4580009cdd6a Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Jun 2023 17:19:47 +0200 Subject: [PATCH 04/11] Implementation in hyperdags --- include/graphblas/hyperdags/blas3.hpp | 180 ++++++++++++++++++++++ include/graphblas/hyperdags/hyperdags.hpp | 17 +- src/graphblas/hyperdags/hyperdags.cpp | 12 ++ 3 files changed, 206 insertions(+), 3 deletions(-) diff --git a/include/graphblas/hyperdags/blas3.hpp b/include/graphblas/hyperdags/blas3.hpp index ee0c10f36..55cf3294b 100644 --- a/include/graphblas/hyperdags/blas3.hpp +++ b/include/graphblas/hyperdags/blas3.hpp @@ -332,6 +332,186 @@ namespace grb { return ret; } + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, hyperdags, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, hyperdags, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, hyperdags, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + const RC ret = foldl< descr >( + internal::getMatrix( A ), + internal::getMatrix( mask ), + internal::getMatrix( B ), + monoid + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return ret; } + if( nrows( B ) == 0 || ncols( B ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 3 > sourcesC{ + getID( internal::getMatrix(A) ), + getID( internal::getMatrix(mask) ), + getID( internal::getMatrix(B) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDL_MATRIX_MASK_MATRIX_MONOID, + sourcesP.begin(), sourcesP.end(), + sourcesC.begin(), sourcesC.end(), + destinations.begin(), destinations.end() + ); + return ret; + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, hyperdags, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, hyperdags, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + const RC ret = foldl< descr >( + internal::getMatrix( A ), + internal::getMatrix( B ), + monoid + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + if( nrows( B ) == 0 || ncols( B ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 2 > sourcesC{ + getID( internal::getMatrix(A) ), + getID( internal::getMatrix(B) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDR_MATRIX_MATRIX_MONOID, + sourcesP.begin(), sourcesP.end(), + sourcesC.begin(), sourcesC.end(), + destinations.begin(), destinations.end() + ); + return ret; + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, hyperdags, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, hyperdags, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, hyperdags, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + const RC ret = foldr< descr >( + internal::getMatrix( A ), + internal::getMatrix( mask ), + internal::getMatrix( B ), + monoid + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return ret; } + if( nrows( B ) == 0 || ncols( B ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 3 > sourcesC{ + getID( internal::getMatrix(A) ), + getID( internal::getMatrix(mask) ), + getID( internal::getMatrix(B) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDR_MATRIX_MASK_MATRIX_MONOID, + sourcesP.begin(), sourcesP.end(), + sourcesC.begin(), sourcesC.end(), + destinations.begin(), destinations.end() + ); + return ret; + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, hyperdags, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, hyperdags, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + const RC ret = foldr< descr >( + internal::getMatrix( A ), + internal::getMatrix( B ), + monoid + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + if( nrows( B ) == 0 || ncols( B ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 2 > sourcesC{ + getID( internal::getMatrix(A) ), + getID( internal::getMatrix(B) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDR_MATRIX_MATRIX_MONOID, + sourcesP.begin(), sourcesP.end(), + sourcesC.begin(), sourcesC.end(), + destinations.begin(), destinations.end() + ); + return ret; + } + } // end namespace grb #endif diff --git a/include/graphblas/hyperdags/hyperdags.hpp b/include/graphblas/hyperdags/hyperdags.hpp index 4ef0e0059..b912ddd32 100644 --- a/include/graphblas/hyperdags/hyperdags.hpp +++ b/include/graphblas/hyperdags/hyperdags.hpp @@ -488,12 +488,19 @@ namespace grb { EWISEMUL_VECTOR_VECTOR_ALPHA_BETA_RING, - EWISELAMBDA_FUNC_VECTOR + EWISELAMBDA_FUNC_VECTOR, + FOLDL_MATRIX_MASK_MATRIX_MONOID, + + FOLDL_MATRIX_MATRIX_MONOID, + + FOLDR_MATRIX_MASK_MATRIX_MONOID, + + FOLDR_MATRIX_MATRIX_MONOID }; /** \internal How many operation vertex types exist. */ - const constexpr size_t numOperationVertexTypes = 106; + const constexpr size_t numOperationVertexTypes = 110; /** \internal An array of all operation vertex types. */ const constexpr enum OperationVertexType @@ -604,7 +611,11 @@ namespace grb { EWISEMUL_VECTOR_VECTOR_ALPHA_VECTOR_RING, EWISEMUL_VECTOR_VECTOR_VECTOR_BETA_RING, EWISEMUL_VECTOR_VECTOR_ALPHA_BETA_RING, - EWISELAMBDA_FUNC_VECTOR + EWISELAMBDA_FUNC_VECTOR, + FOLDL_MATRIX_MASK_MATRIX_MONOID, + FOLDL_MATRIX_MATRIX_MONOID, + FOLDR_MATRIX_MASK_MATRIX_MONOID, + FOLDR_MATRIX_MATRIX_MONOID }; /** \internal @returns The operation vertex type as a string. */ diff --git a/src/graphblas/hyperdags/hyperdags.cpp b/src/graphblas/hyperdags/hyperdags.cpp index 6000f3af7..55cdaaea3 100644 --- a/src/graphblas/hyperdags/hyperdags.cpp +++ b/src/graphblas/hyperdags/hyperdags.cpp @@ -379,6 +379,18 @@ std::string grb::internal::hyperdags::toString( case GETID_MATRIX: return "getID( matrix )"; + + case FOLDL_MATRIX_MASK_MATRIX_MONOID: + return "foldl( matrix, mask, matrix, monoid )"; + + case FOLDL_MATRIX_MATRIX_MONOID: + return "foldl( matrix, matrix, monoid )"; + + case FOLDR_MATRIX_MASK_MATRIX_MONOID: + return "foldr( matrix, mask, matrix, monoid )"; + + case FOLDR_MATRIX_MATRIX_MONOID: + return "foldr( matrix, matrix, monoid )"; } assert( false ); From 3bd364c5d3ef57c9ff7c7eaa273d914b6cd1a053 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Jun 2023 17:19:55 +0200 Subject: [PATCH 05/11] Implementation in bsp1d --- include/graphblas/bsp1d/blas3.hpp | 155 ++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/include/graphblas/bsp1d/blas3.hpp b/include/graphblas/bsp1d/blas3.hpp index 386beb164..dac420c94 100644 --- a/include/graphblas/bsp1d/blas3.hpp +++ b/include/graphblas/bsp1d/blas3.hpp @@ -205,6 +205,161 @@ namespace grb { return internal::checkGlobalErrorStateOrClear( C, ret ); } + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, BSP1D, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldl( BSP1D, matrix, mask, matrix, monoid )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 || grb::nnz( mask ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldl< descr >( + internal::getLocal( A ), + internal::getLocal( mask ), + internal::getLocal( B ), + monoid + ); + + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, BSP1D, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldl( BSP1D, matrix, matrix, monoid )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 || grb::nnz( B ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldl< descr >( + internal::getLocal( A ), + internal::getLocal( B ), + monoid + ); + + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, BSP1D, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldr( BSP1D, matrix, mask, matrix, monoid )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 || grb::nnz( mask ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldr< descr >( + internal::getLocal( A ), + internal::getLocal( mask ), + internal::getLocal( B ), + monoid + ); + + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, BSP1D, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { +#ifdef _DEBUG + std::cout << "In grb::foldr( BSP1D, matrix, matrix, monoid )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 || grb::nnz( B ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldr< descr >( + internal::getLocal( A ), + internal::getLocal( B ), + monoid + ); + + return rc; + } + } // namespace grb #endif From 0a3da01b2afa28dc210d6bd08b94f4fac363d656 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Wed, 28 Jun 2023 11:30:45 +0200 Subject: [PATCH 06/11] Masked variant implemented in reference+omp --- include/graphblas/blas0.hpp | 33 +++++++++ include/graphblas/reference/blas3.hpp | 100 ++++++++++++++++++++++++-- tests/unit/fold_matrix_to_matrix.cpp | 3 +- 3 files changed, 129 insertions(+), 7 deletions(-) diff --git a/include/graphblas/blas0.hpp b/include/graphblas/blas0.hpp index 751b2cf14..5e1b1aaa2 100644 --- a/include/graphblas/blas0.hpp +++ b/include/graphblas/blas0.hpp @@ -32,6 +32,7 @@ #include //enable_if #include "graphblas/descriptors.hpp" +#include "graphblas/identities.hpp" #include "graphblas/rc.hpp" #include "graphblas/type_traits.hpp" @@ -604,6 +605,38 @@ namespace grb { }; + template< typename MaskType > + struct MaskHasValue { + + public: + template < Descriptor descr = descriptors::no_operation, typename MaskStruct > + MaskHasValue( const MaskStruct& mask_raw, const size_t k ) { + bool hasValue = (bool) mask_raw.getValue( k, grb::identities::logical_false() ); + if (descr & grb::descriptors::invert_mask) { + hasValue = !hasValue; + } + value = hasValue; + } + + bool value; + }; + + template<> + struct MaskHasValue< void > { + + public: + template < Descriptor descr = descriptors::no_operation, typename MaskStruct > + MaskHasValue( const MaskStruct& mask_raw, const size_t k ) : + value(not (descr & grb::descriptors::invert_mask)){ + (void) mask_raw; + (void) k; + } + + const bool value; + + }; + + } // namespace internal } // namespace grb diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index adf791b55..e7ca15f24 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -1255,7 +1255,7 @@ namespace grb { // Check mask dimensions if( m != m_B || n != n_B ) { - _DEBUG_THREADESAFE_PRINT( "Mask dimensions do not match input matrix dimensions\n" ); + _DEBUG_THREADESAFE_PRINT( "Dimensions of matrices do not match!\n" ); return MISMATCH; } @@ -1326,16 +1326,104 @@ namespace grb { const Monoid &monoid = Monoid() ) { _DEBUG_THREADESAFE_PRINT( "In grb::internal::fold_matrix_matrix_masked_generic( reference )\n" ); - RC rc = UNSUPPORTED; - (void) A; - (void) mask; - (void) B; - (void) monoid; + RC rc = SUCCESS; if( grb::nnz(mask) == 0 || grb::nnz(A) == 0 ) { return rc; } + const auto &A_crs_raw = internal::getCRS( A ); + const auto &A_ccs_raw = internal::getCCS( A ); + const auto &mask_raw = descr & grb::descriptors::transpose_left ? + internal::getCCS( mask ) : internal::getCRS( mask ); + const auto &B_raw = descr & grb::descriptors::transpose_right ? + internal::getCCS( B ) : internal::getCRS( B ); + const size_t m = nrows( A ); + const size_t n = ncols( A ); + const size_t m_mask = descr & grb::descriptors::transpose_left || descr & grb::descriptors::transpose_left ? + ncols( mask ) : nrows( mask ); + const size_t n_mask = descr & grb::descriptors::transpose_left || descr & grb::descriptors::transpose_left ? + nrows( mask ) : ncols( mask ); + const size_t m_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? + ncols( B ) : nrows( B ); + const size_t n_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? + nrows( B ) : ncols( B ); + + // Check mask dimensions + if( m != m_B || n != n_B || m != m_mask || n != n_mask ) { + _DEBUG_THREADESAFE_PRINT( "Dimensions of matrices do not match!\n" ); + return MISMATCH; + } + + RC local_rc = rc; + const auto& op = monoid.getOperator(); + const InputType B_identity = monoid.template getIdentity< InputType >(); + +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, mask_raw, B_raw, rc, std::cout) firstprivate(local_rc, m, op, B_identity) +#endif + { + size_t start_row = 0; + size_t end_row = m; +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + config::OMP::localRange( start_row, end_row, 0, m ); +#endif + for( auto i = start_row; i < end_row; ++i ) { + auto B_k = B_raw.col_start[ i ]; + auto mask_k = mask_raw.col_start[ i ]; + for( auto k = A_crs_raw.col_start[ i ]; k < A_crs_raw.col_start[ i + 1 ]; ++k ) { + auto k_col = A_crs_raw.row_index[ k ]; + + // Increment the pointer of mask until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) + while( mask_k < mask_raw.col_start[ i + 1 ] && mask_raw.row_index[ mask_k ] > k_col ) { + _DEBUG_THREADESAFE_PRINT( "NEquals MASK coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + mask_k++; + } + + if( mask_raw.row_index[ B_k ] != k_col ) { + mask_k++; + _DEBUG_THREADESAFE_PRINT( "Skip MASK value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ B_k ] ) + " )\n" ); + continue; + } + + if( not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { + _DEBUG_THREADESAFE_PRINT( "Skip MASK value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ B_k ] ) + " )\n" ); + continue; + } + + // Increment the pointer of B until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) + while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > k_col ) { + _DEBUG_THREADESAFE_PRINT( "NEquals B coordinate: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); + B_k++; + } + + // Get B value (or identity if not found) + auto B_val = B_identity; + if( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] == k_col ) { + _DEBUG_THREADESAFE_PRINT( "Found B value at: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); + B_val = B_raw.values[ B_k ]; + B_k++; + } else { + _DEBUG_THREADESAFE_PRINT( "Not found B, using identity: ( " + std::to_string( i ) + ";" + std::to_string( k_col ) + " ) = " + std::to_string( B_val ) + "\n" ); + } + + // Get A value + const auto a_val_before = A_crs_raw.values[ k ]; + _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( k_col ) + " ) = " + std::to_string( a_val_before ) + "\n" ); + // Compute the fold for this coordinate + local_rc = local_rc ? local_rc : grb::apply< descr >( A_crs_raw.values[ k ], a_val_before, B_val, op ); + local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, B_val, op ); + _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( a_val_before ) + ") = " + std::to_string( A_ccs_raw.values[ k ] ) + "\n" ); + } + } + +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp critical +#endif + { // Reduction with the global return code + rc = rc ? rc : local_rc; + } + } return rc; } diff --git a/tests/unit/fold_matrix_to_matrix.cpp b/tests/unit/fold_matrix_to_matrix.cpp index 42c77a4dc..f89e5e50e 100644 --- a/tests/unit/fold_matrix_to_matrix.cpp +++ b/tests/unit/fold_matrix_to_matrix.cpp @@ -43,7 +43,7 @@ using namespace grb; constexpr bool SKIP_FOLDL = false; constexpr bool SKIP_FOLDR = false; constexpr bool SKIP_UNMASKED = false; -constexpr bool SKIP_MASKED = true; // Not implemented yet +constexpr bool SKIP_MASKED = false; // Not implemented yet #define _DEBUG @@ -129,6 +129,7 @@ void grb_program( const input< T, M, S, MonoidFoldl, MonoidFoldr > & in, grb::RC rc = RC::SUCCESS; printSparseMatrix( in.initial, "initial" ); + printSparseMatrix( in.B, "B" ); printSparseMatrix( in.expected, "expected" ); if( not in.skip_unmasked && not SKIP_FOLDL && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldl From 89c40e22056f8ace0bbac88a6a653d79b40b535e Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Wed, 28 Jun 2023 13:57:57 +0200 Subject: [PATCH 07/11] Implementation in nonblocking (delegating) --- include/graphblas/nonblocking/blas3.hpp | 160 ++++++++++++++++++++++++ tests/unit/CMakeLists.txt | 2 +- tests/unit/unittests.sh | 14 +-- 3 files changed, 167 insertions(+), 9 deletions(-) diff --git a/include/graphblas/nonblocking/blas3.hpp b/include/graphblas/nonblocking/blas3.hpp index 5a222c7f2..afaa85cc1 100644 --- a/include/graphblas/nonblocking/blas3.hpp +++ b/include/graphblas/nonblocking/blas3.hpp @@ -571,6 +571,166 @@ namespace grb { ); } + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, nonblocking, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, nonblocking, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, nonblocking, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldl( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldl< descr, Monoid >( + internal::getRefMatrix( A ), + internal::getRefMatrix( mask ), + internal::getRefMatrix( B ), + monoid + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldl( + Matrix< IOType, nonblocking, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, nonblocking, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldl( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldl< descr, Monoid >( + internal::getRefMatrix( A ), + internal::getRefMatrix( B ), + monoid + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, nonblocking, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, nonblocking, RIT_M, CIT_M, NIT_M > &mask, + const Matrix< InputType, nonblocking, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldr( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldr< descr, Monoid >( + internal::getRefMatrix( A ), + internal::getRefMatrix( mask ), + internal::getRefMatrix( B ), + monoid + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Monoid, + typename IOType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_B, typename CIT_B, typename NIT_B + > + RC foldr( + Matrix< IOType, nonblocking, RIT_A, CIT_A, NIT_A > &A, + const Matrix< InputType, nonblocking, RIT_B, CIT_B, NIT_B > &B, + const Monoid &monoid = Monoid(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_monoid< Monoid >::value, void + >::type * const = nullptr + ) { + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldr( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldr< descr, Monoid >( + internal::getRefMatrix( A ), + internal::getRefMatrix( B ), + monoid + ); + } + } // namespace grb #undef NO_CAST_ASSERT diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 4ef2d1ab3..996d62e83 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -146,7 +146,7 @@ add_grb_executables( eWiseMul eWiseMul.cpp ) add_grb_executables( fold_matrix_to_matrix fold_matrix_to_matrix.cpp - BACKENDS reference reference_omp hyperdags bsp1d hybrid + BACKENDS reference reference_omp hyperdags bsp1d hybrid nonblocking ) add_grb_executables( muladd muladd.cpp diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index e957cfec6..137a55ed6 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -222,14 +222,12 @@ for MODE in ${MODES}; do grep 'Test OK' ${TEST_OUT_DIR}/ewiseapply_large_${MODE}_${BACKEND}_${P}_${T} || echo "Test FAILED" echo " " - if [ "$BACKEND" != "nonblocking" ]; then - echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing sparse" - echo " matrix in-place using an operator." - $runner ${TEST_BIN_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND} ${P} &> ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log - head -1 ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log - grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" - echo " " - fi + echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing sparse" + echo " matrix in-place using an operator." + $runner ${TEST_BIN_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND} ${P} &> ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" + echo " " echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing dense" echo " vectors into scalars using operators and monoids." From 7e34ba3f67b9af34e8d80ca8f2e25b4557f13bcb Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 3 Jul 2023 17:28:26 +0200 Subject: [PATCH 08/11] Add safe-guard for masked version --- include/graphblas/reference/blas3.hpp | 37 ++++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index e7ca15f24..a67cc1511 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -1274,16 +1274,19 @@ namespace grb { for( auto i = start_row; i < end_row; ++i ) { auto B_k = B_raw.col_start[ i ]; for( auto k = A_crs_raw.col_start[ i ]; k < A_crs_raw.col_start[ i + 1 ]; ++k ) { - auto k_col = A_crs_raw.row_index[ k ]; + const auto j = A_crs_raw.row_index[ k ]; // Increment the mask pointer until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) - while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > k_col ) { + while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > j ) { _DEBUG_THREADESAFE_PRINT( "NEquals B coordinate: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); B_k++; } + if( B_k >= B_raw.col_start[ i + 1 ] ) { + _DEBUG_THREADESAFE_PRINT( "Not value left in B for this column\n" ); + break; + } - if( B_raw.row_index[ B_k ] < k_col ) { - B_k++; + if( B_raw.row_index[ B_k ] != j ) { _DEBUG_THREADESAFE_PRINT( "Skip B value at: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); continue; } @@ -1293,7 +1296,7 @@ namespace grb { _DEBUG_THREADESAFE_PRINT( "B( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " ) = " + std::to_string( B_val ) + "\n" ); // Get A value const auto a_val_before = A_crs_raw.values[ k ]; - _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( k_col ) + " ) = " + std::to_string( a_val_before ) + "\n" ); + _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( j ) + " ) = " + std::to_string( a_val_before ) + "\n" ); // Compute the fold for this coordinate local_rc = local_rc ? local_rc : grb::apply< descr >( A_crs_raw.values[ k ], a_val_before, B_val, op ); local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, B_val, op ); @@ -1372,44 +1375,42 @@ namespace grb { auto B_k = B_raw.col_start[ i ]; auto mask_k = mask_raw.col_start[ i ]; for( auto k = A_crs_raw.col_start[ i ]; k < A_crs_raw.col_start[ i + 1 ]; ++k ) { - auto k_col = A_crs_raw.row_index[ k ]; + auto j = A_crs_raw.row_index[ k ]; // Increment the pointer of mask until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) - while( mask_k < mask_raw.col_start[ i + 1 ] && mask_raw.row_index[ mask_k ] > k_col ) { + while( mask_k < mask_raw.col_start[ i + 1 ] && mask_raw.row_index[ mask_k ] > j ) { _DEBUG_THREADESAFE_PRINT( "NEquals MASK coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); mask_k++; } - - if( mask_raw.row_index[ B_k ] != k_col ) { - mask_k++; - _DEBUG_THREADESAFE_PRINT( "Skip MASK value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ B_k ] ) + " )\n" ); - continue; + if( mask_k >= mask_raw.col_start[ i + 1 ] ) { + _DEBUG_THREADESAFE_PRINT( "Not value left in mask for this column\n" ); + break; } - - if( not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { + + if( mask_raw.row_index[ B_k ] != j || not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { _DEBUG_THREADESAFE_PRINT( "Skip MASK value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ B_k ] ) + " )\n" ); continue; } // Increment the pointer of B until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) - while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > k_col ) { + while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > j ) { _DEBUG_THREADESAFE_PRINT( "NEquals B coordinate: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); B_k++; } // Get B value (or identity if not found) auto B_val = B_identity; - if( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] == k_col ) { + if( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] == j ) { _DEBUG_THREADESAFE_PRINT( "Found B value at: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); B_val = B_raw.values[ B_k ]; B_k++; } else { - _DEBUG_THREADESAFE_PRINT( "Not found B, using identity: ( " + std::to_string( i ) + ";" + std::to_string( k_col ) + " ) = " + std::to_string( B_val ) + "\n" ); + _DEBUG_THREADESAFE_PRINT( "Not found B, using identity: ( " + std::to_string( i ) + ";" + std::to_string( j ) + " ) = " + std::to_string( B_val ) + "\n" ); } // Get A value const auto a_val_before = A_crs_raw.values[ k ]; - _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( k_col ) + " ) = " + std::to_string( a_val_before ) + "\n" ); + _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( j ) + " ) = " + std::to_string( a_val_before ) + "\n" ); // Compute the fold for this coordinate local_rc = local_rc ? local_rc : grb::apply< descr >( A_crs_raw.values[ k ], a_val_before, B_val, op ); local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, B_val, op ); From fbd59aa4645f3e1b0374673979323c74adc2b519 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Thu, 20 Jul 2023 15:33:26 +0200 Subject: [PATCH 09/11] Style fixes --- include/graphblas/reference/blas3.hpp | 308 +++++++++++++++++--------- tests/unit/fold_matrix_to_matrix.cpp | 266 +++++++++++++--------- 2 files changed, 360 insertions(+), 214 deletions(-) diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index a67cc1511..db9af0faf 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -30,29 +30,6 @@ #include "io.hpp" #include "matrix.hpp" -#ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #include -#endif - - -#define OMP_CRITICAL _Pragma("omp critical") - -#ifndef _DEBUG_THREADESAFE_PRINT - #ifndef _DEBUG - #define _DEBUG_THREADESAFE_PRINT( msg ) - #else - #ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #define _DEBUG_THREADESAFE_PRINT( msg ) \ - OMP_CRITICAL \ - { \ - std::cout << "[T" << omp_get_thread_num() << "] - " << msg << std::flush; \ - } - #else - #define _DEBUG_THREADESAFE_PRINT( msg ) std::cout << msg << std::flush; - #endif - #endif -#endif - #define NO_CAST_ASSERT( x, y, z ) \ static_assert( x, \ "\n\n" \ @@ -1235,35 +1212,49 @@ namespace grb { const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, const Monoid &monoid = Monoid() ) { - _DEBUG_THREADESAFE_PRINT( "In grb::internal::fold_matrix_matrix_unmasked_generic( reference )\n" ); - RC rc = SUCCESS; +#ifdef _DEBUG + std::cout << "In grb::internal::fold_matrix_matrix_unmasked_generic( reference )\n" << std::flush; +#endif - if( grb::nnz(B) == 0 || grb::nnz(A) == 0 ) { - return rc; + if( nnz(B) == 0 || nnz(A) == 0 ) { +#ifdef _DEBUG + std::cout << "One or the two matrices are empty, nothing to compute.\n" << std::flush; +#endif + return SUCCESS; } const auto &A_crs_raw = internal::getCRS( A ); const auto &A_ccs_raw = internal::getCCS( A ); - const auto &B_raw = descr & grb::descriptors::transpose_right ? - internal::getCCS( B ) : internal::getCRS( B ); + const auto &B_raw = descr & grb::descriptors::transpose_right + ? internal::getCCS( B ) + : internal::getCRS( B ); const size_t m = nrows( A ); const size_t n = ncols( A ); - const size_t m_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? - ncols( B ) : nrows( B ); - const size_t n_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? - nrows( B ) : ncols( B ); + const size_t m_B = + descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix + ? ncols( B ) + : nrows( B ); + const size_t n_B = + descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix + ? nrows( B ) + : ncols( B ); // Check mask dimensions if( m != m_B || n != n_B ) { - _DEBUG_THREADESAFE_PRINT( "Dimensions of matrices do not match!\n" ); +#ifdef _DEBUG + std::cout << "Dimensions of matrices do not match!\n" << std::flush; +#endif return MISMATCH; } + RC rc = SUCCESS; RC local_rc = rc; - const auto& op = monoid.getOperator(); + const auto &op = monoid.getOperator(); #ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, B_raw, rc, std::cout) firstprivate(local_rc, m, op) + #pragma omp parallel default(none) \ + shared(A_crs_raw, A_ccs_raw, B_raw, rc, std::cout) \ + firstprivate(local_rc, m, op) #endif { size_t start_row = 0; @@ -1273,42 +1264,81 @@ namespace grb { #endif for( auto i = start_row; i < end_row; ++i ) { auto B_k = B_raw.col_start[ i ]; - for( auto k = A_crs_raw.col_start[ i ]; k < A_crs_raw.col_start[ i + 1 ]; ++k ) { + const auto A_k_start = A_crs_raw.col_start[ i ]; + const auto A_k_end = A_crs_raw.col_start[ i + 1 ]; + for( auto k = A_k_start; k < A_k_end; ++k ) { const auto j = A_crs_raw.row_index[ k ]; - - // Increment the mask pointer until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) - while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > j ) { - _DEBUG_THREADESAFE_PRINT( "NEquals B coordinate: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); + /* Increment the mask pointer until we find the right column, + * or a lower column (since the storage withing a row + * is sorted in a descending order) + */ + while( B_k < B_raw.col_start[ i + 1 ] + && B_raw.row_index[ B_k ] > j + ) { B_k++; } + // Check if we are out of values in B if( B_k >= B_raw.col_start[ i + 1 ] ) { - _DEBUG_THREADESAFE_PRINT( "Not value left in B for this column\n" ); +#ifdef _DEBUG + std::cout << "Not value left in B for this column\n" << std::flush; +#endif break; } - + // Check if we found the right column if( B_raw.row_index[ B_k ] != j ) { - _DEBUG_THREADESAFE_PRINT( "Skip B value at: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); +#ifdef _DEBUG + std::cout << "Skip B value at: ( " + std::to_string( i ) + + ";" + std::to_string( B_raw.row_index[ B_k ] ) + + " )\n" << std::flush; +#endif continue; } - const auto B_val = B_raw.values[ B_k ]; + // Get B value + const auto B_val = B_raw.getValue( + B_k, + identities::zero< InputType >::value() + ); +#ifdef _DEBUG + std::cout << "B( " + std::to_string( i ) + ";" + + std::to_string( B_raw.row_index[ B_k ] ) + + " ) = " + std::to_string( B_val ) + "\n" << std::flush; +#endif - _DEBUG_THREADESAFE_PRINT( "B( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " ) = " + std::to_string( B_val ) + "\n" ); // Get A value const auto a_val_before = A_crs_raw.values[ k ]; - _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( j ) + " ) = " + std::to_string( a_val_before ) + "\n" ); +#ifdef _DEBUG + std::cout << "A( " + std::to_string( i ) + ";" + + std::to_string( j ) + " ) = " + + std::to_string( a_val_before ) + "\n" << std::flush; +#endif + // Compute the fold for this coordinate - local_rc = local_rc ? local_rc : grb::apply< descr >( A_crs_raw.values[ k ], a_val_before, B_val, op ); - local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, B_val, op ); - _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( a_val_before ) + ") = " + std::to_string( A_ccs_raw.values[ k ] ) + "\n" ); + local_rc = local_rc + ? local_rc + : apply< descr >( + A_crs_raw.values[ k ], a_val_before, B_val, op + ); + local_rc = local_rc + ? local_rc + : apply< descr >( + A_ccs_raw.values[ k ], a_val_before, B_val, op + ); +#ifdef _DEBUG + std::cout << "Computing: op(" + std::to_string( a_val_before ) + + ", " + std::to_string( a_val_before ) + ") = " + + std::to_string( A_ccs_raw.values[ k ] ) + "\n" << std::flush; +#endif } } + if( local_rc != SUCCESS ) { #ifdef _H_GRB_REFERENCE_OMP_BLAS3 #pragma omp critical #endif - { // Reduction with the global return code - rc = rc ? rc : local_rc; + { // Reduction with the global return code + rc = rc ? rc : local_rc; + } } } return rc; @@ -1328,42 +1358,65 @@ namespace grb { const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, const Monoid &monoid = Monoid() ) { - _DEBUG_THREADESAFE_PRINT( "In grb::internal::fold_matrix_matrix_masked_generic( reference )\n" ); - RC rc = SUCCESS; + typedef typename std::conditional< + std::is_void< MaskType >::value, bool, MaskType + >::type MaskIdentityType; - if( grb::nnz(mask) == 0 || grb::nnz(A) == 0 ) { - return rc; +#ifdef _DEBUG + std::cout << "In grb::internal::fold_matrix_matrix_masked_generic( reference )\n" << std::flush; +#endif + + if( nnz(mask) == 0 || nnz(A) == 0 ) { + return SUCCESS; } - const auto &A_crs_raw = internal::getCRS( A ); - const auto &A_ccs_raw = internal::getCCS( A ); - const auto &mask_raw = descr & grb::descriptors::transpose_left ? - internal::getCCS( mask ) : internal::getCRS( mask ); - const auto &B_raw = descr & grb::descriptors::transpose_right ? - internal::getCCS( B ) : internal::getCRS( B ); + // Descriptors aliases + constexpr bool transpose_left = descr & grb::descriptors::transpose_left; + constexpr bool transpose_right = descr & grb::descriptors::transpose_right; + + auto &A_crs_raw = internal::getCRS( A ); + auto &A_ccs_raw = internal::getCCS( A ); const size_t m = nrows( A ); const size_t n = ncols( A ); - const size_t m_mask = descr & grb::descriptors::transpose_left || descr & grb::descriptors::transpose_left ? - ncols( mask ) : nrows( mask ); - const size_t n_mask = descr & grb::descriptors::transpose_left || descr & grb::descriptors::transpose_left ? - nrows( mask ) : ncols( mask ); - const size_t m_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? - ncols( B ) : nrows( B ); - const size_t n_B = descr & grb::descriptors::transpose_right || descr & grb::descriptors::transpose_matrix ? - nrows( B ) : ncols( B ); + + const auto &mask_raw = transpose_left + ? internal::getCCS( mask ) + : internal::getCRS( mask ); + const size_t m_mask = transpose_left + ? ncols( mask ) + : nrows( mask ); + const size_t n_mask = transpose_left + ? nrows( mask ) + : ncols( mask ); + + const auto &B_raw = transpose_right + ? internal::getCCS( B ) + : internal::getCRS( B ); + const size_t m_B = transpose_right + ? ncols( B ) + : nrows( B ); + const size_t n_B = transpose_right + ? nrows( B ) + : ncols( B ); // Check mask dimensions if( m != m_B || n != n_B || m != m_mask || n != n_mask ) { - _DEBUG_THREADESAFE_PRINT( "Dimensions of matrices do not match!\n" ); +#ifdef _DEBUG + std::cout << "Dimensions of matrices do not match!\n" << std::flush; +#endif return MISMATCH; } + RC rc = SUCCESS; RC local_rc = rc; - const auto& op = monoid.getOperator(); + const auto &op = monoid.getOperator(); + const InputType A_identity = monoid.template getIdentity< IOType >(); const InputType B_identity = monoid.template getIdentity< InputType >(); #ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, mask_raw, B_raw, rc, std::cout) firstprivate(local_rc, m, op, B_identity) + #pragma omp parallel default(none) \ + shared(A_crs_raw, A_ccs_raw, mask_raw, B_raw, rc, std::cout) \ + firstprivate(local_rc, m, op, A_identity, B_identity) #endif { size_t start_row = 0; @@ -1374,55 +1427,100 @@ namespace grb { for( auto i = start_row; i < end_row; ++i ) { auto B_k = B_raw.col_start[ i ]; auto mask_k = mask_raw.col_start[ i ]; - for( auto k = A_crs_raw.col_start[ i ]; k < A_crs_raw.col_start[ i + 1 ]; ++k ) { - auto j = A_crs_raw.row_index[ k ]; - - // Increment the pointer of mask until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) - while( mask_k < mask_raw.col_start[ i + 1 ] && mask_raw.row_index[ mask_k ] > j ) { - _DEBUG_THREADESAFE_PRINT( "NEquals MASK coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + const auto A_k_start = A_crs_raw.col_start[ i ]; + const auto A_k_end = A_crs_raw.col_start[ i + 1 ]; + for( auto A_k = A_k_start; A_k < A_k_end; ++A_k ) { + const auto j = A_crs_raw.row_index[ A_k ]; + + /* Increment the mask pointer until we find the right column, + * or a lower column (since the storage withing a row + * is sorted in a descending order) + */ + while( + mask_k < mask_raw.col_start[ i + 1 ] && mask_raw.row_index[ mask_k ] > j + ) { mask_k++; } if( mask_k >= mask_raw.col_start[ i + 1 ] ) { - _DEBUG_THREADESAFE_PRINT( "Not value left in mask for this column\n" ); +#ifdef _DEBUG + std::cout << "Not value left in for this column\n" << std::flush; +#endif break; } - - if( mask_raw.row_index[ B_k ] != j || not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { - _DEBUG_THREADESAFE_PRINT( "Skip MASK value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ B_k ] ) + " )\n" ); + + const bool mask_has_value = mask_raw.getValue( + mask_k, + identities::logical_true::value() + ); + if( mask_raw.row_index[ B_k ] != j || !mask_has_value ) { +#ifdef _DEBUG + std::cout << "Skip value at: ( " + std::to_string( i ) + + ";" + std::to_string( mask_raw.row_index[ B_k ] ) + + " )\n" << std::flush; +#endif continue; } - // Increment the pointer of B until we find the right column, or a lower column (since the storage withing a row is sorted in a descending order) - while( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] > j ) { - _DEBUG_THREADESAFE_PRINT( "NEquals B coordinate: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); + /* Increment the mask pointer until we find the right column, + * or a lower column (since the storage withing a row + * is sorted in a descending order) + */ + while( B_k < B_raw.col_start[ i + 1 ] + && B_raw.row_index[ B_k ] > j + ) { B_k++; } // Get B value (or identity if not found) - auto B_val = B_identity; + InputType B_val = B_identity; if( B_k < B_raw.col_start[ i + 1 ] && B_raw.row_index[ B_k ] == j ) { - _DEBUG_THREADESAFE_PRINT( "Found B value at: ( " + std::to_string( i ) + ";" + std::to_string( B_raw.row_index[ B_k ] ) + " )\n" ); +#ifdef _DEBUG + std::cout << "Found B value at: ( " + std::to_string( i ) + + ";" + std::to_string( B_raw.row_index[ B_k ] ) + + " )\n" << std::flush; +#endif B_val = B_raw.values[ B_k ]; B_k++; } else { - _DEBUG_THREADESAFE_PRINT( "Not found B, using identity: ( " + std::to_string( i ) + ";" + std::to_string( j ) + " ) = " + std::to_string( B_val ) + "\n" ); +#ifdef _DEBUG + std::cout << "Not found B, using identity: ( " + + std::to_string( i ) + ";" + std::to_string( j ) + + " ) = " + std::to_string( B_val ) + "\n" << std::flush; +#endif } // Get A value - const auto a_val_before = A_crs_raw.values[ k ]; - _DEBUG_THREADESAFE_PRINT( "A( " + std::to_string( i ) + ";" + std::to_string( j ) + " ) = " + std::to_string( a_val_before ) + "\n" ); + const IOType A_val = A_crs_raw.getValue( A_k, A_identity ); +#ifdef _DEBUG + std::cout << "A( " + std::to_string( i ) + ";" + + std::to_string( j ) + " ) = " + + std::to_string( A_val ) + "\n" << std::flush; +#endif + // Compute the fold for this coordinate - local_rc = local_rc ? local_rc : grb::apply< descr >( A_crs_raw.values[ k ], a_val_before, B_val, op ); - local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, B_val, op ); - _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( a_val_before ) + ") = " + std::to_string( A_ccs_raw.values[ k ] ) + "\n" ); + IOType result; + local_rc = local_rc + ? local_rc + : apply< descr >( + result, A_val, B_val, op + ); + A_crs_raw.setValue( A_k, result ); + A_ccs_raw.setValue( A_k, result ); +#ifdef _DEBUG + std::cout << "Computing: op(" + std::to_string( A_val ) + + ", " + std::to_string( B_val ) + ") = " + + std::to_string( result ) + "\n" << std::flush; +#endif } } + if( local_rc != SUCCESS ){ #ifdef _H_GRB_REFERENCE_OMP_BLAS3 #pragma omp critical #endif - { // Reduction with the global return code - rc = rc ? rc : local_rc; + { // Reduction with the global return code + rc = rc ? rc : local_rc; + } } } return rc; @@ -1553,7 +1651,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Monoid, - typename IOType, typename MaskType, typename InputType, + typename IOType, typename MaskType, typename InputType, typename RIT_A, typename CIT_A, typename NIT_A, typename RIT_M, typename CIT_M, typename NIT_M, typename RIT_B, typename CIT_B, typename NIT_B @@ -1563,7 +1661,7 @@ namespace grb { const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, const Monoid &monoid = Monoid(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && !grb::is_object< MaskType >::value && @@ -1601,7 +1699,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Monoid, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT_A, typename CIT_A, typename NIT_A, typename RIT_B, typename CIT_B, typename NIT_B > @@ -1609,7 +1707,7 @@ namespace grb { Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, const Monoid &monoid = Monoid(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_monoid< Monoid >::value, void @@ -1625,7 +1723,7 @@ namespace grb { "grb::foldl ( reference, IOType <- op( IOType, InputType ): " "called with a prefactor input type that does not match the first domain of the given operator" ); - static_assert( + static_assert( (std::is_same< typename Monoid::D2, InputType >::value), "grb::foldr ( reference, IOType <- op( IOType, InputType ): " "called with a prefactor input type that does not match the second domain of the given operator" @@ -1647,7 +1745,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Monoid, - typename IOType, typename MaskType, typename InputType, + typename IOType, typename MaskType, typename InputType, typename RIT_A, typename CIT_A, typename NIT_A, typename RIT_M, typename CIT_M, typename NIT_M, typename RIT_B, typename CIT_B, typename NIT_B @@ -1657,7 +1755,7 @@ namespace grb { const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, const Monoid &monoid = Monoid(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && !grb::is_object< MaskType >::value && @@ -1695,7 +1793,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Monoid, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT_A, typename CIT_A, typename NIT_A, typename RIT_B, typename CIT_B, typename NIT_B > @@ -1703,7 +1801,7 @@ namespace grb { Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, const Matrix< InputType, reference, RIT_B, CIT_B, NIT_B > &B, const Monoid &monoid = Monoid(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_monoid< Monoid >::value, void diff --git a/tests/unit/fold_matrix_to_matrix.cpp b/tests/unit/fold_matrix_to_matrix.cpp index f89e5e50e..bd7f5988a 100644 --- a/tests/unit/fold_matrix_to_matrix.cpp +++ b/tests/unit/fold_matrix_to_matrix.cpp @@ -16,7 +16,9 @@ */ /* - * Tests for the foldl+r( Matrix[in,out], T[in], Operator ) API call + * Tests for: + * - foldl+r( Matrix[in,out], Matrix[in], Monoid ) + * - foldl+r( Matrix[in,out], Mask[in], Matrix[in], Monoid ) * * @author Benjamin Lozes * @date 27/05/2023 @@ -45,49 +47,16 @@ constexpr bool SKIP_FOLDR = false; constexpr bool SKIP_UNMASKED = false; constexpr bool SKIP_MASKED = false; // Not implemented yet -#define _DEBUG - -template< class Iterator > -void printSparseMatrixIterator( size_t rows, size_t cols, Iterator begin, Iterator end, const std::string & name = "", std::ostream & os = std::cout ) { -#ifndef _DEBUG - return; -#endif - std::cout << "Matrix \"" << name << "\" (" << rows << "x" << cols << "):" << std::endl << "[" << std::endl; - if( rows > 50 || cols > 50 ) { - os << " Matrix too large to print" << std::endl; - } else { - // os.precision( 3 ); - for( size_t y = 0; y < rows; y++ ) { - os << std::string( 3, ' ' ); - for( size_t x = 0; x < cols; x++ ) { - auto nnz_val = std::find_if( begin, end, [ y, x ]( const typename std::iterator_traits< Iterator >::value_type & a ) { - return a.first.first == y && a.first.second == x; - } ); - if( nnz_val != end ) - os << std::fixed << ( *nnz_val ).second; - else - os << '_'; - os << " "; - } - os << std::endl; - } - } - os << "]" << std::endl; - std::flush( os ); -} - template< typename D > -void printSparseMatrix( const grb::Matrix< D > & mat, const std::string & name = "", std::ostream & os = std::cout ) { - grb::wait( mat ); - printSparseMatrixIterator( grb::nrows( mat ), grb::ncols( mat ), mat.cbegin(), mat.cend(), name, os ); -} +bool are_matrices_equals( + const Matrix< D > & A, + const Matrix< D > & B +) { + if( nrows( A ) != nrows( B ) || ncols( A ) != ncols( B ) ) { return false ; } -template< typename D > -bool are_matrices_equals( const grb::Matrix< D > & A, const grb::Matrix< D > & B ) { - if( grb::nrows( A ) != grb::nrows( B ) || grb::ncols( A ) != grb::ncols( B ) ) - return false; grb::wait( A ); grb::wait( B ); + std::vector< std::pair< std::pair< size_t, size_t >, D > > A_vec( A.cbegin(), A.cend() ); std::vector< std::pair< std::pair< size_t, size_t >, D > > B_vec( B.cbegin(), B.cend() ); return std::is_permutation( A_vec.cbegin(), A_vec.cend(), B_vec.cbegin() ); @@ -101,83 +70,120 @@ template< typename T, typename M, typename S, class MonoidFoldl, class MonoidFol struct input { const char * test_label; const char * test_description; - const grb::Matrix< T > & initial; - const grb::Matrix< M > & mask; - const grb::Matrix< T > & B; - const grb::Matrix< T > & expected; + const Matrix< T > & initial; + const Matrix< M > & mask; + const Matrix< T > & B; + const Matrix< T > & expected; const bool skip_masked, skip_unmasked; const MonoidFoldl & monoidFoldl; const MonoidFoldr & monoidFoldr; input( const char * test_label = "", const char * test_description = "", - const grb::Matrix< T > & initial = { 0, 0 }, - const grb::Matrix< M > & mask = { 0, 0 }, - const grb::Matrix< T > & B = { 0, 0 }, - const grb::Matrix< T > & expected = { 0, 0 }, + const Matrix< T > & initial = { 0, 0 }, + const Matrix< M > & mask = { 0, 0 }, + const Matrix< T > & B = { 0, 0 }, + const Matrix< T > & expected = { 0, 0 }, bool skip_masked = false, bool skip_unmasked = false, const MonoidFoldl & monoidFoldl = MonoidFoldl(), const MonoidFoldr & monoidFoldr = MonoidFoldr() ) : - test_label( test_label ), - test_description( test_description ), initial( initial ), mask( mask ), B( B ), expected( expected ), skip_masked( skip_masked ), skip_unmasked( skip_unmasked ), monoidFoldl( monoidFoldl ), - monoidFoldr( monoidFoldr ) {} + test_label( test_label ), + test_description( test_description ), + initial( initial ), + mask( mask ), + B( B ), + expected( expected ), + skip_masked( skip_masked ), + skip_unmasked( skip_unmasked ), + monoidFoldl( monoidFoldl ), + monoidFoldr( monoidFoldr ) {} }; template< typename T, typename M, typename S, class MonoidFoldl, class MonoidFoldr > -void grb_program( const input< T, M, S, MonoidFoldl, MonoidFoldr > & in, grb::RC & rc ) { - rc = RC::SUCCESS; +void grb_program( + const input< T, M, S, MonoidFoldl, MonoidFoldr > & in, + RC & rc +) { + rc = SUCCESS; - printSparseMatrix( in.initial, "initial" ); - printSparseMatrix( in.B, "B" ); - printSparseMatrix( in.expected, "expected" ); + // Unmasked variant foldl + if( !in.skip_unmasked && !SKIP_FOLDL && !SKIP_UNMASKED && rc == SUCCESS ) { + std::cout << "foldl( unmasked ) \"" << in.test_label << "\": "; - if( not in.skip_unmasked && not SKIP_FOLDL && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldl - grb::Matrix< T > result = in.initial; - foldl( result, in.B, in.monoidFoldl ); - std::cout << "foldl (unmasked) \"" << in.test_label << "\": "; - rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); - if( rc == RC::SUCCESS ) - std::cout << "OK" << std::flush << std::endl; - else - std::cerr << "Failed" << std::endl << in.test_description << std::endl; - printSparseMatrix( result, "foldl (unmasked) result" ); + Matrix< T > result = in.initial; + rc = rc ? rc : foldl( result, in.B, in.monoidFoldl ); + if( rc == SUCCESS ) { + std::cout << "Execution OK" << std::flush << std::endl; + } else { + std::cerr << "Execution failed - " << std::endl << in.test_description << std::endl; + } + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? SUCCESS : FAILED ); + if( rc == SUCCESS ) { + std::cout << "Check OK" << std::flush << std::endl; + } else { + std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } } - if( not in.skip_masked && not SKIP_FOLDL && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldl - grb::Matrix< T > result = in.initial; - foldl( result, in.mask, in.B, in.monoidFoldl ); - std::cout << "foldl (masked) \"" << in.test_label << "\": "; - rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); - if( rc == RC::SUCCESS ) - std::cout << "OK" << std::endl; - else - std::cerr << "Failed" << std::endl << in.test_description << std::endl; - printSparseMatrix( result, "foldl (masked) result" ); + // Masked variant foldl + if( !in.skip_masked && !SKIP_FOLDL && !SKIP_MASKED && rc == SUCCESS ) { + std::cout << "foldl( masked ) \"" << in.test_label << "\": "; + + Matrix< T > result = in.initial; + rc = rc ? rc : foldl( result, in.mask, in.B, in.monoidFoldl ); + if( rc == SUCCESS ) { + std::cout << "Execution OK" << std::flush << std::endl; + } else { + std::cerr << "Execution failed - " << std::endl << in.test_description << std::endl; + } + + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? SUCCESS : FAILED ); + if( rc == SUCCESS ) { + std::cout << "Check OK" << std::flush << std::endl; + } else { + std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } } - if( not in.skip_unmasked && not SKIP_FOLDR && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldr - grb::Matrix< T > result = in.initial; - foldr( result, in.B, in.monoidFoldr ); - std::cout << "foldr (unmasked) \"" << in.test_label << "\": "; - rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); - if( rc == RC::SUCCESS ) - std::cout << "OK" << std::endl; - else - std::cerr << "Failed" << std::endl << in.test_description << std::endl; - printSparseMatrix( result, "foldr (unmasked) result" ); + // Unmasked variant foldr + if( !in.skip_unmasked && !SKIP_FOLDR && !SKIP_UNMASKED && rc == SUCCESS ) { + std::cout << "foldr( unmasked ) \"" << in.test_label << "\": "; + + Matrix< T > result = in.initial; + rc = rc ? rc : foldr( result, in.B, in.monoidFoldr ); + if( rc == SUCCESS ) { + std::cout << "Execution OK" << std::flush << std::endl; + } else { + std::cerr << "Execution failed - " << std::endl << in.test_description << std::endl; + } + + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? SUCCESS : FAILED ); + if( rc == SUCCESS ) { + std::cout << "Check OK" << std::flush << std::endl; + } else { + std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } } - if( not in.skip_masked && not SKIP_FOLDR && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldr - grb::Matrix< T > result = in.initial; - foldr( result, in.mask, in.B, in.monoidFoldr ); - std::cout << "foldr (masked) \"" << in.test_label << "\": "; - rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); - if( rc == RC::SUCCESS ) - std::cout << "OK" << std::endl; - else - std::cerr << "Failed" << std::endl << in.test_description << std::endl; - printSparseMatrix( result, "foldr (masked) result" ); + // Masked variant foldr + if( !in.skip_masked && !SKIP_FOLDR && !SKIP_MASKED && rc == SUCCESS ) { + std::cout << "foldr( masked ) \"" << in.test_label << "\": "; + + Matrix< T > result = in.initial; + rc = rc ? rc : foldr( result, in.mask, in.B, in.monoidFoldr ); + if( rc == SUCCESS ) { + std::cout << "Execution OK" << std::flush << std::endl; + } else { + std::cerr << "Execution failed - " << std::endl << in.test_description << std::endl; + } + + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? SUCCESS : FAILED ); + if( rc == SUCCESS ) { + std::cout << "Check OK" << std::flush << std::endl; + } else { + std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } } } @@ -202,30 +208,51 @@ int main( int argc, char ** argv ) { std::cout << "This is functional test " << argv[ 0 ] << "\n"; grb::Launcher< AUTOMATIC > launcher; - grb::RC rc = RC::SUCCESS; + RC rc = SUCCESS; // Identity matrix: I Matrix< int > I( n, n ); std::vector< size_t > I_coords( n ); std::vector< int > I_vals( n, 1 ); std::iota( I_coords.begin(), I_coords.end(), 0 ); - buildMatrixUnique( I, I_coords.data(), I_coords.data(), I_vals.data(), I_vals.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( I, I_coords.data(), I_coords.data(), I_vals.data(), I_vals.size(), SEQUENTIAL ) + ); { // Test 01: I *. I -> I const std::string label( "Test 01" ); - const std::string description( "A: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Mask: Identity void matrix (matching the input).\n" + "B: Identity int [" + - std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Operator: mul\n" + "Expected: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]" ); + const std::string description( "A: Identity int [" + std::to_string( n ) + ";" + + std::to_string( n ) + "]\n" + + "Mask: Identity void matrix (matching the input).\n" + + "B: Identity int [" + std::to_string( n ) + ";" + + std::to_string( n ) + "]\n" + "Operator: mul\n" + + "Expected: Identity int [" + std::to_string( n ) + + ";" + std::to_string( n ) + "]" ); // Mask: Pattern identity Matrix< void > mask( n, n ); - buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ) + ); // B: Identity Matrix< int > B = I; // Expected matrix: Identity Matrix< int > expected = I; // Run test std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, grb::Monoid< grb::operators::mul< int >, grb::identities::one >, grb::Monoid< grb::operators::mul< int >, grb::identities::one > > in { label.c_str(), - description.c_str(), I, mask, B, expected }; + input< + int, + void, + int, + Monoid< operators::mul< int >, identities::one >, + Monoid< operators::mul< int >, identities::one > + > in { + label.c_str(), + description.c_str(), + I, + mask, + B, + expected + }; if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { std::cerr << "Launching " << label << " failed" << std::endl; return 255; @@ -235,21 +262,42 @@ int main( int argc, char ** argv ) { { // Test 01: I +. I -> 2 * I const std::string label( "Test 01" ); - const std::string description( "A: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Mask: Identity void matrix (matching the input).\n" + "B: Identity int [" + - std::to_string( n ) + ";" + std::to_string( n ) + "]\n" + "Operator: add\n" + "Expected: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" ); + const std::string description( "A: Identity int [" + std::to_string( n ) + ";" + + std::to_string( n ) + "]\n" + + "Mask: Identity void matrix (matching the input).\n" + + "B: Identity int [" + std::to_string( n ) + ";" + + std::to_string( n ) + "]\n" + "Operator: add\n" + + "Expected: Identity int [" + std::to_string( n ) + + ";" + std::to_string( n ) + "] * 2" ); // Mask: Pattern identity Matrix< void > mask( n, n ); - buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ) + ); // B: Identity Matrix< int > B = I; // Expected matrix: Identity * 2 Matrix< int > expected( n, n ); std::vector< int > expected_vals( n, 2 ); - buildMatrixUnique( expected, I_coords.data(), I_coords.data(), expected_vals.data(), expected_vals.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( expected, I_coords.data(), I_coords.data(), expected_vals.data(), expected_vals.size(), SEQUENTIAL ) + ); // Run test std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, grb::Monoid< grb::operators::add< int >, grb::identities::zero >, grb::Monoid< grb::operators::add< int >, grb::identities::zero > > in { label.c_str(), - description.c_str(), I, mask, B, expected }; + input< + int, + void, + int, + Monoid< operators::add< int >, identities::zero >, + Monoid< operators::add< int >, identities::zero > + > in { + label.c_str(), + description.c_str(), + I, + mask, + B, + expected + }; if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { std::cerr << "Launching " << label << " failed" << std::endl; return 255; From 58be0567ae9c0aed53813b3bbd645e90f8a186e0 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Thu, 20 Jul 2023 16:32:00 +0200 Subject: [PATCH 10/11] Replace assertions with error messages --- tests/unit/fold_matrix_to_matrix.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/unit/fold_matrix_to_matrix.cpp b/tests/unit/fold_matrix_to_matrix.cpp index bd7f5988a..d5e5155bc 100644 --- a/tests/unit/fold_matrix_to_matrix.cpp +++ b/tests/unit/fold_matrix_to_matrix.cpp @@ -48,15 +48,15 @@ constexpr bool SKIP_UNMASKED = false; constexpr bool SKIP_MASKED = false; // Not implemented yet template< typename D > -bool are_matrices_equals( - const Matrix< D > & A, - const Matrix< D > & B +bool are_matrices_equals( + const Matrix< D > & A, + const Matrix< D > & B ) { if( nrows( A ) != nrows( B ) || ncols( A ) != ncols( B ) ) { return false ; } grb::wait( A ); grb::wait( B ); - + std::vector< std::pair< std::pair< size_t, size_t >, D > > A_vec( A.cbegin(), A.cend() ); std::vector< std::pair< std::pair< size_t, size_t >, D > > B_vec( B.cbegin(), B.cend() ); return std::is_permutation( A_vec.cbegin(), A_vec.cend(), B_vec.cbegin() ); @@ -127,7 +127,7 @@ void grb_program( } // Masked variant foldl - if( !in.skip_masked && !SKIP_FOLDL && !SKIP_MASKED && rc == SUCCESS ) { + if( !in.skip_masked && !SKIP_FOLDL && !SKIP_MASKED && rc == SUCCESS ) { std::cout << "foldl( masked ) \"" << in.test_label << "\": "; Matrix< T > result = in.initial; @@ -137,7 +137,7 @@ void grb_program( } else { std::cerr << "Execution failed - " << std::endl << in.test_description << std::endl; } - + rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? SUCCESS : FAILED ); if( rc == SUCCESS ) { std::cout << "Check OK" << std::flush << std::endl; @@ -147,7 +147,7 @@ void grb_program( } // Unmasked variant foldr - if( !in.skip_unmasked && !SKIP_FOLDR && !SKIP_UNMASKED && rc == SUCCESS ) { + if( !in.skip_unmasked && !SKIP_FOLDR && !SKIP_UNMASKED && rc == SUCCESS ) { std::cout << "foldr( unmasked ) \"" << in.test_label << "\": "; Matrix< T > result = in.initial; @@ -215,9 +215,13 @@ int main( int argc, char ** argv ) { std::vector< size_t > I_coords( n ); std::vector< int > I_vals( n, 1 ); std::iota( I_coords.begin(), I_coords.end(), 0 ); - assert( SUCCESS == + if( SUCCESS != buildMatrixUnique( I, I_coords.data(), I_coords.data(), I_vals.data(), I_vals.size(), SEQUENTIAL ) - ); + ) { + std::cerr << "Error building identity matrix" << std::endl; + rc = FAILED; + return 1; + } { // Test 01: I *. I -> I const std::string label( "Test 01" ); @@ -230,7 +234,7 @@ int main( int argc, char ** argv ) { + ";" + std::to_string( n ) + "]" ); // Mask: Pattern identity Matrix< void > mask( n, n ); - assert( SUCCESS == + assert( SUCCESS == buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ) ); // B: Identity @@ -271,7 +275,7 @@ int main( int argc, char ** argv ) { + ";" + std::to_string( n ) + "] * 2" ); // Mask: Pattern identity Matrix< void > mask( n, n ); - assert( SUCCESS == + assert( SUCCESS == buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ) ); // B: Identity @@ -279,7 +283,7 @@ int main( int argc, char ** argv ) { // Expected matrix: Identity * 2 Matrix< int > expected( n, n ); std::vector< int > expected_vals( n, 2 ); - assert( SUCCESS == + assert( SUCCESS == buildMatrixUnique( expected, I_coords.data(), I_coords.data(), expected_vals.data(), expected_vals.size(), SEQUENTIAL ) ); // Run test From d9a7bdec644fac61717c788d01b2534774f77797 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Feb 2024 11:55:59 +0100 Subject: [PATCH 11/11] Merge bugfix --- tests/unit/fold_matrix_to_matrix.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/unit/fold_matrix_to_matrix.cpp b/tests/unit/fold_matrix_to_matrix.cpp index d5e5155bc..10753fdc1 100644 --- a/tests/unit/fold_matrix_to_matrix.cpp +++ b/tests/unit/fold_matrix_to_matrix.cpp @@ -234,9 +234,12 @@ int main( int argc, char ** argv ) { + ";" + std::to_string( n ) + "]" ); // Mask: Pattern identity Matrix< void > mask( n, n ); - assert( SUCCESS == + if( SUCCESS != buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ) - ); + ) { + throw std::runtime_error( + "[Line " + std::to_string(__LINE__) + "]: Error building mask" ); + } // B: Identity Matrix< int > B = I; // Expected matrix: Identity @@ -275,17 +278,23 @@ int main( int argc, char ** argv ) { + ";" + std::to_string( n ) + "] * 2" ); // Mask: Pattern identity Matrix< void > mask( n, n ); - assert( SUCCESS == + if( SUCCESS != buildMatrixUnique( mask, I_coords.data(), I_coords.data(), I_coords.size(), SEQUENTIAL ) - ); + ) { + throw std::runtime_error( + "[Line " + std::to_string(__LINE__) + "]: Error building mask" ); + } // B: Identity Matrix< int > B = I; // Expected matrix: Identity * 2 Matrix< int > expected( n, n ); std::vector< int > expected_vals( n, 2 ); - assert( SUCCESS == + if( SUCCESS != buildMatrixUnique( expected, I_coords.data(), I_coords.data(), expected_vals.data(), expected_vals.size(), SEQUENTIAL ) - ); + ) { + throw std::runtime_error( + "[Line " + std::to_string(__LINE__) + "]: Error building mask" ); + } // Run test std::cout << "-- Running " << label << " --" << std::endl; input<