Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Teuchos_MatrixMarket_Raw_Adder.hpp
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines