diff --git a/include/graphblas/base/blas3.hpp b/include/graphblas/base/blas3.hpp index c22bfba71..cda175c7d 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; + } + /** * @} */ diff --git a/include/graphblas/bsp1d/blas3.hpp b/include/graphblas/bsp1d/blas3.hpp index 386beb164..4a8e28a9a 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; @@ -205,6 +215,170 @@ namespace grb { return internal::checkGlobalErrorStateOrClear( C, 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, 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 + 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 rc = foldl< descr >( + internal::getLocal( A ), + internal::getLocal( mask ), + x, + op + ); + 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, 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 + if( nnz( A ) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix is empty, nothing to compute.\n"; +#endif + return SUCCESS; + } + + // Do local folding + RC rc = foldl< descr >( + internal::getLocal( A ), + x, + op + ); + 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 + > + 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 + 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 rc = foldr< descr >( + internal::getLocal( A ), + internal::getLocal( mask ), + x, + op + ); + if( collectives<>::allreduce( rc, operators::any_or< RC >() ) != SUCCESS ) { + return PANIC; + } + return SUCCESS; + } + + 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 + if( nnz( A ) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix is empty, nothing to compute.\n"; +#endif + return SUCCESS; + } + + // Do local folding + RC rc = foldr< descr >( + internal::getLocal( A ), + x, + op + ); + if( collectives<>::allreduce( rc, operators::any_or< RC >() ) != SUCCESS ) { + return PANIC; + } + return SUCCESS; + } + } // namespace grb #endif diff --git a/include/graphblas/hyperdags/blas3.hpp b/include/graphblas/hyperdags/blas3.hpp index 1ac87ff50..c1c6fbe3b 100644 --- a/include/graphblas/hyperdags/blas3.hpp +++ b/include/graphblas/hyperdags/blas3.hpp @@ -334,6 +334,203 @@ 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 + ) { + 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 ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + + 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) ) + }; + 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 + ) { + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return SUCCESS; } + + const RC ret = foldl< descr >( + internal::getMatrix( A ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + + 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) ) + }; + 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 + ) { + 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 ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + + + 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) ) + }; + 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 + ) { + if( nrows( A ) == 0 || ncols( A ) == 0 ) { return SUCCESS; } + + const RC ret = foldr< descr >( + internal::getMatrix( A ), + x, + op + ); + if( ret != SUCCESS ) { return ret; } + + 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) ) + }; + 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/include/graphblas/nonblocking/blas3.hpp b/include/graphblas/nonblocking/blas3.hpp index c52c9aaff..cd5eee112 100644 --- a/include/graphblas/nonblocking/blas3.hpp +++ b/include/graphblas/nonblocking/blas3.hpp @@ -571,6 +571,163 @@ 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 + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldl( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldl< descr, 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 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 + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldl( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldl< descr, 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 + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldr( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldr< descr, 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 + if( internal::NONBLOCKING::warn_if_not_native && + config::PIPELINE::warn_if_not_native + ) { + std::cerr << "Warning: foldr( nonblocking ) currently delegates to a " + << "blocking implementation.\n" + << " Further similar such warnings will be suppressed.\n"; + internal::NONBLOCKING::warn_if_not_native = false; + } + + // nonblocking execution is not supported + // first, execute any computation that is not completed + internal::le.execution(); + + // second, delegate to the reference backend + return foldr< descr, Operator >( internal::getRefMatrix( A ), x, op ); + } } // namespace grb #undef NO_CAST_ASSERT diff --git a/include/graphblas/reference/blas3.hpp b/include/graphblas/reference/blas3.hpp index 868547183..6b52b6abf 100644 --- a/include/graphblas/reference/blas3.hpp +++ b/include/graphblas/reference/blas3.hpp @@ -1204,6 +1204,202 @@ namespace grb { return SUCCESS; } + template< + Descriptor descr = descriptors::no_operation, + class Operator, + bool left_fold, + typename InputType, typename IOType, + typename RIT, typename CIT, typename NIT + > + RC scale_unmasked_generic( + Matrix< IOType, reference, RIT, CIT, NIT > &A, + const InputType &x, + const Operator &op = Operator() + ) { +#ifdef _DEBUG + std::cout << "In grb::internal::scale_unmasked_generic( reference )\n" << std::flush; +#endif + + if( nnz( A ) == 0 ) { +#ifdef _DEBUG + std::cout << "Matrix is empty, nothing to compute.\n" << std::flush; +#endif + 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 global_rc = SUCCESS; + +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp parallel default(none) \ + shared(A_crs_raw, A_ccs_raw, global_rc) \ + firstprivate(x, A_nnz, op) +#endif + { + 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 ) { + // 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 ], 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 ], lhs, rhs, op + ); + } + + if( local_rc != SUCCESS ) { +#ifdef _H_GRB_REFERENCE_OMP_BLAS3 + #pragma omp critical +#endif + { // Reduction with the global return code + global_rc = global_rc ? global_rc : local_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 + > + RC scale_masked_generic( + 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() + ) { +#ifdef _DEBUG + std::cout << "In grb::internal::scale_masked_generic( reference )\n" << std::flush; +#endif + + if( nnz(mask) == 0 ) { + return SUCCESS; + } + + if( descr & descriptors::force_row_major && descr & descriptors::transpose_left ) { +#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 ) { +#ifdef _DEBUG + std::cout << "force_row_major and transpose_right are not supported together\n" << std::flush; +#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_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 + ? ncols( mask ) + : nrows( mask ); + const size_t n_mask = descr & descriptors::transpose_left + ? 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 + std::cout << "Mask dimensions do not match input matrix dimensions\n" << std::flush; +#endif + return MISMATCH; + } + + // 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 ); + + RC global_rc = SUCCESS; + + 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( !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 global_rc; + } + } // namespace internal /** @@ -1227,7 +1423,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, @@ -1284,7 +1481,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, @@ -1326,6 +1524,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, true >( + 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, true >( + 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, false >( + 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, false >( + A, x, op + ); + } + } // namespace grb #undef NO_CAST_ASSERT 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 ); diff --git a/tests/performance/bench_kernels.h b/tests/performance/bench_kernels.h index 8fea223b3..29901a1dc 100644 --- a/tests/performance/bench_kernels.h +++ b/tests/performance/bench_kernels.h @@ -62,7 +62,7 @@ void bench_kernels_axpy( const size_t n ); -/** +/** * Executes the inner-product computation \f$ alpha = (x,y) \f$ with \a x and * \a y vectors of length \a n. * diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index ab0dac498..3179d1445 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -153,6 +153,10 @@ add_grb_executables( factories factories.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 bsp1d hybrid hyperdags nonblocking +) + 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..98506290c --- /dev/null +++ b/tests/unit/fold_matrix_scalar_to_matrix.cpp @@ -0,0 +1,291 @@ + +/* + * 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: + * - 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 + * + * 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< typename D, typename T > +bool are_matrices_equals( + const Matrix< D > & A, + const Matrix< T > & B ) +{ + if( nrows( A ) != nrows( B ) || ncols( A ) != ncols( B ) || nnz( A ) != nnz( B ) ) + return false; + return std::is_permutation( A.cbegin(), A.cend(), B.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 Matrix< T > & initial; + const Matrix< M > & mask; + const S scalar; + 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 Matrix< T > & initial = {0,0}, + const Matrix< M > & mask = {0,0}, + const S scalar = 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 ), + 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 > +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::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::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } + + std::cout << "OK" << std::endl; + } + + // 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::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::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } + + std::cout << "OK" << std::endl; + } + + // 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::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::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } + + std::cout << "OK" << std::endl; + } + + // 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::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::cerr << "Check failed - " << std::endl << in.test_description << std::endl; + } + + std::cout << "OK" << std::endl; + } +} + +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; + 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"; + const int k = 2; + + const main_input_t in = {k, n}; + RC rc = grb::Launcher< AUTOMATIC >().exec( &main_grb, in, rc ); + + + if( rc != SUCCESS ) { + std::cout << "Test FAILED (" << grb::toString( rc ) << ")" << std::endl; + return rc; + } + + std::cout << "Test OK" << std::endl; + return 0; +} diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index 5b7dfc16a..81d0d3c63 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -280,6 +280,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