|
Teuchos Package Browser (Single Doxygen Collection)
Version of the Day
|
00001 // @HEADER 00002 // *********************************************************************** 00003 // 00004 // Tpetra: Templated Linear Algebra Services Package 00005 // Copyright (2008) Sandia Corporation 00006 // 00007 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 00008 // the U.S. Government retains certain rights in this software. 00009 // 00010 // Redistribution and use in source and binary forms, with or without 00011 // modification, are permitted provided that the following conditions are 00012 // met: 00013 // 00014 // 1. Redistributions of source code must retain the above copyright 00015 // notice, this list of conditions and the following disclaimer. 00016 // 00017 // 2. Redistributions in binary form must reproduce the above copyright 00018 // notice, this list of conditions and the following disclaimer in the 00019 // documentation and/or other materials provided with the distribution. 00020 // 00021 // 3. Neither the name of the Corporation nor the names of the 00022 // contributors may be used to endorse or promote products derived from 00023 // this software without specific prior written permission. 00024 // 00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00036 // 00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00038 // 00039 // ************************************************************************ 00040 // @HEADER 00041 00042 #ifndef __Teuchos_MatrixMarket_Raw_Adder_hpp 00043 #define __Teuchos_MatrixMarket_Raw_Adder_hpp 00044 00045 #include "Teuchos_ConfigDefs.hpp" 00046 #include "Teuchos_ArrayRCP.hpp" 00047 #include "Teuchos_CommHelpers.hpp" 00048 #include "Teuchos_ParameterList.hpp" 00049 #include "Teuchos_MatrixMarket_Banner.hpp" 00050 #include "Teuchos_MatrixMarket_CoordDataReader.hpp" 00051 00052 #include <algorithm> 00053 #include <fstream> 00054 #include <iostream> 00055 #include <iterator> 00056 #include <vector> 00057 #include <stdexcept> 00058 00059 00060 namespace Teuchos { 00061 namespace MatrixMarket { 00062 namespace Raw { 00086 template<class Scalar, class Ordinal> 00087 class Element { 00088 public: 00090 Element () : 00091 rowIndex_ (Teuchos::OrdinalTraits<Ordinal>::invalid ()), 00092 colIndex_ (Teuchos::OrdinalTraits<Ordinal>::invalid ()), 00093 value_ (Teuchos::ScalarTraits<Scalar>::zero ()) 00094 {} 00095 00097 Element (const Ordinal i, const Ordinal j, const Scalar& Aij) : 00098 rowIndex_ (i), colIndex_ (j), value_ (Aij) {} 00099 00101 bool operator== (const Element& rhs) { 00102 return rowIndex_ == rhs.rowIndex_ && colIndex_ == rhs.colIndex_; 00103 } 00104 00106 bool operator!= (const Element& rhs) { 00107 return ! (*this == rhs); 00108 } 00109 00111 bool operator< (const Element& rhs) const { 00112 if (rowIndex_ < rhs.rowIndex_) 00113 return true; 00114 else if (rowIndex_ > rhs.rowIndex_) 00115 return false; 00116 else { // equal 00117 return colIndex_ < rhs.colIndex_; 00118 } 00119 } 00120 00126 template<class BinaryFunction> 00127 void merge (const Element& rhs, const BinaryFunction& f) { 00128 TEUCHOS_TEST_FOR_EXCEPTION( 00129 rowIndex() != rhs.rowIndex() || colIndex() != rhs.colIndex(), 00130 std::invalid_argument, 00131 "Attempt to merge elements at different locations in the sparse " 00132 "matrix. The current element is at (" << rowIndex() << ", " 00133 << colIndex() << ") and the element you asked me to merge with it " 00134 "is at (" << rhs.rowIndex() << ", " << rhs.colIndex() << "). This " 00135 "probably indicates a bug in the sparse matrix reader."); 00136 00137 value_ = f (rhs.value_, value_); 00138 } 00139 00146 void merge (const Element& rhs, const bool replace=false) { 00147 TEUCHOS_TEST_FOR_EXCEPTION( 00148 rowIndex() != rhs.rowIndex() || colIndex() != rhs.colIndex(), 00149 std::invalid_argument, 00150 "Attempt to merge elements at different locations in the sparse " 00151 "matrix. The current element is at (" << rowIndex() << ", " 00152 << colIndex() << ") and the element you asked me to merge with it " 00153 "is at (" << rhs.rowIndex() << ", " << rhs.colIndex() << "). This " 00154 "probably indicates a bug in the sparse matrix reader."); 00155 00156 if (replace) { 00157 value_ = rhs.value_; 00158 } 00159 else { 00160 value_ += rhs.value_; 00161 } 00162 } 00163 00165 Ordinal rowIndex() const { return rowIndex_; } 00166 00168 Ordinal colIndex() const { return colIndex_; } 00169 00171 Scalar value() const { return value_; } 00172 00173 private: 00174 Ordinal rowIndex_, colIndex_; 00175 Scalar value_; 00176 }; 00177 00188 template<class Scalar, class Ordinal> 00189 std::ostream& 00190 operator<< (std::ostream& out, const Element<Scalar, Ordinal>& elt) 00191 { 00192 typedef ScalarTraits<Scalar> STS; 00193 std::ios::fmtflags f( out.flags() ); 00194 // Non-Ordinal types are floating-point types. In order not to 00195 // lose information when we print a floating-point type, we have 00196 // to set the number of digits to print. C++ standard behavior 00197 // in the default locale seems to be to print only five decimal 00198 // digits after the decimal point; this does not suffice for 00199 // double precision. We solve the problem of how many digits to 00200 // print more generally below. It's a rough solution so please 00201 // feel free to audit and revise it. 00202 // 00203 // FIXME (mfh 01 Feb 2011) 00204 // This really calls for the following approach: 00205 // 00206 // Guy L. Steele and Jon L. White, "How to print floating-point 00207 // numbers accurately", 20 Years of the ACM/SIGPLAN Conference 00208 // on Programming Language Design and Implementation 00209 // (1979-1999): A Selection, 2003. 00210 if (! STS::isOrdinal) { 00211 // std::scientific, std::fixed, and default are the three 00212 // output states for floating-point numbers. A reasonable 00213 // user-defined floating-point type should respect these 00214 // flags; hopefully it does. 00215 out << std::scientific; 00216 00217 // Decimal output is standard for Matrix Market format. 00218 out << std::setbase (10); 00219 00220 // Compute the number of decimal digits required for expressing 00221 // a Scalar, by comparing with IEEE 754 double precision (16 00222 // decimal digits, 53 binary digits). This would be easier if 00223 // Teuchos exposed std::numeric_limits<T>::digits10, alas. 00224 const double numDigitsAsDouble = 00225 16 * ((double) STS::t() / (double) ScalarTraits<double>::t()); 00226 // Adding 0.5 and truncating is a portable "floor". 00227 const int numDigits = static_cast<int> (numDigitsAsDouble + 0.5); 00228 00229 // Precision to which a Scalar should be written. Add one 00230 // for good measure, since 17 is necessary for IEEE 754 00231 // doubles. 00232 out << std::setprecision (numDigits + 1); 00233 } 00234 out << elt.rowIndex () << " " << elt.colIndex () << " "; 00235 if (STS::isComplex) { 00236 out << STS::real (elt.value ()) << " " << STS::imag (elt.value ()); 00237 } 00238 else { 00239 out << elt.value (); 00240 } 00241 // Restore flags 00242 out.flags( f ); 00243 return out; 00244 } 00245 00282 template<class Scalar, class Ordinal> 00283 class Adder { 00284 public: 00285 typedef Ordinal index_type; 00286 typedef Scalar value_type; 00287 typedef Element<Scalar, Ordinal> element_type; 00288 typedef typename std::vector<element_type>::size_type size_type; 00289 00300 Adder () : 00301 expectedNumRows_(0), 00302 expectedNumCols_(0), 00303 expectedNumEntries_(0), 00304 seenNumRows_(0), 00305 seenNumCols_(0), 00306 seenNumEntries_(0), 00307 tolerant_ (true), 00308 debug_ (false) 00309 {} 00310 00328 Adder (const Ordinal expectedNumRows, 00329 const Ordinal expectedNumCols, 00330 const Ordinal expectedNumEntries, 00331 const bool tolerant=false, 00332 const bool debug=false) : 00333 expectedNumRows_(expectedNumRows), 00334 expectedNumCols_(expectedNumCols), 00335 expectedNumEntries_(expectedNumEntries), 00336 seenNumRows_(0), 00337 seenNumCols_(0), 00338 seenNumEntries_(0), 00339 tolerant_ (tolerant), 00340 debug_ (debug) 00341 {} 00342 00363 void 00364 operator() (const Ordinal i, 00365 const Ordinal j, 00366 const Scalar& Aij, 00367 const bool countAgainstTotal=true) 00368 { 00369 if (! tolerant_) { 00370 const bool indexPairOutOfRange = i < 1 || j < 1 || 00371 i > expectedNumRows_ || j > expectedNumCols_; 00372 00373 TEUCHOS_TEST_FOR_EXCEPTION(indexPairOutOfRange, 00374 std::invalid_argument, "Matrix is " << expectedNumRows_ << " x " 00375 << expectedNumCols_ << ", so entry A(" << i << "," << j << ") = " 00376 << Aij << " is out of range."); 00377 if (countAgainstTotal) { 00378 TEUCHOS_TEST_FOR_EXCEPTION(seenNumEntries_ >= expectedNumEntries_, 00379 std::invalid_argument, "Cannot add entry A(" << i << "," << j 00380 << ") = " << Aij << " to matrix; already have expected number " 00381 "of entries " << expectedNumEntries_ << "."); 00382 } 00383 } 00384 // i and j are 1-based indices, but we store them as 0-based. 00385 elts_.push_back (element_type (i-1, j-1, Aij)); 00386 00387 // Keep track of the rightmost column containing a matrix 00388 // entry, and the bottommost row containing a matrix entry. 00389 // This gives us a lower bound for the dimensions of the 00390 // matrix, and a check for the reported dimensions of the 00391 // matrix in the Matrix Market file. 00392 seenNumRows_ = std::max (seenNumRows_, i); 00393 seenNumCols_ = std::max (seenNumCols_, j); 00394 if (countAgainstTotal) { 00395 ++seenNumEntries_; 00396 } 00397 } 00398 00408 void 00409 print (std::ostream& out, const bool doMerge, const bool replace=false) 00410 { 00411 if (doMerge) { 00412 merge (replace); 00413 } else { 00414 std::sort (elts_.begin(), elts_.end()); 00415 } 00416 // Print out the results, delimited by newlines. 00417 typedef std::ostream_iterator<element_type> iter_type; 00418 std::copy (elts_.begin(), elts_.end(), iter_type (out, "\n")); 00419 } 00420 00443 std::pair<size_type, size_type> 00444 merge (const bool replace=false) 00445 { 00446 typedef typename std::vector<element_type>::iterator iter_type; 00447 00448 // Start with a sorted container. Element objects sort in 00449 // lexicographic order of their (row, column) indices, for 00450 // easy conversion to CSR format. If you expect that the 00451 // elements will usually be sorted in the desired order, you 00452 // can check first whether they are already sorted. We have 00453 // no such expectation, so we don't even bother to spend the 00454 // extra O(# entries) operations to check. 00455 std::sort (elts_.begin(), elts_.end()); 00456 00457 // Walk through the array of elements in place, merging 00458 // duplicates and pushing unique elements up to the front of 00459 // the array. We can't use std::unique for this because it 00460 // doesn't let us merge duplicate elements; it only removes 00461 // them from the sequence. 00462 size_type numUnique = 0; 00463 iter_type cur = elts_.begin(); 00464 if (cur == elts_.end()) { // No elements to merge 00465 return std::make_pair (numUnique, size_type (0)); 00466 } 00467 else { 00468 iter_type next = cur; 00469 ++next; // There is one unique element 00470 ++numUnique; 00471 while (next != elts_.end()) { 00472 if (*cur == *next) { 00473 // Merge in the duplicated element *next 00474 cur->merge (*next, replace); 00475 } else { 00476 // *cur is already a unique element. Move over one to 00477 // *make space for the new unique element. 00478 ++cur; 00479 *cur = *next; // Add the new unique element 00480 ++numUnique; 00481 } 00482 // Look at the "next" not-yet-considered element 00483 ++next; 00484 } 00485 // Remember how many elements we removed before resizing. 00486 const size_type numRemoved = elts_.size() - numUnique; 00487 elts_.resize (numUnique); 00488 return std::make_pair (numUnique, numRemoved); 00489 } 00490 } 00491 00534 void 00535 mergeAndConvertToCSR (size_type& numUniqueElts, 00536 size_type& numRemovedElts, 00537 Teuchos::ArrayRCP<Ordinal>& rowptr, 00538 Teuchos::ArrayRCP<Ordinal>& colind, 00539 Teuchos::ArrayRCP<Scalar>& values, 00540 const bool replace=false) 00541 { 00542 using Teuchos::arcp; 00543 using Teuchos::ArrayRCP; 00544 00545 std::pair<size_type, size_type> mergeResult = merge (replace); 00546 00547 // At this point, elts_ is already in CSR order. 00548 // Now we can allocate and fill the ind and val arrays. 00549 ArrayRCP<Ordinal> ind = arcp<Ordinal> (elts_.size ()); 00550 ArrayRCP<Scalar> val = arcp<Scalar> (elts_.size ()); 00551 00552 // Number of rows in the matrix. 00553 const Ordinal nrows = tolerant_ ? seenNumRows_ : expectedNumRows_; 00554 ArrayRCP<Ordinal> ptr = arcp<Ordinal> (nrows + 1); 00555 00556 // Copy over the elements, and fill in the ptr array with 00557 // offsets. Note that merge() sorted the entries by row 00558 // index, so we can assume the row indices are increasing in 00559 // the list of entries. 00560 Ordinal curRow = 0; 00561 Ordinal curInd = 0; 00562 typedef typename std::vector<element_type>::const_iterator iter_type; 00563 ptr[0] = 0; // ptr always has at least one entry. 00564 for (iter_type it = elts_.begin(); it != elts_.end(); ++it) { 00565 const Ordinal i = it->rowIndex (); 00566 const Ordinal j = it->colIndex (); 00567 const Scalar Aij = it->value (); 00568 00569 TEUCHOS_TEST_FOR_EXCEPTION(i < curRow, std::logic_error, "The " 00570 "current matrix entry's row index " << i << " is less then what " 00571 "should be the current row index lower bound " << curRow << "."); 00572 for (Ordinal k = curRow+1; k <= i; ++k) { 00573 ptr[k] = curInd; 00574 } 00575 curRow = i; 00576 00577 TEUCHOS_TEST_FOR_EXCEPTION( 00578 static_cast<size_t> (curInd) >= elts_.size (), 00579 std::logic_error, "The current index " << curInd << " into ind " 00580 "and val is >= the number of matrix entries " << elts_.size () 00581 << "."); 00582 ind[curInd] = j; 00583 val[curInd] = Aij; 00584 ++curInd; 00585 } 00586 for (Ordinal k = curRow+1; k <= nrows; ++k) { 00587 ptr[k] = curInd; 00588 } 00589 00590 // Assign to outputs here, to ensure the strong exception 00591 // guarantee (assuming that ArrayRCP's operator= doesn't 00592 // throw). 00593 rowptr = ptr; 00594 colind = ind; 00595 values = val; 00596 numUniqueElts = mergeResult.first; 00597 numRemovedElts = mergeResult.second; 00598 } 00599 00601 const std::vector<element_type>& getEntries() const { 00602 return elts_; 00603 } 00604 00606 void clear() { 00607 seenNumRows_ = 0; 00608 seenNumCols_ = 0; 00609 seenNumEntries_ = 0; 00610 elts_.resize (0); 00611 } 00612 00616 const Ordinal numRows() const { return seenNumRows_; } 00617 00621 const Ordinal numCols() const { return seenNumCols_; } 00622 00623 private: 00624 Ordinal expectedNumRows_, expectedNumCols_, expectedNumEntries_; 00625 Ordinal seenNumRows_, seenNumCols_, seenNumEntries_; 00626 bool tolerant_; 00627 bool debug_; 00628 00630 std::vector<element_type> elts_; 00631 }; 00632 } // namespace Raw 00633 } // namespace MatrixMarket 00634 } // namespace Teuchos 00635 00636 #endif // #ifndef __Teuchos_MatrixMarket_Raw_Adder_hpp
1.7.6.1