Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_MatrixMarket_Raw_Adder.hpp
1 // @HEADER
2 // *****************************************************************************
3 // Tpetra: Templated Linear Algebra Services Package
4 //
5 // Copyright 2008 NTESS and the Tpetra contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef __Teuchos_MatrixMarket_Raw_Adder_hpp
11 #define __Teuchos_MatrixMarket_Raw_Adder_hpp
12 
13 #include "Teuchos_ConfigDefs.hpp"
14 #include "Teuchos_ArrayRCP.hpp"
15 #include "Teuchos_CommHelpers.hpp"
17 #include "Teuchos_MatrixMarket_Banner.hpp"
18 #include "Teuchos_MatrixMarket_CoordDataReader.hpp"
19 
20 #include <algorithm>
21 #include <fstream>
22 #include <iostream>
23 #include <iterator>
24 #include <vector>
25 #include <stdexcept>
26 
27 
28 namespace Teuchos {
29  namespace MatrixMarket {
30  namespace Raw {
54  template<class Scalar, class Ordinal>
55  class Element {
56  public:
58  Element () :
59  rowIndex_ (Teuchos::OrdinalTraits<Ordinal>::invalid ()),
60  colIndex_ (Teuchos::OrdinalTraits<Ordinal>::invalid ()),
61  value_ (Teuchos::ScalarTraits<Scalar>::zero ())
62  {}
63 
65  Element (const Ordinal i, const Ordinal j, const Scalar& Aij) :
66  rowIndex_ (i), colIndex_ (j), value_ (Aij) {}
67 
69  bool operator== (const Element& rhs) {
70  return rowIndex_ == rhs.rowIndex_ && colIndex_ == rhs.colIndex_;
71  }
72 
74  bool operator!= (const Element& rhs) {
75  return ! (*this == rhs);
76  }
77 
79  bool operator< (const Element& rhs) const {
80  if (rowIndex_ < rhs.rowIndex_)
81  return true;
82  else if (rowIndex_ > rhs.rowIndex_)
83  return false;
84  else { // equal
85  return colIndex_ < rhs.colIndex_;
86  }
87  }
88 
94  template<class BinaryFunction>
95  void merge (const Element& rhs, const BinaryFunction& f) {
97  rowIndex() != rhs.rowIndex() || colIndex() != rhs.colIndex(),
98  std::invalid_argument,
99  "Attempt to merge elements at different locations in the sparse "
100  "matrix. The current element is at (" << rowIndex() << ", "
101  << colIndex() << ") and the element you asked me to merge with it "
102  "is at (" << rhs.rowIndex() << ", " << rhs.colIndex() << "). This "
103  "probably indicates a bug in the sparse matrix reader.");
104 
105  value_ = f (rhs.value_, value_);
106  }
107 
114  void merge (const Element& rhs, const bool replace=false) {
116  rowIndex() != rhs.rowIndex() || colIndex() != rhs.colIndex(),
117  std::invalid_argument,
118  "Attempt to merge elements at different locations in the sparse "
119  "matrix. The current element is at (" << rowIndex() << ", "
120  << colIndex() << ") and the element you asked me to merge with it "
121  "is at (" << rhs.rowIndex() << ", " << rhs.colIndex() << "). This "
122  "probably indicates a bug in the sparse matrix reader.");
123 
124  if (replace) {
125  value_ = rhs.value_;
126  }
127  else {
128  value_ += rhs.value_;
129  }
130  }
131 
133  Ordinal rowIndex() const { return rowIndex_; }
134 
136  Ordinal colIndex() const { return colIndex_; }
137 
139  Scalar value() const { return value_; }
140 
141  private:
142  Ordinal rowIndex_, colIndex_;
143  Scalar value_;
144  };
145 
156  template<class Scalar, class Ordinal>
157  std::ostream&
158  operator<< (std::ostream& out, const Element<Scalar, Ordinal>& elt)
159  {
160  typedef ScalarTraits<Scalar> STS;
161  std::ios::fmtflags f( out.flags() );
162  // Non-Ordinal types are floating-point types. In order not to
163  // lose information when we print a floating-point type, we have
164  // to set the number of digits to print. C++ standard behavior
165  // in the default locale seems to be to print only five decimal
166  // digits after the decimal point; this does not suffice for
167  // double precision. We solve the problem of how many digits to
168  // print more generally below. It's a rough solution so please
169  // feel free to audit and revise it.
170  //
171  // FIXME (mfh 01 Feb 2011)
172  // This really calls for the following approach:
173  //
174  // Guy L. Steele and Jon L. White, "How to print floating-point
175  // numbers accurately", 20 Years of the ACM/SIGPLAN Conference
176  // on Programming Language Design and Implementation
177  // (1979-1999): A Selection, 2003.
178  if (! STS::isOrdinal) {
179  // std::scientific, std::fixed, and default are the three
180  // output states for floating-point numbers. A reasonable
181  // user-defined floating-point type should respect these
182  // flags; hopefully it does.
183  out << std::scientific;
184 
185  // Decimal output is standard for Matrix Market format.
186  out << std::setbase (10);
187 
188  // Compute the number of decimal digits required for expressing
189  // a Scalar, by comparing with IEEE 754 double precision (16
190  // decimal digits, 53 binary digits). This would be easier if
191  // Teuchos exposed std::numeric_limits<T>::digits10, alas.
192  const double numDigitsAsDouble =
193  16 * ((double) STS::t() / (double) ScalarTraits<double>::t());
194  // Adding 0.5 and truncating is a portable "floor".
195  const int numDigits = static_cast<int> (numDigitsAsDouble + 0.5);
196 
197  // Precision to which a Scalar should be written. Add one
198  // for good measure, since 17 is necessary for IEEE 754
199  // doubles.
200  out << std::setprecision (numDigits + 1);
201  }
202  out << elt.rowIndex () << " " << elt.colIndex () << " ";
203  if (STS::isComplex) {
204  out << STS::real (elt.value ()) << " " << STS::imag (elt.value ());
205  }
206  else {
207  out << elt.value ();
208  }
209  // Restore flags
210  out.flags( f );
211  return out;
212  }
213 
250  template<class Scalar, class Ordinal>
251  class Adder {
252  public:
253  typedef Ordinal index_type;
254  typedef Scalar value_type;
256  typedef typename std::vector<element_type>::size_type size_type;
257 
268  Adder () :
269  expectedNumRows_(0),
270  expectedNumCols_(0),
271  expectedNumEntries_(0),
272  seenNumRows_(0),
273  seenNumCols_(0),
274  seenNumEntries_(0),
275  tolerant_ (true),
276  debug_ (false)
277  {}
278 
296  Adder (const Ordinal expectedNumRows,
297  const Ordinal expectedNumCols,
298  const Ordinal expectedNumEntries,
299  const bool tolerant=false,
300  const bool debug=false) :
301  expectedNumRows_(expectedNumRows),
302  expectedNumCols_(expectedNumCols),
303  expectedNumEntries_(expectedNumEntries),
304  seenNumRows_(0),
305  seenNumCols_(0),
306  seenNumEntries_(0),
307  tolerant_ (tolerant),
308  debug_ (debug)
309  {}
310 
331  void
332  operator() (const Ordinal i,
333  const Ordinal j,
334  const Scalar& Aij,
335  const bool countAgainstTotal=true)
336  {
337  if (! tolerant_) {
338  const bool indexPairOutOfRange = i < 1 || j < 1 ||
339  i > expectedNumRows_ || j > expectedNumCols_;
340 
341  TEUCHOS_TEST_FOR_EXCEPTION(indexPairOutOfRange,
342  std::invalid_argument, "Matrix is " << expectedNumRows_ << " x "
343  << expectedNumCols_ << ", so entry A(" << i << "," << j << ") = "
344  << Aij << " is out of range.");
345  if (countAgainstTotal) {
346  TEUCHOS_TEST_FOR_EXCEPTION(seenNumEntries_ >= expectedNumEntries_,
347  std::invalid_argument, "Cannot add entry A(" << i << "," << j
348  << ") = " << Aij << " to matrix; already have expected number "
349  "of entries " << expectedNumEntries_ << ".");
350  }
351  }
352  // i and j are 1-based indices, but we store them as 0-based.
353  elts_.push_back (element_type (i-1, j-1, Aij));
354 
355  // Keep track of the rightmost column containing a matrix
356  // entry, and the bottommost row containing a matrix entry.
357  // This gives us a lower bound for the dimensions of the
358  // matrix, and a check for the reported dimensions of the
359  // matrix in the Matrix Market file.
360  seenNumRows_ = std::max (seenNumRows_, i);
361  seenNumCols_ = std::max (seenNumCols_, j);
362  if (countAgainstTotal) {
363  ++seenNumEntries_;
364  }
365  }
366 
376  void
377  print (std::ostream& out, const bool doMerge, const bool replace=false)
378  {
379  if (doMerge) {
380  merge (replace);
381  } else {
382  std::sort (elts_.begin(), elts_.end());
383  }
384  // Print out the results, delimited by newlines.
385  typedef std::ostream_iterator<element_type> iter_type;
386  std::copy (elts_.begin(), elts_.end(), iter_type (out, "\n"));
387  }
388 
411  std::pair<size_type, size_type>
412  merge (const bool replace=false)
413  {
414  typedef typename std::vector<element_type>::iterator iter_type;
415 
416  // Start with a sorted container. Element objects sort in
417  // lexicographic order of their (row, column) indices, for
418  // easy conversion to CSR format. If you expect that the
419  // elements will usually be sorted in the desired order, you
420  // can check first whether they are already sorted. We have
421  // no such expectation, so we don't even bother to spend the
422  // extra O(# entries) operations to check.
423  std::sort (elts_.begin(), elts_.end());
424 
425  // Walk through the array of elements in place, merging
426  // duplicates and pushing unique elements up to the front of
427  // the array. We can't use std::unique for this because it
428  // doesn't let us merge duplicate elements; it only removes
429  // them from the sequence.
430  size_type numUnique = 0;
431  iter_type cur = elts_.begin();
432  if (cur == elts_.end()) { // No elements to merge
433  return std::make_pair (numUnique, size_type (0));
434  }
435  else {
436  iter_type next = cur;
437  ++next; // There is one unique element
438  ++numUnique;
439  while (next != elts_.end()) {
440  if (*cur == *next) {
441  // Merge in the duplicated element *next
442  cur->merge (*next, replace);
443  } else {
444  // *cur is already a unique element. Move over one to
445  // *make space for the new unique element.
446  ++cur;
447  *cur = *next; // Add the new unique element
448  ++numUnique;
449  }
450  // Look at the "next" not-yet-considered element
451  ++next;
452  }
453  // Remember how many elements we removed before resizing.
454  const size_type numRemoved = elts_.size() - numUnique;
455  elts_.resize (numUnique);
456  return std::make_pair (numUnique, numRemoved);
457  }
458  }
459 
502  void
503  mergeAndConvertToCSR (size_type& numUniqueElts,
504  size_type& numRemovedElts,
508  const bool replace=false)
509  {
510  using Teuchos::arcp;
511  using Teuchos::ArrayRCP;
512 
513  std::pair<size_type, size_type> mergeResult = merge (replace);
514 
515  // At this point, elts_ is already in CSR order.
516  // Now we can allocate and fill the ind and val arrays.
517  ArrayRCP<Ordinal> ind = arcp<Ordinal> (elts_.size ());
518  ArrayRCP<Scalar> val = arcp<Scalar> (elts_.size ());
519 
520  // Number of rows in the matrix.
521  const Ordinal nrows = tolerant_ ? seenNumRows_ : expectedNumRows_;
522  ArrayRCP<Ordinal> ptr = arcp<Ordinal> (nrows + 1);
523 
524  // Copy over the elements, and fill in the ptr array with
525  // offsets. Note that merge() sorted the entries by row
526  // index, so we can assume the row indices are increasing in
527  // the list of entries.
528  Ordinal curRow = 0;
529  Ordinal curInd = 0;
530  typedef typename std::vector<element_type>::const_iterator iter_type;
531  ptr[0] = 0; // ptr always has at least one entry.
532  for (iter_type it = elts_.begin(); it != elts_.end(); ++it) {
533  const Ordinal i = it->rowIndex ();
534  const Ordinal j = it->colIndex ();
535  const Scalar Aij = it->value ();
536 
537  TEUCHOS_TEST_FOR_EXCEPTION(i < curRow, std::logic_error, "The "
538  "current matrix entry's row index " << i << " is less then what "
539  "should be the current row index lower bound " << curRow << ".");
540  for (Ordinal k = curRow+1; k <= i; ++k) {
541  ptr[k] = curInd;
542  }
543  curRow = i;
544 
546  static_cast<size_t> (curInd) >= elts_.size (),
547  std::logic_error, "The current index " << curInd << " into ind "
548  "and val is >= the number of matrix entries " << elts_.size ()
549  << ".");
550  ind[curInd] = j;
551  val[curInd] = Aij;
552  ++curInd;
553  }
554  for (Ordinal k = curRow+1; k <= nrows; ++k) {
555  ptr[k] = curInd;
556  }
557 
558  // Assign to outputs here, to ensure the strong exception
559  // guarantee (assuming that ArrayRCP's operator= doesn't
560  // throw).
561  rowptr = ptr;
562  colind = ind;
563  values = val;
564  numUniqueElts = mergeResult.first;
565  numRemovedElts = mergeResult.second;
566  }
567 
569  const std::vector<element_type>& getEntries() const {
570  return elts_;
571  }
572 
574  void clear() {
575  seenNumRows_ = 0;
576  seenNumCols_ = 0;
577  seenNumEntries_ = 0;
578  elts_.resize (0);
579  }
580 
584  const Ordinal numRows() const { return seenNumRows_; }
585 
589  const Ordinal numCols() const { return seenNumCols_; }
590 
594  const Ordinal numEntries() const { return seenNumEntries_; }
595 
596 
597  private:
598  Ordinal expectedNumRows_, expectedNumCols_, expectedNumEntries_;
599  Ordinal seenNumRows_, seenNumCols_, seenNumEntries_;
600  bool tolerant_;
601  bool debug_;
602 
604  std::vector<element_type> elts_;
605  };
606  } // namespace Raw
607  } // namespace MatrixMarket
608 } // namespace Teuchos
609 
610 #endif // #ifndef __Teuchos_MatrixMarket_Raw_Adder_hpp
std::pair< size_type, size_type > merge(const bool replace=false)
Merge duplicate elements.
const std::vector< element_type > & getEntries() const
A temporary const view of the entries of the matrix.
bool operator<(const Element &rhs) const
Lexicographic order first by row index, then by column index.
Element(const Ordinal i, const Ordinal j, const Scalar &Aij)
Create a sparse matrix entry at (i,j) with value Aij.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
Scalar value() const
Value (A(rowIndex(), colIndex()) of this Element.
Teuchos header file which uses auto-configuration information to include necessary C++ headers...
void clear()
Clear all the added matrix entries and reset metadata.
void mergeAndConvertToCSR(size_type &numUniqueElts, size_type &numRemovedElts, Teuchos::ArrayRCP< Ordinal > &rowptr, Teuchos::ArrayRCP< Ordinal > &colind, Teuchos::ArrayRCP< Scalar > &values, const bool replace=false)
Merge duplicate elements and convert to zero-based CSR.
Element()
Default constructor: an invalid entry of the matrix.
This structure defines some basic traits for a scalar field type.
Templated Parameter List class.
Ordinal rowIndex() const
Row index (zero-based) of this Element.
bool operator==(const Element &rhs)
Ignore the matrix value for comparisons.
This structure defines some basic traits for the ordinal field type.
const Ordinal numRows() const
Computed number of rows.
void print(std::ostream &out, const bool doMerge, const bool replace=false)
Print the sparse matrix data.
Stores one entry of a sparse matrix.
void operator()(const Ordinal i, const Ordinal j, const Scalar &Aij, const bool countAgainstTotal=true)
Add an entry to the sparse matrix.
const Ordinal numCols() const
Computed number of columns.
void merge(const Element &rhs, const BinaryFunction &f)
Merge rhs into this Element, using custom binary function.
bool operator!=(const Element &rhs)
Ignore the matrix value for comparisons.
void merge(const Element &rhs, const bool replace=false)
Merge rhs into this Element, either by addition or replacement.
Ordinal colIndex() const
Column index (zero-based) of this Element.
const Ordinal numEntries() const
Computed number of columns.
Adder(const Ordinal expectedNumRows, const Ordinal expectedNumCols, const Ordinal expectedNumEntries, const bool tolerant=false, const bool debug=false)
Standard constructor.
Reference-counted smart pointer for managing arrays.
To be used with Checker for &quot;raw&quot; sparse matrix input.