diff --git a/include/graphblas/bsp1d/blas2.hpp b/include/graphblas/bsp1d/blas2.hpp index e62c81971..c893accc5 100644 --- a/include/graphblas/bsp1d/blas2.hpp +++ b/include/graphblas/bsp1d/blas2.hpp @@ -90,6 +90,9 @@ namespace grb { return SUCCESS; } if( descr & descriptors::dense ) { + if( nnz( u ) < size( u ) ) { + return ILLEGAL; + } if( nnz( v ) < size( v ) ) { return ILLEGAL; } @@ -233,6 +236,9 @@ namespace grb { return SUCCESS; } if( descr & descriptors::dense ) { + if( nnz( u ) < size( u ) ) { + return ILLEGAL; + } if( nnz( v ) < size( v ) ) { return ILLEGAL; } diff --git a/include/graphblas/nonblocking/blas2.hpp b/include/graphblas/nonblocking/blas2.hpp index 448f80a02..6981b62bd 100644 --- a/include/graphblas/nonblocking/blas2.hpp +++ b/include/graphblas/nonblocking/blas2.hpp @@ -499,6 +499,19 @@ namespace grb { if( nnz( v ) < size( v ) ) { #ifdef _DEBUG std::cout << "\t Dense descriptor given but input vector was sparse\n"; +#endif + return ILLEGAL; + } + if( nnz( u ) < size( u ) ) { +#ifdef _DEBUG + std::cout << "\t Dense descriptor given but output vector was sparse\n"; +#endif + return ILLEGAL; + } + if( size( mask ) > 0 && nnz( mask ) < size( mask ) ) { +#ifdef _DEBUG + std::cout << "\t Dense descriptor given but output mask has sparse " + << "structure\n"; #endif return ILLEGAL; } diff --git a/include/graphblas/reference/blas2.hpp b/include/graphblas/reference/blas2.hpp index 7a4d3e135..cd8931014 100644 --- a/include/graphblas/reference/blas2.hpp +++ b/include/graphblas/reference/blas2.hpp @@ -951,6 +951,12 @@ namespace grb { if( nnz( v ) < size( v ) ) { #ifdef _DEBUG std::cout << "\t Dense descriptor given but input vector was sparse\n"; +#endif + return ILLEGAL; + } + if( nnz( u ) < size( u ) ) { +#ifdef _DEBUG + std::cout << "\t Dense descriptor given but output vector was sparse\n"; #endif return ILLEGAL; } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 5cd03cf77..40302ee5d 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -272,6 +272,10 @@ add_grb_executables( dense_spmv dense_spmv.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) +add_grb_executables( illegal_spmv illegal_spmv.cpp + BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking +) + add_grb_executables( moveMatrix moveMatrix.cpp BACKENDS reference reference_omp bsp1d hybrid hyperdags nonblocking ) diff --git a/tests/unit/illegal_spmv.cpp b/tests/unit/illegal_spmv.cpp new file mode 100644 index 000000000..d0d0823ca --- /dev/null +++ b/tests/unit/illegal_spmv.cpp @@ -0,0 +1,180 @@ + +/* + * 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 + +bool exec_tests( + const grb::Matrix< void > &A, const grb::Matrix< char > &B, + grb::Vector< bool > &out, grb::Vector< bool > &out2, + const grb::Vector< char > &in, const grb::Vector< char > &in2, + const size_t offset +) { + grb::semirings::boolean ring; + + // at least one of the vectors is sparse + assert( + grb::nnz( out ) < grb::size( out ) || + grb::nnz( out2 ) < grb::size( out2 ) || + grb::nnz( in ) < grb::size( in ) || + grb::nnz( in2 ) < grb::size( in2 ) + ); + + // test 1, exec + grb::RC rc = grb::mxv< grb::descriptors::dense >( out, A, in2, ring ); + if( rc != grb::ILLEGAL ) { + std::cerr << "\t test " << offset << " FAILED\n"; + return false; + } + + // test 2, exec + rc = grb::vxm< grb::descriptors::dense >( out2, in, A, ring ); + if( rc != grb::ILLEGAL ) { + std::cerr << "\t test " << (1 + offset) << " FAILED\n"; + return false; + } + + // test 3, exec + rc = grb::mxv< grb::descriptors::dense >( out2, B, in, ring ); + if( rc != grb::ILLEGAL ) { + std::cerr << "\t test " << (2 + offset) << " FAILED\n"; + return false; + } + + // test 4, exec + rc = grb::vxm< grb::descriptors::dense >( out, in2, B, ring ); + if( rc != grb::ILLEGAL ) { + std::cerr << "\t test " << (3 + offset) << " FAILED\n"; + return false; + } + + return true; +} + +void grb_program( const size_t &n, grb::RC &rc ) { + + // repeatedly used containers + grb::Matrix< void > A( n, 2 * n ); + grb::Matrix< char > B( 2 * n, n ); + grb::Vector< bool > out( n ), out2( 2 * n ); + grb::Vector< char > in( n ), in2( 2 * n ); + + // run tests 1-4 + if( !exec_tests( A, B, out, out2, in, in2, 1 ) ) { + rc = grb::FAILED; + return; + } + + // init tests 5-8 + rc = grb::setElement( out, true, 0 ); + rc = rc ? rc : grb::setElement( out2, true, 0 ); + if( rc != grb::SUCCESS ) { + std::cout << "Test batch 5-8: initialisation FAILED\n"; + return; + } + + // run tests 5-8 + if( !exec_tests( A, B, out, out2, in, in2, 5 ) ) { + rc = grb::FAILED; + return; + } + + // init tests 9-12 + rc = grb::set( in, 1 ); + rc = rc ? rc : grb::set( in2, 1 ); + rc = rc ? rc : grb::setElement( in, 0, n - 1 ); + rc = rc ? rc : grb::setElement( in2, 0, 2 * n - 1 ); + rc = rc ? rc : grb::set( out, in, false ); + rc = rc ? rc : grb::set( out2, in2, false ); + if( rc != grb::SUCCESS ) { + std::cout << "Test batch 9-12: initialisation FAILED\n"; + return; + } + + // run tests 9-12 + if( !exec_tests( A, B, out, out2, in, in2, 9 ) ) { + rc = grb::FAILED; + return; + } + + // init tests 13-16 + rc = rc ? rc : grb::set( in, out, 2 ); + rc = rc ? rc : grb::set( in2, out2, 3 ); + rc = rc ? rc : grb::set( out, true ); + rc = rc ? rc : grb::set( out2, true ); + if( rc != grb::SUCCESS ) { + std::cout << "Test batch 13-16: initialisation FAILED\n"; + return; + } + + // run tests 13-16 + if( !exec_tests( A, B, out, out2, in, in2, 13 ) ) { + rc = grb::FAILED; + return; + } +} + +int main( int argc, char ** argv ) { + // defaults + bool printUsage = false; + size_t in = 100; + + // error checking + if( argc > 2 ) { + printUsage = true; + } + if( argc == 2 ) { + size_t read; + std::istringstream ss( argv[ 1 ] ); + if( ! ( ss >> read ) ) { + std::cerr << "Error parsing first argument\n"; + printUsage = true; + } else if( ! ss.eof() ) { + std::cerr << "Error parsing first argument\n"; + printUsage = true; + } else if( read % 2 != 0 ) { + std::cerr << "Given value for n is odd\n"; + printUsage = true; + } else { + // all OK + in = read; + } + } + if( printUsage ) { + std::cerr << "Usage: " << argv[ 0 ] << " [n]\n"; + std::cerr << " -n (optional, default is 100): an even integer, the " + "test size.\n"; + return 1; + } + + std::cout << "This is functional test " << argv[ 0 ] << "\n"; + grb::Launcher< grb::AUTOMATIC > launcher; + grb::RC out; + if( launcher.exec( &grb_program, in, out, true ) != grb::SUCCESS ) { + std::cerr << "Launching test FAILED\n"; + return 255; + } + if( out != grb::SUCCESS ) { + std::cerr << "Test FAILED (" << grb::toString( out ) << ")" << std::endl; + } else { + std::cout << "Test OK" << std::endl; + } + return 0; +} + diff --git a/tests/unit/unittests.sh b/tests/unit/unittests.sh index 0b49d2421..70cb92dea 100755 --- a/tests/unit/unittests.sh +++ b/tests/unit/unittests.sh @@ -607,6 +607,20 @@ for MODE in ${MODES}; do fi echo " " + echo ">>> [x] [ ] Testing matrix times vector using at least one sparse" + echo " vector while the dense descriptor is set" + $runner ${TEST_BIN_DIR}/illegal_spmv_${MODE}_${BACKEND} &> ${TEST_OUT_DIR}/illegal_spmv_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/illegal_spmv_${MODE}_${BACKEND}_${P}_${T}.log + grep -i 'Test OK' ${TEST_OUT_DIR}/illegal_spmv_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" + echo " " + + echo ">>> [x] [ ] Testing matrix times vector using at least one sparse" + echo " vector while the dense descriptor is set (large vectors)" + $runner ${TEST_BIN_DIR}/illegal_spmv_${MODE}_${BACKEND} 100000 &> ${TEST_OUT_DIR}/illegal_spmv_large_${MODE}_${BACKEND}_${P}_${T}.log + head -1 ${TEST_OUT_DIR}/illegal_spmv_large_${MODE}_${BACKEND}_${P}_${T}.log + grep -i 'Test OK' ${TEST_OUT_DIR}/illegal_spmv_large_${MODE}_${BACKEND}_${P}_${T}.log || echo "Test FAILED" + echo " " + echo ">>> [x] [ ] Testing matrix times dense vector using the double (+,*)" echo " semiring where matrix elements are doubles and vector" echo " elements ints. The input matrix is taken from west0497."