Skip to content

Commit adfdd1e

Browse files
authored
Resolving bug in grb::dot with nonblocking backend (#398)
The implementation of the dot-product for the nonblocking backend could iterate over different nonzero indices in its input vectors. This bug did not materialise always, and did not materialise if the dot-product was called with the dense descriptor. This MR extends the unit test of the dot product to test more challenging sparse nonzero patterns, especially patterns that differ amongst the inputs, and add tests with two vectors with partial overlap as well as with zero overlap. The thus-extended unit test was able to trigger the pre-existing bug, which this MR also addresses. In addressing the bug, furthermore, issue #408 was uncovered. Thanks to @GiovaGa for finding the bug, and for proposing an initial fix and unit test extension!
1 parent b314d4f commit adfdd1e

File tree

2 files changed

+90
-8
lines changed

2 files changed

+90
-8
lines changed

include/graphblas/nonblocking/blas1.hpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10398,6 +10398,9 @@ namespace grb {
1039810398
// internal namespace for implementation of grb::dot
1039910399
namespace internal {
1040010400

10401+
/**
10402+
* Invariant: x should have fewer nonzeroes than y
10403+
*/
1040110404
template<
1040210405
Descriptor descr,
1040310406
#ifdef GRB_BOOLEAN_DISPATCHER
@@ -10432,6 +10435,7 @@ namespace grb {
1043210435
#else
1043310436
(void) upper_bound;
1043410437
#endif
10438+
assert( local_x.nonzeroes() <= local_y.nonzeroes() );
1043510439

1043610440
// get raw alias
1043710441
const InputType1 * __restrict__ a = internal::getRaw( x );
@@ -10450,8 +10454,7 @@ namespace grb {
1045010454

1045110455
// prepare registers
1045210456
for( size_t k = 0; k < AnyOp::blocksize; ++k, ++i ) {
10453-
mask[ k ] = already_dense_input_x ||
10454-
local_x.assigned( already_dense_input_y ? i : local_y.index( i ) );
10457+
mask[ k ] = already_dense_input_x || local_y.assigned(local_x.index( i ));
1045510458
}
1045610459

1045710460
// rewind
@@ -10461,9 +10464,9 @@ namespace grb {
1046110464
for( size_t k = 0; k < AnyOp::blocksize; ++k, ++i ) {
1046210465
if( mask[ k ] ) {
1046310466
xx[ k ] = static_cast< typename AnyOp::D1 >(
10464-
a[ ( already_dense_input_y ? i : local_y.index( i ) ) + lower_bound ] );
10467+
a[ ( already_dense_input_x ? i : local_x.index( i ) ) + lower_bound ] );
1046510468
yy[ k ] = static_cast< typename AnyOp::D2 >(
10466-
b[ ( already_dense_input_y ? i : local_y.index( i ) ) + lower_bound ] );
10469+
b[ ( already_dense_input_x ? i : local_x.index( i ) ) + lower_bound ] );
1046710470
}
1046810471
}
1046910472

@@ -10504,9 +10507,9 @@ namespace grb {
1050410507
for( ; i < local_nz; ++i ) {
1050510508
typename AddMonoid::D3 temp =
1050610509
addMonoid.template getIdentity< typename AddMonoid::D3 >();
10507-
const size_t index = ( already_dense_input_y ? i : local_y.index( i ) ) +
10510+
const size_t index = ( already_dense_input_x ? i : local_x.index( i ) ) +
1050810511
lower_bound;
10509-
if( already_dense_input_x || local_x.assigned( index - lower_bound ) ) {
10512+
if( already_dense_input_y || local_y.assigned( index - lower_bound ) ) {
1051010513
apply( temp, a[ index ], b[ index ], anyOp );
1051110514
foldr( temp, thread_local_output, addMonoid.getOperator() );
1051210515
}
@@ -10658,7 +10661,9 @@ namespace grb {
1065810661
already_dense_input_y, already_dense_input_x,
1065910662
array_reduced[ thread_id ],
1066010663
lower_bound, upper_bound,
10661-
local_y, local_x, x, y, local_y_nz,
10664+
local_y, local_x,
10665+
x, y,
10666+
local_y_nz,
1066210667
addMonoid, anyOp
1066310668
);
1066410669
}

tests/unit/dot.cpp

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void grb_program( const size_t &n, grb::RC &rc ) {
2727

2828
// repeatedly used containers
2929
grb::Vector< bool > even_mask( n );
30+
grb::Vector< bool > odd_mask( n );
3031
grb::Vector< size_t > temp( n );
3132
grb::Vector< double > left( n );
3233
grb::Vector< double > right( n );
@@ -42,7 +43,14 @@ void grb_program( const size_t &n, grb::RC &rc ) {
4243
}, temp );
4344
rc = rc ? rc : grb::set( even_mask, temp, true );
4445
if( rc != grb::SUCCESS ) {
45-
std::cerr << "\t initialisation of mask FAILED\n";
46+
std::cerr << "\t initialisation of even mask FAILED\n";
47+
return;
48+
}
49+
50+
// create odd mask
51+
rc = grb::set< grb::descriptors::invert_mask >( odd_mask, temp, true );
52+
if( rc != grb::SUCCESS ) {
53+
std::cerr << "\t initialisation of odd mask FAILED\n";
4654
return;
4755
}
4856

@@ -160,6 +168,75 @@ void grb_program( const size_t &n, grb::RC &rc ) {
160168
return;
161169
}
162170

171+
// test 5, init
172+
rc = rc ? rc : grb::clear( x );
173+
rc = rc ? rc : grb::clear( y );
174+
assert( rc == grb::SUCCESS );
175+
int true_dot = 0;
176+
alpha = 0;
177+
178+
rc = rc ? rc : grb::set< grb::descriptors::use_index >( y, 0 );
179+
for( size_t i : {2,3,5,7,13,17,19,23,29} ){
180+
if( i >= n ) break;
181+
rc = rc ? rc : grb::setElement( x, 1, i );
182+
true_dot += i;
183+
}
184+
assert( rc == grb::SUCCESS );
185+
186+
// test 5, exec
187+
rc = grb::dot( alpha, x, y, intRing );
188+
if( rc != SUCCESS ) {
189+
std::cerr << "\t test 5 (sparse non constant-value vectors) dot FAILED\n";
190+
return;
191+
}
192+
193+
// test 5, check
194+
if( alpha != true_dot ) {
195+
std::cerr << "\t test 5 (sparse non constant-value vectors) unexpected value "
196+
<< alpha << ", expected " << true_dot << ".\n";
197+
rc = FAILED;
198+
return;
199+
}
200+
201+
// test 6, init
202+
rc = grb::set( y, x );
203+
rc = rc ? rc : grb::set< grb::descriptors::use_index >( x, 0 );
204+
alpha = 0;
205+
206+
// test 6, exec
207+
rc = rc ? rc : grb::dot( alpha, x, y, intRing );
208+
if( rc != SUCCESS ) {
209+
std::cerr << "\t test 6 (swapped version of test 5) dot FAILED\n";
210+
return;
211+
}
212+
213+
// test 6, check
214+
if( alpha != true_dot ) {
215+
std::cerr << "\t test 6 (swapped version of test 5) unexpected value "
216+
<< alpha << ", expected " << true_dot << ".\n";
217+
rc = FAILED;
218+
return;
219+
}
220+
221+
// test 7, init
222+
rc = grb::set< grb::descriptors::use_index >( x, odd_mask, 0 );
223+
rc = rc ? rc : grb::set< grb::descriptors::use_index >( y, even_mask, 0 );
224+
alpha = 0;
225+
226+
// test 7, exec
227+
rc = rc ? rc : grb::dot( alpha, x, y, intRing );
228+
if( rc != SUCCESS ) {
229+
std::cerr << "\t test 7 (sparse dot no overlap) FAILED\n";
230+
return;
231+
}
232+
233+
// test 7, check
234+
if( alpha != 0 ) {
235+
std::cerr << "\t test 7 (sparse dot no overlap) unexpected value "
236+
<< alpha << ", expected 0.\n";
237+
rc = FAILED;
238+
return;
239+
}
163240
}
164241

165242
int main( int argc, char ** argv ) {

0 commit comments

Comments
 (0)