From cbf7253af41b67d3faf447364ec84853483c527b Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Wed, 7 Jun 2023 17:02:59 +0200 Subject: [PATCH 1/5] SSSP algorithm signature + utils functions --- include/graphblas/algorithms/sssp.hpp | 187 ++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 include/graphblas/algorithms/sssp.hpp diff --git a/include/graphblas/algorithms/sssp.hpp b/include/graphblas/algorithms/sssp.hpp new file mode 100644 index 000000000..fa43f18fc --- /dev/null +++ b/include/graphblas/algorithms/sssp.hpp @@ -0,0 +1,187 @@ +/* + * Copyright 2023 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. + */ + +/** + * @brief SSSP (Single-Source Shortest-Path) algorithm. + * + * @author B. Lozes + * @date: June 05th, 2023 + */ + +#ifndef _H_GRB_SSSP +#define _H_GRB_SSSP + +#include +#include +#include + +#include + +#include + +#define _DEBUG + +namespace grb { + + namespace algorithms { + + namespace utils { + 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)rows; + (void)cols; + (void)begin; + (void)end; + (void)name; + (void)os; + +#ifdef _DEBUG + std::cout << "Matrix \"" << name << "\" (" << rows << "x" << cols << "):" << std::endl << "[" << std::endl; + if( rows > 50 || cols > 50 ) { + os << " 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; +#endif + } + + template< class Iterator > + void printSparsePatternMatrixIterator( size_t rows, size_t cols, Iterator begin, Iterator end, const std::string & name = "", std::ostream & os = std::cout ) { + (void)rows; + (void)cols; + (void)begin; + (void)end; + (void)name; + (void)os; + +#ifdef _DEBUG + std::cout << "Matrix \"" << name << "\" (" << rows << "x" << cols << "):" << std::endl << "[" << std::endl; + if( rows > 50 || cols > 50 ) { + os << " 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 == y && a.second == x; + } ); + if( nnz_val != end ) + os << "X"; + else + os << '_'; + os << " "; + } + os << std::endl; + } + } + os << "]" << std::endl; +#endif + } + + template< typename D > + void printSparseMatrix( const grb::Matrix< D > & mat, const std::string & name ) { + grb::wait( mat ); + printSparseMatrixIterator( grb::nrows( mat ), grb::ncols( mat ), mat.cbegin(), mat.cend(), name, std::cout ); + } + + template<> + void printSparseMatrix< void >( const grb::Matrix< void > & mat, const std::string & name ) { + grb::wait( mat ); + printSparsePatternMatrixIterator( grb::nrows( mat ), grb::ncols( mat ), mat.cbegin(), mat.cend(), name, std::cout ); + } + + template< typename D > + void printSparseVector( const grb::Vector< D > & v, const std::string & name ) { + (void)v; + (void)name; +#ifdef _DEBUG + grb::wait( v ); + std::cout << " [ "; + if( grb::size( v ) > 50 ) { + std::cout << "too large to print " << std::endl; + } else if( grb::nnz( v ) <= 0 ) { + for( size_t i = 0; i < grb::size( v ); i++ ) + std::cout << "_ "; + } else { + size_t nnz_idx = 0; + auto it = v.cbegin(); + for( size_t i = 0; i < grb::size( v ); i++ ) { + if( nnz_idx < grb::nnz( v ) && i == it->first ) { + std::cout << it->second << " "; + nnz_idx++; + if( nnz_idx < grb::nnz( v ) ) + ++it; + } else { + std::cout << "_ "; + } + } + } + std::cout << " ] - " + << "Vector \"" << name << "\" (" << grb::size( v ) << ")" << std::endl; +#endif + } + + template< typename T > + void printStdVector( const std::vector< T > & vector, const std::string & name ) { + (void)vector; + (void)name; +#ifdef _DEBUG + std::cout << " [ "; + if( vector.size() > 50 ) { + std::cout << "too large to print " << std::endl; + } else { + for( const T & e : vector ) + std::cout << e << " "; + } + std::cout << " ] - " + << "Vector \"" << name << "\" (" << vector.size() << ")" << std::endl; +#endif + } + + void debugPrint( const std::string & msg, std::ostream & os = std::cout ) { +#ifdef _DEBUG + os << msg; +#endif + } + } // namespace utils + + template< typename D > + grb::RC sssp( const Matrix< D > & A, size_t root, grb::Vector< size_t > & distances ) { + grb::RC rc = grb::RC::SUCCESS; + return rc; + } + + } // namespace algorithms + +} // namespace grb + +#endif // _H_GRB_SSSP From 9019dcf38564fd5ba3d9ca79f89f77bae5846f6f Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Wed, 7 Jun 2023 17:25:04 +0200 Subject: [PATCH 2/5] Smoke test for SSSP with hard-coded simple cases --- include/graphblas/algorithms/sssp.hpp | 15 +- tests/smoke/CMakeLists.txt | 5 + tests/smoke/smoketests.sh | 6 + tests/smoke/sssp.cpp | 239 ++++++++++++++++++++++++++ 4 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 tests/smoke/sssp.cpp diff --git a/include/graphblas/algorithms/sssp.hpp b/include/graphblas/algorithms/sssp.hpp index fa43f18fc..5cdec14da 100644 --- a/include/graphblas/algorithms/sssp.hpp +++ b/include/graphblas/algorithms/sssp.hpp @@ -174,9 +174,20 @@ namespace grb { } } // namespace utils - template< typename D > - grb::RC sssp( const Matrix< D > & A, size_t root, grb::Vector< size_t > & distances ) { + template< + typename D, + typename std::enable_if< std::is_arithmetic< D >::value, int >::type = 0 + > + grb::RC sssp( const Matrix< D > & A, size_t root, grb::Vector< D > & distances ) { grb::RC rc = grb::RC::SUCCESS; + + // Resize the output vector and fill it with -1, except for the root node which is set to 0 + rc = rc ? rc : grb::resize( distances, grb::nrows( A ) ); + rc = rc ? rc : grb::set( distances, -1L ); + rc = rc ? rc : grb::setElement( distances, root, 0 ); + + + return rc; } diff --git a/tests/smoke/CMakeLists.txt b/tests/smoke/CMakeLists.txt index 1f99446ee..3dd2159af 100644 --- a/tests/smoke/CMakeLists.txt +++ b/tests/smoke/CMakeLists.txt @@ -180,6 +180,11 @@ add_grb_executables( kcore_decomposition kcore_decomposition.cpp BACKENDS reference reference_omp hyperdags nonblocking bsp1d hybrid ) +add_grb_executables( sssp sssp.cpp + ADDITIONAL_LINK_LIBRARIES test_utils_headers + BACKENDS reference reference_omp hyperdags nonblocking +) + # targets to list and build the test for this category get_property( smoke_tests_list GLOBAL PROPERTY tests_category_smoke ) add_custom_target( "list_tests_category_smoke" diff --git a/tests/smoke/smoketests.sh b/tests/smoke/smoketests.sh index cd2025dab..902feac54 100755 --- a/tests/smoke/smoketests.sh +++ b/tests/smoke/smoketests.sh @@ -296,6 +296,12 @@ for BACKEND in ${BACKENDS[@]}; do fi echo " " + echo ">>> [x] [ ] Testing the Single-Source Shortest Path algorithm" + $runner ${TEST_BIN_DIR}/sssp_${BACKEND} &> ${TEST_OUT_DIR}/sssp_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/sssp_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/sssp_${BACKEND}_${P}_${T}.log || echo "Test FAILED" + echo " " + for ((i=0;i<${#LABELTEST_SIZES[@]};++i)); do LABELTEST_SIZE=${LABELTEST_SIZES[i]} diff --git a/tests/smoke/sssp.cpp b/tests/smoke/sssp.cpp new file mode 100644 index 000000000..385d66528 --- /dev/null +++ b/tests/smoke/sssp.cpp @@ -0,0 +1,239 @@ +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +template< typename T > +grb::Vector< T > stdToGrbVector( const std::vector< T > & in ) { + grb::Vector< T > out( in.size() ); + for( size_t i = 0; i < in.size(); i++ ) + grb::setElement( out, in[ i ], i ); + return out; +} + +template< typename T > +struct input_t { + grb::Matrix< T > A; + size_t root; + const grb::Vector< T > & expected_distances; +}; + +struct output_t { + grb::RC rc = grb::RC::SUCCESS; + grb::utils::TimerResults times; + size_t data_in_local; +}; + +template< typename T > +void grbProgram( const struct input_t< T > & input, struct output_t & output ) { + std::cout << std::endl << "Running SSSP" << std::endl; + grb::utils::Timer timer; + + grb::Vector< T > distances( grb::nrows( input.A ) ); + + timer.reset(); + output.rc = output.rc ? output.rc : grb::algorithms::sssp( input.A, input.root, distances ); + timer.reset(); + + // Check distances by comparing it with the expected one + if( std::equal( input.expected_distances.cbegin(), input.expected_distances.cend(), distances.cbegin() ) ) { + std::cout << "SUCCESS: distances are correct" << std::endl; + } else { + std::cerr << "FAILED: distances are incorrect" << std::endl; + std::cerr << "distances != expected_distances" << std::endl; + for( size_t i = 0; i < grb::nrows( input.A ); i++ ) + std::cerr << std::string( 3, ' ' ) << distances[ i ] << " | " << input.expected_distances[ i ] << std::endl; + output.rc = grb::RC::FAILED; + } +} + +int main( int argc, char ** argv ) { + (void)argc; + (void)argv; + constexpr size_t niterations = 1; + + grb::Benchmarker< grb::EXEC_MODE::AUTOMATIC > benchmarker; + std::cout << "Test executable: " << argv[ 0 ] << std::endl; + + // Check if we are testing on a file + if( argc != 1 && argc != 4 ) { + std::cerr << "Usage: \n\t" << argv[ 0 ] << " [ ]" << std::endl; + return 1; + } + bool test_on_file = ( argc == 4 ); + + if( test_on_file ) { // Test on a file + std::string graph_filepath( argv[ 1 ] ); + size_t root = std::stoul( argv[ 2 ] ); + std::string expected_distances_filepath( argv[ 3 ] ); + + std::cout << "-- Running test on file " << graph_filepath << std::endl; + + // Read matrix from file + grb::utils::MatrixFileReader< double > reader( graph_filepath, false, true ); + size_t r = reader.n(), c = reader.m(); + assert( r == c ); + grb::Matrix< double > A( r, c ); + grb::RC rc_build = buildMatrixUnique( A, reader.cbegin( grb::IOMode::SEQUENTIAL ), reader.cend( grb::IOMode::SEQUENTIAL ), grb::IOMode::PARALLEL ); + if( rc_build != grb::RC::SUCCESS ) { + std::cerr << "ERROR during buildMatrixUnique: rc = " << rc_build << std::endl; + return 1; + } + std::cout << "Matrix read successfully" << std::endl; + grb::Vector< double > expected_distances( r ); + // TODO: Read expected_distances vector from file + // Run the algorithm + input_t< double > input { A, root, expected_distances }; + output_t output; + grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc ) { + std::cerr << "ERROR during execution on file " << graph_filepath << ": rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + } else { + + /** Matrix A1: + * + * Schema: + * 0 ----- 1 + * | \ + * | \ + * | \ + * 2 3 + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< double > expected_distances { 0, 1, 1, 1 }; + std::cout << "-- Running test on A1 (directed, root " + std::to_string( root ) + ")" << std::endl; + grb::Matrix< double > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 0 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< double > A_values( A_rows.size(), 1 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); + input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + + /** Matrix A2: + * + * Schema: + * 0 ----- 2 ----- 3 + * | + * | + * | + * 1 + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< double > expected_distances { 0, 1, 1, 2 }; + std::cout << "-- Running test on A2 (directed, root " + std::to_string( root ) + ")" << std::endl; + grb::Matrix< double > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< double > A_values( A_rows.size(), 1 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); + input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + + /** Matrix A3: + * + * Schema: + * + * 0 ----- 1 ----- 2 ----- 3 + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< double > expected_distances { 0, 1, 2, 3 }; + std::cout << "-- Running test on A2.1 (directed, root " + std::to_string( root ) + ")" << std::endl; + grb::Matrix< double > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< double > A_values( A_rows.size(), 1 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); + input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + { // Directed version, root = 0, uniform weights = 10 + size_t root = 0; + std::vector< double > expected_distances { 0, 10, 10, 30 }; + std::cout << "-- Running test on A2.2 (directed, root " + std::to_string( root ) + ")" << std::endl; + grb::Matrix< double > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< double > A_values( A_rows.size(), 10 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); + input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + } + std::cout << "Test OK" << std::endl; + + return 0; +} From 1b167f472b878f6ddd3e6e3d591586120e071939 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 18 Jul 2023 12:11:43 +0200 Subject: [PATCH 3/5] SSSP implementation draft --- include/graphblas/algorithms/sssp.hpp | 156 ++++++++++++++-- tests/smoke/smoketests.sh | 6 +- tests/smoke/sssp.cpp | 239 ------------------------- tests/unit/CMakeLists.txt | 5 + tests/unit/sssp.cpp | 247 ++++++++++++++++++++++++++ tests/unit/unittests.sh | 7 + 6 files changed, 399 insertions(+), 261 deletions(-) create mode 100644 tests/unit/sssp.cpp diff --git a/include/graphblas/algorithms/sssp.hpp b/include/graphblas/algorithms/sssp.hpp index 5cdec14da..8f1e42c3f 100644 --- a/include/graphblas/algorithms/sssp.hpp +++ b/include/graphblas/algorithms/sssp.hpp @@ -133,16 +133,18 @@ namespace grb { std::cout << "_ "; } else { size_t nnz_idx = 0; - auto it = v.cbegin(); for( size_t i = 0; i < grb::size( v ); i++ ) { - if( nnz_idx < grb::nnz( v ) && i == it->first ) { - std::cout << it->second << " "; - nnz_idx++; - if( nnz_idx < grb::nnz( v ) ) - ++it; - } else { - std::cout << "_ "; - } + if( nnz_idx < grb::nnz( v ) ) { + auto found = std::find_if( v.cbegin(), v.cend(), [ i ]( const std::pair & a ) { + return a.first == i; + } ); + if( found != v.cend() ){ + std::cout << std::showpos << found->second << " "; + nnz_idx++; + continue; + } + } + std::cout << "__ "; } } std::cout << " ] - " @@ -174,19 +176,139 @@ namespace grb { } } // namespace utils - template< + /** + * Single-source-shortest-path (SSSP) algorithm. + * + * This version computes the minimum distance from the root to each vertex. + * + * @tparam D Matrix values type + * @tparam T Distance type + * + * + * @param[in] A Matrix to explore + * @param[in] root Root vertex from which to start the exploration + * @param[out] explored_all Whether all vertices have been explored + * @param[out] max_level Maximum level reached by the BFS algorithm + * @param[out] distances Vector containing the minumum distance to + * reach each vertex. + * Needs to be pre-allocated with nrows(A) values. + * @param[in] x Buffer vector, needs to be pre-allocated + * with 1 value. + * @param[in] y Buffer vector, no pre-allocation needed. + * @param[in] max_iterations Max number of iterations to perform + * (default: -1, no limit) + * @param[in] not_find_distance Distance to use for vertices that have + * not been reached (default: -1) + * + * \parblock + * \par Possible output values: + * -# max_level: [0, nrows(A) - 1] + * -# distances: - [0, nrows(A) - 1] for reached vertices, + * - not_find_distance for unreached vertices + * \endparblock + * + * \warning Distance type T must be a signed integer type. + * + * \note The matrix A can be a pattern matrix, in which case + * the identity of the semiring is used as the weight of each edge. + * + * \note The distance to the root is set to zero. + */ + template< + Descriptor descr = descriptors::no_operation, typename D, - typename std::enable_if< std::is_arithmetic< D >::value, int >::type = 0 + typename T = long, + class MinAddSemiring = Semiring< + operators::min< T >, + operators::add< T >, + identities::infinity, + identities::zero + >, + class MaxMonoid = Monoid< + operators::max< T >, + identities::negative_infinity + >, + class MinNegativeMonoid = Monoid< + operators::min< T >, + identities::zero + > > - grb::RC sssp( const Matrix< D > & A, size_t root, grb::Vector< D > & distances ) { - grb::RC rc = grb::RC::SUCCESS; + RC sssp( + const Matrix< D > &A, + size_t root, + bool &explored_all, + size_t &max_level, + Vector< T > &distances, + Vector< T > &x, + Vector< T > &y, + const long max_iterations = -1L, + const T not_find_distance = std::numeric_limits< T >::max(), + const MinAddSemiring semiring = MinAddSemiring(), + const MaxMonoid max_monoid = MaxMonoid(), + const MinNegativeMonoid min_negative_monoid = MinNegativeMonoid(), + const std::enable_if< + std::is_arithmetic< T >::value + && is_semiring< MinAddSemiring >::value + && is_monoid< MaxMonoid >::value + && is_monoid< MinNegativeMonoid >::value, + void + > * const = nullptr + ) { + RC rc = SUCCESS; + const size_t nvertices = nrows( A ); + utils::printSparseMatrix( A, "A" ); + + assert( nrows( A ) == ncols( A ) ); + assert( size( x ) == nvertices ); + assert( size( y ) == nvertices ); + assert( capacity( x ) >= 1 ); + assert( capacity( y ) >= 0 ); // Resize the output vector and fill it with -1, except for the root node which is set to 0 - rc = rc ? rc : grb::resize( distances, grb::nrows( A ) ); - rc = rc ? rc : grb::set( distances, -1L ); - rc = rc ? rc : grb::setElement( distances, root, 0 ); + rc = rc ? rc : resize( distances, nrows( A ) ); + rc = rc ? rc : set( distances, not_find_distance ); + rc = rc ? rc : setElement( distances, root, static_cast< T >( 0 ) ); + utils::printSparseVector( distances, "distances" ); + + // Set x to the root node, initial distance is 0 + rc = rc ? rc : setElement( x, static_cast< T >( 0 ), root ); + utils::printSparseVector( x, "x" ); + rc = rc ? rc : set( y, static_cast< T >( 0 ) ); + utils::printSparseVector( y, "y" ); + + size_t max_iter = max_iterations < 0 ? nvertices : max_iterations; + max_level = 0; + explored_all = false; + for( size_t level = 1; level <= max_iter; level++ ) { +#ifdef _DEBUG + std::cout << "** Level " << level << ":" << std::endl << std::flush; +#endif + max_level = level; + + rc = rc ? rc : clear( y ); - + utils::printSparseVector( x, "x" ); + rc = rc ? rc : vxm< descr >( y, x, A, semiring, Phase::RESIZE ); + rc = rc ? rc : vxm< descr >( y, x, A, semiring, Phase::EXECUTE ); + utils::printSparseVector( y, "y" ); + + rc = rc ? rc : foldl( distances, y, y, operators::min< T >(), Phase::RESIZE ); + rc = rc ? rc : foldl( distances, distances, y, operators::min< T >(), Phase::EXECUTE ); + utils::printSparseVector( distances, "distances" ); + + T max_distance = 0; + rc = rc ? rc : foldl( max_distance, distances, max_monoid ); + if( max_distance < not_find_distance ) { + explored_all = true; +#ifdef _DEBUG + std::cout << "Explored " << level << " levels to discover all of the " + << nvertices << " vertices.\n" << std::flush; +#endif + break; + } + + std::swap( x, y ); + } return rc; } diff --git a/tests/smoke/smoketests.sh b/tests/smoke/smoketests.sh index 902feac54..3a2bafc51 100755 --- a/tests/smoke/smoketests.sh +++ b/tests/smoke/smoketests.sh @@ -296,11 +296,7 @@ for BACKEND in ${BACKENDS[@]}; do fi echo " " - echo ">>> [x] [ ] Testing the Single-Source Shortest Path algorithm" - $runner ${TEST_BIN_DIR}/sssp_${BACKEND} &> ${TEST_OUT_DIR}/sssp_${BACKEND}_${P}_${T}.log - head -1 ${TEST_OUT_DIR}/sssp_${BACKEND}_${P}_${T}.log - grep 'Test OK' ${TEST_OUT_DIR}/sssp_${BACKEND}_${P}_${T}.log || echo "Test FAILED" - echo " " + # TODO: Add SSSP test here using a given dataset for ((i=0;i<${#LABELTEST_SIZES[@]};++i)); do diff --git a/tests/smoke/sssp.cpp b/tests/smoke/sssp.cpp index 385d66528..e69de29bb 100644 --- a/tests/smoke/sssp.cpp +++ b/tests/smoke/sssp.cpp @@ -1,239 +0,0 @@ -/* - * Copyright 2021 Huawei Technologies Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -template< typename T > -grb::Vector< T > stdToGrbVector( const std::vector< T > & in ) { - grb::Vector< T > out( in.size() ); - for( size_t i = 0; i < in.size(); i++ ) - grb::setElement( out, in[ i ], i ); - return out; -} - -template< typename T > -struct input_t { - grb::Matrix< T > A; - size_t root; - const grb::Vector< T > & expected_distances; -}; - -struct output_t { - grb::RC rc = grb::RC::SUCCESS; - grb::utils::TimerResults times; - size_t data_in_local; -}; - -template< typename T > -void grbProgram( const struct input_t< T > & input, struct output_t & output ) { - std::cout << std::endl << "Running SSSP" << std::endl; - grb::utils::Timer timer; - - grb::Vector< T > distances( grb::nrows( input.A ) ); - - timer.reset(); - output.rc = output.rc ? output.rc : grb::algorithms::sssp( input.A, input.root, distances ); - timer.reset(); - - // Check distances by comparing it with the expected one - if( std::equal( input.expected_distances.cbegin(), input.expected_distances.cend(), distances.cbegin() ) ) { - std::cout << "SUCCESS: distances are correct" << std::endl; - } else { - std::cerr << "FAILED: distances are incorrect" << std::endl; - std::cerr << "distances != expected_distances" << std::endl; - for( size_t i = 0; i < grb::nrows( input.A ); i++ ) - std::cerr << std::string( 3, ' ' ) << distances[ i ] << " | " << input.expected_distances[ i ] << std::endl; - output.rc = grb::RC::FAILED; - } -} - -int main( int argc, char ** argv ) { - (void)argc; - (void)argv; - constexpr size_t niterations = 1; - - grb::Benchmarker< grb::EXEC_MODE::AUTOMATIC > benchmarker; - std::cout << "Test executable: " << argv[ 0 ] << std::endl; - - // Check if we are testing on a file - if( argc != 1 && argc != 4 ) { - std::cerr << "Usage: \n\t" << argv[ 0 ] << " [ ]" << std::endl; - return 1; - } - bool test_on_file = ( argc == 4 ); - - if( test_on_file ) { // Test on a file - std::string graph_filepath( argv[ 1 ] ); - size_t root = std::stoul( argv[ 2 ] ); - std::string expected_distances_filepath( argv[ 3 ] ); - - std::cout << "-- Running test on file " << graph_filepath << std::endl; - - // Read matrix from file - grb::utils::MatrixFileReader< double > reader( graph_filepath, false, true ); - size_t r = reader.n(), c = reader.m(); - assert( r == c ); - grb::Matrix< double > A( r, c ); - grb::RC rc_build = buildMatrixUnique( A, reader.cbegin( grb::IOMode::SEQUENTIAL ), reader.cend( grb::IOMode::SEQUENTIAL ), grb::IOMode::PARALLEL ); - if( rc_build != grb::RC::SUCCESS ) { - std::cerr << "ERROR during buildMatrixUnique: rc = " << rc_build << std::endl; - return 1; - } - std::cout << "Matrix read successfully" << std::endl; - grb::Vector< double > expected_distances( r ); - // TODO: Read expected_distances vector from file - // Run the algorithm - input_t< double > input { A, root, expected_distances }; - output_t output; - grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc ) { - std::cerr << "ERROR during execution on file " << graph_filepath << ": rc = " << bench_rc << std::endl; - return bench_rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; - } - } else { - - /** Matrix A1: - * - * Schema: - * 0 ----- 1 - * | \ - * | \ - * | \ - * 2 3 - * - */ - { // Directed version, root = 0, uniform weights = 1 - size_t root = 0; - std::vector< double > expected_distances { 0, 1, 1, 1 }; - std::cout << "-- Running test on A1 (directed, root " + std::to_string( root ) + ")" << std::endl; - grb::Matrix< double > A( 4, 4 ); - std::vector< size_t > A_rows { { 0, 0, 0 } }; - std::vector< size_t > A_cols { { 1, 2, 3 } }; - std::vector< double > A_values( A_rows.size(), 1 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); - input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; - } - std::cout << std::endl; - } - - /** Matrix A2: - * - * Schema: - * 0 ----- 2 ----- 3 - * | - * | - * | - * 1 - * - */ - { // Directed version, root = 0, uniform weights = 1 - size_t root = 0; - std::vector< double > expected_distances { 0, 1, 1, 2 }; - std::cout << "-- Running test on A2 (directed, root " + std::to_string( root ) + ")" << std::endl; - grb::Matrix< double > A( 4, 4 ); - std::vector< size_t > A_rows { { 0, 0, 2 } }; - std::vector< size_t > A_cols { { 1, 2, 3 } }; - std::vector< double > A_values( A_rows.size(), 1 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); - input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; - } - std::cout << std::endl; - } - - /** Matrix A3: - * - * Schema: - * - * 0 ----- 1 ----- 2 ----- 3 - * - */ - { // Directed version, root = 0, uniform weights = 1 - size_t root = 0; - std::vector< double > expected_distances { 0, 1, 2, 3 }; - std::cout << "-- Running test on A2.1 (directed, root " + std::to_string( root ) + ")" << std::endl; - grb::Matrix< double > A( 4, 4 ); - std::vector< size_t > A_rows { { 0, 0, 2 } }; - std::vector< size_t > A_cols { { 1, 2, 3 } }; - std::vector< double > A_values( A_rows.size(), 1 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); - input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; - } - std::cout << std::endl; - } - { // Directed version, root = 0, uniform weights = 10 - size_t root = 0; - std::vector< double > expected_distances { 0, 10, 10, 30 }; - std::cout << "-- Running test on A2.2 (directed, root " + std::to_string( root ) + ")" << std::endl; - grb::Matrix< double > A( 4, 4 ); - std::vector< size_t > A_rows { { 0, 0, 2 } }; - std::vector< size_t > A_cols { { 1, 2, 3 } }; - std::vector< double > A_values( A_rows.size(), 10 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), grb::IOMode::PARALLEL ); - input_t< double > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - grb::RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; - } - std::cout << std::endl; - } - } - std::cout << "Test OK" << std::endl; - - return 0; -} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 16999fd42..ed2d18793 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -215,6 +215,11 @@ add_grb_executables( spy spy.cpp BACKENDS reference reference_omp hyperdags nonblocking ) +add_grb_executables( sssp_unit sssp.cpp + ADDITIONAL_LINK_LIBRARIES test_utils_headers + BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking +) + add_grb_executables( dense_spmv dense_spmv.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) diff --git a/tests/unit/sssp.cpp b/tests/unit/sssp.cpp new file mode 100644 index 000000000..0dcc196fc --- /dev/null +++ b/tests/unit/sssp.cpp @@ -0,0 +1,247 @@ +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +using namespace grb; +using weight_t = int; + +template< typename T > +Vector< T > stdToGrbVector( const std::vector< T > & in ) { + Vector< T > out( in.size() ); + for( size_t i = 0; i < in.size(); i++ ) + grb::setElement( out, in[ i ], i ); + return out; +} + +template< typename T > +struct input_t { + Matrix< T > A; + size_t root; + const Vector< T > & expected_distances; +}; + +struct output_t { + RC rc = SUCCESS; + utils::TimerResults times; + size_t data_in_local; +}; + +template< typename T > +void grbProgram( const struct input_t< T > & input, struct output_t & output ) { + std::cout << std::endl << "Running SSSP" << std::endl; + output.rc = SUCCESS; + utils::Timer timer; + + timer.reset(); + bool explored_all = false; + size_t max_level = 0; + Vector< T > distances( grb::nrows( input.A ) ); + Vector< T > x( grb::nrows( input.A ) ), y( grb::nrows( input.A ) ); + output.times.preamble = timer.time(); + + timer.reset(); + output.rc = algorithms::sssp( input.A, input.root, explored_all, max_level, distances, x, y ); + output.times.useful = timer.time(); + + // Check distances by comparing it with the expected one + if( std::equal( input.expected_distances.cbegin(), input.expected_distances.cend(), distances.cbegin() ) ) { + std::cout << "SUCCESS: distances are correct" << std::endl; + } else { + std::cerr << "FAILED: distances are incorrect" << std::endl; + std::cerr << "distances != expected_distances" << std::endl; + for( size_t i = 0; i < grb::nrows( input.A ); i++ ) + std::cerr << std::string( 3, ' ' ) << distances[ i ] << " | " << input.expected_distances[ i ] << std::endl; + output.rc = FAILED; + } +} + +int main( int argc, char ** argv ) { + (void)argc; + (void)argv; + constexpr size_t niterations = 1; + + grb::Benchmarker< grb::EXEC_MODE::AUTOMATIC > benchmarker; + std::cout << "Test executable: " << argv[ 0 ] << std::endl; + + // Check if we are testing on a file + if( argc != 1 && argc != 4 ) { + std::cerr << "Usage: \n\t" << argv[ 0 ] << " [ ]" << std::endl; + return 1; + } + bool test_on_file = ( argc == 4 ); + + /** Matrix A0: Fully connected acyclic graph + * + * Schema: + * 0 ----- 1 + * | \ / | + * | X | + * | / \ | + * 2 ----- 3 + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< weight_t > expected_distances { 0, 1, 1, 1 }; + std::cout << "-- Running test on A0 (undirected, acyclic, root " + std::to_string( root ) + ")" << std::endl; + Matrix< weight_t > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 } }; + std::vector< size_t > A_cols { { 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2 } }; + std::vector< weight_t > A_values( A_rows.size(), 1 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + + /** Matrix A1: + * + * Schema: + * 0 ----- 1 + * | \ + * | \ + * | \ + * 2 3 + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< weight_t > expected_distances { 0, 1, 1, 1 }; + std::cout << "-- Running test on A1 (directed, root " + std::to_string( root ) + ")" << std::endl; + Matrix< weight_t > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 0 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< weight_t > A_values( A_rows.size(), 1 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + + /** Matrix A2: + * + * Schema: + * 0 ----- 2 ----- 3 + * | + * | + * | + * 1 + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< weight_t > expected_distances { 0, 1, 1, 2 }; + std::cout << "-- Running test on A2 (directed, root " + std::to_string( root ) + ")" << std::endl; + Matrix< weight_t > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< weight_t > A_values( A_rows.size(), 1 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + + /** Matrix A3: + * + * Schema: + * + * 0 ----- 1 ----- 2 ----- 3 + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< weight_t > expected_distances { 0, 1, 2, 3 }; + std::cout << "-- Running test on A2.1 (directed, root " + std::to_string( root ) + ")" << std::endl; + Matrix< weight_t > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< weight_t > A_values( A_rows.size(), 1 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + { // Directed version, root = 0, uniform weights = 10 + size_t root = 0; + std::vector< weight_t > expected_distances { 0, 10, 10, 30 }; + std::cout << "-- Running test on A2.2 (directed, root " + std::to_string( root ) + ")" << std::endl; + Matrix< weight_t > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 3 } }; + std::vector< weight_t > A_values( A_rows.size(), 10 ); + grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; + output_t output; + RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); + if( bench_rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << output.rc << std::endl; + return output.rc; + } + std::cout << std::endl; + } + + std::cout << "Test OK" << std::endl; + + return 0; +} diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index 3817164c8..1acd419b5 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -637,6 +637,13 @@ for MODE in ${MODES}; do fi echo " " + echo ">>> [x] [ ] Testing the Single-Source-Shortest-Path (SSSP) algorithm on small pre-defined" + echo " graphs testing specific border cases." + $runner ${TEST_BIN_DIR}/sssp_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/sssp_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/sssp_${MODE}_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/sssp_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" + echo " " + #if [ "$BACKEND" = "reference_omp" ]; then # echo "Additional standardised unit tests not yet supported for the ${BACKEND} backend" # echo From b9d46bddd01699b9754b3b4a05582de5a6ff0838 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Tue, 18 Jul 2023 15:40:21 +0200 Subject: [PATCH 4/5] Implementation of the smoke-test + refactor --- tests/smoke/sssp.cpp | 155 +++++++++++++++++++++++++++++++++++++++++++ tests/unit/sssp.cpp | 63 +++++++++--------- 2 files changed, 187 insertions(+), 31 deletions(-) diff --git a/tests/smoke/sssp.cpp b/tests/smoke/sssp.cpp index e69de29bb..d02b33166 100644 --- a/tests/smoke/sssp.cpp +++ b/tests/smoke/sssp.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace grb; + +template< typename T > +Vector< T > stdToGrbVector( const std::vector< T > & in ) { + Vector< T > out( in.size() ); + for( size_t i = 0; i < in.size(); i++ ) + setElement( out, in[ i ], i ); + return out; +} + +template< typename T > +struct input_t { + const Matrix< T >& A; + size_t root; + const Vector< T > & expected_distances; + + // Empty constructor necessary for distributed backends + input_t( + const Matrix& A = {0,0}, + size_t root = 0, + const Vector< T > & expected_distances = {0} + ) : A( A ), root( root ), expected_distances( expected_distances ) {} +}; + +struct output_t { + RC rc = SUCCESS; + utils::TimerResults times; +}; + +template< typename T > +void grbProgram( const struct input_t< T > & input, struct output_t & output ) { + std::cout << std::endl << "Running SSSP" << std::endl; + output.rc = SUCCESS; + utils::Timer timer; + + timer.reset(); + bool explored_all = false; + size_t max_level = 0; + Vector< T > distances( grb::nrows( input.A ) ); + Vector< T > x( grb::nrows( input.A ) ), y( grb::nrows( input.A ) ); + output.times.preamble = timer.time(); + + timer.reset(); + output.rc = algorithms::sssp( input.A, input.root, explored_all, max_level, distances, x, y ); + output.times.useful = timer.time(); + + // Check distances by comparing it with the expected one + bool expected_equals = std::equal( + input.expected_distances.cbegin(), input.expected_distances.cend(), distances.cbegin() + ); + if( expected_equals ) { + std::cout << "SUCCESS: distances are correct" << std::endl; + } else { + std::cerr << "FAILED: distances are incorrect" << std::endl; + std::cerr << "distances != expected_distances" << std::endl; + for( size_t i = 0; i < grb::nrows( input.A ); i++ ) + std::cerr << std::string( 3, ' ' ) << distances[ i ] << " | " + << input.expected_distances[ i ] << std::endl; + output.rc = FAILED; + } +} + +int main( int argc, char ** argv ) { + (void)argc; + (void)argv; + + Benchmarker< AUTOMATIC > benchmarker; + std::cout << "Test executable: " << argv[ 0 ] << std::endl; + + if( argc < 4 ) { + std::cerr << "Usage: \n\t" + << argv[ 0 ] + << " " + << " " + << " " + << " " + << " [ inner_iterations=1 ]" + << " [ outer_iterations=1 ]" + << std::endl; + return 1; + } + std::string dataset( argv[ 1 ] ); + bool direct = ( strcmp( argv[ 2 ], "direct" ) == 0 ); + size_t root = std::stoul( argv[ 3 ] ); + std::string expected_distances_filepath( argv[ 4 ] ); + size_t inner_iterations = ( argc >= 5 ) ? std::stoul( argv[ 4 ] ) : 1; + size_t outer_iterations = ( argc >= 6 ) ? std::stoul( argv[ 5 ] ) : 1; + + std::cout << "-- Running test on file: " << dataset << std::endl; + + // Read matrix from file + utils::MatrixFileReader< double > reader( dataset, direct, true ); + size_t r = reader.n(), c = reader.m(); + assert( r == c ); + Matrix< double > A( r, c ); + RC rc_build = buildMatrixUnique( + A, reader.cbegin( SEQUENTIAL ), reader.cend( SEQUENTIAL ), SEQUENTIAL + ); + if( rc_build != SUCCESS ) { + std::cerr << "ERROR during buildMatrixUnique: rc = " + << grb::toString( rc_build ) << std::endl; + return 1; + } + std::cout << "Matrix read successfully" << std::endl; + + Vector< double > expected_distances( r ); + // TODO: Read expected_distances vector from file + + // Run the algorithm + input_t< double > input { A, root, expected_distances }; + output_t output; + RC bench_rc = benchmarker.exec( + &grbProgram, input, output, inner_iterations, outer_iterations, true + ); + if( bench_rc ) { + std::cerr << "ERROR during execution on file " << dataset + << ": rc = " << grb::toString(bench_rc) << std::endl; + return bench_rc; + } else if( output.rc ) { + std::cerr << "Test failed: rc = " << grb::toString(output.rc) << std::endl; + return output.rc; + } + + std::cerr << std::flush; + std::cout << "Test OK" << std::endl << std::flush; + + return 0; +} diff --git a/tests/unit/sssp.cpp b/tests/unit/sssp.cpp index 0dcc196fc..e75dfb367 100644 --- a/tests/unit/sssp.cpp +++ b/tests/unit/sssp.cpp @@ -17,14 +17,12 @@ #include #include #include +#include -#include - +#include #include #include #include - -#include #include using namespace grb; @@ -43,12 +41,18 @@ struct input_t { Matrix< T > A; size_t root; const Vector< T > & expected_distances; + + // Empty constructor necessary for distributed backends + input_t( + const Matrix& A = {0,0}, + size_t root = 0, + const Vector< T > & expected_distances = {0} + ) : A( A ), root( root ), expected_distances( expected_distances ) {} }; struct output_t { RC rc = SUCCESS; utils::TimerResults times; - size_t data_in_local; }; template< typename T > @@ -83,17 +87,14 @@ void grbProgram( const struct input_t< T > & input, struct output_t & output ) { int main( int argc, char ** argv ) { (void)argc; (void)argv; - constexpr size_t niterations = 1; - grb::Benchmarker< grb::EXEC_MODE::AUTOMATIC > benchmarker; + Launcher< AUTOMATIC > launcher; std::cout << "Test executable: " << argv[ 0 ] << std::endl; - // Check if we are testing on a file - if( argc != 1 && argc != 4 ) { - std::cerr << "Usage: \n\t" << argv[ 0 ] << " [ ]" << std::endl; + if( argc != 1 ) { + std::cerr << "Usage: \n\t" << argv[ 0 ] << std::endl; return 1; } - bool test_on_file = ( argc == 4 ); /** Matrix A0: Fully connected acyclic graph * @@ -116,10 +117,10 @@ int main( int argc, char ** argv ) { grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; output_t output; - RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; + RC rc = launcher.exec( &grbProgram, input, output, true ); + if( rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << rc << std::endl; + return rc; } else if( output.rc ) { std::cerr << "Test failed: rc = " << output.rc << std::endl; return output.rc; @@ -148,10 +149,10 @@ int main( int argc, char ** argv ) { grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; output_t output; - RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; + RC rc = launcher.exec( &grbProgram, input, output, true ); + if( rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << rc << std::endl; + return rc; } else if( output.rc ) { std::cerr << "Test failed: rc = " << output.rc << std::endl; return output.rc; @@ -180,10 +181,10 @@ int main( int argc, char ** argv ) { grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; output_t output; - RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; + RC rc = launcher.exec( &grbProgram, input, output, true ); + if( rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << rc << std::endl; + return rc; } else if( output.rc ) { std::cerr << "Test failed: rc = " << output.rc << std::endl; return output.rc; @@ -209,10 +210,10 @@ int main( int argc, char ** argv ) { grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; output_t output; - RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; + RC rc = launcher.exec( &grbProgram, input, output, true ); + if( rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << rc << std::endl; + return rc; } else if( output.rc ) { std::cerr << "Test failed: rc = " << output.rc << std::endl; return output.rc; @@ -230,10 +231,10 @@ int main( int argc, char ** argv ) { grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; output_t output; - RC bench_rc = benchmarker.exec( &grbProgram, input, output, niterations, 1, true ); - if( bench_rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << bench_rc << std::endl; - return bench_rc; + RC rc = launcher.exec( &grbProgram, input, output, true ); + if( rc != SUCCESS ) { + std::cerr << "ERROR during execution: rc = " << rc << std::endl; + return rc; } else if( output.rc ) { std::cerr << "Test failed: rc = " << output.rc << std::endl; return output.rc; From 32538cdaf487793d47e72f7a86b60979c46d7ed6 Mon Sep 17 00:00:00 2001 From: Benjamin Lozes Date: Mon, 22 Jan 2024 12:14:36 +0100 Subject: [PATCH 5/5] Allocation bug fix in the unit-test --- tests/unit/sssp.cpp | 230 ++++++++++++++++++++++------------------ tests/unit/unittests.sh | 6 +- 2 files changed, 131 insertions(+), 105 deletions(-) diff --git a/tests/unit/sssp.cpp b/tests/unit/sssp.cpp index e75dfb367..d31b4f952 100644 --- a/tests/unit/sssp.cpp +++ b/tests/unit/sssp.cpp @@ -14,15 +14,11 @@ * limitations under the License. */ -#include #include #include -#include #include #include -#include -#include #include using namespace grb; @@ -40,70 +36,51 @@ template< typename T > struct input_t { Matrix< T > A; size_t root; - const Vector< T > & expected_distances; + const std::vector< T >& expected_distances; // Empty constructor necessary for distributed backends - input_t( + explicit input_t( const Matrix& A = {0,0}, size_t root = 0, - const Vector< T > & expected_distances = {0} + const std::vector< T >& expected_distances = {} ) : A( A ), root( root ), expected_distances( expected_distances ) {} }; -struct output_t { - RC rc = SUCCESS; - utils::TimerResults times; -}; - template< typename T > -void grbProgram( const struct input_t< T > & input, struct output_t & output ) { +RC test_case( const struct input_t< T > & input ) { std::cout << std::endl << "Running SSSP" << std::endl; - output.rc = SUCCESS; - utils::Timer timer; + RC rc = SUCCESS; - timer.reset(); bool explored_all = false; size_t max_level = 0; Vector< T > distances( grb::nrows( input.A ) ); Vector< T > x( grb::nrows( input.A ) ), y( grb::nrows( input.A ) ); - output.times.preamble = timer.time(); - timer.reset(); - output.rc = algorithms::sssp( input.A, input.root, explored_all, max_level, distances, x, y ); - output.times.useful = timer.time(); + rc = rc ? rc : algorithms::sssp( input.A, input.root, explored_all, max_level, distances, x, y ); // Check distances by comparing it with the expected one - if( std::equal( input.expected_distances.cbegin(), input.expected_distances.cend(), distances.cbegin() ) ) { - std::cout << "SUCCESS: distances are correct" << std::endl; - } else { + bool equals_distances = true; + for( size_t i = 0; i < grb::nrows( input.A ) && equals_distances; i++ ) { + equals_distances &= (input.expected_distances[ i ] == distances[ i ]); + } + if( not equals_distances ) { std::cerr << "FAILED: distances are incorrect" << std::endl; std::cerr << "distances != expected_distances" << std::endl; for( size_t i = 0; i < grb::nrows( input.A ); i++ ) std::cerr << std::string( 3, ' ' ) << distances[ i ] << " | " << input.expected_distances[ i ] << std::endl; - output.rc = FAILED; + rc = FAILED; } -} -int main( int argc, char ** argv ) { - (void)argc; - (void)argv; + return rc; +} - Launcher< AUTOMATIC > launcher; - std::cout << "Test executable: " << argv[ 0 ] << std::endl; +void grb_test_suite( const void *, const size_t, RC& rc ) { - if( argc != 1 ) { - std::cerr << "Usage: \n\t" << argv[ 0 ] << std::endl; - return 1; - } - - /** Matrix A0: Fully connected acyclic graph + /** Matrix A0: Fully connected graph * - * Schema: - * 0 ----- 1 - * | \ / | - * | X | - * | / \ | - * 2 ----- 3 + * [0] [1] + * ├───────┤ + * [2] [3] * */ { // Directed version, root = 0, uniform weights = 1 @@ -114,21 +91,17 @@ int main( int argc, char ** argv ) { std::vector< size_t > A_rows { { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 } }; std::vector< size_t > A_cols { { 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2 } }; std::vector< weight_t > A_values( A_rows.size(), 1 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); - input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - RC rc = launcher.exec( &grbProgram, input, output, true ); + buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, expected_distances }; + rc = test_case(input ); if( rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << rc << std::endl; - return rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; + std::cerr << "Test failed: rc = " << rc << std::endl; + return; } std::cout << std::endl; } - /** Matrix A1: + /** Matrix A1: Node [0] connected to all other nodes * * Schema: * 0 ----- 1 @@ -137,6 +110,11 @@ int main( int argc, char ** argv ) { * | \ * 2 3 * + * [0] ──┬──▶ [1] + * │ │ + * ▼ ▼ + * [2] [3] + * */ { // Directed version, root = 0, uniform weights = 1 size_t root = 0; @@ -146,28 +124,22 @@ int main( int argc, char ** argv ) { std::vector< size_t > A_rows { { 0, 0, 0 } }; std::vector< size_t > A_cols { { 1, 2, 3 } }; std::vector< weight_t > A_values( A_rows.size(), 1 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); - input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - RC rc = launcher.exec( &grbProgram, input, output, true ); + buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, expected_distances }; + rc = test_case(input ); if( rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << rc << std::endl; - return rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; + std::cerr << "Test failed: rc = " << rc << std::endl; + return; } std::cout << std::endl; } /** Matrix A2: * - * Schema: - * 0 ----- 2 ----- 3 - * | - * | - * | - * 1 + * [0] ──▶ [2] ──▶ [3] + * │ + * ▼ + * [1] * */ { // Directed version, root = 0, uniform weights = 1 @@ -178,70 +150,124 @@ int main( int argc, char ** argv ) { std::vector< size_t > A_rows { { 0, 0, 2 } }; std::vector< size_t > A_cols { { 1, 2, 3 } }; std::vector< weight_t > A_values( A_rows.size(), 1 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); - input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - RC rc = launcher.exec( &grbProgram, input, output, true ); + buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, expected_distances }; + rc = test_case(input ); if( rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << rc << std::endl; - return rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; + std::cerr << "Test failed: rc = " << rc << std::endl; + return; } std::cout << std::endl; } /** Matrix A3: * - * Schema: - * - * 0 ----- 1 ----- 2 ----- 3 + * [0] ──▶ [1] ──▶ [2] ──▶ [3] * */ { // Directed version, root = 0, uniform weights = 1 size_t root = 0; std::vector< weight_t > expected_distances { 0, 1, 2, 3 }; - std::cout << "-- Running test on A2.1 (directed, root " + std::to_string( root ) + ")" << std::endl; + std::cout << "-- Running test on A3.1 (directed, root " + std::to_string( root ) + ")" << std::endl; Matrix< weight_t > A( 4, 4 ); - std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_rows { { 0, 1, 2 } }; std::vector< size_t > A_cols { { 1, 2, 3 } }; std::vector< weight_t > A_values( A_rows.size(), 1 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); - input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - RC rc = launcher.exec( &grbProgram, input, output, true ); + buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, expected_distances }; + rc = test_case(input ); if( rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << rc << std::endl; - return rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; + std::cerr << "Test failed: rc = " << rc << std::endl; + return; } std::cout << std::endl; } { // Directed version, root = 0, uniform weights = 10 size_t root = 0; - std::vector< weight_t > expected_distances { 0, 10, 10, 30 }; - std::cout << "-- Running test on A2.2 (directed, root " + std::to_string( root ) + ")" << std::endl; + std::vector< weight_t > expected_distances { 0, 10, 20, 30 }; + std::cout << "-- Running test on A3.2 (directed, root " + std::to_string( root ) + ")" << std::endl; Matrix< weight_t > A( 4, 4 ); - std::vector< size_t > A_rows { { 0, 0, 2 } }; + std::vector< size_t > A_rows { { 0, 1, 2 } }; std::vector< size_t > A_cols { { 1, 2, 3 } }; std::vector< weight_t > A_values( A_rows.size(), 10 ); - grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); - input_t< weight_t > input { A, root, stdToGrbVector( expected_distances ) }; - output_t output; - RC rc = launcher.exec( &grbProgram, input, output, true ); + buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, expected_distances }; + rc = test_case(input ); + if( rc != SUCCESS ) { + std::cerr << "Test failed: rc = " << rc << std::endl; + return; + } + std::cout << std::endl; + } + + /** Matrix A4: + * Graph A3 with a shortcut from [0] to [2] + * + * [0] ──▶ [1] ──▶ [2] ──▶ [3] + * │ ▲ + * └───────────────┘ + * + */ + { // Directed version, root = 0, uniform weights = 1 + size_t root = 0; + std::vector< weight_t > expected_distances { 0, 1, 1, 2 }; + std::cout << "-- Running test on A4.1 (directed, root " + std::to_string( root ) + ")" << std::endl; + Matrix< weight_t > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 1, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 2, 3 } }; + std::vector< weight_t > A_values( A_rows.size(), 1 ); + buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, expected_distances }; + rc = test_case(input ); + if( rc != SUCCESS ) { + std::cerr << "Test failed: rc = " << rc << std::endl; + return; + } + std::cout << std::endl; + } + { // Directed version, root = 0, uniform weights = 10 + size_t root = 0; + std::vector< weight_t > expected_distances { 0, 10, 10, 20 }; + std::cout << "-- Running test on A4.2 (directed, root " + std::to_string( root ) + ")" << std::endl; + Matrix< weight_t > A( 4, 4 ); + std::vector< size_t > A_rows { { 0, 0, 1, 2 } }; + std::vector< size_t > A_cols { { 1, 2, 2, 3 } }; + std::vector< weight_t > A_values( A_rows.size(), 10 ); + buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_rows.size(), PARALLEL ); + input_t< weight_t > input { A, root, expected_distances }; + rc = test_case( input ); if( rc != SUCCESS ) { - std::cerr << "ERROR during execution: rc = " << rc << std::endl; - return rc; - } else if( output.rc ) { - std::cerr << "Test failed: rc = " << output.rc << std::endl; - return output.rc; + std::cerr << "Test failed: rc = " << rc << std::endl; + return; } std::cout << std::endl; } +} +int main( int argc, char ** argv ) { + (void)argc; + (void)argv; + + Launcher< AUTOMATIC > launcher; + std::cout << "Test executable: " << argv[ 0 ] << std::endl; + + if( argc != 1 ) { + std::cerr << "Usage: \n\t" << argv[ 0 ] << std::endl; + return 1; + } + + RC success = SUCCESS; + RC execution_rc = launcher.exec( &grb_test_suite, nullptr, 0, success, true ); + if( execution_rc != SUCCESS ) { + std::cerr << "ERROR during execution: execution_rc is " + << toString(execution_rc) << std::endl; + return execution_rc; + } + + if( success != SUCCESS ) { + std::cerr << "Test FAILED. Return code (RC) is " << grb::toString(success) << std::endl; + return FAILED; + } std::cout << "Test OK" << std::endl; return 0; diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index 1acd419b5..260d72edf 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -639,9 +639,9 @@ for MODE in ${MODES}; do echo ">>> [x] [ ] Testing the Single-Source-Shortest-Path (SSSP) algorithm on small pre-defined" echo " graphs testing specific border cases." - $runner ${TEST_BIN_DIR}/sssp_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/sssp_${MODE}_${BACKEND}_${P}_${T}.log - head -1 ${TEST_OUT_DIR}/sssp_${MODE}_${BACKEND}_${P}_${T}.log - grep 'Test OK' ${TEST_OUT_DIR}/sssp_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" + $runner ${TEST_BIN_DIR}/sssp_unit_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/sssp_unit_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/sssp_unit_${MODE}_${BACKEND}_${P}_${T}.log + grep 'Test OK' ${TEST_OUT_DIR}/sssp_unit_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" echo " " #if [ "$BACKEND" = "reference_omp" ]; then