From 820a1469d9c685c1dd9f336bd66acce75ace9cb3 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Fri, 23 Jun 2023 15:36:38 +0200 Subject: [PATCH 01/14] Declaration of grb::foldl+r in base --- include/graphblas/base/blas3.hpp | 178 +++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/include/graphblas/base/blas3.hpp b/include/graphblas/base/blas3.hpp index 425f7bc7a..4e488be36 100644 --- a/include/graphblas/base/blas3.hpp +++ b/include/graphblas/base/blas3.hpp @@ -442,6 +442,184 @@ namespace grb { return ret == SUCCESS ? UNSUPPORTED : ret; } + /** + * Scales, or \em folds, a matrix into a matrix, using a constant scalar. + * + * @tparam descr The descriptor to be used (descriptors::no_operation if + * left unspecified). + * @tparam Operator The operator to use for scaling. + * @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 scalar \a x. + * + * @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] x The input scalat to scale with. + * @param[in] op The monoid under which to perform this scaling. + * + * @return grb::SUCCESS When the call completed successfully. + * @return grb::MISMATCH If a \a mask was not empty and does not have size + * equal to \a A. + * + * @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_right: mask^T will be considered + * instead of \a mask. + * - 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 Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + 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 InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_masked_matrix_scalar_foldl = false; + assert( should_not_call_base_masked_matrix_scalar_foldl ); +#endif + (void) A; + (void) x; + (void) mask; + (void) op; + return UNSUPPORTED; + } + + /** + * Scales, or \em folds, a matrix into a matrix, using a constant scalar. + * Left-to-right unmasked variant. + * + * Please see the masked grb::foldl variant for a full description. + */ + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT, + Backend backend + > + RC foldl( + Matrix< IOType, backend, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_unmasked_matrix_scalar_foldl = false; + assert( should_not_call_base_unmasked_matrix_scalar_foldl ); +#endif + (void) A; + (void) x; + (void) op; + return UNSUPPORTED; + } + + /** + * Scales, or \em folds, a matrix into a matrix, using a constant scalar. + * Right-to-left masked variant. + * + * Please see the masked grb::foldl variant for a full description. + */ + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M, + 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 InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_masked_matrix_scalar_foldr = false; + assert( should_not_call_base_masked_matrix_scalar_foldr ); +#endif + (void) A; + (void) x; + (void) mask; + (void) op; + return UNSUPPORTED; + } + + /** + * Scales, or \em folds, a matrix into a matrix, using a constant scalar. + * Right-to-left unmasked variant. + * + * Please see the masked grb::foldl variant for a full description. + */ + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT, + Backend backend + > + RC foldr( + Matrix< IOType, backend, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { +#ifndef NDEBUG + const bool should_not_call_base_unmasked_matrix_scalar_foldr = false; + assert( should_not_call_base_unmasked_matrix_scalar_foldr ); +#endif + (void) A; + (void) x; + (void) op; + return UNSUPPORTED; + } + /** * @} */ From a7f0a4e58eebf27aee1fab60c0303fcaf59e223f Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 26 Jun 2023 13:45:44 +0200 Subject: [PATCH 02/14] Add unit-test --- tests/unit/CMakeLists.txt | 4 + tests/unit/fold_matrix_scalar_to_matrix.cpp | 242 ++++++++++++++++++++ tests/unit/unittests.sh | 7 + 3 files changed, 253 insertions(+) create mode 100644 tests/unit/fold_matrix_scalar_to_matrix.cpp diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 16999fd42..1ac746506 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_scalar_to_matrix fold_matrix_scalar_to_matrix.cpp + BACKENDS reference reference_omp hyperdags +) + add_grb_executables( muladd muladd.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp new file mode 100644 index 000000000..933905ac9 --- /dev/null +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -0,0 +1,242 @@ + +/* + * 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 26/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 = false; + +#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 OpFoldl, class OpFoldr > +struct input { + const char * test_label; + const char * test_description; + const grb::Matrix< T > & initial; + const grb::Matrix< M > & mask; + const S scalar; + const grb::Matrix< T > & expected; + const OpFoldl & opFoldl; + const OpFoldr & opFoldr = OpFoldr(); + + input( const char * test_label, + const char * test_description, + const grb::Matrix< T > & initial, + const grb::Matrix< M > & mask, + const S scalar, + const grb::Matrix< T > & expected, + const OpFoldl & opFoldl = OpFoldl(), + const OpFoldr & opFoldr = OpFoldr() ) : + test_label( test_label ), + test_description( test_description ), initial( initial ), mask( mask ), scalar( scalar ), expected( expected ), opFoldl( opFoldl ), opFoldr( opFoldr ) {} +}; + +template< typename T, typename M, typename S, class OpFoldl, class OpFoldr > +void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, grb::RC & rc ) { + rc = RC::SUCCESS; + + printSparseMatrix( in.initial, "initial" ); + printSparseMatrix( in.expected, "expected" ); + + if( not SKIP_FOLDL && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldl + grb::Matrix< T > result = in.initial; + foldl( result, in.scalar, in.opFoldl ); + 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 SKIP_FOLDL && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldl + grb::Matrix< T > result = in.initial; + foldl( result, in.mask, in.scalar, in.opFoldl ); + 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 SKIP_FOLDR && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldr + grb::Matrix< T > result = in.initial; + foldr( result, in.scalar, in.opFoldr ); + 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 SKIP_FOLDR && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldr + grb::Matrix< T > result = in.initial; + foldr( result, in.mask, in.scalar, in.opFoldr ); + 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; + + if( ! rc ) { // Identity square * 2 + const int k = 2; + const std::string label( "Test 01" ); + const std::string description( "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + + "]\n" + "Mask: Identity void matrix.\n" + "k = 2\n" + "Operator: mul\n" + "Expected: Identity int [" + + std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" ); + // Initial matrix + Matrix< int > initial( n, n ); + std::vector< size_t > initial_rows( n ), initial_cols( n ); + std::vector< int > initial_values( n, 1 ); + std::iota( initial_rows.begin(), initial_rows.end(), 0 ); + std::iota( initial_cols.begin(), initial_cols.end(), 0 ); + buildMatrixUnique( initial, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ); + // Mask + Matrix< void > mask( n, n ); + buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), initial_rows.size(), SEQUENTIAL ); + // Expected matrix + Matrix< int > expected( n, n ); + std::vector< int > expected_values( n, 2 ); + buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ); + std::cout << "-- Running " << label << " --" << std::endl; + input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { label.c_str(), description.c_str(), initial, mask, k, 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..344749782 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -222,6 +222,13 @@ for MODE in ${MODES}; do grep 'Test OK' ${TEST_OUT_DIR}/ewiseapply_large_${MODE}_${BACKEND}_${P}_${T} || echo "Test FAILED" echo " " + echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing sparse" + echo " matrix in-place using operators." + $runner ${TEST_BIN_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_scalar_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." $runner ${TEST_BIN_DIR}/fold_to_scalar_${MODE}_${BACKEND} ${P} &> ${TEST_OUT_DIR}/fold_to_scalar_${MODE}_${BACKEND}_${P}_${T}.log From f1fc0e9ec8de32bc6bb64e71dc97ddef28f1ab35 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 26 Jun 2023 13:45:56 +0200 Subject: [PATCH 03/14] Implementation in reference+omp --- include/graphblas/blas0.hpp | 33 +++ include/graphblas/reference/blas3.hpp | 365 ++++++++++++++++++++++++++ 2 files changed, 398 insertions(+) diff --git a/include/graphblas/blas0.hpp b/include/graphblas/blas0.hpp index 751b2cf14..ee79299f7 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 = mask_raw.getValue( k, identities::logical_false() ); + if (descr & grb::descriptors::invert_mask) { + hasValue = !hasValue; + } + value = hasValue; + } + + const 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 f3f918734..68ce86e75 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,149 @@ namespace grb { return SUCCESS; } + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename InputType, typename IOType, + typename RIT, typename CIT, typename NIT + > + RC scale_unmasked_generic( + const Matrix< IOType, reference, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator() + ) { + _DEBUG_THREADESAFE_PRINT( "In grb::internal::scale_unmasked_generic( reference )\n" ); + RC rc = SUCCESS; + + const auto &A_crs_raw = grb::internal::getCRS( A ); + const auto &A_ccs_raw = grb::internal::getCCS( A ); + const size_t A_nnz = nnz( A ); + if( grb::nnz( A ) == 0 ) { + return rc; + } + + RC local_rc = rc; + +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, rc, std::cout) firstprivate(x, local_rc, A_nnz, op) +#endif + { + size_t start = 0; + size_t end = A_nnz; +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + config::OMP::localRange( start, end, 0, A_nnz ); +#endif + + for( size_t idx = start; idx < end; ++idx ) { + // Get A value + const IOType a_val_before = A_crs_raw.values[ idx ]; + _DEBUG_THREADESAFE_PRINT( "A.CRS.values[ " + std::to_string( idx ) + " ] = " + 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[ idx ], a_val_before, x, op ); + local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ idx ], a_val_before, x, op ); + _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( x ) + ") = " + std::to_string( A_ccs_raw.values[ idx ] ) + "\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 Operator, + typename InputType, typename IOType, typename MaskType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC scale_masked_generic( + const Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator() + ) { + _DEBUG_THREADESAFE_PRINT( "In grb::internal::scale_masked_generic( reference )\n" ); + RC rc = 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_right ? + internal::getCCS( mask ) : internal::getCRS( mask ); + const size_t m = nrows( A ); + const size_t n = ncols( A ); + const size_t m_mask = descr & grb::descriptors::transpose_left ? + ncols( mask ) : nrows( mask ); + const size_t n_mask = descr & grb::descriptors::transpose_left ? + nrows( mask ) : ncols( mask ); + + // Check mask dimensions + if( m != m_mask || n != n_mask ) { + _DEBUG_THREADESAFE_PRINT( "Mask dimensions do not match input matrix dimensions\n" ); + return MISMATCH; + } + + RC local_rc = rc; + +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, mask_raw, rc, std::cout) firstprivate(x, 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 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 mask pointer until we find the right column, or an higher one + while( mask_raw.row_index[ mask_k ] < k_col && mask_k < mask_raw.col_start[ i + 1 ] ) { + _DEBUG_THREADESAFE_PRINT( "Skipping masked coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + mask_k++; + } + // if there is no value for this coordinate, skip it + if( mask_raw.row_index[ mask_k ] != k_col ) { + _DEBUG_THREADESAFE_PRINT( "Skipped masked coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + continue; + } + + // Get mask value + if( not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { + _DEBUG_THREADESAFE_PRINT( "Skipped masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + continue; + } + + // Increment the mask pointer in order to skip the next while loop (best case) + mask_k++; + + // Get A value + const IOType 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, x, op ); + local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, x, op ); + _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( x ) + ") = " + 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; + } + } // namespace internal /** @@ -1326,6 +1488,209 @@ namespace grb { ); } + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldl( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + // static checks + static_assert( !std::is_same< InputType, void >::value, + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "the operator version of foldl cannot be used if the " + "input matrix is a pattern matrix (of type void)" + ); + 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 Operator::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 Operator::D2, InputType >::value), + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "called with a postfactor input type that does not match the second domain of the given operator" + ); + static_assert( (std::is_same< typename Operator::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, scalar, op)\n"; +#endif + + return internal::scale_masked_generic< descr, Operator >( + A, mask, x, op + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldl( + Matrix< IOType, reference, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + // static checks + // static checks + static_assert( !std::is_same< InputType, void >::value, + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "the operator version of foldl cannot be used if the " + "input matrix is a pattern matrix (of type void)" + ); + 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 Operator::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 Operator::D2, InputType >::value), + "grb::foldl ( reference, IOType <- op( IOType, InputType ): " + "called with a postfactor input type that does not match the second domain of the given operator" + ); + static_assert( (std::is_same< typename Operator::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, scalar, op)\n"; +#endif + + return internal::scale_unmasked_generic< descr, Operator >( + A, x, op + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldr( + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::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< InputType, void >::value, + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "the operator version of foldr cannot be used if the " + "scalar is of type void" + ); + static_assert( (std::is_same< typename Operator::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 Operator::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 Operator::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, scalar, op)\n"; +#endif + + return internal::scale_masked_generic< descr, Operator >( + A, mask, x, op + ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldr( + Matrix< IOType, reference, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::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< InputType, void >::value, + "grb::foldr ( reference, IOType <- op( InputType, IOType ): " + "the operator version of foldr cannot be used if the " + "scalar is of type void" + ); + static_assert( (std::is_same< typename Operator::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 Operator::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 Operator::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, scalar, op)\n"; +#endif + + return internal::scale_unmasked_generic< descr, Operator >( + A, x, op + ); + } + } // namespace grb #undef NO_CAST_ASSERT From 6d12b2dd1dd8bff6966c8b6938eaefae2dc2fe74 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 26 Jun 2023 15:29:54 +0200 Subject: [PATCH 04/14] Extend test + small improvement for masked variant --- include/graphblas/reference/blas3.hpp | 4 + tests/unit/fold_matrix_scalar_to_matrix.cpp | 85 ++++++++++++++------- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index 68ce86e75..007a4517b 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -1292,6 +1292,10 @@ namespace grb { _DEBUG_THREADESAFE_PRINT( "In grb::internal::scale_masked_generic( reference )\n" ); RC rc = SUCCESS; + if(grb::nnz(mask) == 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_right ? diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp index 933905ac9..a95ab6833 100644 --- a/tests/unit/fold_matrix_scalar_to_matrix.cpp +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -105,6 +105,7 @@ struct input { const grb::Matrix< M > & mask; const S scalar; const grb::Matrix< T > & expected; + const bool skip_masked, skip_unmasked; const OpFoldl & opFoldl; const OpFoldr & opFoldr = OpFoldr(); @@ -114,10 +115,13 @@ struct input { const grb::Matrix< M > & mask, const S scalar, const grb::Matrix< T > & expected, + bool skip_masked = false, + bool skip_unmasked = false, const OpFoldl & opFoldl = OpFoldl(), const OpFoldr & opFoldr = OpFoldr() ) : test_label( test_label ), - test_description( test_description ), initial( initial ), mask( mask ), scalar( scalar ), expected( expected ), opFoldl( opFoldl ), opFoldr( opFoldr ) {} + test_description( test_description ), initial( initial ), mask( mask ), scalar( scalar ), expected( expected ), skip_masked( skip_masked ), skip_unmasked( skip_unmasked ), opFoldl( opFoldl ), + opFoldr( opFoldr ) {} }; template< typename T, typename M, typename S, class OpFoldl, class OpFoldr > @@ -127,7 +131,7 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, grb::RC & rc ) printSparseMatrix( in.initial, "initial" ); printSparseMatrix( in.expected, "expected" ); - if( not SKIP_FOLDL && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldl + 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.scalar, in.opFoldl ); std::cout << "foldl (unmasked) \"" << in.test_label << "\": "; @@ -139,7 +143,7 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, grb::RC & rc ) printSparseMatrix( result, "foldl (unmasked) result" ); } - if( not SKIP_FOLDL && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldl + 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.scalar, in.opFoldl ); std::cout << "foldl (masked) \"" << in.test_label << "\": "; @@ -151,7 +155,7 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, grb::RC & rc ) printSparseMatrix( result, "foldl (masked) result" ); } - if( not SKIP_FOLDR && not SKIP_UNMASKED && rc == RC::SUCCESS ) { // Unmasked foldr + 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.scalar, in.opFoldr ); std::cout << "foldr (unmasked) \"" << in.test_label << "\": "; @@ -163,7 +167,7 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, grb::RC & rc ) printSparseMatrix( result, "foldr (unmasked) result" ); } - if( not SKIP_FOLDR && not SKIP_MASKED && rc == RC::SUCCESS ) { // Masked foldr + 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.scalar, in.opFoldr ); std::cout << "foldr (masked) \"" << in.test_label << "\": "; @@ -201,14 +205,6 @@ int main( int argc, char ** argv ) { if( ! rc ) { // Identity square * 2 const int k = 2; - const std::string label( "Test 01" ); - const std::string description( "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + - "]\n" - "Mask: Identity void matrix.\n" - "k = 2\n" - "Operator: mul\n" - "Expected: Identity int [" + - std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" ); // Initial matrix Matrix< int > initial( n, n ); std::vector< size_t > initial_rows( n ), initial_cols( n ); @@ -216,20 +212,57 @@ int main( int argc, char ** argv ) { std::iota( initial_rows.begin(), initial_rows.end(), 0 ); std::iota( initial_cols.begin(), initial_cols.end(), 0 ); buildMatrixUnique( initial, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ); - // Mask - Matrix< void > mask( n, n ); - buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), initial_rows.size(), SEQUENTIAL ); - // Expected matrix - Matrix< int > expected( n, n ); - std::vector< int > expected_values( n, 2 ); - buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ); - std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { label.c_str(), description.c_str(), initial, mask, k, expected }; - if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { - std::cerr << "Launching " << label << " failed" << std::endl; - return 255; + + { + const std::string label( "Test 01" ); + const std::string description( "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + + "]\n" + "Mask: Identity void matrix (matching the input).\n" + "k = 2\n" + "Operator: mul\n" + "Expected: Identity int [" + + std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" ); + // Mask (matching the input matrix) + Matrix< void > mask( n, n ); + buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), initial_rows.size(), SEQUENTIAL ); + // Expected matrix + Matrix< int > expected( n, n ); + std::vector< int > expected_values( n, 2 ); + buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ); + + std::cout << "-- Running " << label << " --" << std::endl; + input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { label.c_str(), description.c_str(), initial, mask, k, 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; + } + + { + const std::string label( "Test 02" ); + const std::string description( "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + + "]\n" + "Mask: Identity void matrix (empty).\n" + "k = 2\n" + "Operator: mul\n" + "Expected: Identity int [" + + std::to_string( n ) + ";" + std::to_string( n ) + "]" ); + // Mask (matching the input matrix) + Matrix< void > mask( n, n ); + buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), 0, SEQUENTIAL ); + // Expected matrix + Matrix< int > expected( n, n ); + buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ); + + std::cout << "-- Running " << label << " --" << std::endl; + input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { label.c_str(), description.c_str(), initial, mask, k, expected, false, true }; + if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { + std::cerr << "Launching " << label << " failed" << std::endl; + return 255; + } + std::cout << std::endl << std::flush; } - std::cout << std::endl << std::flush; } if( rc != SUCCESS ) { From 104d290f3548403f17ab4c2c46400ed09af7ef65 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 26 Jun 2023 22:35:13 +0200 Subject: [PATCH 05/14] Fix masked iteration pattern --- include/graphblas/reference/blas3.hpp | 29 ++++++++++----------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index 007a4517b..aa8324671 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -1230,7 +1230,7 @@ namespace grb { typename RIT, typename CIT, typename NIT > RC scale_unmasked_generic( - const Matrix< IOType, reference, RIT, CIT, NIT > &A, + Matrix< IOType, reference, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator() ) { @@ -1284,7 +1284,7 @@ namespace grb { typename RIT_M, typename CIT_M, typename NIT_M > RC scale_masked_generic( - const Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, + Matrix< IOType, reference, RIT_A, CIT_A, NIT_A > &A, const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator() @@ -1329,28 +1329,21 @@ namespace grb { 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 an higher one - while( mask_raw.row_index[ mask_k ] < k_col && mask_k < mask_raw.col_start[ i + 1 ] ) { - _DEBUG_THREADESAFE_PRINT( "Skipping masked coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_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( mask_k < mask_raw.col_start[ i + 1 ] && mask_raw.row_index[ mask_k ] > k_col ) { + _DEBUG_THREADESAFE_PRINT( "NEquals masked coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); mask_k++; } - // if there is no value for this coordinate, skip it - if( mask_raw.row_index[ mask_k ] != k_col ) { - _DEBUG_THREADESAFE_PRINT( "Skipped masked coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); - continue; - } - - // Get mask value - if( not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { - _DEBUG_THREADESAFE_PRINT( "Skipped masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + + if( mask_raw.row_index[ mask_k ] < k_col || not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { + mask_k++; + _DEBUG_THREADESAFE_PRINT( "Skip masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); continue; } - // Increment the mask pointer in order to skip the next while loop (best case) - mask_k++; - + _DEBUG_THREADESAFE_PRINT( "Found masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); // Get A value - const IOType a_val_before = A_crs_raw.values[ k ]; + 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, x, op ); From e44e7ed1d5d5fedb476f9a403eb9696dcbe137a9 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Jun 2023 09:44:53 +0200 Subject: [PATCH 06/14] Implementation in hyperdags --- include/graphblas/hyperdags/blas3.hpp | 168 ++++++++++++++++++++++ include/graphblas/hyperdags/hyperdags.hpp | 18 ++- src/graphblas/hyperdags/hyperdags.cpp | 12 ++ 3 files changed, 195 insertions(+), 3 deletions(-) diff --git a/include/graphblas/hyperdags/blas3.hpp b/include/graphblas/hyperdags/blas3.hpp index ee0c10f36..7335b5859 100644 --- a/include/graphblas/hyperdags/blas3.hpp +++ b/include/graphblas/hyperdags/blas3.hpp @@ -332,6 +332,174 @@ namespace grb { return ret; } + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldl( + Matrix< IOType, hyperdags, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, hyperdags, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + const RC ret = foldl< descr >( + internal::getMatrix( A ), + internal::getMatrix( mask ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 2 > sourcesC{ + getID( internal::getMatrix(A) ), + getID( internal::getMatrix(mask) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDL_MATRIX_MATRIX_BETA_OP, + sourcesP.begin(), sourcesP.end(), + sourcesC.begin(), sourcesC.end(), + destinations.begin(), destinations.end() + ); + return ret; + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldl( + Matrix< IOType, hyperdags, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + const RC ret = foldl< descr >( + internal::getMatrix( A ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 1 > sourcesC{ + getID( internal::getMatrix(A) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDL_MATRIX_BETA_OP, + sourcesP.begin(), sourcesP.end(), + sourcesC.begin(), sourcesC.end(), + destinations.begin(), destinations.end() + ); + return ret; + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldr( + Matrix< IOType, hyperdags, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, hyperdags, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + const RC ret = foldr< descr >( + internal::getMatrix( A ), + internal::getMatrix( mask ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 2 > sourcesC{ + getID( internal::getMatrix(A) ), + getID( internal::getMatrix(mask) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDR_MATRIX_MATRIX_BETA_OP, + sourcesP.begin(), sourcesP.end(), + sourcesC.begin(), sourcesC.end(), + destinations.begin(), destinations.end() + ); + return ret; + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldr( + Matrix< IOType, hyperdags, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + const RC ret = foldr< descr >( + internal::getMatrix( A ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } + std::array< const void *, 0 > sourcesP{}; + std::array< uintptr_t, 1 > sourcesC{ + getID( internal::getMatrix(A) ) + }; + std::array< uintptr_t, 1 > destinations{ + getID( internal::getMatrix(A) ) + }; + internal::hyperdags::generator.addOperation( + internal::hyperdags::FOLDR_MATRIX_BETA_OP, + 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..6d14ec2e7 100644 --- a/include/graphblas/hyperdags/hyperdags.hpp +++ b/include/graphblas/hyperdags/hyperdags.hpp @@ -488,12 +488,20 @@ namespace grb { EWISEMUL_VECTOR_VECTOR_ALPHA_BETA_RING, - EWISELAMBDA_FUNC_VECTOR + EWISELAMBDA_FUNC_VECTOR, + + FOLDL_MATRIX_MATRIX_BETA_OP, + + FOLDL_MATRIX_BETA_OP, + + FOLDR_MATRIX_MATRIX_BETA_OP, + + FOLDR_MATRIX_BETA_OP }; /** \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 +612,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_MATRIX_BETA_OP, + FOLDL_MATRIX_BETA_OP, + FOLDR_MATRIX_MATRIX_BETA_OP, + FOLDR_MATRIX_BETA_OP }; /** \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..5caa1ba82 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_MATRIX_BETA_OP: + return "foldl( matrix, matrix, scalar )"; + + case FOLDL_MATRIX_BETA_OP: + return "foldl( matrix, scalar )"; + + case FOLDR_MATRIX_MATRIX_BETA_OP: + return "foldr( matrix, matrix, scalar )"; + + case FOLDR_MATRIX_BETA_OP: + return "foldr( matrix, scalar )"; } assert( false ); From e3813c667bee33578b8e9974cdc61083606d99ba Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 27 Jun 2023 11:52:04 +0200 Subject: [PATCH 07/14] Implementation for BSP1D backend --- include/graphblas/bsp1d/blas3.hpp | 134 ++++++++++++++++++++ tests/unit/CMakeLists.txt | 2 +- tests/unit/fold_matrix_scalar_to_matrix.cpp | 12 +- tests/unit/unittests.sh | 14 +- 4 files changed, 149 insertions(+), 13 deletions(-) diff --git a/include/graphblas/bsp1d/blas3.hpp b/include/graphblas/bsp1d/blas3.hpp index 386beb164..7e1702378 100644 --- a/include/graphblas/bsp1d/blas3.hpp +++ b/include/graphblas/bsp1d/blas3.hpp @@ -205,6 +205,140 @@ namespace grb { return internal::checkGlobalErrorStateOrClear( C, ret ); } + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldl( + Matrix< IOType, BSP1D, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldl( BSP1D, matrix, scalar, op )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldl< descr >( internal::getLocal( A ), x, op ); + + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldl( + Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldl( BSP1D, matrix, mask, scalar, op )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldl< descr >( internal::getLocal( A ), internal::getLocal( mask ), x, op ); + + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldr( + Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldr( BSP1D, matrix, scalar, mask, op )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldr< descr >( internal::getLocal( A ), internal::getLocal( mask ), x, op ); + + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldr( + Matrix< IOType, BSP1D, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { +#ifdef _DEBUG + std::cout << "In grb::foldr( BSP1D, matrix, scalar, op )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldr< descr >( internal::getLocal( A ), x, op ); + + return rc; + } + } // namespace grb #endif diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 1ac746506..e4046cc87 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_scalar_to_matrix fold_matrix_scalar_to_matrix.cpp - BACKENDS reference reference_omp hyperdags + BACKENDS reference reference_omp hyperdags bsp1d hybrid ) add_grb_executables( muladd muladd.cpp diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp index a95ab6833..83db54fed 100644 --- a/tests/unit/fold_matrix_scalar_to_matrix.cpp +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -109,12 +109,12 @@ struct input { const OpFoldl & opFoldl; const OpFoldr & opFoldr = OpFoldr(); - input( const char * test_label, - const char * test_description, - const grb::Matrix< T > & initial, - const grb::Matrix< M > & mask, - const S scalar, - const grb::Matrix< T > & expected, + input( const char * test_label = "", + const char * test_description = "", + const grb::Matrix< T > & initial = {0,0}, + const grb::Matrix< M > & mask = {0,0}, + const S scalar = 0, + const grb::Matrix< T > & expected = {0,0}, bool skip_masked = false, bool skip_unmasked = false, const OpFoldl & opFoldl = OpFoldl(), diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index 344749782..8b162b2fc 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -222,12 +222,14 @@ for MODE in ${MODES}; do grep 'Test OK' ${TEST_OUT_DIR}/ewiseapply_large_${MODE}_${BACKEND}_${P}_${T} || echo "Test FAILED" echo " " - echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing sparse" - echo " matrix in-place using operators." - $runner ${TEST_BIN_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log - head -1 ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log - grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" - echo " " + if [ "$BACKEND" != "nonblocking" ]; then + echo ">>> [x] [x] Testing grb::foldl and grb::foldr reducing sparse" + echo " matrix in-place using operators." + $runner ${TEST_BIN_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_scalar_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." From d80ed92968520f54da5d418b18a1ba97cf791861 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Wed, 28 Jun 2023 14:05:14 +0200 Subject: [PATCH 08/14] Implementation in nonblocking (delegating) --- include/graphblas/nonblocking/blas3.hpp | 120 ++++++++++++++++++++++++ tests/unit/CMakeLists.txt | 2 +- tests/unit/unittests.sh | 14 ++- 3 files changed, 127 insertions(+), 9 deletions(-) diff --git a/include/graphblas/nonblocking/blas3.hpp b/include/graphblas/nonblocking/blas3.hpp index 5a222c7f2..efc796f31 100644 --- a/include/graphblas/nonblocking/blas3.hpp +++ b/include/graphblas/nonblocking/blas3.hpp @@ -571,6 +571,126 @@ namespace grb { ); } + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldl( + Matrix< IOType, nonblocking, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, nonblocking, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldl( nonblocking, matrix, mask, scalar, op )\n"; +#endif + RC rc = SUCCESS; + + if( grb::nnz( A ) == 0 ) { + return rc; + } + + // Do local folding + rc = foldl< descr >( internal::getRefMatrix( A ), internal::getRefMatrix( mask ), x, op ); + + return rc; + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldl( + Matrix< IOType, nonblocking, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldl( nonblocking, matrix, scalar, op )\n"; +#endif + // 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, Operator >( internal::getRefMatrix( A ), x, op ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename MaskType, typename InputType, + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M + > + RC foldr( + Matrix< IOType, nonblocking, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, nonblocking, RIT_M, CIT_M, NIT_M > &mask, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + !grb::is_object< MaskType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { + +#ifdef _DEBUG + std::cout << "In grb::foldr( nonblocking, matrix, scalar, mask, op )\n"; +#endif + // 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, Operator >( internal::getRefMatrix( A ), internal::getRefMatrix( mask ), x, op ); + } + + template< + Descriptor descr = descriptors::no_operation, + class Operator, + typename IOType, typename InputType, + typename RIT, typename CIT, typename NIT + > + RC foldr( + Matrix< IOType, nonblocking, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator(), + const typename std::enable_if< + !grb::is_object< IOType >::value && + !grb::is_object< InputType >::value && + grb::is_operator< Operator >::value, void + >::type * const = nullptr + ) { +#ifdef _DEBUG + std::cout << "In grb::foldr( nonblocking, matrix, scalar, op )\n"; +#endif + // 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, Operator >( internal::getRefMatrix( A ), x, op ); + } } // namespace grb #undef NO_CAST_ASSERT diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index e4046cc87..1a3392fab 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_scalar_to_matrix fold_matrix_scalar_to_matrix.cpp - BACKENDS reference reference_omp hyperdags bsp1d hybrid + BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) add_grb_executables( muladd muladd.cpp diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index 8b162b2fc..344749782 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 operators." - $runner ${TEST_BIN_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log - head -1 ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log - grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_scalar_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 operators." + $runner ${TEST_BIN_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/fold_matrix_scalar_to_matrix_${MODE}_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/fold_matrix_scalar_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 e61608763710442e1b2c0cf0082865f2570fa6d9 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 3 Jul 2023 15:18:20 +0200 Subject: [PATCH 09/14] Add safe-guard for masked version --- include/graphblas/reference/blas3.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index aa8324671..81dad0c9c 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -1327,16 +1327,18 @@ namespace grb { for( auto i = start_row; i < end_row; ++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 ]; + 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( 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 masked coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); mask_k++; } - - if( mask_raw.row_index[ mask_k ] < k_col || not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { - mask_k++; + if( mask_k >= mask_raw.col_start[ i + 1 ] ) { + _DEBUG_THREADESAFE_PRINT( "No value left for this column\n" ); + break; + } + if( mask_raw.row_index[ mask_k ] < j || not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { _DEBUG_THREADESAFE_PRINT( "Skip masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); continue; } @@ -1344,7 +1346,7 @@ namespace grb { _DEBUG_THREADESAFE_PRINT( "Found masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\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, x, op ); local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, x, op ); From 71d03458ff513bb108a118a6fc47a525746d8440 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 10 Jul 2023 15:12:22 +0200 Subject: [PATCH 10/14] Style fixes --- include/graphblas/base/blas3.hpp | 32 ++-- include/graphblas/blas0.hpp | 33 ---- include/graphblas/bsp1d/blas3.hpp | 62 +++++-- include/graphblas/hyperdags/blas3.hpp | 24 +-- include/graphblas/nonblocking/blas3.hpp | 16 +- include/graphblas/reference/blas3.hpp | 171 +++++++++++++------- tests/unit/fold_matrix_scalar_to_matrix.cpp | 55 +++++-- 7 files changed, 239 insertions(+), 154 deletions(-) diff --git a/include/graphblas/base/blas3.hpp b/include/graphblas/base/blas3.hpp index 4e488be36..cf34549ae 100644 --- a/include/graphblas/base/blas3.hpp +++ b/include/graphblas/base/blas3.hpp @@ -457,7 +457,7 @@ namespace grb { * @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] x The input scalat to scale with. + * @param[in] x The input scalat to scale with. * @param[in] op The monoid under which to perform this scaling. * * @return grb::SUCCESS When the call completed successfully. @@ -473,7 +473,7 @@ namespace grb { * - 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_right: mask^T will be considered + * - descriptors::transpose_right: mask^T will be considered * instead of \a mask. * - descriptors::invert_mask: Not supported yet. * @@ -489,7 +489,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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, Backend backend @@ -499,7 +499,7 @@ namespace grb { const Matrix< MaskType, backend, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -518,15 +518,15 @@ namespace grb { } /** - * Scales, or \em folds, a matrix into a matrix, using a constant scalar. + * Scales, or \em folds, a matrix into a matrix, using a constant scalar. * Left-to-right unmasked variant. - * + * * Please see the masked grb::foldl variant for a full description. */ template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT, Backend backend > @@ -534,7 +534,7 @@ namespace grb { Matrix< IOType, backend, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void @@ -551,15 +551,15 @@ namespace grb { } /** - * Scales, or \em folds, a matrix into a matrix, using a constant scalar. + * Scales, or \em folds, a matrix into a matrix, using a constant scalar. * Right-to-left masked variant. - * + * * Please see the masked grb::foldl variant for a full description. */ template< Descriptor descr = descriptors::no_operation, class Operator, - 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, Backend backend @@ -569,7 +569,7 @@ namespace grb { const Matrix< MaskType, backend, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -588,15 +588,15 @@ namespace grb { } /** - * Scales, or \em folds, a matrix into a matrix, using a constant scalar. + * Scales, or \em folds, a matrix into a matrix, using a constant scalar. * Right-to-left unmasked variant. - * + * * Please see the masked grb::foldl variant for a full description. */ template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT, Backend backend > @@ -604,7 +604,7 @@ namespace grb { Matrix< IOType, backend, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void diff --git a/include/graphblas/blas0.hpp b/include/graphblas/blas0.hpp index ee79299f7..751b2cf14 100644 --- a/include/graphblas/blas0.hpp +++ b/include/graphblas/blas0.hpp @@ -32,7 +32,6 @@ #include //enable_if #include "graphblas/descriptors.hpp" -#include "graphblas/identities.hpp" #include "graphblas/rc.hpp" #include "graphblas/type_traits.hpp" @@ -605,38 +604,6 @@ 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 = mask_raw.getValue( k, identities::logical_false() ); - if (descr & grb::descriptors::invert_mask) { - hasValue = !hasValue; - } - value = hasValue; - } - - const 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/bsp1d/blas3.hpp b/include/graphblas/bsp1d/blas3.hpp index 7e1702378..1da229995 100644 --- a/include/graphblas/bsp1d/blas3.hpp +++ b/include/graphblas/bsp1d/blas3.hpp @@ -111,7 +111,9 @@ namespace grb { return resize( out, nnz( mask ) ); } else { local_rc = grb::set< descr >( - internal::getLocal( out ), internal::getLocal( mask ), val + internal::getLocal( out ), + internal::getLocal( mask ), + val ); } return internal::checkGlobalErrorStateOrClear( out, local_rc ); @@ -148,7 +150,10 @@ namespace grb { mul, RESIZE ); - if( collectives<>::allreduce( ret, operators::any_or< RC >() ) != SUCCESS ) { + if( collectives<>::allreduce( + ret, operators::any_or< RC >() + ) != SUCCESS ) + { return PANIC; } else { return ret; @@ -157,7 +162,8 @@ namespace grb { assert( phase == EXECUTE ); local_rc = eWiseApply< descr >( internal::getLocal( C ), - internal::getLocal( A ), internal::getLocal( B ), + internal::getLocal( A ), + internal::getLocal( B ), mul, EXECUTE ); @@ -190,12 +196,16 @@ namespace grb { assert( phase != TRY ); RC ret = eWiseApply< descr >( internal::getLocal( C ), - internal::getLocal( A ), internal::getLocal( B ), + internal::getLocal( A ), + internal::getLocal( B ), op, phase ); if( phase == RESIZE ) { - if( collectives<>::allreduce( ret, operators::any_or< RC >() ) != SUCCESS ) { + if( collectives<>::allreduce( + ret, operators::any_or< RC >() + ) != SUCCESS ) + { return PANIC; } else { return SUCCESS; @@ -208,14 +218,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename MaskType, typename InputType, + typename IOType, typename MaskType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldl( Matrix< IOType, BSP1D, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - 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 && @@ -233,7 +243,11 @@ namespace grb { } // Do local folding - rc = foldl< descr >( internal::getLocal( A ), x, op ); + rc = foldl< descr >( + internal::getLocal( A ), + x, + op + ); return rc; } @@ -241,7 +255,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -250,7 +264,7 @@ namespace grb { const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -268,7 +282,12 @@ namespace grb { } // Do local folding - rc = foldl< descr >( internal::getLocal( A ), internal::getLocal( mask ), x, op ); + rc = foldl< descr >( + internal::getLocal( A ), + internal::getLocal( mask ), + x, + op + ); return rc; } @@ -276,7 +295,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -285,7 +304,7 @@ namespace grb { const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -303,7 +322,12 @@ namespace grb { } // Do local folding - rc = foldr< descr >( internal::getLocal( A ), internal::getLocal( mask ), x, op ); + rc = foldr< descr >( + internal::getLocal( A ), + internal::getLocal( mask ), + x, + op + ); return rc; } @@ -311,14 +335,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldr( Matrix< IOType, BSP1D, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void @@ -334,7 +358,11 @@ namespace grb { } // Do local folding - rc = foldr< descr >( internal::getLocal( A ), x, op ); + rc = foldr< descr >( + internal::getLocal( A ), + x, + op + ); return rc; } diff --git a/include/graphblas/hyperdags/blas3.hpp b/include/graphblas/hyperdags/blas3.hpp index 7335b5859..eab4c2bb9 100644 --- a/include/graphblas/hyperdags/blas3.hpp +++ b/include/graphblas/hyperdags/blas3.hpp @@ -335,7 +335,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -344,7 +344,7 @@ namespace grb { const Matrix< MaskType, hyperdags, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -365,7 +365,7 @@ namespace grb { getID( internal::getMatrix(A) ), getID( internal::getMatrix(mask) ) }; - std::array< uintptr_t, 1 > destinations{ + std::array< uintptr_t, 1 > destinations{ getID( internal::getMatrix(A) ) }; internal::hyperdags::generator.addOperation( @@ -380,14 +380,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldl( Matrix< IOType, hyperdags, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void @@ -404,7 +404,7 @@ namespace grb { std::array< uintptr_t, 1 > sourcesC{ getID( internal::getMatrix(A) ) }; - std::array< uintptr_t, 1 > destinations{ + std::array< uintptr_t, 1 > destinations{ getID( internal::getMatrix(A) ) }; internal::hyperdags::generator.addOperation( @@ -419,7 +419,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -428,7 +428,7 @@ namespace grb { const Matrix< MaskType, hyperdags, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -449,7 +449,7 @@ namespace grb { getID( internal::getMatrix(A) ), getID( internal::getMatrix(mask) ) }; - std::array< uintptr_t, 1 > destinations{ + std::array< uintptr_t, 1 > destinations{ getID( internal::getMatrix(A) ) }; internal::hyperdags::generator.addOperation( @@ -464,14 +464,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldr( Matrix< IOType, hyperdags, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void @@ -488,7 +488,7 @@ namespace grb { std::array< uintptr_t, 1 > sourcesC{ getID( internal::getMatrix(A) ) }; - std::array< uintptr_t, 1 > destinations{ + std::array< uintptr_t, 1 > destinations{ getID( internal::getMatrix(A) ) }; internal::hyperdags::generator.addOperation( diff --git a/include/graphblas/nonblocking/blas3.hpp b/include/graphblas/nonblocking/blas3.hpp index efc796f31..bbdf1f1b9 100644 --- a/include/graphblas/nonblocking/blas3.hpp +++ b/include/graphblas/nonblocking/blas3.hpp @@ -574,7 +574,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -583,7 +583,7 @@ namespace grb { const Matrix< MaskType, nonblocking, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -609,14 +609,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldl( Matrix< IOType, nonblocking, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void @@ -637,7 +637,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -646,7 +646,7 @@ namespace grb { const Matrix< MaskType, nonblocking, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -668,14 +668,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldr( Matrix< IOType, nonblocking, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index 81dad0c9c..793080302 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -34,21 +34,21 @@ #include #endif - -#define OMP_CRITICAL _Pragma("omp critical") - -#ifndef _DEBUG_THREADESAFE_PRINT +#ifndef _DEBUG_PRINT #ifndef _DEBUG - #define _DEBUG_THREADESAFE_PRINT( msg ) + #define _DEBUG_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; \ - } + #ifdef _GRB_WITH_OMP + #ifndef _GRB_DEBUG_CRITICAL_SECTION + #define _GRB_DEBUG_CRITICAL_SECTION _Pragma("omp critical(_GRB_DEBUG_CRITICAL_SECTION)") + #endif + #define _DEBUG_PRINT( msg ) \ + _GRB_DEBUG_CRITICAL_SECTION \ + { \ + std::cout << "[T" << omp_get_thread_num() << "] - " << msg << std::flush; \ + } #else - #define _DEBUG_THREADESAFE_PRINT( msg ) std::cout << msg << std::flush; + #define _DEBUG_PRINT( msg ) std::cout << msg << std::flush; #endif #endif #endif @@ -1234,20 +1234,22 @@ namespace grb { const InputType &x, const Operator &op = Operator() ) { - _DEBUG_THREADESAFE_PRINT( "In grb::internal::scale_unmasked_generic( reference )\n" ); - RC rc = SUCCESS; + _DEBUG_PRINT( "In grb::internal::scale_unmasked_generic( reference )\n" ); const auto &A_crs_raw = grb::internal::getCRS( A ); const auto &A_ccs_raw = grb::internal::getCCS( A ); const size_t A_nnz = nnz( A ); if( grb::nnz( A ) == 0 ) { - return rc; + return RC::SUCCESS; } + RC rc = SUCCESS; RC local_rc = rc; #ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, rc, std::cout) firstprivate(x, local_rc, A_nnz, op) + #pragma omp parallel default(none) \ + shared(A_crs_raw, A_ccs_raw, rc, std::cout) \ + firstprivate(x, local_rc, A_nnz, op) #endif { size_t start = 0; @@ -1255,15 +1257,25 @@ namespace grb { #ifdef _H_GRB_REFERENCE_OMP_BLAS3 config::OMP::localRange( start, end, 0, A_nnz ); #endif - for( size_t idx = start; idx < end; ++idx ) { // Get A value const IOType a_val_before = A_crs_raw.values[ idx ]; - _DEBUG_THREADESAFE_PRINT( "A.CRS.values[ " + std::to_string( idx ) + " ] = " + std::to_string( a_val_before ) + "\n" ); + _DEBUG_PRINT( "A.CRS.values[ " + std::to_string( idx ) + + " ] = " + 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[ idx ], a_val_before, x, op ); - local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ idx ], a_val_before, x, op ); - _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( x ) + ") = " + std::to_string( A_ccs_raw.values[ idx ] ) + "\n" ); + local_rc = local_rc + ? local_rc + : grb::apply< descr >( + A_crs_raw.values[ idx ], a_val_before, x, op + ); + local_rc = local_rc + ? local_rc + : grb::apply< descr >( + A_ccs_raw.values[ idx ], a_val_before, x, op + ); + _DEBUG_PRINT( "Computing: op(" + std::to_string( a_val_before ) + + ", " + std::to_string( x ) + ") = " + + std::to_string( A_ccs_raw.values[ idx ] ) + "\n" ); } #ifdef _H_GRB_REFERENCE_OMP_BLAS3 @@ -1289,34 +1301,52 @@ namespace grb { const InputType &x, const Operator &op = Operator() ) { - _DEBUG_THREADESAFE_PRINT( "In grb::internal::scale_masked_generic( reference )\n" ); - RC rc = SUCCESS; + typedef typename std::conditional< + std::is_void< MaskType >::value, bool, MaskType + >::type MaskIdentityType; + + _DEBUG_PRINT( "In grb::internal::scale_masked_generic( reference )\n" ); - if(grb::nnz(mask) == 0) { - return rc; + if( grb::nnz(mask) == 0 ) { + return RC::SUCCESS; + } + + if( descr & descriptors::force_row_major && descr & descriptors::transpose_left ) { + _DEBUG_PRINT( "force_row_major and transpose_left are not supported together\n" ); + return RC::ILLEGAL; + } + if( descr & descriptors::force_row_major && descr & descriptors::transpose_right ) { + _DEBUG_PRINT( "force_row_major and transpose_right are not supported together\n" ); + return RC::ILLEGAL; } const auto &A_crs_raw = internal::getCRS( A ); const auto &A_ccs_raw = internal::getCCS( A ); - const auto &mask_raw = descr & grb::descriptors::transpose_right ? - internal::getCCS( mask ) : internal::getCRS( mask ); + const auto &mask_raw = descr & grb::descriptors::transpose_right + ? internal::getCCS( mask ) + : internal::getCRS( mask ); const size_t m = nrows( A ); const size_t n = ncols( A ); - const size_t m_mask = descr & grb::descriptors::transpose_left ? - ncols( mask ) : nrows( mask ); - const size_t n_mask = descr & grb::descriptors::transpose_left ? - nrows( mask ) : ncols( mask ); + const size_t m_mask = descr & grb::descriptors::transpose_left + ? ncols( mask ) + : nrows( mask ); + const size_t n_mask = descr & grb::descriptors::transpose_left + ? nrows( mask ) + : ncols( mask ); // Check mask dimensions if( m != m_mask || n != n_mask ) { - _DEBUG_THREADESAFE_PRINT( "Mask dimensions do not match input matrix dimensions\n" ); - return MISMATCH; + _DEBUG_PRINT( "Mask dimensions do not match input matrix dimensions\n" ); + return RC::MISMATCH; } + RC rc = SUCCESS; RC local_rc = rc; #ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #pragma omp parallel default(none) shared(A_crs_raw, A_ccs_raw, mask_raw, rc, std::cout) firstprivate(x, local_rc, m, op) + #pragma omp parallel default(none) \ + shared(A_crs_raw, A_ccs_raw, mask_raw, rc, std::cout) \ + firstprivate(x, local_rc, m, op) #endif { size_t start_row = 0; @@ -1326,31 +1356,60 @@ namespace grb { #endif for( auto i = start_row; i < end_row; ++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 ) { + const auto k_start = A_crs_raw.col_start[ i ]; + const auto k_end = A_crs_raw.col_start[ i + 1 ]; + for( auto k = k_start; k < 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( mask_k < mask_raw.col_start[ i + 1 ] && mask_raw.row_index[ mask_k ] > j ) { - _DEBUG_THREADESAFE_PRINT( "NEquals masked coordinate: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_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( 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( "No value left for this column\n" ); + _DEBUG_PRINT( "No value left for this column\n" ); break; - } - if( mask_raw.row_index[ mask_k ] < j || not MaskHasValue< MaskType >( mask_raw, mask_k ).value ) { - _DEBUG_THREADESAFE_PRINT( "Skip masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + } + + const bool mask_has_value = mask_raw.getValue( + mask_k, + identities::logical_true::value() + ); + if( mask_raw.row_index[ mask_k ] < j || !mask_has_value ) { + _DEBUG_PRINT( "Skip masked value at: ( " + + std::to_string( i ) + ";" + + std::to_string( mask_raw.row_index[ mask_k ] ) + + " )\n" ); continue; } - _DEBUG_THREADESAFE_PRINT( "Found masked value at: ( " + std::to_string( i ) + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + " )\n" ); + _DEBUG_PRINT( "Found masked value at: ( " + + std::to_string( i ) + ";" + + std::to_string( mask_raw.row_index[ mask_k ] ) + + " )\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" ); + _DEBUG_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, x, op ); - local_rc = local_rc ? local_rc : grb::apply< descr >( A_ccs_raw.values[ k ], a_val_before, x, op ); - _DEBUG_THREADESAFE_PRINT( "Computing: op(" + std::to_string( a_val_before ) + ", " + std::to_string( x ) + ") = " + std::to_string( A_ccs_raw.values[ k ] ) + "\n" ); + local_rc = local_rc + ? local_rc + : grb::apply< descr >( + A_crs_raw.values[ k ], a_val_before, x, op + ); + local_rc = local_rc + ? local_rc + : grb::apply< descr >( + A_ccs_raw.values[ k ], a_val_before, x, op + ); + _DEBUG_PRINT( "Computing: op(" + std::to_string( a_val_before ) + + ", " + std::to_string( x ) + ") = " + + std::to_string( A_ccs_raw.values[ k ] ) + "\n" ); } } @@ -1490,7 +1549,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -1499,7 +1558,7 @@ namespace grb { const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -1542,14 +1601,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldl( Matrix< IOType, reference, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void @@ -1592,7 +1651,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - 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 > @@ -1601,7 +1660,7 @@ namespace grb { const Matrix< MaskType, reference, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), - 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 && @@ -1644,14 +1703,14 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, - typename IOType, typename InputType, + typename IOType, typename InputType, typename RIT, typename CIT, typename NIT > RC foldr( Matrix< IOType, reference, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), - const typename std::enable_if< + const typename std::enable_if< !grb::is_object< IOType >::value && !grb::is_object< InputType >::value && grb::is_operator< Operator >::value, void diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp index 83db54fed..7559933d6 100644 --- a/tests/unit/fold_matrix_scalar_to_matrix.cpp +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -48,11 +48,19 @@ constexpr bool SKIP_MASKED = false; #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 ) { +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; + 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 { @@ -60,9 +68,14 @@ void printSparseMatrixIterator( size_t rows, size_t cols, Iterator begin, Iterat 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; - } ); + 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 @@ -77,13 +90,20 @@ void printSparseMatrixIterator( size_t rows, size_t cols, Iterator begin, Iterat } template< typename D > -void printSparseMatrix( const grb::Matrix< D > & mat, const std::string & name = "", std::ostream & os = std::cout ) { +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 ) { +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 ); @@ -119,9 +139,16 @@ struct input { bool skip_unmasked = false, const OpFoldl & opFoldl = OpFoldl(), const OpFoldr & opFoldr = OpFoldr() ) : - test_label( test_label ), - test_description( test_description ), initial( initial ), mask( mask ), scalar( scalar ), expected( expected ), skip_masked( skip_masked ), skip_unmasked( skip_unmasked ), opFoldl( opFoldl ), - opFoldr( opFoldr ) {} + test_label( test_label ), + test_description( test_description ), + initial( initial ), + mask( mask ), + scalar( scalar ), + expected( expected ), + skip_masked( skip_masked ), + skip_unmasked( skip_unmasked ), + opFoldl( opFoldl ), + opFoldr( opFoldr ) {} }; template< typename T, typename M, typename S, class OpFoldl, class OpFoldr > @@ -231,7 +258,9 @@ int main( int argc, char ** argv ) { buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ); std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { label.c_str(), description.c_str(), initial, mask, k, expected }; + input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { + label.c_str(), description.c_str(), initial, mask, k, expected + }; if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { std::cerr << "Launching " << label << " failed" << std::endl; return 255; @@ -256,7 +285,9 @@ int main( int argc, char ** argv ) { buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ); std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { label.c_str(), description.c_str(), initial, mask, k, expected, false, true }; + input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { + label.c_str(), description.c_str(), initial, mask, k, expected, false, true + }; if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { std::cerr << "Launching " << label << " failed" << std::endl; return 255; From 2b2793fcaeca9bbfb8403f8beab53d97731ababb Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 11 Jul 2023 10:55:03 +0200 Subject: [PATCH 11/14] Style fix + hyperdags fix --- include/graphblas/bsp1d/blas3.hpp | 86 ++++--- include/graphblas/hyperdags/blas3.hpp | 49 +++- include/graphblas/nonblocking/blas3.hpp | 51 +++- include/graphblas/reference/blas3.hpp | 143 ++++++----- tests/unit/fold_matrix_scalar_to_matrix.cpp | 263 ++++++++++---------- 5 files changed, 338 insertions(+), 254 deletions(-) diff --git a/include/graphblas/bsp1d/blas3.hpp b/include/graphblas/bsp1d/blas3.hpp index 1da229995..4a8e28a9a 100644 --- a/include/graphblas/bsp1d/blas3.hpp +++ b/include/graphblas/bsp1d/blas3.hpp @@ -219,10 +219,12 @@ namespace grb { Descriptor descr = descriptors::no_operation, class Operator, typename IOType, typename MaskType, typename InputType, - typename RIT, typename CIT, typename NIT + typename RIT_A, typename CIT_A, typename NIT_A, + typename RIT_M, typename CIT_M, typename NIT_M > RC foldl( - Matrix< IOType, BSP1D, RIT, CIT, NIT > &A, + Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, + const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, const InputType &x, const Operator &op = Operator(), const typename std::enable_if< @@ -234,34 +236,36 @@ namespace grb { ) { #ifdef _DEBUG - std::cout << "In grb::foldl( BSP1D, matrix, scalar, op )\n"; + std::cout << "In grb::foldl( BSP1D, matrix, mask, scalar, op )\n"; #endif - RC rc = SUCCESS; - - if( grb::nnz( A ) == 0 ) { - return rc; + if( nnz( A ) == 0 || nnz( mask) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix / mask is empty, nothing to compute.\n"; +#endif + return SUCCESS; } // Do local folding - rc = foldl< descr >( + RC rc = foldl< descr >( internal::getLocal( A ), + internal::getLocal( mask ), x, op ); - - return rc; + if( collectives<>::allreduce( rc, operators::any_or< RC >() ) != SUCCESS ) { + return PANIC; + } + return SUCCESS; } template< Descriptor descr = descriptors::no_operation, class Operator, 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, typename CIT, typename NIT > RC foldl( - Matrix< IOType, BSP1D, RIT_A, CIT_A, NIT_A > &A, - const Matrix< MaskType, BSP1D, RIT_M, CIT_M, NIT_M > &mask, + Matrix< IOType, BSP1D, RIT, CIT, NIT > &A, const InputType &x, const Operator &op = Operator(), const typename std::enable_if< @@ -273,23 +277,25 @@ namespace grb { ) { #ifdef _DEBUG - std::cout << "In grb::foldl( BSP1D, matrix, mask, scalar, op )\n"; + std::cout << "In grb::foldl( BSP1D, matrix, scalar, op )\n"; #endif - RC rc = SUCCESS; - - if( grb::nnz( A ) == 0 ) { - return rc; + if( nnz( A ) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix is empty, nothing to compute.\n"; +#endif + return SUCCESS; } // Do local folding - rc = foldl< descr >( + RC rc = foldl< descr >( internal::getLocal( A ), - internal::getLocal( mask ), x, op ); - - return rc; + if( collectives<>::allreduce( rc, operators::any_or< RC >() ) != SUCCESS ) { + return PANIC; + } + return SUCCESS; } template< @@ -315,21 +321,24 @@ namespace grb { #ifdef _DEBUG std::cout << "In grb::foldr( BSP1D, matrix, scalar, mask, op )\n"; #endif - RC rc = SUCCESS; - - if( grb::nnz( A ) == 0 ) { - return rc; + if( nnz( A ) == 0 || nnz( mask) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix / mask is empty, nothing to compute.\n"; +#endif + return SUCCESS; } // Do local folding - rc = foldr< descr >( + RC rc = foldr< descr >( internal::getLocal( A ), internal::getLocal( mask ), x, op ); - - return rc; + if( collectives<>::allreduce( rc, operators::any_or< RC >() ) != SUCCESS ) { + return PANIC; + } + return SUCCESS; } template< @@ -351,20 +360,23 @@ namespace grb { #ifdef _DEBUG std::cout << "In grb::foldr( BSP1D, matrix, scalar, op )\n"; #endif - RC rc = SUCCESS; - - if( grb::nnz( A ) == 0 ) { - return rc; + if( nnz( A ) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix is empty, nothing to compute.\n"; +#endif + return SUCCESS; } // Do local folding - rc = foldr< descr >( + RC rc = foldr< descr >( internal::getLocal( A ), x, op ); - - return rc; + if( collectives<>::allreduce( rc, operators::any_or< RC >() ) != SUCCESS ) { + return PANIC; + } + return SUCCESS; } } // namespace grb diff --git a/include/graphblas/hyperdags/blas3.hpp b/include/graphblas/hyperdags/blas3.hpp index eab4c2bb9..e7a0a4422 100644 --- a/include/graphblas/hyperdags/blas3.hpp +++ b/include/graphblas/hyperdags/blas3.hpp @@ -351,6 +351,9 @@ namespace grb { grb::is_operator< Operator >::value, void >::type * const = nullptr ) { + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return SUCCESS; } + if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return SUCCESS; } + const RC ret = foldl< descr >( internal::getMatrix( A ), internal::getMatrix( mask ), @@ -358,9 +361,12 @@ namespace grb { op ); if( ret != SUCCESS ) { return ret; } - if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } - if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return ret; } - std::array< const void *, 0 > sourcesP{}; + + internal::hyperdags::generator.addSource( + internal::hyperdags::SCALAR, + &x + ); + std::array< const void *, 1 > sourcesP{ &x }; std::array< uintptr_t, 2 > sourcesC{ getID( internal::getMatrix(A) ), getID( internal::getMatrix(mask) ) @@ -374,6 +380,7 @@ namespace grb { sourcesC.begin(), sourcesC.end(), destinations.begin(), destinations.end() ); + return ret; } @@ -393,14 +400,20 @@ namespace grb { grb::is_operator< Operator >::value, void >::type * const = nullptr ) { + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return SUCCESS; } + const RC ret = foldl< descr >( internal::getMatrix( A ), x, op ); if( ret != SUCCESS ) { return ret; } - if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } - std::array< const void *, 0 > sourcesP{}; + + internal::hyperdags::generator.addSource( + internal::hyperdags::SCALAR, + &x + ); + std::array< const void *, 1 > sourcesP{ &x }; std::array< uintptr_t, 1 > sourcesC{ getID( internal::getMatrix(A) ) }; @@ -413,6 +426,7 @@ namespace grb { sourcesC.begin(), sourcesC.end(), destinations.begin(), destinations.end() ); + return ret; } @@ -435,6 +449,9 @@ namespace grb { grb::is_operator< Operator >::value, void >::type * const = nullptr ) { + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return SUCCESS; } + if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return SUCCESS; } + const RC ret = foldr< descr >( internal::getMatrix( A ), internal::getMatrix( mask ), @@ -442,9 +459,13 @@ namespace grb { op ); if( ret != SUCCESS ) { return ret; } - if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } - if( nrows( mask ) == 0 || ncols( mask ) == 0 ) { return ret; } - std::array< const void *, 0 > sourcesP{}; + + + internal::hyperdags::generator.addSource( + internal::hyperdags::SCALAR, + &x + ); + std::array< const void *, 1 > sourcesP{ &x }; std::array< uintptr_t, 2 > sourcesC{ getID( internal::getMatrix(A) ), getID( internal::getMatrix(mask) ) @@ -458,6 +479,7 @@ namespace grb { sourcesC.begin(), sourcesC.end(), destinations.begin(), destinations.end() ); + return ret; } @@ -477,14 +499,20 @@ namespace grb { grb::is_operator< Operator >::value, void >::type * const = nullptr ) { + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return SUCCESS; } + const RC ret = foldr< descr >( internal::getMatrix( A ), x, op ); if( ret != SUCCESS ) { return ret; } - if( nrows( A ) == 0 || ncols( A ) == 0 ) { return ret; } - std::array< const void *, 0 > sourcesP{}; + + internal::hyperdags::generator.addSource( + internal::hyperdags::SCALAR, + &x + ); + std::array< const void *, 1 > sourcesP{ &x }; std::array< uintptr_t, 1 > sourcesC{ getID( internal::getMatrix(A) ) }; @@ -497,6 +525,7 @@ namespace grb { sourcesC.begin(), sourcesC.end(), destinations.begin(), destinations.end() ); + return ret; } diff --git a/include/graphblas/nonblocking/blas3.hpp b/include/graphblas/nonblocking/blas3.hpp index bbdf1f1b9..12e24ea23 100644 --- a/include/graphblas/nonblocking/blas3.hpp +++ b/include/graphblas/nonblocking/blas3.hpp @@ -594,16 +594,26 @@ namespace grb { #ifdef _DEBUG std::cout << "In grb::foldl( nonblocking, matrix, mask, scalar, op )\n"; #endif - RC rc = SUCCESS; - - if( grb::nnz( A ) == 0 ) { - return rc; + 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; } - // Do local folding - rc = foldl< descr >( internal::getRefMatrix( A ), internal::getRefMatrix( mask ), x, op ); + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); - return rc; + // second, delegate to the reference backend + return foldl< descr, Operator >( + internal::getRefMatrix( A ), + internal::getRefMatrix( mask ), + x, + op + ); } template< @@ -626,6 +636,15 @@ namespace grb { #ifdef _DEBUG std::cout << "In grb::foldl( nonblocking, matrix, scalar, op )\n"; #endif + 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(); @@ -657,6 +676,15 @@ namespace grb { #ifdef _DEBUG std::cout << "In grb::foldr( nonblocking, matrix, scalar, mask, op )\n"; #endif + 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(); @@ -684,6 +712,15 @@ namespace grb { #ifdef _DEBUG std::cout << "In grb::foldr( nonblocking, matrix, scalar, op )\n"; #endif + 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(); diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index 793080302..55e6abde3 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -34,25 +34,6 @@ #include #endif -#ifndef _DEBUG_PRINT - #ifndef _DEBUG - #define _DEBUG_PRINT( msg ) - #else - #ifdef _GRB_WITH_OMP - #ifndef _GRB_DEBUG_CRITICAL_SECTION - #define _GRB_DEBUG_CRITICAL_SECTION _Pragma("omp critical(_GRB_DEBUG_CRITICAL_SECTION)") - #endif - #define _DEBUG_PRINT( msg ) \ - _GRB_DEBUG_CRITICAL_SECTION \ - { \ - std::cout << "[T" << omp_get_thread_num() << "] - " << msg << std::flush; \ - } - #else - #define _DEBUG_PRINT( msg ) std::cout << msg << std::flush; - #endif - #endif -#endif - #define NO_CAST_ASSERT( x, y, z ) \ static_assert( x, \ "\n\n" \ @@ -1234,15 +1215,21 @@ namespace grb { const InputType &x, const Operator &op = Operator() ) { - _DEBUG_PRINT( "In grb::internal::scale_unmasked_generic( reference )\n" ); +#ifdef _DEBUG + std::cout << "In grb::internal::scale_unmasked_generic( reference )\n" << std::flush; +#endif - const auto &A_crs_raw = grb::internal::getCRS( A ); - const auto &A_ccs_raw = grb::internal::getCCS( A ); - const size_t A_nnz = nnz( A ); - if( grb::nnz( A ) == 0 ) { - return RC::SUCCESS; + if( nnz( A ) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix is 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 size_t A_nnz = nnz( A ); + RC rc = SUCCESS; RC local_rc = rc; @@ -1260,22 +1247,26 @@ namespace grb { for( size_t idx = start; idx < end; ++idx ) { // Get A value const IOType a_val_before = A_crs_raw.values[ idx ]; - _DEBUG_PRINT( "A.CRS.values[ " + std::to_string( idx ) - + " ] = " + std::to_string( a_val_before ) + "\n" ); +#ifdef _DEBUG + std::cout << "A.CRS.values[ " + std::to_string( idx ) + " ] = " + + 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 >( + : apply< descr >( A_crs_raw.values[ idx ], a_val_before, x, op ); local_rc = local_rc ? local_rc - : grb::apply< descr >( + : apply< descr >( A_ccs_raw.values[ idx ], a_val_before, x, op ); - _DEBUG_PRINT( "Computing: op(" + std::to_string( a_val_before ) + - ", " + std::to_string( x ) + ") = " - + std::to_string( A_ccs_raw.values[ idx ] ) + "\n" ); +#ifdef _DEBUG + std::cout << "Computing: op(" + std::to_string( a_val_before ) + + ", " + std::to_string( x ) + ") = " + + std::to_string( A_ccs_raw.values[ idx ] ) + "\n" << std::flush; +#endif } #ifdef _H_GRB_REFERENCE_OMP_BLAS3 @@ -1305,39 +1296,47 @@ namespace grb { std::is_void< MaskType >::value, bool, MaskType >::type MaskIdentityType; - _DEBUG_PRINT( "In grb::internal::scale_masked_generic( reference )\n" ); +#ifdef _DEBUG + std::cout << "In grb::internal::scale_masked_generic( reference )\n" << std::flush; +#endif - if( grb::nnz(mask) == 0 ) { - return RC::SUCCESS; + if( nnz(mask) == 0 ) { + return SUCCESS; } if( descr & descriptors::force_row_major && descr & descriptors::transpose_left ) { - _DEBUG_PRINT( "force_row_major and transpose_left are not supported together\n" ); - return RC::ILLEGAL; +#ifdef _DEBUG + std::cout << "force_row_major and transpose_left are not supported together\n" << std::flush; +#endif + return ILLEGAL; } if( descr & descriptors::force_row_major && descr & descriptors::transpose_right ) { - _DEBUG_PRINT( "force_row_major and transpose_right are not supported together\n" ); - return RC::ILLEGAL; +#ifdef _DEBUG + std::cout << "force_row_major and transpose_right are not supported together\n" << std::flush; +#endif + return ILLEGAL; } const auto &A_crs_raw = internal::getCRS( A ); const auto &A_ccs_raw = internal::getCCS( A ); - const auto &mask_raw = descr & grb::descriptors::transpose_right + const auto &mask_raw = descr & descriptors::transpose_right ? internal::getCCS( mask ) : internal::getCRS( mask ); const size_t m = nrows( A ); const size_t n = ncols( A ); - const size_t m_mask = descr & grb::descriptors::transpose_left + const size_t m_mask = descr & descriptors::transpose_left ? ncols( mask ) : nrows( mask ); - const size_t n_mask = descr & grb::descriptors::transpose_left + const size_t n_mask = descr & descriptors::transpose_left ? nrows( mask ) : ncols( mask ); // Check mask dimensions if( m != m_mask || n != n_mask ) { - _DEBUG_PRINT( "Mask dimensions do not match input matrix dimensions\n" ); - return RC::MISMATCH; +#ifdef _DEBUG + std::cout << "Mask dimensions do not match input matrix dimensions\n" << std::flush; +#endif + return MISMATCH; } RC rc = SUCCESS; @@ -1369,9 +1368,11 @@ namespace grb { { mask_k++; } - + if( mask_k >= mask_raw.col_start[ i + 1 ] ) { - _DEBUG_PRINT( "No value left for this column\n" ); +#ifdef _DEBUG + std::cout << "No value left for this column\n" << std::flush; +#endif break; } @@ -1380,44 +1381,52 @@ namespace grb { identities::logical_true::value() ); if( mask_raw.row_index[ mask_k ] < j || !mask_has_value ) { - _DEBUG_PRINT( "Skip masked value at: ( " - + std::to_string( i ) + ";" - + std::to_string( mask_raw.row_index[ mask_k ] ) - + " )\n" ); +#ifdef _DEBUG + std::cout << "Skip masked value at: ( " + std::to_string( i ) + + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + + " )\n" << std::flush; +#endif continue; } - _DEBUG_PRINT( "Found masked value at: ( " - + std::to_string( i ) + ";" - + std::to_string( mask_raw.row_index[ mask_k ] ) - + " )\n" ); +#ifdef _DEBUG + std::cout << "Found masked value at: ( " + std::to_string( i ) + + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) + + " )\n" << std::flush; +#endif // Get A value const auto a_val_before = A_crs_raw.values[ k ]; - _DEBUG_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 >( + : apply< descr >( A_crs_raw.values[ k ], a_val_before, x, op ); local_rc = local_rc ? local_rc - : grb::apply< descr >( + : apply< descr >( A_ccs_raw.values[ k ], a_val_before, x, op ); - _DEBUG_PRINT( "Computing: op(" + std::to_string( a_val_before ) - + ", " + std::to_string( x ) + ") = " - + std::to_string( A_ccs_raw.values[ k ] ) + "\n" ); +#ifdef _DEBUG + std::cout << "Computing: op(" + std::to_string( a_val_before ) + + ", " + std::to_string( x ) + ") = " + + 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; + } } } @@ -1447,7 +1456,8 @@ namespace grb { const Matrix< InputType2, reference, RIT3, CIT3, NIT3 > &B, const MulMonoid &mulmono, const Phase phase = EXECUTE, - const typename std::enable_if< !grb::is_object< OutputType >::value && + const typename std::enable_if< + !grb::is_object< OutputType >::value && !grb::is_object< InputType1 >::value && !grb::is_object< InputType2 >::value && grb::is_monoid< MulMonoid >::value, @@ -1504,7 +1514,8 @@ namespace grb { const Matrix< InputType2, reference, RIT3, CIT3, NIT3 > &B, const Operator &mulOp, const Phase phase = EXECUTE, - const typename std::enable_if< !grb::is_object< OutputType >::value && + const typename std::enable_if< + !grb::is_object< OutputType >::value && !grb::is_object< InputType1 >::value && !grb::is_object< InputType2 >::value && grb::is_operator< Operator >::value, diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp index 7559933d6..1c90bbdc9 100644 --- a/tests/unit/fold_matrix_scalar_to_matrix.cpp +++ b/tests/unit/fold_matrix_scalar_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], T[in], Operator ) + * - foldl+r( Matrix[in,out], Mask[in], T[in], Operator ) * * @author Benjamin Lozes * @date 26/05/2023 @@ -45,71 +47,21 @@ constexpr bool SKIP_FOLDR = false; constexpr bool SKIP_UNMASKED = false; constexpr bool SKIP_MASKED = false; -#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 ); -} +// #define _DEBUG template< typename D > -bool are_matrices_equals( - const grb::Matrix< D > & A, - const grb::Matrix< D > & B ) +bool are_matrices_equals( + const Matrix< D > & A, + const Matrix< D > & B ) { - if( grb::nrows( A ) != grb::nrows( B ) || grb::ncols( A ) != grb::ncols( 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() ); + 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() ); } @@ -121,89 +73,118 @@ template< typename T, typename M, typename S, class OpFoldl, class OpFoldr > struct input { const char * test_label; const char * test_description; - const grb::Matrix< T > & initial; - const grb::Matrix< M > & mask; + const Matrix< T > & initial; + const Matrix< M > & mask; const S scalar; - const grb::Matrix< T > & expected; + const Matrix< T > & expected; const bool skip_masked, skip_unmasked; const OpFoldl & opFoldl; const OpFoldr & opFoldr = OpFoldr(); input( const char * test_label = "", const char * test_description = "", - const grb::Matrix< T > & initial = {0,0}, - const grb::Matrix< M > & mask = {0,0}, + const Matrix< T > & initial = {0,0}, + const Matrix< M > & mask = {0,0}, const S scalar = 0, - const grb::Matrix< T > & expected = {0,0}, + const Matrix< T > & expected = {0,0}, bool skip_masked = false, bool skip_unmasked = false, const OpFoldl & opFoldl = OpFoldl(), const OpFoldr & opFoldr = OpFoldr() ) : test_label( test_label ), - test_description( test_description ), - initial( initial ), - mask( mask ), - scalar( scalar ), + test_description( test_description ), + initial( initial ), + mask( mask ), + scalar( scalar ), expected( expected ), - skip_masked( skip_masked ), - skip_unmasked( skip_unmasked ), + skip_masked( skip_masked ), + skip_unmasked( skip_unmasked ), opFoldl( opFoldl ), opFoldr( opFoldr ) {} }; template< typename T, typename M, typename S, class OpFoldl, class OpFoldr > -void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & 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.scalar, in.opFoldl ); - std::cout << "foldl (unmasked) \"" << in.test_label << "\": "; - rc = rc ? rc : ( are_matrices_equals( result, in.expected ) ? RC::SUCCESS : RC::FAILED ); - if( rc == RC::SUCCESS ) +void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, RC & rc ) { + rc = SUCCESS; + + // Unmasked variant foldl + if( !in.skip_unmasked && !SKIP_FOLDL && !SKIP_UNMASKED && rc == SUCCESS ) { + std::cout << "foldl( unmasked ) \"" << in.test_label << "\": "; + + Matrix< T > result = in.initial; + rc = rc ? rc : foldl( result, in.scalar, in.opFoldl ); + if( 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" ); + } 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 << "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.scalar, in.opFoldl ); - 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.scalar, in.opFoldl ); + if( rc == SUCCESS ) { + std::cout << "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 << "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.scalar, in.opFoldr ); - 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.scalar, in.opFoldr ); + if( rc == SUCCESS ) { + std::cout << "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 << "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.scalar, in.opFoldr ); - 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.scalar, in.opFoldr ); + if( rc == SUCCESS ) { + std::cout << "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 << "OK" << std::flush << std::endl; + } else { + std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } } } @@ -228,9 +209,9 @@ 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; - if( ! rc ) { // Identity square * 2 + if( !rc ) { // Identity square * 2 const int k = 2; // Initial matrix Matrix< int > initial( n, n ); @@ -238,28 +219,36 @@ int main( int argc, char ** argv ) { std::vector< int > initial_values( n, 1 ); std::iota( initial_rows.begin(), initial_rows.end(), 0 ); std::iota( initial_cols.begin(), initial_cols.end(), 0 ); - buildMatrixUnique( initial, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( initial, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ) + ); { const std::string label( "Test 01" ); - const std::string description( "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + + const std::string description( + "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" "Mask: Identity void matrix (matching the input).\n" "k = 2\n" "Operator: mul\n" "Expected: Identity int [" + - std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" ); + std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" + ); // Mask (matching the input matrix) Matrix< void > mask( n, n ); - buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), initial_rows.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), initial_rows.size(), SEQUENTIAL ) + ); // Expected matrix Matrix< int > expected( n, n ); std::vector< int > expected_values( n, 2 ); - buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ) + ); std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { - label.c_str(), description.c_str(), initial, mask, k, expected + input< int, void, int, operators::mul< int >, operators::mul< int > > in { + label.c_str(), description.c_str(), initial, mask, k, expected }; if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { std::cerr << "Launching " << label << " failed" << std::endl; @@ -270,22 +259,28 @@ int main( int argc, char ** argv ) { { const std::string label( "Test 02" ); - const std::string description( "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + + const std::string description( + "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + "]\n" "Mask: Identity void matrix (empty).\n" "k = 2\n" "Operator: mul\n" "Expected: Identity int [" + - std::to_string( n ) + ";" + std::to_string( n ) + "]" ); + std::to_string( n ) + ";" + std::to_string( n ) + "]" + ); // Mask (matching the input matrix) Matrix< void > mask( n, n ); - buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), 0, SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), 0, SEQUENTIAL ) + ); // Expected matrix Matrix< int > expected( n, n ); - buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ); + assert( SUCCESS == + buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ) + ); std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, grb::operators::mul< int >, grb::operators::mul< int > > in { + input< int, void, int, operators::mul< int >, operators::mul< int > > in { label.c_str(), description.c_str(), initial, mask, k, expected, false, true }; if( launcher.exec( &grb_program, in, rc, true ) != SUCCESS ) { @@ -299,8 +294,8 @@ int main( int argc, char ** argv ) { if( rc != SUCCESS ) { std::cout << "Test FAILED (" << grb::toString( rc ) << ")" << std::endl; return rc; - } else { - std::cout << "Test OK" << std::endl; - return 0; } + + std::cout << "Test OK" << std::endl; + return 0; } From ffceb6d2eb044860a4f38e0208af4166d0d05989 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Thu, 20 Jul 2023 16:31:12 +0200 Subject: [PATCH 12/14] Replace assertions with error messages --- tests/unit/fold_matrix_scalar_to_matrix.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp index 1c90bbdc9..a4238921f 100644 --- a/tests/unit/fold_matrix_scalar_to_matrix.cpp +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -219,9 +219,13 @@ int main( int argc, char ** argv ) { std::vector< int > initial_values( n, 1 ); std::iota( initial_rows.begin(), initial_rows.end(), 0 ); std::iota( initial_cols.begin(), initial_cols.end(), 0 ); - assert( SUCCESS == + if( SUCCESS != buildMatrixUnique( initial, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ) - ); + ) { + std::cerr << "Building initial matrix failed" << std::endl; + rc = FAILED; + return 1; + } { const std::string label( "Test 01" ); From 4c80b38d662f5c9baa63c840886c2d1bf95aa02a Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Sun, 25 Feb 2024 20:17:20 +0100 Subject: [PATCH 13/14] Bugfixes --- include/graphblas/reference/blas3.hpp | 217 +++++++++----------- tests/unit/fold_matrix_scalar_to_matrix.cpp | 40 ++-- 2 files changed, 108 insertions(+), 149 deletions(-) diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index 55e6abde3..0d268885c 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -1207,6 +1207,7 @@ namespace grb { template< Descriptor descr = descriptors::no_operation, class Operator, + bool left_fold, typename InputType, typename IOType, typename RIT, typename CIT, typename NIT > @@ -1226,62 +1227,68 @@ namespace grb { return SUCCESS; } + constexpr bool crs_only = descr & descriptors::force_row_major; + const auto &A_crs_raw = internal::getCRS( A ); const auto &A_ccs_raw = internal::getCCS( A ); const size_t A_nnz = nnz( A ); - RC rc = SUCCESS; - RC local_rc = rc; + RC global_rc = SUCCESS; #ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #pragma omp parallel default(none) \ - shared(A_crs_raw, A_ccs_raw, rc, std::cout) \ - firstprivate(x, local_rc, A_nnz, op) + #pragma omp parallel default(none) \ + shared(A_crs_raw, A_ccs_raw, global_rc) \ + firstprivate(x, A_nnz, op) #endif { - size_t start = 0; - size_t end = A_nnz; + RC local_rc = SUCCESS; + #ifdef _H_GRB_REFERENCE_OMP_BLAS3 + size_t start, end; config::OMP::localRange( start, end, 0, A_nnz ); +#else + const size_t start = 0; + const size_t end = A_nnz; #endif for( size_t idx = start; idx < end; ++idx ) { - // Get A value - const IOType a_val_before = A_crs_raw.values[ idx ]; -#ifdef _DEBUG - std::cout << "A.CRS.values[ " + std::to_string( idx ) + " ] = " - + std::to_string( a_val_before ) + "\n" << std::flush; -#endif - // Compute the fold for this coordinate + // CRS section + const IOType lhs = left_fold ? A_crs_raw.values[ idx ] : x; + const IOType rhs = left_fold ? x : A_crs_raw.values[ idx ]; local_rc = local_rc - ? local_rc - : apply< descr >( - A_crs_raw.values[ idx ], a_val_before, x, op - ); + ? local_rc + : apply< descr >( + A_crs_raw.values[ idx ], lhs, rhs, op + ); + } + + for( size_t idx = start; idx < end && !crs_only; ++idx ) { + // CCS section + const IOType lhs = left_fold ? A_ccs_raw.values[ idx ] : x; + const IOType rhs = left_fold ? x : A_ccs_raw.values[ idx ]; local_rc = local_rc - ? local_rc - : apply< descr >( - A_ccs_raw.values[ idx ], a_val_before, x, op - ); -#ifdef _DEBUG - std::cout << "Computing: op(" + std::to_string( a_val_before ) - + ", " + std::to_string( x ) + ") = " - + std::to_string( A_ccs_raw.values[ idx ] ) + "\n" << std::flush; -#endif + ? local_rc + : apply< descr >( + A_ccs_raw.values[ idx ], lhs, rhs, op + ); } + if( local_rc != SUCCESS ) { #ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #pragma omp critical + #pragma omp critical #endif - { // Reduction with the global return code - rc = rc ? rc : local_rc; + { // Reduction with the global return code + global_rc = global_rc ? global_rc : local_rc; + } } } - return rc; + + return global_rc; } template< Descriptor descr = descriptors::no_operation, class Operator, + bool left_fold, typename InputType, typename IOType, typename MaskType, typename RIT_A, typename CIT_A, typename NIT_A, typename RIT_M, typename CIT_M, typename NIT_M @@ -1292,10 +1299,6 @@ namespace grb { const InputType &x, const Operator &op = Operator() ) { - typedef typename std::conditional< - std::is_void< MaskType >::value, bool, MaskType - >::type MaskIdentityType; - #ifdef _DEBUG std::cout << "In grb::internal::scale_masked_generic( reference )\n" << std::flush; #endif @@ -1316,12 +1319,12 @@ namespace grb { #endif return ILLEGAL; } + constexpr bool crs_only = descr & descriptors::force_row_major; const auto &A_crs_raw = internal::getCRS( A ); const auto &A_ccs_raw = internal::getCCS( A ); - const auto &mask_raw = descr & descriptors::transpose_right - ? internal::getCCS( mask ) - : internal::getCRS( mask ); + const auto &mask_crs_raw = internal::getCRS( mask ); + const auto &mask_ccs_raw = internal::getCCS( mask ); const size_t m = nrows( A ); const size_t n = ncols( A ); const size_t m_mask = descr & descriptors::transpose_left @@ -1331,6 +1334,11 @@ namespace grb { ? nrows( mask ) : ncols( mask ); + // if no mask is provided, call the unmasked version + if( m_mask * n_mask == 0 ) { + return scale_unmasked_generic< descr, Operator, false >( A, x, op ); + } + // Check mask dimensions if( m != m_mask || n != n_mask ) { #ifdef _DEBUG @@ -1339,98 +1347,57 @@ namespace grb { return MISMATCH; } - RC rc = SUCCESS; - RC local_rc = rc; - -#ifdef _H_GRB_REFERENCE_OMP_BLAS3 - #pragma omp parallel default(none) \ - shared(A_crs_raw, A_ccs_raw, mask_raw, rc, std::cout) \ - firstprivate(x, 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 mask_k = mask_raw.col_start[ i ]; - const auto k_start = A_crs_raw.col_start[ i ]; - const auto k_end = A_crs_raw.col_start[ i + 1 ]; - for( auto k = k_start; k < 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( 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 ] ) { -#ifdef _DEBUG - std::cout << "No value left for this column\n" << std::flush; -#endif - break; - } + // Initialise coordinates bitmask + char * arr1 = nullptr, * buf1 = nullptr; + InputType * vbuf1 = nullptr; + internal::getMatrixBuffers( arr1, buf1, vbuf1, 1, A ); + internal::Coordinates< reference > coors; + coors.set( arr1, false, buf1, m ); - const bool mask_has_value = mask_raw.getValue( - mask_k, - identities::logical_true::value() - ); - if( mask_raw.row_index[ mask_k ] < j || !mask_has_value ) { -#ifdef _DEBUG - std::cout << "Skip masked value at: ( " + std::to_string( i ) - + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) - + " )\n" << std::flush; -#endif - continue; - } + RC global_rc = SUCCESS; -#ifdef _DEBUG - std::cout << "Found masked value at: ( " + std::to_string( i ) - + ";" + std::to_string( mask_raw.row_index[ mask_k ] ) - + " )\n" << std::flush; -#endif - // Get A value - const auto a_val_before = A_crs_raw.values[ k ]; -#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 - : apply< descr >( - A_crs_raw.values[ k ], a_val_before, x, op - ); - local_rc = local_rc - ? local_rc - : apply< descr >( - A_ccs_raw.values[ k ], a_val_before, x, op - ); -#ifdef _DEBUG - std::cout << "Computing: op(" + std::to_string( a_val_before ) - + ", " + std::to_string( x ) + ") = " - + std::to_string( A_ccs_raw.values[ k ] ) - + "\n" << std::flush; -#endif + for( size_t i = 0; i < m; ++i ) { + coors.clear(); + for( size_t k = mask_crs_raw.col_start[ i ]; k < mask_crs_raw.col_start[ i + 1 ]; ++k ) { + coors.assign( mask_crs_raw.row_index[ k ] ); + } + for( size_t l = A_crs_raw.col_start[ i ]; l < A_crs_raw.col_start[ i + 1 ]; ++l ) { + if( !coors.assigned( A_crs_raw.row_index[ l ] ) ) { + continue; } + const IOType lhs = left_fold ? A_crs_raw.values[ l ] : x; + const IOType rhs = left_fold ? x : A_crs_raw.values[ l ]; + global_rc = global_rc + ? global_rc + : apply< descr >( + A_crs_raw.values[ l ], lhs, rhs, op + ); } + } - 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; + if( !crs_only ) { + for( size_t i = 0; i < n; ++i ) { + // CCS section + coors.clear(); + for( size_t k = mask_ccs_raw.col_start[ i ]; k < mask_ccs_raw.col_start[ i + 1 ]; ++k ) { + coors.assign( mask_ccs_raw.row_index[ k ] ); + } + for( size_t l = A_ccs_raw.col_start[ i ]; l < A_ccs_raw.col_start[ i + 1 ]; ++l ) { + if( !coors.assigned( A_crs_raw.row_index[ l ] ) ) { + continue; + } + const IOType lhs = left_fold ? A_ccs_raw.values[ l ] : x; + const IOType rhs = left_fold ? x : A_ccs_raw.values[ l ]; + global_rc = global_rc + ? global_rc + : apply< descr >( + A_ccs_raw.values[ l ], lhs, rhs, op + ); } } } - return rc; + return global_rc; } } // namespace internal @@ -1604,7 +1571,7 @@ namespace grb { std::cout << "In grb::foldl (reference, matrix, mask, scalar, op)\n"; #endif - return internal::scale_masked_generic< descr, Operator >( + return internal::scale_masked_generic< descr, Operator, true >( A, mask, x, op ); } @@ -1654,7 +1621,7 @@ namespace grb { std::cout << "In grb::foldl (reference, matrix, scalar, op)\n"; #endif - return internal::scale_unmasked_generic< descr, Operator >( + return internal::scale_unmasked_generic< descr, Operator, true >( A, x, op ); } @@ -1706,7 +1673,7 @@ namespace grb { std::cout << "In grb::foldr (reference, matrix, mask, scalar, op)\n"; #endif - return internal::scale_masked_generic< descr, Operator >( + return internal::scale_masked_generic< descr, Operator, false >( A, mask, x, op ); } @@ -1755,7 +1722,7 @@ namespace grb { std::cout << "In grb::foldr (reference, matrix, scalar, op)\n"; #endif - return internal::scale_unmasked_generic< descr, Operator >( + return internal::scale_unmasked_generic< descr, Operator, false >( A, x, op ); } diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp index a4238921f..ff227a8cd 100644 --- a/tests/unit/fold_matrix_scalar_to_matrix.cpp +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -113,18 +113,16 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, RC & rc ) { Matrix< T > result = in.initial; rc = rc ? rc : foldl( result, in.scalar, in.opFoldl ); - if( rc == SUCCESS ) { - std::cout << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { 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 << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; } + + std::cout << "OK" << std::endl; } // Masked variant foldl @@ -133,18 +131,16 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, RC & rc ) { Matrix< T > result = in.initial; rc = rc ? rc : foldl( result, in.mask, in.scalar, in.opFoldl ); - if( rc == SUCCESS ) { - std::cout << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { 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 << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; } + + std::cout << "OK" << std::endl; } // Unmasked variant foldr @@ -153,18 +149,16 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, RC & rc ) { Matrix< T > result = in.initial; rc = rc ? rc : foldr( result, in.scalar, in.opFoldr ); - if( rc == SUCCESS ) { - std::cout << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { 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 << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; } + + std::cout << "OK" << std::endl; } // Masked variant foldr @@ -173,18 +167,16 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, RC & rc ) { Matrix< T > result = in.initial; rc = rc ? rc : foldr( result, in.mask, in.scalar, in.opFoldr ); - if( rc == SUCCESS ) { - std::cout << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { 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 << "OK" << std::flush << std::endl; - } else { + if( rc != SUCCESS ) { std::cerr << "Check failed - " << std::endl << in.test_description << std::endl; } + + std::cout << "OK" << std::endl; } } From 78f7037a4cd2fdb7794994b03f660d0ec89d34d1 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Sun, 25 Feb 2024 22:02:58 +0100 Subject: [PATCH 14/14] Second bugfixes --- tests/unit/fold_matrix_scalar_to_matrix.cpp | 182 ++++++++++---------- 1 file changed, 88 insertions(+), 94 deletions(-) diff --git a/tests/unit/fold_matrix_scalar_to_matrix.cpp b/tests/unit/fold_matrix_scalar_to_matrix.cpp index ff227a8cd..98506290c 100644 --- a/tests/unit/fold_matrix_scalar_to_matrix.cpp +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -49,20 +49,14 @@ constexpr bool SKIP_MASKED = false; // #define _DEBUG -template< typename D > +template< typename D, typename T > bool are_matrices_equals( const Matrix< D > & A, - const Matrix< D > & B ) + const Matrix< T > & B ) { - if( nrows( A ) != nrows( B ) || ncols( A ) != ncols( B ) ) + if( nrows( A ) != nrows( B ) || ncols( A ) != ncols( B ) || nnz( A ) != nnz( 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() ); + return std::is_permutation( A.cbegin(), A.cend(), B.cbegin() ); } /** @@ -180,6 +174,87 @@ void grb_program( const input< T, M, S, OpFoldl, OpFoldr > & in, RC & rc ) { } } +struct main_input_t { + const int k; + const size_t n; +}; + +void main_grb(const main_input_t &in, RC& rc) { + const size_t n = in.n; + const auto k = in.k; + + // Initial matrix + Matrix< int > initial( n, n ); + std::vector< size_t > initial_rows( n ), initial_cols( n ); + std::vector< int > initial_values( n, 1 ); + std::iota( initial_rows.begin(), initial_rows.end(), 0 ); + std::iota( initial_cols.begin(), initial_cols.end(), 0 ); + if( SUCCESS != + buildMatrixUnique( initial, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ) + ) { throw std::runtime_error("[Line " + std::to_string(__LINE__) + "]: Building initial matrix failed"); } + + { + const std::string label( "Test 01" ); + const std::string description( + "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + + "]\n" + "Mask: Identity void matrix (matching the input).\n" + "k = 2\n" + "Operator: mul\n" + "Expected: Identity int [" + + std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" + ); + // Mask (matching the input matrix) + Matrix< void > mask( n, n ); + if( SUCCESS != + buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), initial_rows.size(), SEQUENTIAL ) + ) { throw std::runtime_error("[Line " + std::to_string(__LINE__) + "]: Building mask matrix failed"); } + // Expected matrix + Matrix< int > expected( n, n ); + std::vector< int > expected_values( n, 2 ); + if( SUCCESS != + buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ) + ) { throw std::runtime_error("[Line " + std::to_string(__LINE__) + "]: Building mask matrix failed"); } + + std::cout << "-- Running " << label << " --" << std::endl; + input< int, void, int, operators::mul< int >, operators::mul< int > > in { + label.c_str(), description.c_str(), initial, mask, k, expected + }; + grb_program( in, rc ); + std::cout << std::endl << std::flush; + } + + { + const std::string label( "Test 02" ); + const std::string description( + "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + + "]\n" + "Mask: Identity void matrix (empty).\n" + "k = 2\n" + "Operator: mul\n" + "Expected: Identity int [" + + std::to_string( n ) + ";" + std::to_string( n ) + "]" + ); + // Mask (matching the input matrix) + Matrix< void > mask( n, n ); + if( SUCCESS != + buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), 0, SEQUENTIAL ) + ) { throw std::runtime_error("[Line " + std::to_string(__LINE__) + "]: Building mask matrix failed"); } + // Expected matrix + Matrix< int > expected( n, n ); + if( SUCCESS != + buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ) + ) { throw std::runtime_error("[Line " + std::to_string(__LINE__) + "]: Building mask matrix failed"); } + + std::cout << "-- Running " << label << " --" << std::endl; + input< int, void, int, operators::mul< int >, operators::mul< int > > in { + label.c_str(), description.c_str(), initial, mask, k, expected, false, true + }; + grb_program( in, rc ); + std::cout << std::endl << std::flush; + } +} + int main( int argc, char ** argv ) { // defaults bool printUsage = false; @@ -200,92 +275,11 @@ int main( int argc, char ** argv ) { } std::cout << "This is functional test " << argv[ 0 ] << "\n"; - grb::Launcher< AUTOMATIC > launcher; - RC rc = SUCCESS; - - if( !rc ) { // Identity square * 2 - const int k = 2; - // Initial matrix - Matrix< int > initial( n, n ); - std::vector< size_t > initial_rows( n ), initial_cols( n ); - std::vector< int > initial_values( n, 1 ); - std::iota( initial_rows.begin(), initial_rows.end(), 0 ); - std::iota( initial_cols.begin(), initial_cols.end(), 0 ); - if( SUCCESS != - buildMatrixUnique( initial, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ) - ) { - std::cerr << "Building initial matrix failed" << std::endl; - rc = FAILED; - return 1; - } + const int k = 2; - { - const std::string label( "Test 01" ); - const std::string description( - "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + - "]\n" - "Mask: Identity void matrix (matching the input).\n" - "k = 2\n" - "Operator: mul\n" - "Expected: Identity int [" + - std::to_string( n ) + ";" + std::to_string( n ) + "] * 2" - ); - // Mask (matching the input matrix) - Matrix< void > mask( n, n ); - assert( SUCCESS == - buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), initial_rows.size(), SEQUENTIAL ) - ); - // Expected matrix - Matrix< int > expected( n, n ); - std::vector< int > expected_values( n, 2 ); - assert( SUCCESS == - buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), expected_values.data(), expected_values.size(), SEQUENTIAL ) - ); - - std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, operators::mul< int >, operators::mul< int > > in { - label.c_str(), description.c_str(), initial, mask, k, 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; - } + const main_input_t in = {k, n}; + RC rc = grb::Launcher< AUTOMATIC >().exec( &main_grb, in, rc ); - { - const std::string label( "Test 02" ); - const std::string description( - "Initial: Identity int [" + std::to_string( n ) + ";" + std::to_string( n ) + - "]\n" - "Mask: Identity void matrix (empty).\n" - "k = 2\n" - "Operator: mul\n" - "Expected: Identity int [" + - std::to_string( n ) + ";" + std::to_string( n ) + "]" - ); - // Mask (matching the input matrix) - Matrix< void > mask( n, n ); - assert( SUCCESS == - buildMatrixUnique( mask, initial_rows.data(), initial_cols.data(), 0, SEQUENTIAL ) - ); - // Expected matrix - Matrix< int > expected( n, n ); - assert( SUCCESS == - buildMatrixUnique( expected, initial_rows.data(), initial_cols.data(), initial_values.data(), initial_values.size(), SEQUENTIAL ) - ); - - std::cout << "-- Running " << label << " --" << std::endl; - input< int, void, int, operators::mul< int >, operators::mul< int > > in { - label.c_str(), description.c_str(), initial, mask, k, expected, false, true - }; - 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;