Teuchos - Trilinos Tools Package  Version of the Day
Teuchos_MatrixMarket_Raw_Checker.hpp
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 
45 #include "Teuchos_MatrixMarket_Raw_Adder.hpp"
46 #include "Teuchos_MatrixMarket_SymmetrizingAdder.hpp"
47 #include "Teuchos_MatrixMarket_CoordDataReader.hpp"
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 
128  getValidParameters () const
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
Abstract interface for distributed-memory communication.
virtual int getRank() const =0
Returns the rank of this process.
Tool for debugging the syntax of a Matrix Market file containing a sparse matrix.
Checker()
Constructor that sets default Boolean parameters.
Checker(const bool echo, const bool tolerant, const bool debug)
Constructor that takes Boolean parameters.
void setParameters(const RCP< ParameterList > &params)
Set parameters from the given ParameterList.
Checker(const RCP< ParameterList > &params)
Constructor that takes a ParameterList of parameters.
bool readFile(const Teuchos::Comm< int > &comm, const std::string &filename)
Read the sparse matrix from the given file.
bool read(const Teuchos::Comm< int > &comm, const RCP< std::istream > &in)
Read the sparse matrix from the given input stream.
Smart reference counting pointer class for automatic garbage collection.
bool is_null() const
Returns true if the underlying pointer is null.
T * get() const
Get the raw C++ pointer to the underlying object.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
Matrix Market file utilities.
"Raw" input of sparse matrices from Matrix Market files.
The Teuchos namespace contains all of the classes, structs and enums used by Teuchos,...
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.