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 // 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_Checker_hpp
11 #define __Teuchos_MatrixMarket_Raw_Checker_hpp
12 
16 
17 
18 namespace Teuchos {
19  namespace MatrixMarket {
20  namespace Raw {
35  template<class Scalar, class Ordinal>
36  class Checker {
37  public:
47  Checker (const bool echo, const bool tolerant, const bool debug) :
48  echo_ (echo), tolerant_ (tolerant), debug_ (debug)
49  {}
50 
52  Checker () :
53  echo_ (false), tolerant_ (false), debug_ (false)
54  {}
55 
66  Checker (const RCP<ParameterList>& params) :
67  echo_ (false), tolerant_ (false), debug_ (false)
68  {
69  setParameters (params);
70  }
71 
75  void
77  {
78  // Default parameter values.
79  bool echo = false;
80  bool tolerant = false;
81  bool debug = false;
82 
83  // Read parameters.
84  echo = params->get ("Echo to stdout", echo);
85  tolerant = params->get ("Parse tolerantly", tolerant);
86  debug = params->get ("Debug mode", debug);
87 
88  // No side effects on the class until ParameterList
89  // processing is complete.
90  echo_ = echo;
91  tolerant_ = tolerant;
92  debug_ = debug;
93  }
94 
97  {
98  // Default parameter values.
99  const bool echo = false;
100  const bool tolerant = false;
101  const bool debug = false;
102 
103  // Set default parameters with documentation.
104  RCP<ParameterList> params = parameterList ("Matrix Market Checker");
105  params->set ("Echo to stdout", echo, "Whether to echo the sparse "
106  "matrix to stdout after reading it");
107  params->set ("Parse tolerantly", tolerant, "Whether to tolerate "
108  "syntax errors when parsing the Matrix Market file");
109  params->set ("Debug mode", debug, "Whether to print debugging output "
110  "to stderr, on all participating MPI processes");
111 
112  return rcp_const_cast<const ParameterList> (params);
113  }
114 
125  bool
127  const std::string& filename)
128  {
129  using std::cerr;
130  using std::endl;
131 
132  const int myRank = comm.getRank ();
133  // Teuchos::broadcast doesn't accept a bool; we use an int
134  // instead, with the usual 1->true, 0->false Boolean
135  // interpretation.
136  int didReadFile = 0;
137  RCP<std::ifstream> in; // only valid on Rank 0
138  if (myRank == 0) {
139  if (debug_) {
140  cerr << "Attempting to open file \"" << filename
141  << "\" on Rank 0...";
142  }
143  in = rcp (new std::ifstream (filename.c_str()));
144  if (! *in) {
145  didReadFile = 0;
146  if (debug_) {
147  cerr << "failed." << endl;
148  }
149  }
150  else {
151  didReadFile = 1;
152  if (debug_) {
153  cerr << "succeeded." << endl;
154  }
155  }
156  }
157  Teuchos::broadcast (comm, 0, &didReadFile);
158  // All MPI processes should throw at the same time, or none.
159  TEUCHOS_TEST_FOR_EXCEPTION(! didReadFile, std::runtime_error,
160  "Failed to open input file \"" + filename + "\".");
161  // Only Rank 0 will try to dereference "in".
162  return read (comm, in);
163  }
164 
175  bool
176  read (const Teuchos::Comm<int>& comm,
177  const RCP<std::istream>& in)
178  {
179  using std::cerr;
180  using std::endl;
181 
182  const int myRank = comm.getRank ();
183  std::pair<bool, std::string> result;
184  int msgSize = 0; // Size of error message (if any)
185  if (myRank == 0) {
186  if (in.is_null()) {
187  result.first = false;
188  result.second = "Input stream is null on Rank 0";
189  }
190  else {
191  if (debug_) {
192  cerr << "About to read from input stream on Rank 0" << endl;
193  }
194  result = readOnRank0 (*in);
195  if (debug_) {
196  if (result.first) {
197  cerr << "Successfully read sparse matrix from "
198  "input stream on Rank 0" << endl;
199  }
200  else {
201  cerr << "Failed to read sparse matrix from input "
202  "stream on Rank 0" << endl;
203  }
204  }
205  }
206  if (result.first) {
207  msgSize = 0;
208  }
209  else {
210  msgSize = result.second.size();
211  }
212  }
213  int success = result.first ? 1 : 0;
214  Teuchos::broadcast (comm, 0, &success);
215  if (! success) {
216  if (! tolerant_) {
217  // Tell all ranks how long the error message is, so
218  // they can make space for it in order to receive
219  // the broadcast of the error message.
220  Teuchos::broadcast (comm, 0, &msgSize);
221 
222  if (msgSize > 0) {
223  std::string errMsg (msgSize, ' ');
224  if (myRank == 0) {
225  std::copy (result.second.begin(), result.second.end(),
226  errMsg.begin());
227  }
228  Teuchos::broadcast (comm, 0, static_cast<int> (msgSize), &errMsg[0]);
229  TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, errMsg);
230  }
231  else {
232  TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error,
233  "Unknown error when reading Matrix Market sparse matrix file; "
234  "the error is \"unknown\" because the error message has length 0.");
235  }
236  }
237  else if (myRank == 0) {
238  using std::cerr;
239  using std::endl;
240  cerr << "The following error occurred when reading the "
241  "sparse matrix: " << result.second << endl;
242  }
243  }
244  return success;
245  }
246 
247  private:
249  bool echo_;
251  bool tolerant_;
253  bool debug_;
254 
270  readBanner (std::istream& in, size_t& lineNumber)
271  {
272  using std::cerr;
273  using std::endl;
274  std::string line; // The presumed banner line
275 
276  // The first line of the Matrix Market file should always be
277  // the banner line. In tolerant mode, we allow comment
278  // lines before the banner line. This complicates detection
279  // of comment lines a bit.
280  if (tolerant_) {
281  // Keep reading lines until we get a noncomment line.
282  const bool maybeBannerLine = true;
283  size_t numLinesRead = 0;
284  bool commentLine = false;
285  do {
286  // Try to read a line from the input stream.
287  const bool readFailed = ! getline (in, line);
288  TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
289  "Failed to get Matrix Market banner line from input, after reading "
290  << numLinesRead << "line" << (numLinesRead != 1 ? "s." : "."));
291  // We read a line from the input stream.
292  ++lineNumber;
293  ++numLinesRead;
294  size_t start, size; // Output args of checkCommentLine
295  commentLine = checkCommentLine (line, start, size, lineNumber,
296  tolerant_, maybeBannerLine);
297  } while (commentLine); // Loop until we find a noncomment line.
298  }
299  else {
300  const bool readFailed = ! getline (in, line);
301  TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
302  "Failed to get Matrix Market banner line from input. This "
303  "probably means that the file is empty (contains zero lines).");
304  }
305 
306  if (debug_) {
307  cerr << "Raw::Checker::readBanner: Here is the presumed banner line:"
308  << endl << line << endl;
309  }
310 
311  // Assume that the noncomment line we found is the banner line.
312  RCP<Banner> banner;
313  try {
314  banner = rcp (new Banner (line, tolerant_));
315  } catch (std::exception& e) {
316  TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
317  "Matrix Market file's banner line contains syntax error(s): "
318  << e.what ());
319  }
320  return rcp_const_cast<const Banner> (banner);
321  }
322 
332  std::pair<bool, std::string>
333  readOnRank0 (std::istream& in)
334  {
335  using std::cerr;
336  using std::cout;
337  using std::endl;
338  typedef ScalarTraits<Scalar> STS;
339 
340  // This "Adder" knows how to add sparse matrix entries,
341  // given a line of data from the file. It also stores the
342  // entries and can sort them.
343  typedef Adder<Scalar, Ordinal> raw_adder_type;
344  // SymmetrizingAdder "advices" (yes, I'm using that as a verb)
345  // the original Adder, so that additional entries are filled
346  // in symmetrically, if the Matrix Market banner line
347  // specified a symmetry type other than "general".
348  typedef SymmetrizingAdder<raw_adder_type> adder_type;
349 
350  // Current line number of the input stream.
351  size_t lineNumber = 1;
352 
353  // Construct the "Banner" (matrix metadata, including type
354  // and symmetry information, but not dimensions).
355  std::ostringstream err;
356  RCP<const Banner> pBanner;
357  try {
358  pBanner = readBanner (in, lineNumber);
359  }
360  catch (std::exception& e) {
361  err << "Failed to read Matrix Market file's Banner: " << e.what();
362  return std::make_pair (false, err.str());
363  }
364  //
365  // Validate the metadata in the Banner.
366  //
367  if (pBanner->matrixType () != "coordinate") {
368  err << "Matrix Market input file must contain a \"coordinate\"-"
369  "format sparse matrix in order to create a sparse matrix object "
370  "from it.";
371  return std::make_pair (false, err.str ());
372  }
373  else if (! STS::isComplex && pBanner->dataType () == "complex") {
374  err << "The Matrix Market sparse matrix file contains complex-"
375  "valued data, but you are try to read the data into a sparse "
376  "matrix containing real values (your matrix's Scalar type is "
377  "real).";
378  return std::make_pair (false, err.str ());
379  }
380  else if (pBanner->dataType () != "real" &&
381  pBanner->dataType () != "complex") {
382  err << "Only real or complex data types (no pattern or integer "
383  "matrices) are currently supported.";
384  return std::make_pair (false, err.str ());
385  }
386  if (debug_) {
387  cerr << "Banner line:" << endl << *pBanner << endl;
388  }
389 
390  // The reader will invoke the adder (see below) once for
391  // each matrix entry it reads from the input stream.
392  typedef CoordDataReader<adder_type, Ordinal, Scalar,
393  STS::isComplex> reader_type;
394  // We will set the adder below, after calling readDimensions().
395  reader_type reader;
396 
397  // Read in the dimensions of the sparse matrix: (# rows, #
398  // columns, # matrix entries (counting duplicates as
399  // separate entries)). The second element of the pair tells
400  // us whether the values were gotten successfully.
401  std::pair<Tuple<Ordinal, 3>, bool> dims =
402  reader.readDimensions (in, lineNumber, tolerant_);
403  if (! dims.second) {
404  err << "Error reading Matrix Market sparse matrix "
405  "file: failed to read coordinate dimensions.";
406  return std::make_pair (false, err.str ());
407  }
408  // These are "expected" values read from the input stream's
409  // metadata. The actual matrix entries read from the input
410  // stream might not conform to their constraints. We allow
411  // such nonconformity only in "tolerant" mode; otherwise, we
412  // throw an exception.
413  const Ordinal numRows = dims.first[0];
414  const Ordinal numCols = dims.first[1];
415  const Ordinal numEntries = dims.first[2];
416  if (debug_) {
417  cerr << "Reported dimensions: " << numRows << " x " << numCols
418  << ", with " << numEntries << " entries (counting possible "
419  << "duplicates)." << endl;
420  }
421 
422  // The "raw" adder knows about the expected matrix
423  // dimensions, but doesn't know about symmetry.
424  RCP<raw_adder_type> rawAdder =
425  rcp (new raw_adder_type (numRows, numCols, numEntries,
426  tolerant_, debug_));
427  // The symmetrizing adder knows about symmetry.
428  RCP<adder_type> adder =
429  rcp (new adder_type (rawAdder, pBanner->symmType ()));
430 
431  // Give the adder to the reader.
432  reader.setAdder (adder);
433 
434  // Read the sparse matrix entries. "results" just tells us if
435  // and where there were any bad lines of input. The actual
436  // sparse matrix entries are stored in the (raw) Adder object.
437  std::pair<bool, std::vector<size_t> > results =
438  reader.read (in, lineNumber, tolerant_, debug_);
439  if (debug_) {
440  if (results.first) {
441  cerr << "Matrix Market file successfully read" << endl;
442  }
443  else {
444  cerr << "Failed to read Matrix Market file" << endl;
445  }
446  }
447 
448  // Report any bad line number(s).
449  if (! results.first) {
450  if (! tolerant_) {
451  err << "The Matrix Market input stream had syntax error(s)."
452  " Here is the error report." << endl;
453  reportBadness (err, results);
454  err << endl;
455  return std::make_pair (false, err.str ());
456  }
457  else {
458  if (debug_) {
459  reportBadness (cerr, results);
460  }
461  }
462  }
463  // We're done reading in the sparse matrix. If we're in
464  // "echo" mode, print out the matrix entries to stdout. The
465  // entries will have been symmetrized if applicable.
466  if (echo_) {
467  const bool doMerge = false;
468  const bool replace = false;
469  rawAdder->print (cout, doMerge, replace);
470  cout << endl;
471  }
472  return std::make_pair (true, err.str());
473  }
474 
476  void
477  reportBadness (std::ostream& out,
478  const std::pair<bool, std::vector<size_t> >& results)
479  {
480  using std::endl;
481  const size_t numErrors = results.second.size();
482  const size_t maxNumErrorsToReport = 20;
483  out << numErrors << " errors when reading Matrix Market sparse "
484  "matrix file." << endl;
485  if (numErrors > maxNumErrorsToReport) {
486  out << "-- We do not report individual errors when there "
487  "are more than " << maxNumErrorsToReport << ".";
488  }
489  else if (numErrors == 1) {
490  out << "Error on line " << results.second[0] << endl;
491  }
492  else if (numErrors > 1) {
493  out << "Errors on lines {";
494  for (size_t k = 0; k < numErrors-1; ++k) {
495  out << results.second[k] << ", ";
496  }
497  out << results.second[numErrors-1] << "}" << endl;
498  }
499  }
500  }; // end of class Checker
501  } // namespace Raw
502  } // namespace MatrixMarket
503 } // namespace Teuchos
504 
505 #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.