|
| 1 | + |
| 2 | +/* |
| 3 | + * Copyright 2021 Huawei Technologies Co., Ltd. |
| 4 | + * |
| 5 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | + * you may not use this file except in compliance with the License. |
| 7 | + * You may obtain a copy of the License at |
| 8 | + * |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * See the License for the specific language governing permissions and |
| 15 | + * limitations under the License. |
| 16 | + */ |
| 17 | + |
| 18 | +/* |
| 19 | + * @author Benjamin Lozes |
| 20 | + * @date 24th of May, 2023 |
| 21 | + * |
| 22 | + * @brief Test for eWiseApply(Matrix, Monoid) |
| 23 | + * and eWiseApply(Matrix, Operator) variants |
| 24 | + * |
| 25 | + * This test is meant to ensure the behaviour of the eWiseApply(Matrix, Monoid) |
| 26 | + * and eWiseApply(Matrix, Operator) variants is correct. Precisely, we expect |
| 27 | + * the following behaviour: |
| 28 | + * - eWiseApply(Matrix, Monoid) should apply the monoid to all elements of |
| 29 | + * the two matrices, INCLUDING the couples (non_zero, zero), using the |
| 30 | + * provided identity value for the zero elements. |
| 31 | + * - eWiseApply(Matrix, Operator) should apply the operator to all elements |
| 32 | + * of the two matrices, EXCLUDING the couples (non_zero, zero) |
| 33 | + * |
| 34 | + */ |
| 35 | + |
| 36 | +#include <iostream> |
| 37 | +#include <numeric> |
| 38 | +#include <sstream> |
| 39 | +#include <vector> |
| 40 | + |
| 41 | +#include <graphblas.hpp> |
| 42 | + |
| 43 | +#define _DEBUG |
| 44 | + |
| 45 | +using nz_type = int; |
| 46 | + |
| 47 | +constexpr size_t M = 10; |
| 48 | +constexpr size_t N = 10; |
| 49 | +constexpr nz_type A_INITIAL_VALUE = 1; |
| 50 | +constexpr nz_type B_INITIAL_VALUE = 3; |
| 51 | + |
| 52 | +namespace utils { |
| 53 | + template< class Iterator > |
| 54 | + void printSparseMatrixIterator( size_t rows, size_t cols, Iterator begin, Iterator end, const std::string & name = "", std::ostream & os = std::cout ) { |
| 55 | +#ifndef _DEBUG |
| 56 | + return; |
| 57 | +#endif |
| 58 | + std::cout << "Matrix \"" << name << "\" (" << rows << "x" << cols << "):" << std::endl << "[" << std::endl; |
| 59 | + if( rows > 50 || cols > 50 ) { |
| 60 | + os << " Matrix too large to print" << std::endl; |
| 61 | + } else { |
| 62 | + // os.precision( 3 ); |
| 63 | + for( size_t y = 0; y < rows; y++ ) { |
| 64 | + os << std::string( 3, ' ' ); |
| 65 | + for( size_t x = 0; x < cols; x++ ) { |
| 66 | + auto nnz_val = std::find_if( begin, end, [ y, x ]( const typename std::iterator_traits< Iterator >::value_type & a ) { |
| 67 | + return a.first.first == y && a.first.second == x; |
| 68 | + } ); |
| 69 | + if( nnz_val != end ) |
| 70 | + os << std::fixed << ( *nnz_val ).second; |
| 71 | + else |
| 72 | + os << '_'; |
| 73 | + os << " "; |
| 74 | + } |
| 75 | + os << std::endl; |
| 76 | + } |
| 77 | + } |
| 78 | + os << "]" << std::endl; |
| 79 | + std::flush( os ); |
| 80 | + } |
| 81 | + |
| 82 | + template< typename D > |
| 83 | + void printSparseMatrix( const grb::Matrix< D > & mat, const std::string & name = "", std::ostream & os = std::cout ) { |
| 84 | + grb::wait( mat ); |
| 85 | + printSparseMatrixIterator( grb::nrows( mat ), grb::ncols( mat ), mat.cbegin(), mat.cend(), name, os ); |
| 86 | + } |
| 87 | + |
| 88 | + template< typename D > |
| 89 | + bool equals_matrix( const grb::Matrix< D > & A, const grb::Matrix< D > & B ) { |
| 90 | + if( grb::nrows( A ) != grb::nrows( B ) || grb::ncols( A ) != grb::ncols( B ) ) |
| 91 | + return false; |
| 92 | + grb::wait( A ); |
| 93 | + grb::wait( B ); |
| 94 | + std::vector< std::pair< std::pair< size_t, size_t >, D > > A_vec( A.cbegin(), A.cend() ); |
| 95 | + std::vector< std::pair< std::pair< size_t, size_t >, D > > B_vec( B.cbegin(), B.cend() ); |
| 96 | + return std::is_permutation( A_vec.cbegin(), A_vec.cend(), B_vec.cbegin() ); |
| 97 | + } |
| 98 | +} // namespace utils |
| 99 | + |
| 100 | +template< class Monoid > |
| 101 | +struct input_t { |
| 102 | + const grb::Matrix< nz_type > & A; |
| 103 | + const grb::Matrix< nz_type > & B; |
| 104 | + const grb::Matrix< nz_type > & C_monoid; |
| 105 | + const grb::Matrix< nz_type > & C_operator; |
| 106 | + const Monoid & monoid; |
| 107 | + |
| 108 | + input_t( |
| 109 | + const grb::Matrix< nz_type > & A = {0,0}, |
| 110 | + const grb::Matrix< nz_type > & B = {0,0}, |
| 111 | + const grb::Matrix< nz_type > & C_monoid = {0,0}, |
| 112 | + const grb::Matrix< nz_type > & C_operator = {0,0}, |
| 113 | + const Monoid & monoid = Monoid() ) : |
| 114 | + A( A ), B( B ), C_monoid( C_monoid ), C_operator( C_operator ), monoid( monoid ) {} |
| 115 | +}; |
| 116 | + |
| 117 | +struct output_t { |
| 118 | + grb::RC rc; |
| 119 | +}; |
| 120 | + |
| 121 | +template< class Monoid > |
| 122 | +void grb_program( const input_t< Monoid > & input, output_t & output ) { |
| 123 | + static_assert( grb::is_monoid< Monoid >::value, "Monoid required" ); |
| 124 | + const auto & op = input.monoid.getOperator(); |
| 125 | + grb::wait( input.A ); |
| 126 | + grb::wait( input.B ); |
| 127 | + |
| 128 | + auto & rc = output.rc; |
| 129 | + |
| 130 | + utils::printSparseMatrix( input.A, "A" ); |
| 131 | + utils::printSparseMatrix( input.B, "B" ); |
| 132 | + |
| 133 | + { // Operator variant |
| 134 | + std::cout << "-- eWiseApply using Operator, supposed to be annihilating non-zeroes -> INTERSECTION\n"; |
| 135 | + grb::Matrix< nz_type > C( grb::nrows( input.A ), grb::ncols( input.A ) ); |
| 136 | + rc = grb::eWiseApply( C, input.A, input.B, op, grb::Phase::RESIZE ); |
| 137 | + grb::wait( C ); |
| 138 | + if( rc != grb::RC::SUCCESS ) { |
| 139 | + std::cerr << "Error: Phase::RESIZE\n"; |
| 140 | + return; |
| 141 | + } |
| 142 | + rc = grb::eWiseApply( C, input.A, input.B, op, grb::Phase::EXECUTE ); |
| 143 | + grb::wait( C ); |
| 144 | + if( rc != grb::RC::SUCCESS ) { |
| 145 | + std::cerr << "Error: Phase::EXECUTE\n"; |
| 146 | + return; |
| 147 | + } |
| 148 | + |
| 149 | + if( ! utils::equals_matrix( C, input.C_operator ) ) { |
| 150 | + std::cerr << "Error: Wrong result\n"; |
| 151 | + utils::printSparseMatrix( C, "Obtained (operator)", std::cerr ); |
| 152 | + utils::printSparseMatrix( input.C_operator, "Truth (operator)", std::cerr ); |
| 153 | + rc = grb::RC::FAILED; |
| 154 | + return; |
| 155 | + } |
| 156 | + |
| 157 | + std::cout << "Result (operator) is correct\n"; |
| 158 | + } |
| 159 | + |
| 160 | + { // Monoid variant |
| 161 | + std::cout << "-- eWiseApply using Monoid, supposed to consider non-zeroes as the identity -> UNION\n"; |
| 162 | + grb::Matrix< nz_type > C( grb::nrows( input.A ), grb::ncols( input.A ) ); |
| 163 | + rc = grb::eWiseApply( C, input.A, input.B, input.monoid, grb::Phase::RESIZE ); |
| 164 | + grb::wait( C ); |
| 165 | + if( rc != grb::RC::SUCCESS ) { |
| 166 | + std::cerr << "Error: Phase::RESIZE\n"; |
| 167 | + return; |
| 168 | + } |
| 169 | + rc = grb::eWiseApply( C, input.A, input.B, input.monoid, grb::Phase::EXECUTE ); |
| 170 | + grb::wait( C ); |
| 171 | + if( rc != grb::RC::SUCCESS ) { |
| 172 | + std::cerr << "Error: Phase::EXECUTE\n"; |
| 173 | + return; |
| 174 | + } |
| 175 | + |
| 176 | + if( ! utils::equals_matrix( C, input.C_monoid ) ) { |
| 177 | + std::cerr << "Error: Wrong result\n"; |
| 178 | + utils::printSparseMatrix( C, "Obtained (monoid)", std::cerr ); |
| 179 | + utils::printSparseMatrix( input.C_monoid, "Truth (monoid)", std::cerr ); |
| 180 | + rc = grb::RC::FAILED; |
| 181 | + return; |
| 182 | + } |
| 183 | + |
| 184 | + std::cout << "Result (monoid) is correct\n"; |
| 185 | + } |
| 186 | + |
| 187 | + rc = grb::RC::SUCCESS; |
| 188 | +} |
| 189 | + |
| 190 | +int main( int argc, char ** argv ) { |
| 191 | + (void) argc; |
| 192 | + (void) argv; |
| 193 | + |
| 194 | + if(argc > 1) std::cout << "Usage: " << argv[ 0 ] << std::endl; |
| 195 | + |
| 196 | + std::cout << "This is functional test " << argv[ 0 ] << std::endl; |
| 197 | + grb::Launcher< grb::EXEC_MODE::AUTOMATIC > launcher; |
| 198 | + grb::RC rc = grb::RC::SUCCESS; |
| 199 | + |
| 200 | + // Create input data |
| 201 | + /** Matrix A: Row matrix filled with A_INITIAL_VALUE |
| 202 | + * X X X X X |
| 203 | + * _ _ _ _ _ |
| 204 | + * _ _ _ _ _ (...) |
| 205 | + * _ _ _ _ _ |
| 206 | + * _ _ _ _ _ |
| 207 | + * (...) |
| 208 | + */ |
| 209 | + grb::Matrix< nz_type > A( M, N, N ); |
| 210 | + std::vector< size_t > A_rows( N, 0 ), A_cols( N, 0 ); |
| 211 | + std::vector< nz_type > A_values( N, A_INITIAL_VALUE ); |
| 212 | + std::iota( A_cols.begin(), A_cols.end(), 0 ); |
| 213 | + rc = grb::buildMatrixUnique( A, A_rows.data(), A_cols.data(), A_values.data(), A_values.size(), grb::IOMode::SEQUENTIAL ); |
| 214 | + assert( rc == grb::RC::SUCCESS ); |
| 215 | + |
| 216 | + /** Matrix B: Column matrix filled with B_INITIAL_VALUE |
| 217 | + * Y _ _ _ _ |
| 218 | + * Y _ _ _ _ |
| 219 | + * Y _ _ _ _ (...) |
| 220 | + * Y _ _ _ _ |
| 221 | + * Y _ _ _ _ |
| 222 | + * (...) |
| 223 | + */ |
| 224 | + grb::Matrix< nz_type > B( M, N, N ); |
| 225 | + std::vector< size_t > B_rows( M, 0 ), B_cols( M, 0 ); |
| 226 | + std::vector< nz_type > B_values( M, B_INITIAL_VALUE ); |
| 227 | + std::iota( B_rows.begin(), B_rows.end(), 0 ); |
| 228 | + rc = grb::buildMatrixUnique( B, B_rows.data(), B_cols.data(), B_values.data(), B_values.size(), grb::IOMode::SEQUENTIAL ); |
| 229 | + assert( rc == grb::RC::SUCCESS ); |
| 230 | + |
| 231 | + { |
| 232 | + /** Matrix C_monoid_truth: Union of A and B |
| 233 | + * X+Y X X X X |
| 234 | + * Y ___ ___ ___ ___ |
| 235 | + * Y ___ ___ ___ ___ (...) |
| 236 | + * Y ___ ___ ___ ___ |
| 237 | + * Y ___ ___ ___ ___ |
| 238 | + * (...) |
| 239 | + */ |
| 240 | + grb::Matrix< nz_type > C_monoid_truth( M, N ); |
| 241 | + size_t nvalues = grb::nrows( A ) + grb::ncols( B ) - 1; |
| 242 | + std::vector< size_t > C_monoid_truth_rows( nvalues, 0 ), C_monoid_truth_cols( nvalues, 0 ); |
| 243 | + std::vector< nz_type > C_monoid_truth_values( nvalues, 0 ); |
| 244 | + C_monoid_truth_values[ 0 ] = A_INITIAL_VALUE + B_INITIAL_VALUE; |
| 245 | + std::iota( C_monoid_truth_rows.begin() + grb::nrows( A ), C_monoid_truth_rows.end(), 1 ); |
| 246 | + std::iota( C_monoid_truth_cols.begin() + 1, C_monoid_truth_cols.begin() + grb::nrows( A ), 1 ); |
| 247 | + std::fill( C_monoid_truth_values.begin() + 1, C_monoid_truth_values.begin() + grb::nrows( A ), A_INITIAL_VALUE ); |
| 248 | + std::fill( C_monoid_truth_values.begin() + grb::nrows( A ), C_monoid_truth_values.end(), B_INITIAL_VALUE ); |
| 249 | + rc = grb::buildMatrixUnique( C_monoid_truth, C_monoid_truth_rows.data(), C_monoid_truth_cols.data(), C_monoid_truth_values.data(), C_monoid_truth_values.size(), grb::IOMode::SEQUENTIAL ); |
| 250 | + assert( rc == grb::RC::SUCCESS ); |
| 251 | + |
| 252 | + /** Matrix C_op_truth: Intersection of A and B |
| 253 | + * X+Y ___ ___ ___ ___ |
| 254 | + * ___ ___ ___ ___ ___ |
| 255 | + * ___ ___ ___ ___ ___ (...) |
| 256 | + * ___ ___ ___ ___ ___ |
| 257 | + * ___ ___ ___ ___ ___ |
| 258 | + * (...) |
| 259 | + */ |
| 260 | + grb::Matrix< nz_type > C_op_truth( M, N ); |
| 261 | + std::vector< size_t > C_op_truth_rows( 1, 0 ), C_op_truth_cols( 1, 0 ); |
| 262 | + std::vector< nz_type > C_op_truth_values( 1, A_INITIAL_VALUE + B_INITIAL_VALUE ); |
| 263 | + rc = grb::buildMatrixUnique( C_op_truth, C_op_truth_rows.data(), C_op_truth_cols.data(), C_op_truth_values.data(), C_op_truth_values.size(), grb::IOMode::SEQUENTIAL ); |
| 264 | + assert( rc == grb::RC::SUCCESS ); |
| 265 | + |
| 266 | + { /** Test using addition operator, same type for lhs and rhs |
| 267 | + */ |
| 268 | + input_t< grb::Monoid< grb::operators::add< nz_type >, grb::identities::zero > > input { A, B, C_monoid_truth, C_op_truth, |
| 269 | + grb::Monoid< grb::operators::add< nz_type >, grb::identities::zero >() }; |
| 270 | + output_t output { grb::RC::SUCCESS }; |
| 271 | + // Run the test |
| 272 | + rc = launcher.exec( &grb_program, input, output, false ); |
| 273 | + // Check the result |
| 274 | + assert( rc == grb::RC::SUCCESS ); |
| 275 | + if( output.rc != grb::RC::SUCCESS ) { |
| 276 | + std::cout << "Test FAILED (" << grb::toString( output.rc ) << ")" << std::endl; |
| 277 | + return 1; |
| 278 | + } |
| 279 | + } |
| 280 | + } |
| 281 | + |
| 282 | + std::cout << "Test OK" << std::endl; |
| 283 | + return 0; |
| 284 | +} |
0 commit comments