ALP User Documentation  0.8.preview
Algebraic Programming User Documentation
label.hpp
Go to the documentation of this file.
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 
27 #ifndef _H_GRB_LABEL
28 #define _H_GRB_LABEL
29 
30 #include <iostream>
31 
32 #include <graphblas.hpp>
33 
34 
35 namespace grb {
36 
37  namespace algorithms {
38 
39 #ifdef _DEBUG
40  constexpr size_t MaxPrinting = 20;
41  constexpr size_t MaxAnyPrinting = 100;
42 
43  // take a vector and display with a message
44  static void printVector(
45  const Vector< double > &v, const std::string message
46  ) {
47  size_t zeros = 0;
48  size_t ones = 0;
49  size_t size = grb::size( v );
50  if( size > MaxAnyPrinting ) {
51  return;
52  }
53  std::cerr << "\t " << message << ": ";
54  for( Vector< double >::const_iterator it = v.begin(); it != v.end(); ++it ) {
55  const std::pair< size_t, double > iter = *it;
56  const double val = iter.second;
57  if( val < INFINITY ) {
58  if( size > MaxPrinting ) {
59  zeros += ( val == 0 ) ? 1 : 0;
60  ones += ( val == 1 ) ? 1 : 0;
61  } else {
62  std::cerr << val << " ";
63  }
64  }
65  }
66  if( size > MaxPrinting ) {
67  std::cerr << zeros << " zeros; " << ones << " ones.";
68  }
69  std::cerr << "\n";
70  }
71 #endif
72 
122  template< typename IOType >
124  Vector< IOType > &out,
125  const Vector< IOType > &y, const Matrix< IOType > &W,
126  const size_t n, const size_t l,
127  const size_t maxIterations = 1000
128  ) {
129  // label propagation vectors and matrices operate over the real domain
130  Semiring<
133  > reals;
135  grb::Monoid<
138  > orMonoid;
139  const IOType zero = reals.template getZero< IOType >();
140 
141  // dynamic checks
142  if( nrows( W ) != n || ncols( W ) != n ||
143  size( y ) != n || size( out ) != n
144  ) {
145  return ILLEGAL;
146  }
147  if( capacity( out ) != n ) {
148  return ILLEGAL;
149  }
150  if( n == 0 ) {
151  return SUCCESS;
152  }
153  if( l == 0 ) {
154  return ILLEGAL;
155  }
156 
157  const size_t s = spmd<>::pid();
158 
159  // compute the diagonal matrix D from the weight matrix W
160  // we represent D as a vector so we can use it to generate the probabilities
161  // matrix P
162  Vector< IOType > multiplier( n );
163  RC ret = set( multiplier, static_cast< IOType >(1) ); // a vector of 1's
164 
165  Vector< IOType > diagonals( n );
166  ret = ret ? ret : set( diagonals, zero );
167  ret = ret ? ret : mxv< descriptors::dense >(
168  diagonals, W, multiplier, reals
169  ); // W*multiplier will sum each row
170 #ifdef _DEBUG
171  printVector( diagonals, "diagonals matrix in vector form" );
172 #endif
173 
174  // compute the probabilistic transition matrix P as inverse of D * W
175  // use diagonals vector to directly compute probabilistic transition matrix P
176  // only the existing non-zero elements in W will map to P
177  // the inverse of D is represented via the inverse element in the diagonals
178  // vector
179  //
180  // update: the application of Dinv is now done within a lambda following the
181  // mxv on the original matrix P
182 
183  // make diagonals equal its inverse
184  ret = ret ? ret : eWiseLambda( [ &diagonals ]( const size_t i ) {
185  diagonals[ i ] = 1.0 / diagonals[ i ];
186  }, diagonals
187  );
188 
189  // set up current and new solution functions
190  Vector< IOType > f( n );
191  Vector< IOType > fNext( n );
192  Vector< bool > mask( n );
193  for( size_t i = 0; ret == SUCCESS && i < l; ++i ) {
194  ret = setElement( mask, true, i );
195  }
196 
197  // fix f = y for the input set of labels
198  ret = ret ? ret : set( f, y );
199 
200  // whether two successive solutions are different
201  // initially set to true so that the computation may begin
202  bool different = true;
203  // compute f as P*f
204  // main loop completes when function f is stable
205  size_t iter = 1;
206  while( ret == SUCCESS && different && iter < maxIterations ) {
207 
208 #ifdef _DEBUG
209  if( n < MaxAnyPrinting ) {
210  std::cerr << "\t iteration " << iter << "\n";
211  }
212 
213  // fNext = P*f
214  std::cerr << "\t pre- set/mxv nnz( f ) = " << nnz( f ) << ", "
215  << "fNext = " << nnz( fNext ) << "\n";
216 #endif
217  ret = ret ? ret : set( fNext, zero );
218  ret = ret ? ret : mxv( fNext, W, f, reals );
219 #ifdef _DEBUG
220  std::cerr << "\t post-set/mxv nnz( f ) = " << nnz( f ) << ", "
221  << "nnz( fNext ) = " << nnz( fNext ) << "\n";
222  printVector( f, "Previous iteration solution" );
223  printVector( fNext, "New iteration solution" );
224 #endif
225 
226  // maintain the solution function in domain {0,1}
227  // can use the masked variant of vector assign when available ?
228  ret = ret ? ret : eWiseLambda( [ &fNext, &diagonals ]( const size_t i ) {
229  fNext[ i ] = ( fNext[ i ] * diagonals[ i ] < 0.5 ? 0 : 1 );
230  }, diagonals, fNext
231  );
232 #ifdef _DEBUG
233  printVector( fNext, "New iteration solution after threshold cutoff" );
234  std::cerr << "\t pre-set nnz( fNext ) = " << nnz( fNext ) << ", "
235  << "nnz( mask ) = " << nnz( mask ) << "\n";
236 #endif
237  // clamps the first l labelled nodes
238  ret = ret ? ret : foldl(
239  fNext, mask,
240  f,
242  );
243  assert( ret == SUCCESS );
244 #ifdef _DEBUG
245  std::cerr << "\t post-set nnz( fNext ) = " << nnz( fNext ) << "\n";
246  printVector(
247  fNext,
248  "New iteration solution after threshold cutoff and clamping"
249  );
250 #endif
251  // test for stability
252  different = false;
253  ret = ret ? ret : dot( different, f, fNext, orMonoid, notEqualOp );
254 
255  // update f for the next iteration
256 #ifdef _DEBUG
257  std::cerr << "\t pre-set nnz(f) = " << nnz( f ) << "\n";
258 #endif
259  std::swap( f, fNext );
260 #ifdef _DEBUG
261  std::cerr << "\t post-set nnz(f) = " << nnz( f ) << "\n";
262 #endif
263  // go to next iteration
264  (void) ++iter;
265  }
266 
267  if( ret == SUCCESS ) {
268  if( different ) {
269  if( s == 0 ) {
270  std::cerr << "Info: label propagation did not converge after "
271  << (iter-1) << " iterations\n";
272  }
273  return FAILED;
274  } else {
275  if( s == 0 ) {
276  std::cerr << "Info: label propagation converged in "
277  << (iter-1) << " iterations\n";
278  }
279  std::swap( out, f );
280  return SUCCESS;
281  }
282  }
283 
284  // done
285  if( s == 0 ) {
286  std::cerr << "Warning: label propagation exiting with " << toString(ret)
287  << "\n";
288  }
289  return ret;
290  }
291 
292  } // namespace algorithms
293 
294 } // namespace grb
295 
296 #endif // end _H_GRB_LABEL
297 
RC set(Vector< DataType, backend, Coords > &x, const T val, const Phase &phase=EXECUTE, const typename std::enable_if< !grb::is_object< DataType >::value &&!grb::is_object< T >::value, void >::type *const =nullptr) noexcept
Sets all elements of a vector to the given value.
Definition: io.hpp:858
Standard identity for numerical addition.
Definition: identities.hpp:57
A call to a primitive has determined that one of its arguments was illegal as per the specification o...
Definition: rc.hpp:143
An ALP/GraphBLAS matrix.
Definition: matrix.hpp:72
Operator that returns false whenever its inputs compare equal, and true otherwise.
Definition: ops.hpp:405
RC
Return codes of ALP primitives.
Definition: rc.hpp:47
A GraphBLAS vector.
Definition: vector.hpp:64
Standard identity for numerical multiplication.
Definition: identities.hpp:79
size_t nnz(const Vector< DataType, backend, Coords > &x) noexcept
Request the number of nonzeroes in a given vector.
Definition: io.hpp:479
This operator multiplies the two input parameters and writes the result to the output variable.
Definition: ops.hpp:208
size_t nrows(const Matrix< InputType, backend, RIT, CIT, NIT > &A) noexcept
Requests the row size of a given matrix.
Definition: io.hpp:286
Standard identitity for the logical or operator.
Definition: identities.hpp:151
size_t ncols(const Matrix< InputType, backend, RIT, CIT, NIT > &A) noexcept
Requests the column size of a given matrix.
Definition: io.hpp:339
RC foldl(IOType &x, const Vector< InputType, backend, Coords > &y, const Vector< MaskType, backend, Coords > &mask, const Monoid &monoid=Monoid(), const typename std::enable_if< !grb::is_object< IOType >::value &&!grb::is_object< InputType >::value &&!grb::is_object< MaskType >::value &&grb::is_monoid< Monoid >::value, void >::type *const =nullptr)
Reduces, or folds, a vector into a scalar.
Definition: blas1.hpp:3840
static size_t pid() noexcept
Definition: spmd.hpp:61
RC mxv(Vector< IOType, backend, Coords > &u, const Vector< InputType3, backend, Coords > &u_mask, const Matrix< InputType2, backend, RIT, CIT, NIT > &A, const Vector< InputType1, backend, Coords > &v, const Vector< InputType4, backend, Coords > &v_mask, const Semiring &semiring=Semiring(), const Phase &phase=EXECUTE, const typename std::enable_if< grb::is_semiring< Semiring >::value &&!grb::is_object< IOType >::value &&!grb::is_object< InputType1 >::value &&!grb::is_object< InputType2 >::value &&!grb::is_object< InputType3 >::value &&!grb::is_object< InputType4 >::value, void >::type *const =nullptr)
Right-handed in-place doubly-masked sparse matrix times vector multiplication, .
Definition: blas2.hpp:243
RC label(Vector< IOType > &out, const Vector< IOType > &y, const Matrix< IOType > &W, const size_t n, const size_t l, const size_t maxIterations=1000)
The label propagation algorithm.
Definition: label.hpp:123
Indicates when one of the grb::algorithms has failed to achieve its intended result,...
Definition: rc.hpp:154
This operator discards all left-hand side input and simply copies the right-hand side input to the ou...
Definition: ops.hpp:115
This operator takes the sum of the two input parameters and writes it to the output variable.
Definition: ops.hpp:175
The ALP/GraphBLAS namespace.
Definition: graphblas.hpp:477
The main header to include in order to use the ALP/GraphBLAS API.
RC dot(OutputType &z, const Vector< InputType1, backend, Coords > &x, const Vector< InputType2, backend, Coords > &y, const AddMonoid &addMonoid=AddMonoid(), const AnyOp &anyOp=AnyOp(), const Phase &phase=EXECUTE, const typename std::enable_if< !grb::is_object< OutputType >::value &&!grb::is_object< InputType1 >::value &&!grb::is_object< InputType2 >::value &&grb::is_monoid< AddMonoid >::value &&grb::is_operator< AnyOp >::value, void >::type *const =nullptr)
Calculates the dot product, , under a given additive monoid and multiplicative operator.
Definition: blas1.hpp:4056
RC eWiseLambda(const Func f, const Vector< DataType, backend, Coords > &x, Args...)
Executes an arbitrary element-wise user-defined function f on any number of vectors of equal length.
Definition: blas1.hpp:3746
size_t size(const Vector< DataType, backend, Coords > &x) noexcept
Request the size of a given vector.
Definition: io.hpp:235
Indicates the primitive has executed successfully.
Definition: rc.hpp:54
size_t capacity(const Vector< InputType, backend, Coords > &x) noexcept
Queries the capacity of the given ALP/GraphBLAS container.
Definition: io.hpp:388
The logical or.
Definition: ops.hpp:462
A generalised semiring.
Definition: semiring.hpp:190
RC setElement(Vector< DataType, backend, Coords > &x, const T val, const size_t i, const Phase &phase=EXECUTE, const typename std::enable_if< !grb::is_object< DataType >::value &&!grb::is_object< T >::value, void >::type *const =nullptr)
Sets the element of a given vector at a given position to a given value.
Definition: io.hpp:1129
std::string toString(const RC code)
A generalised monoid.
Definition: monoid.hpp:54