Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_MatrixMarket_Raw_Checker.hpp
Go to the documentation of this file.
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Tpetra: Templated Linear Algebra Services Package
5 // Copyright (2008) Sandia Corporation
6 //
7 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8 // the U.S. Government retains certain rights in this software.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
38 //
39 // ************************************************************************
40 // @HEADER
41 
42 #ifndef __Teuchos_MatrixMarket_Raw_Checker_hpp
43 #define __Teuchos_MatrixMarket_Raw_Checker_hpp
44 
48 
49 
50 namespace Teuchos {
51  namespace MatrixMarket {
52  namespace Raw {
67  template<class Scalar, class Ordinal>
68  class Checker {
69  public:
79  Checker (const bool echo, const bool tolerant, const bool debug) :
80  echo_ (echo), tolerant_ (tolerant), debug_ (debug)
81  {}
82 
84  Checker () :
85  echo_ (false), tolerant_ (false), debug_ (false)
86  {}
87 
98  Checker (const RCP<ParameterList>& params) :
99  echo_ (false), tolerant_ (false), debug_ (false)
100  {
101  setParameters (params);
102  }
103 
107  void
109  {
110  // Default parameter values.
111  bool echo = false;
112  bool tolerant = false;
113  bool debug = false;
114 
115  // Read parameters.
116  echo = params->get ("Echo to stdout", echo);
117  tolerant = params->get ("Parse tolerantly", tolerant);
118  debug = params->get ("Debug mode", debug);
119 
120  // No side effects on the class until ParameterList
121  // processing is complete.
122  echo_ = echo;
123  tolerant_ = tolerant;
124  debug_ = debug;
125  }
126 
129  {
130  // Default parameter values.
131  const bool echo = false;
132  const bool tolerant = false;
133  const bool debug = false;
134 
135  // Set default parameters with documentation.
136  RCP<ParameterList> params = parameterList ("Matrix Market Checker");
137  params->set ("Echo to stdout", echo, "Whether to echo the sparse "
138  "matrix to stdout after reading it");
139  params->set ("Parse tolerantly", tolerant, "Whether to tolerate "
140  "syntax errors when parsing the Matrix Market file");
141  params->set ("Debug mode", debug, "Whether to print debugging output "
142  "to stderr, on all participating MPI processes");
143 
144  return rcp_const_cast<const ParameterList> (params);
145  }
146 
157  bool
159  const std::string& filename)
160  {
161  using std::cerr;
162  using std::endl;
163 
164  const int myRank = comm.getRank ();
165  // Teuchos::broadcast doesn't accept a bool; we use an int
166  // instead, with the usual 1->true, 0->false Boolean
167  // interpretation.
168  int didReadFile = 0;
169  RCP<std::ifstream> in; // only valid on Rank 0
170  if (myRank == 0) {
171  if (debug_) {
172  cerr << "Attempting to open file \"" << filename
173  << "\" on Rank 0...";
174  }
175  in = rcp (new std::ifstream (filename.c_str()));
176  if (! *in) {
177  didReadFile = 0;
178  if (debug_) {
179  cerr << "failed." << endl;
180  }
181  }
182  else {
183  didReadFile = 1;
184  if (debug_) {
185  cerr << "succeeded." << endl;
186  }
187  }
188  }
189  Teuchos::broadcast (comm, 0, &didReadFile);
190  // All MPI processes should throw at the same time, or none.
191  TEUCHOS_TEST_FOR_EXCEPTION(! didReadFile, std::runtime_error,
192  "Failed to open input file \"" + filename + "\".");
193  // Only Rank 0 will try to dereference "in".
194  return read (comm, in);
195  }
196 
207  bool
208  read (const Teuchos::Comm<int>& comm,
209  const RCP<std::istream>& in)
210  {
211  using std::cerr;
212  using std::endl;
213 
214  const int myRank = comm.getRank ();
215  std::pair<bool, std::string> result;
216  int msgSize = 0; // Size of error message (if any)
217  if (myRank == 0) {
218  if (in.is_null()) {
219  result.first = false;
220  result.second = "Input stream is null on Rank 0";
221  }
222  else {
223  if (debug_) {
224  cerr << "About to read from input stream on Rank 0" << endl;
225  }
226  result = readOnRank0 (*in);
227  if (debug_) {
228  if (result.first) {
229  cerr << "Successfully read sparse matrix from "
230  "input stream on Rank 0" << endl;
231  }
232  else {
233  cerr << "Failed to read sparse matrix from input "
234  "stream on Rank 0" << endl;
235  }
236  }
237  }
238  if (result.first) {
239  msgSize = 0;
240  }
241  else {
242  msgSize = result.second.size();
243  }
244  }
245  int success = result.first ? 1 : 0;
246  Teuchos::broadcast (comm, 0, &success);
247  if (! success) {
248  if (! tolerant_) {
249  // Tell all ranks how long the error message is, so
250  // they can make space for it in order to receive
251  // the broadcast of the error message.
252  Teuchos::broadcast (comm, 0, &msgSize);
253 
254  if (msgSize > 0) {
255  std::string errMsg (msgSize, ' ');
256  if (myRank == 0) {
257  std::copy (result.second.begin(), result.second.end(),
258  errMsg.begin());
259  }
260  Teuchos::broadcast (comm, 0, static_cast<int> (msgSize), &errMsg[0]);
261  TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, errMsg);
262  }
263  else {
264  TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error,
265  "Unknown error when reading Matrix Market sparse matrix file; "
266  "the error is \"unknown\" because the error message has length 0.");
267  }
268  }
269  else if (myRank == 0) {
270  using std::cerr;
271  using std::endl;
272  cerr << "The following error occurred when reading the "
273  "sparse matrix: " << result.second << endl;
274  }
275  }
276  return success;
277  }
278 
279  private:
281  bool echo_;
283  bool tolerant_;
285  bool debug_;
286 
302  readBanner (std::istream& in, size_t& lineNumber)
303  {
304  using std::cerr;
305  using std::endl;
306  std::string line; // The presumed banner line
307 
308  // The first line of the Matrix Market file should always be
309  // the banner line. In tolerant mode, we allow comment
310  // lines before the banner line. This complicates detection
311  // of comment lines a bit.
312  if (tolerant_) {
313  // Keep reading lines until we get a noncomment line.
314  const bool maybeBannerLine = true;
315  size_t numLinesRead = 0;
316  bool commentLine = false;
317  do {
318  // Try to read a line from the input stream.
319  const bool readFailed = ! getline (in, line);
320  TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
321  "Failed to get Matrix Market banner line from input, after reading "
322  << numLinesRead << "line" << (numLinesRead != 1 ? "s." : "."));
323  // We read a line from the input stream.
324  ++lineNumber;
325  ++numLinesRead;
326  size_t start, size; // Output args of checkCommentLine
327  commentLine = checkCommentLine (line, start, size, lineNumber,
328  tolerant_, maybeBannerLine);
329  } while (commentLine); // Loop until we find a noncomment line.
330  }
331  else {
332  const bool readFailed = ! getline (in, line);
333  TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
334  "Failed to get Matrix Market banner line from input. This "
335  "probably means that the file is empty (contains zero lines).");
336  }
337 
338  if (debug_) {
339  cerr << "Raw::Checker::readBanner: Here is the presumed banner line:"
340  << endl << line << endl;
341  }
342 
343  // Assume that the noncomment line we found is the banner line.
344  RCP<Banner> banner;
345  try {
346  banner = rcp (new Banner (line, tolerant_));
347  } catch (std::exception& e) {
348  TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
349  "Matrix Market file's banner line contains syntax error(s): "
350  << e.what ());
351  }
352  return rcp_const_cast<const Banner> (banner);
353  }
354 
364  std::pair<bool, std::string>
365  readOnRank0 (std::istream& in)
366  {
367  using std::cerr;
368  using std::cout;
369  using std::endl;
370  typedef ScalarTraits<Scalar> STS;
371 
372  // This "Adder" knows how to add sparse matrix entries,
373  // given a line of data from the file. It also stores the
374  // entries and can sort them.
375  typedef Adder<Scalar, Ordinal> raw_adder_type;
376  // SymmetrizingAdder "advices" (yes, I'm using that as a verb)
377  // the original Adder, so that additional entries are filled
378  // in symmetrically, if the Matrix Market banner line
379  // specified a symmetry type other than "general".
380  typedef SymmetrizingAdder<raw_adder_type> adder_type;
381 
382  // Current line number of the input stream.
383  size_t lineNumber = 1;
384 
385  // Construct the "Banner" (matrix metadata, including type
386  // and symmetry information, but not dimensions).
387  std::ostringstream err;
388  RCP<const Banner> pBanner;
389  try {
390  pBanner = readBanner (in, lineNumber);
391  }
392  catch (std::exception& e) {
393  err << "Failed to read Matrix Market file's Banner: " << e.what();
394  return std::make_pair (false, err.str());
395  }
396  //
397  // Validate the metadata in the Banner.
398  //
399  if (pBanner->matrixType () != "coordinate") {
400  err << "Matrix Market input file must contain a \"coordinate\"-"
401  "format sparse matrix in order to create a sparse matrix object "
402  "from it.";
403  return std::make_pair (false, err.str ());
404  }
405  else if (! STS::isComplex && pBanner->dataType () == "complex") {
406  err << "The Matrix Market sparse matrix file contains complex-"
407  "valued data, but you are try to read the data into a sparse "
408  "matrix containing real values (your matrix's Scalar type is "
409  "real).";
410  return std::make_pair (false, err.str ());
411  }
412  else if (pBanner->dataType () != "real" &&
413  pBanner->dataType () != "complex") {
414  err << "Only real or complex data types (no pattern or integer "
415  "matrices) are currently supported.";
416  return std::make_pair (false, err.str ());
417  }
418  if (debug_) {
419  cerr << "Banner line:" << endl << *pBanner << endl;
420  }
421 
422  // The reader will invoke the adder (see below) once for
423  // each matrix entry it reads from the input stream.
424  typedef CoordDataReader<adder_type, Ordinal, Scalar,
425  STS::isComplex> reader_type;
426  // We will set the adder below, after calling readDimensions().
427  reader_type reader;
428 
429  // Read in the dimensions of the sparse matrix: (# rows, #
430  // columns, # matrix entries (counting duplicates as
431  // separate entries)). The second element of the pair tells
432  // us whether the values were gotten successfully.
433  std::pair<Tuple<Ordinal, 3>, bool> dims =
434  reader.readDimensions (in, lineNumber, tolerant_);
435  if (! dims.second) {
436  err << "Error reading Matrix Market sparse matrix "
437  "file: failed to read coordinate dimensions.";
438  return std::make_pair (false, err.str ());
439  }
440  // These are "expected" values read from the input stream's
441  // metadata. The actual matrix entries read from the input
442  // stream might not conform to their constraints. We allow
443  // such nonconformity only in "tolerant" mode; otherwise, we
444  // throw an exception.
445  const Ordinal numRows = dims.first[0];
446  const Ordinal numCols = dims.first[1];
447  const Ordinal numEntries = dims.first[2];
448  if (debug_) {
449  cerr << "Reported dimensions: " << numRows << " x " << numCols
450  << ", with " << numEntries << " entries (counting possible "
451  << "duplicates)." << endl;
452  }
453 
454  // The "raw" adder knows about the expected matrix
455  // dimensions, but doesn't know about symmetry.
456  RCP<raw_adder_type> rawAdder =
457  rcp (new raw_adder_type (numRows, numCols, numEntries,
458  tolerant_, debug_));
459  // The symmetrizing adder knows about symmetry.
460  RCP<adder_type> adder =
461  rcp (new adder_type (rawAdder, pBanner->symmType ()));
462 
463  // Give the adder to the reader.
464  reader.setAdder (adder);
465 
466  // Read the sparse matrix entries. "results" just tells us if
467  // and where there were any bad lines of input. The actual
468  // sparse matrix entries are stored in the (raw) Adder object.
469  std::pair<bool, std::vector<size_t> > results =
470  reader.read (in, lineNumber, tolerant_, debug_);
471  if (debug_) {
472  if (results.first) {
473  cerr << "Matrix Market file successfully read" << endl;
474  }
475  else {
476  cerr << "Failed to read Matrix Market file" << endl;
477  }
478  }
479 
480  // Report any bad line number(s).
481  if (! results.first) {
482  if (! tolerant_) {
483  err << "The Matrix Market input stream had syntax error(s)."
484  " Here is the error report." << endl;
485  reportBadness (err, results);
486  err << endl;
487  return std::make_pair (false, err.str ());
488  }
489  else {
490  if (debug_) {
491  reportBadness (cerr, results);
492  }
493  }
494  }
495  // We're done reading in the sparse matrix. If we're in
496  // "echo" mode, print out the matrix entries to stdout. The
497  // entries will have been symmetrized if applicable.
498  if (echo_) {
499  const bool doMerge = false;
500  const bool replace = false;
501  rawAdder->print (cout, doMerge, replace);
502  cout << endl;
503  }
504  return std::make_pair (true, err.str());
505  }
506 
508  void
509  reportBadness (std::ostream& out,
510  const std::pair<bool, std::vector<size_t> >& results)
511  {
512  using std::endl;
513  const size_t numErrors = results.second.size();
514  const size_t maxNumErrorsToReport = 20;
515  out << numErrors << " errors when reading Matrix Market sparse "
516  "matrix file." << endl;
517  if (numErrors > maxNumErrorsToReport) {
518  out << "-- We do not report individual errors when there "
519  "are more than " << maxNumErrorsToReport << ".";
520  }
521  else if (numErrors == 1) {
522  out << "Error on line " << results.second[0] << endl;
523  }
524  else if (numErrors > 1) {
525  out << "Errors on lines {";
526  for (size_t k = 0; k < numErrors-1; ++k) {
527  out << results.second[k] << ", ";
528  }
529  out << results.second[numErrors-1] << "}" << endl;
530  }
531  }
532  }; // end of class Checker
533  } // namespace Raw
534  } // namespace MatrixMarket
535 } // namespace Teuchos
536 
537 #endif // __Teuchos_MatrixMarket_Raw_Checker_hpp
bool checkCommentLine(const std::string &line, size_t &start, size_t &size, const size_t lineNumber, const bool tolerant, const bool maybeBannerLine)
True if the line is a comment line, false otherwise.
RCP< const ParameterList > getValidParameters() const
virtual int getRank() const =0
Returns the rank of this process.
Adds entries with optional symmetry to a sparse matrix.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
void setParameters(const RCP< ParameterList > &params)
Set parameters from the given ParameterList.
bool echo_
Whether to echo the sparse matrix to stdout after reading it.
T * get() const
Get the raw C++ pointer to the underlying object.
Coordinate-format sparse matrix data reader.
This structure defines some basic traits for a scalar field type.
bool debug_
Whether to print debugging output to stderr.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
Checker()
Constructor that sets default Boolean parameters.
bool readFile(const Teuchos::Comm< int > &comm, const std::string &filename)
Read the sparse matrix from the given file.
void reportBadness(std::ostream &out, const std::pair< bool, std::vector< size_t > > &results)
To be called only on MPI Rank 0.
std::pair< bool, std::string > readOnRank0(std::istream &in)
Read the sparse matrix on MPI Rank 0.
A list of parameters of arbitrary type.
Checker(const RCP< ParameterList > &params)
Constructor that takes a ParameterList of parameters.
RCP< const Teuchos::MatrixMarket::Banner > readBanner(std::istream &in, size_t &lineNumber)
Read in the Banner line from the given input stream.
Smart reference counting pointer class for automatic garbage collection.
Tool for debugging the syntax of a Matrix Market file containing a sparse matrix. ...
bool tolerant_
Whether to parse the Matrix Market file tolerantly.
Parse a Matrix Market banner line.
Checker(const bool echo, const bool tolerant, const bool debug)
Constructor that takes Boolean parameters.
bool read(const Teuchos::Comm< int > &comm, const RCP< std::istream > &in)
Read the sparse matrix from the given input stream.
To be used with Checker for &quot;raw&quot; sparse matrix input.
bool is_null() const
Returns true if the underlying pointer is null.