Flow123d
matrix_coo.hpp
Go to the documentation of this file.
1 #ifndef la_matrix_coo_h
2 #define la_matrix_coo_h
3 
4 #define ALLOWED_MEM_OVERHEAD 2.e+6 // entries, this corresponds to 32 MB of
5  // overhead in <int, int, double> matrixCoo
6 //------------------------------------------------------------------------------
7 #include <vector>
8 #include <cassert>
9 #include <cmath>
10 #include <algorithm>
11 #include <iomanip>
12 #include <numeric>
13 
14 //------------------------------------------------------------------------------
15 namespace la{
16  template< typename INDT, typename VALT >
17  class MatrixCoo;
18 }
19 
20 //------------------------------------------------------------------------------
21 /** \brief Temporary storage for a sparse matrix
22  * \details This object stores and handles matrix in coordinate format, i.e.,
23  * <row_index,col_index,value>-combinations for each matrix entry of a sparse matrix.
24  * Functionalities are:
25  * - preparing assembly of elements - allocating proper space to all elements
26  * in unassembled form
27  * - inserting an element - it is user's responsibility not to insert zeros
28  * - finishing assembly of elements - this is the core routine which
29  * - sorts triples, merges entries with the same pair of indices,
30  * truncates the array to assembled size
31  * - 'zeroing' a row and setting its diagonal entry to a given value
32  * - query of number of entries - this counts also zeros if they were inserted
33  * - get inside/outide-a-range counts of elements - for PETSc preallocation
34  * - filling the arrays for a row-compressed data storage for external
35  * solver libraries
36  * - computing inf norm
37  * - cleanup and writing
38  */
39 template< typename INDT, typename VALT >
40 class la::MatrixCoo
41 {
42 public:
43  typedef INDT IndexType;
44  typedef VALT ValueType;
45 
46 public:
47  MatrixCoo( unsigned nRows = 0, unsigned nCols = 0 )
48  : nRows_( nRows ),
49  nCols_( nCols ),
50  sorted_ ( false )
51  {
52  sortedSize_ = 0;
53  }
54 
55  ~MatrixCoo( ) {
56  matrixCooVec_.clear();
57  }
58 
59  //! Return number of non-zeros
60  IndexType nnz() const { return matrixCooVec_.size(); }
61 
62  //! Prepare matrix for assembly - allocate buffer for UNASSEMBLED entries
63  //! serial copies of element matrices to memory
64  //! length = numElements * size( elementMatrix )
65  void prepareAssembly( const IndexType length )
66  {
67  matrixCooVec_.reserve( length );
68 
69  return;
70  }
71 
72  //! Insert a value to unassembled vector
73  void insert( const IndexType i, const IndexType j, const ValueType value )
74  {
75  // combine index pair
76  IndexPair_ ip( i, j );
77 
78  // combine triples
79  Triple_ trip( ip, value );
80 
81  // insert it to buffer in any case
82  matrixCooVec_.push_back( trip );
83 
84  sorted_ = false;
85 
86  // resort if number of new unsorted entries excess a critical value
87  if ( (matrixCooVec_.size() - sortedSize_) > ALLOWED_MEM_OVERHEAD ) {
88  //std::cout << " Performing resorting due to the critical value ..." << std::endl;
89  this -> finishAssembly();
90  }
91 
92  return;
93  }
94  //! Finalize matrix assembly - build the assembled input and truncate matrix
96  {
97  // sort entries to make sure
98  std::sort( matrixCooVec_.begin( ), matrixCooVec_.end( ), TripleLessThan_( ) );
99 
100  // combine the sorted matrix into one vector
101  typename MatrixCooVec_::const_iterator itGlobale = matrixCooVec_.end();
102 
103  typename MatrixCooVec_::const_iterator itSelectb = matrixCooVec_.begin();
104  typename MatrixCooVec_::const_iterator itSelecte = itSelectb;
105 
106  Triple_ tmpTriple;
107 
108  typename MatrixCooVec_::iterator itStore = matrixCooVec_.begin();
109 
110  // for each pair of indices
111  while (itSelectb != itGlobale) {
112 
113  // find any change in indices
114  itSelecte = std::upper_bound( itSelectb, itGlobale, *itSelectb, TripleLessThan_( ) );
115 
116  // sum values in the range
117  tmpTriple = combineTriples_ ( itSelectb, itSelecte );
118 
119  // store new
120  *itStore = tmpTriple;
121  itStore++;
122 
123  // shift ranges
124  itSelectb = itSelecte;
125  }
126 
127  // determine number of nonzeros in assembled matrix
128  unsigned nnz = std::distance( matrixCooVec_.begin( ), itStore);
129 
130  // truncate the vector from the end
131  matrixCooVec_.resize( nnz );
132 
133  // mark the amount of sorted entries
134  sortedSize_ = nnz;
135 
136  sorted_ = true;
137 
138  return;
139  }
140 
141  //! Determine number of nonzeros within a block [low,high) - used for PETSc preallocation
143  std::vector<IndexType> & nnzRowsIn, std::vector<IndexType> & nnzRowsOut)
144  {
145  // sort entries to make sure
146  if ( not sorted_ ) {
147  this -> finishAssembly( );
148  }
149 
150  // run through all entries
151  typename MatrixCooVec_::const_iterator iter = matrixCooVec_.begin();
152  typename MatrixCooVec_::const_iterator itere = matrixCooVec_.end();
153 
154  for ( ; iter != itere; ++iter ) {
155 
156  IndexType row = (*iter).first.first;
157  IndexType col = (*iter).first.second;
158 
159  // check that it belongs to the interval
160  if ( row >= low and row < high ) {
161 
162  IndexType rowLoc = row - low;
163  // it is on my row, now where to put it
164 
165  if ( col >= low and col < high ) {
166  // it is inside the diagonal block
167  nnzRowsIn[rowLoc]++;
168  }
169  else {
170  // it is outside the diagonal block
171  nnzRowsOut[rowLoc]++;
172  }
173  }
174  }
175  }
176 
177  //! Get specific entry
178  void getEntry( IndexType index, IndexType & row, IndexType & col, ValueType & val )
179  {
180  row = matrixCooVec_[ index ].first.first;
181  col = matrixCooVec_[ index ].first.second;
182  val = matrixCooVec_[ index ].second;
183  }
184 
185  //! Zero a row and set diagonal to scalar
186  void zeroRow( const IndexType row, const ValueType factor )
187  {
188 
189  // sort entries to make sure
190  if ( not sorted_ ) {
191  this -> finishAssembly( );
192  }
193 
194  // erasing iterator
195  typename MatrixCooVec_::iterator tripEraseIter;
196  typename MatrixCooVec_::iterator tripEraseIterE;
197 
198  // find constrained row in matrix and set iterator there
199  tripEraseIter = std::lower_bound ( matrixCooVec_.begin(), matrixCooVec_.end(),
200  row, TripleRowIndexLessThan_( ) );
201 
202  // find last entry at the row
203  tripEraseIterE = std::upper_bound( tripEraseIter, matrixCooVec_.end(),
204  *tripEraseIter, TripleRowLessThan_( ) );
205 
206  // find diagonal
207  typename MatrixCooVec_::iterator tripEraseIterDiag;
208  tripEraseIterDiag = std::find_if( tripEraseIter, tripEraseIterE, TripleDiagEntry_( ) );
209 
210  // set row entries to zero
211  for ( ; tripEraseIter != tripEraseIterE; ++tripEraseIter ) {
212  (*tripEraseIter).second = static_cast< ValueType >( 0.0 );
213  }
214 
215  // put value from constraint on diagonal
216  Triple_ tmpTrip = std::make_pair( std::make_pair ( row, row ), factor );
217 
218  if ( tripEraseIterDiag != tripEraseIterE ) {
219  // diagonal entry already present
220  *tripEraseIterDiag = tmpTrip;
221  }
222  else {
223  // nothing on diagonal, add it
224  matrixCooVec_.push_back( tmpTrip );
225  sorted_ = false;
226  }
227  }
228 
229  //! Fill arrays with column indices, row pointers and values for
230  //! row-compressed data storage
232  std::vector<IndexType> & rowPtrs,
233  std::vector<ValueType> & values )
234  {
235 
236  // sort entries to make sure
237  if ( not sorted_ ) {
238  this -> finishAssembly( );
239  }
240 
241  // iterate over all elements and fill the nonzero,
242  // column index, and row pointer arrys
243  typename MatrixCooVec_::const_iterator tvIter = matrixCooVec_.begin();
244  typename MatrixCooVec_::const_iterator tvEnd = matrixCooVec_.end();
245 
246  // reserve space
247  colIndices.reserve(matrixCooVec_.size());
248  values.reserve(matrixCooVec_.size());
249 
250  unsigned nr = this -> deduceNumRows_( );
251  rowPtrs.reserve( nr + 1 );
252 
253  int previousRow = -1;
254  IndexType index = 0;
255 
256  for ( ; tvIter != tvEnd; ++tvIter ) {
257  const IndexType row = (*tvIter).first.first;
258  const IndexType col = (*tvIter).first.second;
259 
260  values.push_back( tvIter -> second );
261  colIndices.push_back( col );
262  if ( row == previousRow ) index++;
263  else rowPtrs.push_back( index++ );
264 
265  previousRow = row;
266  }
267 
268  rowPtrs.push_back( values.size() );
269 
270  return;
271  }
272 
273  //! Fill independent arrays with column indices, row pointers and values
275  std::vector<IndexType> & colIndices,
276  std::vector<ValueType> & values,
277  bool onlyUpperTriangle = false )
278  {
279 
280  // sort entries to make sure
281  if ( not sorted_ ) {
282  this -> finishAssembly( );
283  }
284 
285  // iterate over all elements and fill the nonzero,
286  // column index, and row index arrys
287  typename MatrixCooVec_::const_iterator tvIter = matrixCooVec_.begin();
288  typename MatrixCooVec_::const_iterator tvEnd = matrixCooVec_.end();
289 
290  // reserve space
291  colIndices.reserve(matrixCooVec_.size());
292  rowIndices.reserve(matrixCooVec_.size());
293  values.reserve(matrixCooVec_.size());
294 
295  for ( ; tvIter != tvEnd; ++tvIter ) {
296  const IndexType row = (*tvIter).first.first ;
297  const IndexType col = (*tvIter).first.second ;
298 
299  if ( not onlyUpperTriangle or col >= row ) {
300  values.push_back( (*tvIter).second );
301  rowIndices.push_back( row );
302  colIndices.push_back( col );
303  }
304  }
305  return;
306  }
307 
308  //! destroy all contents
309  void clear()
310  {
311  matrixCooVec_.clear();
312  // force deallocation of the vector
313  MatrixCooVec_().swap( matrixCooVec_ );
314  return;
315  }
316 
317  //! erase all contents
318  void erase()
319  {
320  // erasing iterator
321  typename MatrixCooVec_::iterator tripEraseIter = matrixCooVec_.begin( );
322  typename MatrixCooVec_::iterator tripEraseIterE = matrixCooVec_.end( );
323 
324  // loop to erase values
325  for ( ; tripEraseIter != tripEraseIterE; ++tripEraseIter ) {
326  (*tripEraseIter).second = static_cast< ValueType >(0.0);
327  }
328 
329  return;
330  }
331 
332  //! debug-output
333  std::ostream & write( std::ostream & out )
334  {
335  // sort entries to make sure
336  if ( not sorted_ ) {
337  this -> finishAssembly( );
338  }
339  typename MatrixCooVec_::const_iterator tvIter = matrixCooVec_.begin();
340  typename MatrixCooVec_::const_iterator tvEnd = matrixCooVec_.end();
341  for ( ; tvIter != tvEnd; ++tvIter ) {
342  const IndexType row = (*tvIter).first.first;
343  const IndexType col = (*tvIter).first.second;
344 
345  out << row << " " << col << " "
346  << std::setprecision(12)
347  << (*tvIter).second << std::endl;
348 
349  }
350  return out;
351  }
352 
353  //----------------------------------------------------------------------------
354  //! Compute maximal row sum (infinity-norm) of a matrix in coordinate format.
355  double infNorm()
356  {
357  // sort entries to make sure
358  if ( not sorted_ ) {
359  this -> finishAssembly( );
360  }
361  unsigned nr = this -> deduceNumRows_( );
362  std::vector<double> rowSums( nr, 0.0 );
363  typename MatrixCooVec_::const_iterator iter = matrixCooVec_.begin();
364  typename MatrixCooVec_::const_iterator last = matrixCooVec_.end();
365  for ( ; iter != last; ++iter ) {
366  const IndexType iRow = (*iter).first.first;
367  rowSums[ iRow ] += std::fabs( (*iter).second );
368  }
369 
370  return *std::max_element( rowSums.begin(), rowSums.end() );
371  }
372 
373  //--------------------------------------------------------------------------
374  //! Return Frobenius norm of matrix \f$||a_{ij}||_F=\sqrt{\sum_i\sum_j |a_{ij}|^2}\f$
376  {
377  // sort entries to make sure
378  if ( not sorted_ ) {
379  this -> finishAssembly( );
380  }
381 
382  ValueType sumOfSquares = 0.0;
383 
384  typename MatrixCooVec_::const_iterator iter = matrixCooVec_.begin();
385  typename MatrixCooVec_::const_iterator last = matrixCooVec_.end();
386  for ( ; iter != last; ++iter ) {
387 
388  ValueType val = (*iter).second;
389  sumOfSquares += val * val;
390  }
391 
392  return std::sqrt( sumOfSquares );
393  }
394 
395  //--------------------------------------------------------------------------
396  //! Get scalar suitable to be put on diagonal while fixing BC
397  //! This is the mean value of extremal entries on diagonal - the aim of this guess is to
398  //! be within the spectra of the original matrix - this value immediately corresponds to an
399  //! eigenvalue
400  double getDiagScalar( )
401  {
402 
403  ValueType val;
404  // initialize maximum and minimum
405  if ( matrixCooVec_.size() > 0 ) {
406  val = std::fabs( matrixCooVec_[0].second );
407  }
408  else {
409  val = 0.0;
410  }
411  ValueType maxVal = val;
412  ValueType minVal = val;
413  IndexType row, col;
414  for ( IndexType i = 1; i < static_cast<IndexType>( matrixCooVec_.size() ); i++ ) {
415 
416  row = matrixCooVec_[i].first.first;
417  col = matrixCooVec_[i].first.second;
418 
419  if ( row == col ) {
420  val = std::fabs( matrixCooVec_[i].second );
421 
422  // update maximum
423  if ( val > maxVal ) {
424  maxVal = val;
425  }
426 
427  // update minimum
428  if ( val < minVal ) {
429  minVal = val;
430  }
431  }
432  }
433 
434  // recommend value in between extremal absolute values along diagonal
435  ValueType scalar = ( minVal + maxVal ) / 2.0;
436 
437  return scalar;
438 
439  }
440 
441 private:
442  typedef std::pair<IndexType, IndexType> IndexPair_; //!< pair of indices
443  typedef std::pair<IndexPair_,ValueType> Triple_; //!< triple
444  typedef std::vector<Triple_> MatrixCooVec_;//!< vector of triples
445  MatrixCooVec_ matrixCooVec_;//!< matrix in coordinate format <i,j, values>
446 
447  IndexType nRows_; //!< number of rows of matrix
448  IndexType nCols_; //!< number of columns of matrix
449 
450  unsigned sortedSize_; //!< number of sorted entries
451 
452  bool sorted_; //!< state variable - is matrix sorted?
453 
454  //--------------------------------------------------------------------------
455  // private functions
456 
457  //! deduce number of rows - if it was given by user, use it, otherwise,
458  //! deduce it from the index of the last entry
460  {
461  IndexType nr;
462 
463  if ( nRows_ > 0 ) {
464  // number of rows was given by user
465  nr = nRows_;
466  }
467  else {
468  // deduce number of rows from sorted matrix
469  nr = (*matrixCooVec_.rbegin()).first.first + 1;
470  }
471 
472  return nr;
473  }
474 
475 
476  //! returns true if the index pair of the first entry is smaller than of the second entry
477  struct TripleLessThan_ : std::binary_function< Triple_, Triple_, bool >
478  {
479  bool operator() ( Triple_ firstTriple, Triple_ secondTriple) {
480 
481  IndexPair_ firstTripleIndxPair = firstTriple.first;
482  IndexPair_ secondTripleIndxPair = secondTriple.first;
483 
484  return (firstTripleIndxPair < secondTripleIndxPair);
485  }
486  };
487 
488  //! returns true if the first entry has smaller ROW index than the second one
489  struct TripleRowLessThan_ : std::binary_function< Triple_, Triple_, bool >
490  {
491  bool operator() ( Triple_ firstTriple, Triple_ secondTriple) {
492 
493  IndexType firstRowIndex = firstTriple.first.first;
494  IndexType secondRowIndex = secondTriple.first.first;
495 
496  return (firstRowIndex < secondRowIndex);
497  }
498  };
499 
500  //! returns true if the row of an entry is smaller than a prescribed index
502  {
503  bool operator() ( Triple_ triple, IndexType row ) {
504 
505  IndexType tripleRowIndex = triple.first.first;
506 
507  return (tripleRowIndex < row);
508  }
509  };
510 
511  //! returns true if the entry is on diagonal, i.e. row == column
513  {
514  bool operator() ( Triple_ triple ) {
515 
516  IndexType tripleRowIndex = triple.first.first;
517  IndexType tripleColIndex = triple.first.second;
518 
519  return (tripleRowIndex == tripleColIndex);
520  }
521  };
522 
523  //! merge range of triples by addition
524  Triple_ combineTriples_ ( typename MatrixCooVec_::const_iterator itb, typename MatrixCooVec_::const_iterator ite ) {
525 
526  IndexPair_ tmpPair = (*itb).first;
527  ValueType val = 0.0;
528 
529  for ( ; itb != ite; ++itb) {
530  val += (*itb).second;
531  }
532 
533  Triple_ combinedTriple = std::make_pair(tmpPair, val);
534 
535  return combinedTriple;
536 
537  }
538 };
539 
540 #endif