Teuchos - Trilinos Tools Package Version of the Day
Loading...
Searching...
No Matches
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
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
28namespace Teuchos {
29 namespace MatrixMarket {
30 namespace Raw {
54 template<class Scalar, class Ordinal>
55 class Element {
56 public:
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) const {
70 return rowIndex_ == rhs.rowIndex_ && colIndex_ == rhs.colIndex_;
71 }
72
74 bool operator!= (const Element& rhs) const {
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 {
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
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 {
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
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_;
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;
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
Teuchos header file which uses auto-configuration information to include necessary C++ headers.
Templated Parameter List class.
Reference-counted smart pointer for managing arrays.
To be used with Checker for "raw" sparse matrix input.
void operator()(const Ordinal i, const Ordinal j, const Scalar &Aij, const bool countAgainstTotal=true)
Add an entry to the sparse matrix.
void print(std::ostream &out, const bool doMerge, const bool replace=false)
Print the sparse matrix data.
const std::vector< element_type > & getEntries() const
A temporary const view of the entries of the matrix.
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.
std::pair< size_type, size_type > merge(const bool replace=false)
Merge duplicate elements.
const Ordinal numCols() const
Computed number of columns.
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.
void clear()
Clear all the added matrix entries and reset metadata.
const Ordinal numRows() const
Computed number of rows.
bool operator==(const Element &rhs) const
Ignore the matrix value for comparisons.
bool operator!=(const Element &rhs) const
Ignore the matrix value for comparisons.
Scalar value() const
Value (A(rowIndex(), colIndex()) of this Element.
bool operator<(const Element &rhs) const
Lexicographic order first by row index, then by column index.
void merge(const Element &rhs, const BinaryFunction &f)
Merge rhs into this Element, using custom binary function.
Ordinal colIndex() const
Column index (zero-based) of this Element.
Ordinal rowIndex() const
Row index (zero-based) of this Element.
void merge(const Element &rhs, const bool replace=false)
Merge rhs into this Element, either by addition or replacement.
Element()
Default constructor: an invalid entry of the matrix.
Element(const Ordinal i, const Ordinal j, const Scalar &Aij)
Create a sparse matrix entry at (i,j) with value Aij.
Smart reference counting pointer class for automatic garbage collection.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
Matrix Market file utilities.
"Raw" input of sparse matrices from Matrix Market files.
The Teuchos namespace contains all of the classes, structs and enums used by Teuchos,...
This structure defines some basic traits for the ordinal field type.
This structure defines some basic traits for a scalar field type.