Teuchos - Trilinos Tools Package Version of the Day
Teuchos_MatrixMarket_Raw_Checker.hpp
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_Checker_hpp
00043 #define __Teuchos_MatrixMarket_Raw_Checker_hpp
00044 
00045 #include "Teuchos_MatrixMarket_Raw_Adder.hpp"
00046 #include "Teuchos_MatrixMarket_SymmetrizingAdder.hpp"
00047 #include "Teuchos_MatrixMarket_CoordDataReader.hpp"
00048 
00049 
00050 namespace Teuchos {
00051   namespace MatrixMarket {
00052     namespace Raw {
00067       template<class Scalar, class Ordinal>
00068       class Checker {
00069       public:
00079         Checker (const bool echo, const bool tolerant, const bool debug) :
00080           echo_ (echo), tolerant_ (tolerant), debug_ (debug)
00081         {}
00082 
00084         Checker () :
00085           echo_ (false), tolerant_ (false), debug_ (false)
00086         {}
00087 
00098         Checker (const RCP<ParameterList>& params) :
00099           echo_ (false), tolerant_ (false), debug_ (false)
00100         {
00101           setParameters (params);
00102         }
00103 
00107         void
00108         setParameters (const RCP<ParameterList>& params)
00109         {
00110           // Default parameter values.
00111           bool echo = false;
00112           bool tolerant = false;
00113           bool debug = false;
00114 
00115           // Read parameters.
00116           echo = params->get ("Echo to stdout", echo);
00117           tolerant = params->get ("Parse tolerantly", tolerant);
00118           debug = params->get ("Debug mode", debug);
00119 
00120           // No side effects on the class until ParameterList
00121           // processing is complete.
00122           echo_ = echo;
00123           tolerant_ = tolerant;
00124           debug_ = debug;
00125         }
00126 
00127         RCP<const ParameterList>
00128         getValidParameters () const
00129         {
00130           // Default parameter values.
00131           const bool echo = false;
00132           const bool tolerant = false;
00133           const bool debug = false;
00134 
00135           // Set default parameters with documentation.
00136           RCP<ParameterList> params = parameterList ("Matrix Market Checker");
00137           params->set ("Echo to stdout", echo, "Whether to echo the sparse "
00138                        "matrix to stdout after reading it");
00139           params->set ("Parse tolerantly", tolerant, "Whether to tolerate "
00140                        "syntax errors when parsing the Matrix Market file");
00141           params->set ("Debug mode", debug, "Whether to print debugging output "
00142                        "to stderr, on all participating MPI processes");
00143 
00144           return rcp_const_cast<const ParameterList> (params);
00145         }
00146 
00157         bool
00158         readFile (const Teuchos::Comm<int>& comm,
00159                   const std::string& filename)
00160         {
00161           using std::cerr;
00162           using std::endl;
00163 
00164           const int myRank = comm.getRank ();
00165           // Teuchos::broadcast doesn't accept a bool; we use an int
00166           // instead, with the usual 1->true, 0->false Boolean
00167           // interpretation.
00168           int didReadFile = 0;
00169           RCP<std::ifstream> in; // only valid on Rank 0
00170           if (myRank == 0) {
00171             if (debug_) {
00172               cerr << "Attempting to open file \"" << filename
00173                    << "\" on Rank 0...";
00174             }
00175             in = rcp (new std::ifstream (filename.c_str()));
00176             if (! *in) {
00177               didReadFile = 0;
00178               if (debug_) {
00179                 cerr << "failed." << endl;
00180               }
00181             }
00182             else {
00183               didReadFile = 1;
00184               if (debug_) {
00185                 cerr << "succeeded." << endl;
00186               }
00187             }
00188           }
00189           Teuchos::broadcast (comm, 0, &didReadFile);
00190           // All MPI processes should throw at the same time, or none.
00191           TEUCHOS_TEST_FOR_EXCEPTION(! didReadFile, std::runtime_error,
00192             "Failed to open input file \"" + filename + "\".");
00193           // Only Rank 0 will try to dereference "in".
00194           return read (comm, in);
00195         }
00196 
00207         bool
00208         read (const Teuchos::Comm<int>& comm,
00209               const RCP<std::istream>& in)
00210         {
00211           using std::cerr;
00212           using std::endl;
00213 
00214           const int myRank = comm.getRank ();
00215           std::pair<bool, std::string> result;
00216           int msgSize = 0; // Size of error message (if any)
00217           if (myRank == 0) {
00218             if (in.is_null()) {
00219               result.first = false;
00220               result.second = "Input stream is null on Rank 0";
00221             }
00222             else {
00223               if (debug_) {
00224                 cerr << "About to read from input stream on Rank 0" << endl;
00225               }
00226               result = readOnRank0 (*in);
00227               if (debug_) {
00228                 if (result.first) {
00229                   cerr << "Successfully read sparse matrix from "
00230                     "input stream on Rank 0" << endl;
00231                 }
00232                 else {
00233                   cerr << "Failed to read sparse matrix from input "
00234                     "stream on Rank 0" << endl;
00235                 }
00236               }
00237             }
00238             if (result.first) {
00239               msgSize = 0;
00240             }
00241             else {
00242               msgSize = result.second.size();
00243             }
00244           }
00245           int success = result.first ? 1 : 0;
00246           Teuchos::broadcast (comm, 0, &success);
00247           if (! success) {
00248             if (! tolerant_) {
00249               // Tell all ranks how long the error message is, so
00250               // they can make space for it in order to receive
00251               // the broadcast of the error message.
00252               Teuchos::broadcast (comm, 0, &msgSize);
00253 
00254               if (msgSize > 0) {
00255                 std::string errMsg (msgSize, ' ');
00256                 if (myRank == 0) {
00257                   std::copy (result.second.begin(), result.second.end(),
00258                              errMsg.begin());
00259                 }
00260                 Teuchos::broadcast (comm, 0, static_cast<int> (msgSize), &errMsg[0]);
00261                 TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, errMsg);
00262               }
00263               else {
00264                 TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error,
00265                   "Unknown error when reading Matrix Market sparse matrix file; "
00266                   "the error is \"unknown\" because the error message has length 0.");
00267               }
00268             }
00269             else if (myRank == 0) {
00270               using std::cerr;
00271               using std::endl;
00272               cerr << "The following error occurred when reading the "
00273                 "sparse matrix: " << result.second << endl;
00274             }
00275           }
00276           return success;
00277         }
00278 
00279       private:
00281         bool echo_;
00283         bool tolerant_;
00285         bool debug_;
00286 
00301         RCP<const Teuchos::MatrixMarket::Banner>
00302         readBanner (std::istream& in, size_t& lineNumber)
00303         {
00304           using std::cerr;
00305           using std::endl;
00306           std::string line; // The presumed banner line
00307 
00308           // The first line of the Matrix Market file should always be
00309           // the banner line.  In tolerant mode, we allow comment
00310           // lines before the banner line.  This complicates detection
00311           // of comment lines a bit.
00312           if (tolerant_) {
00313             // Keep reading lines until we get a noncomment line.
00314             const bool maybeBannerLine = true;
00315             size_t numLinesRead = 0;
00316             bool commentLine = false;
00317             do {
00318               // Try to read a line from the input stream.
00319               const bool readFailed = ! getline (in, line);
00320               TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
00321                 "Failed to get Matrix Market banner line from input, after reading "
00322                 << numLinesRead << "line" << (numLinesRead != 1 ? "s." : "."));
00323               // We read a line from the input stream.
00324               ++lineNumber;
00325               ++numLinesRead;
00326               size_t start, size; // Output args of checkCommentLine
00327               commentLine = checkCommentLine (line, start, size, lineNumber,
00328                                               tolerant_, maybeBannerLine);
00329             } while (commentLine); // Loop until we find a noncomment line.
00330           }
00331           else {
00332             const bool readFailed = ! getline (in, line);
00333             TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
00334               "Failed to get Matrix Market banner line from input.  This "
00335               "probably means that the file is empty (contains zero lines).");
00336           }
00337 
00338           if (debug_) {
00339             cerr << "Raw::Checker::readBanner: Here is the presumed banner line:"
00340                  << endl << line << endl;
00341           }
00342 
00343           // Assume that the noncomment line we found is the banner line.
00344           RCP<Banner> banner;
00345           try {
00346             banner = rcp (new Banner (line, tolerant_));
00347           } catch (std::exception& e) {
00348             TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
00349               "Matrix Market file's banner line contains syntax error(s): "
00350               << e.what ());
00351           }
00352           return rcp_const_cast<const Banner> (banner);
00353         }
00354 
00364         std::pair<bool, std::string>
00365         readOnRank0 (std::istream& in)
00366         {
00367           using std::cerr;
00368           using std::cout;
00369           using std::endl;
00370           typedef ScalarTraits<Scalar> STS;
00371 
00372           // This "Adder" knows how to add sparse matrix entries,
00373           // given a line of data from the file.  It also stores the
00374           // entries and can sort them.
00375           typedef Adder<Scalar, Ordinal> raw_adder_type;
00376           // SymmetrizingAdder "advices" (yes, I'm using that as a verb)
00377           // the original Adder, so that additional entries are filled
00378           // in symmetrically, if the Matrix Market banner line
00379           // specified a symmetry type other than "general".
00380           typedef SymmetrizingAdder<raw_adder_type> adder_type;
00381 
00382           // Current line number of the input stream.
00383           size_t lineNumber = 1;
00384 
00385           // Construct the "Banner" (matrix metadata, including type
00386           // and symmetry information, but not dimensions).
00387           std::ostringstream err;
00388           RCP<const Banner> pBanner;
00389           try {
00390             pBanner = readBanner (in, lineNumber);
00391           }
00392           catch (std::exception& e) {
00393             err << "Failed to read Matrix Market file's Banner: " << e.what();
00394             return std::make_pair (false, err.str());
00395           }
00396           //
00397           // Validate the metadata in the Banner.
00398           //
00399           if (pBanner->matrixType () != "coordinate") {
00400             err << "Matrix Market input file must contain a \"coordinate\"-"
00401               "format sparse matrix in order to create a sparse matrix object "
00402               "from it.";
00403             return std::make_pair (false, err.str ());
00404           }
00405           else if (! STS::isComplex && pBanner->dataType () == "complex") {
00406             err << "The Matrix Market sparse matrix file contains complex-"
00407               "valued data, but you are try to read the data into a sparse "
00408               "matrix containing real values (your matrix's Scalar type is "
00409               "real).";
00410             return std::make_pair (false, err.str ());
00411           }
00412           else if (pBanner->dataType () != "real" &&
00413                    pBanner->dataType () != "complex") {
00414             err << "Only real or complex data types (no pattern or integer "
00415               "matrices) are currently supported.";
00416             return std::make_pair (false, err.str ());
00417           }
00418           if (debug_) {
00419             cerr << "Banner line:" << endl << *pBanner << endl;
00420           }
00421 
00422           // The reader will invoke the adder (see below) once for
00423           // each matrix entry it reads from the input stream.
00424           typedef CoordDataReader<adder_type, Ordinal, Scalar,
00425             STS::isComplex> reader_type;
00426           // We will set the adder below, after calling readDimensions().
00427           reader_type reader;
00428 
00429           // Read in the dimensions of the sparse matrix: (# rows, #
00430           // columns, # matrix entries (counting duplicates as
00431           // separate entries)).  The second element of the pair tells
00432           // us whether the values were gotten successfully.
00433           std::pair<Tuple<Ordinal, 3>, bool> dims =
00434             reader.readDimensions (in, lineNumber, tolerant_);
00435           if (! dims.second) {
00436             err << "Error reading Matrix Market sparse matrix "
00437               "file: failed to read coordinate dimensions.";
00438             return std::make_pair (false, err.str ());
00439           }
00440           // These are "expected" values read from the input stream's
00441           // metadata.  The actual matrix entries read from the input
00442           // stream might not conform to their constraints.  We allow
00443           // such nonconformity only in "tolerant" mode; otherwise, we
00444           // throw an exception.
00445           const Ordinal numRows = dims.first[0];
00446           const Ordinal numCols = dims.first[1];
00447           const Ordinal numEntries = dims.first[2];
00448           if (debug_) {
00449             cerr << "Reported dimensions: " << numRows << " x " << numCols
00450                  << ", with " << numEntries << " entries (counting possible "
00451                  << "duplicates)." << endl;
00452           }
00453 
00454           // The "raw" adder knows about the expected matrix
00455           // dimensions, but doesn't know about symmetry.
00456           RCP<raw_adder_type> rawAdder =
00457             rcp (new raw_adder_type (numRows, numCols, numEntries,
00458                                      tolerant_, debug_));
00459           // The symmetrizing adder knows about symmetry.
00460           RCP<adder_type> adder =
00461             rcp (new adder_type (rawAdder, pBanner->symmType ()));
00462 
00463           // Give the adder to the reader.
00464           reader.setAdder (adder);
00465 
00466           // Read the sparse matrix entries.  "results" just tells us if
00467           // and where there were any bad lines of input.  The actual
00468           // sparse matrix entries are stored in the (raw) Adder object.
00469           std::pair<bool, std::vector<size_t> > results =
00470             reader.read (in, lineNumber, tolerant_, debug_);
00471           if (debug_) {
00472             if (results.first) {
00473               cerr << "Matrix Market file successfully read" << endl;
00474             }
00475             else {
00476               cerr << "Failed to read Matrix Market file" << endl;
00477             }
00478           }
00479 
00480           // Report any bad line number(s).
00481           if (! results.first) {
00482             if (! tolerant_) {
00483               err << "The Matrix Market input stream had syntax error(s)."
00484                 "  Here is the error report." << endl;
00485               reportBadness (err, results);
00486               err << endl;
00487               return std::make_pair (false, err.str ());
00488             }
00489             else {
00490               if (debug_) {
00491                 reportBadness (cerr, results);
00492               }
00493             }
00494           }
00495           // We're done reading in the sparse matrix.  If we're in
00496           // "echo" mode, print out the matrix entries to stdout.  The
00497           // entries will have been symmetrized if applicable.
00498           if (echo_) {
00499             const bool doMerge = false;
00500             const bool replace = false;
00501             rawAdder->print (cout, doMerge, replace);
00502             cout << endl;
00503           }
00504           return std::make_pair (true, err.str());
00505         }
00506 
00508         void
00509         reportBadness (std::ostream& out,
00510                        const std::pair<bool, std::vector<size_t> >& results)
00511         {
00512           using std::endl;
00513           const size_t numErrors = results.second.size();
00514           const size_t maxNumErrorsToReport = 20;
00515           out << numErrors << " errors when reading Matrix Market sparse "
00516             "matrix file." << endl;
00517           if (numErrors > maxNumErrorsToReport) {
00518             out << "-- We do not report individual errors when there "
00519               "are more than " << maxNumErrorsToReport << ".";
00520           }
00521           else if (numErrors == 1) {
00522             out << "Error on line " << results.second[0] << endl;
00523           }
00524           else if (numErrors > 1) {
00525             out << "Errors on lines {";
00526             for (size_t k = 0; k < numErrors-1; ++k) {
00527               out << results.second[k] << ", ";
00528             }
00529             out << results.second[numErrors-1] << "}" << endl;
00530           }
00531         }
00532       }; // end of class Checker
00533     } // namespace Raw
00534   } // namespace MatrixMarket
00535 } // namespace Teuchos
00536 
00537 #endif // __Teuchos_MatrixMarket_Raw_Checker_hpp
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines