Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MatrixMarket_Raw_InOutTest.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
18 #include <algorithm>
19 
20 using std::endl;
21 
22 namespace {
23  // Sample Matrix Market sparse matrix file. We include this so we
24  // can test without needing to read in a file. Notice that all the
25  // decimal floating-point values in this example can be represented
26  // exactly in binary floating point. This example has correct
27  // syntax, so you won't need to use tolerant mode to parse it.
28  const char sampleMatrixMarketFile[] =
29  "%%MatrixMarket matrix coordinate real general\n"
30  "5 5 10\n"
31  "5 5 55.0\n"
32  "4 4 44.0\n"
33  "3 3 33.0\n"
34  "2 2 22.0\n"
35  "1 1 11.0\n"
36  "4 5 45.0\n"
37  "3 4 34.0\n"
38  "2 3 23.0\n"
39  "1 2 12.0\n"
40  "1 5 15.0\n";
41 
42  // Sample Matrix Market sparse matrix file for testing symmetric
43  // storage. Matrix Market format for symmetric, skew-symemtric,
44  // etc. specifies that only the lower triangle should be stored.
45  const char symmetricMatrixMarketFile[] =
46  "%%MatrixMarket matrix coordinate real symmetric\n"
47  "5 5 10\n"
48  "5 5 55.0\n"
49  "4 4 44.0\n"
50  "3 3 33.0\n"
51  "2 2 22.0\n"
52  "1 1 11.0\n"
53  "5 4 54.0\n"
54  "4 3 43.0\n"
55  "3 2 32.0\n"
56  "2 1 21.0\n"
57  "5 1 51.0\n";
58 
59 } // namespace (anonymous)
60 
61 // Benchmark driver
62  int
63 main (int argc, char *argv[])
64 {
67  using Teuchos::ArrayRCP;
68  using Teuchos::ArrayView;
69  using Teuchos::Comm;
72  using Teuchos::RCP;
73  using Teuchos::rcp;
74  using Teuchos::rcpFromRef;
75  using Teuchos::SerialComm;
76  using std::cout;
77  using std::cerr;
78  typedef double scalar_type;
79  typedef int ordinal_type;
80 
81  bool success = false;
82  // Verbosity of output
83  bool verbose = true;
84  try {
85  // Name of the Matrix Market sparse matrix file to read. If empty,
86  // use the Matrix Market example embedded as a string in this file.
87  std::string filename;
88  // If true, just check the sparse matrix file. Otherwise,
89  // do a full conversion to CSR (compressed sparse row) format.
90  bool checkOnly = false;
91  // Whether to echo the sparse matrix to stdout after reading it
92  // successfully.
93  bool echo = false;
94  // Whether to parse the Matrix Market file tolerantly.
95  bool tolerant = false;
96  // Whether to print debugging-level output
97  bool debug = false;
98 
99  CommandLineProcessor cmdp (false, true);
100  cmdp.setOption ("filename", &filename,
101  "Name of the Matrix Market sparse matrix file to read.");
102  cmdp.setOption ("checkOnly", "fullTest", &checkOnly,
103  "If true, just check the syntax of the input file. "
104  "Otherwise, do a full test.");
105  cmdp.setOption ("echo", "noecho", &echo,
106  "Whether to echo the sparse matrix contents to stdout "
107  "after reading it successfully.");
108  cmdp.setOption ("tolerant", "strict", &tolerant,
109  "Whether to tolerate syntax errors in the Matrix Market file.");
110  cmdp.setOption ("verbose", "quiet", &verbose,
111  "Print status output to stdout.");
112  cmdp.setOption ("debug", "nodebug", &debug,
113  "Print possibly copious debugging output to stderr.");
114  // Parse the command-line arguments.
115  {
116  const CommandLineProcessor::EParseCommandLineReturn parseResult =
117  cmdp.parse (argc,argv);
118  // If the caller asks us to print the documentation, or does not
119  // explicitly say to run the benchmark, we let this "test" pass
120  // trivially.
121  if (parseResult == CommandLineProcessor::PARSE_HELP_PRINTED) {
122  std::cout << "End Result: TEST PASSED" << endl;
123  return EXIT_SUCCESS;
124  }
126  parseResult != CommandLineProcessor::PARSE_SUCCESSFUL,
127  std::invalid_argument, "Failed to parse command-line arguments.");
128  }
129 
130  // Test reading in the sparse matrix. If no filename or an empty
131  // filename is specified, the test passes trivially.
132  success = true;
133  {
134  // The following tests check reading in different banners. A bug was found
135  // in the banner reader wherein banners with multiple consecutive spaces
136  // were not read correctly. These tests assure that banners with or
137  // without multiple consecutive spaces/tabs are read correctly.
138  bool xs;
139  if (verbose) {
140  cout << "Checking MatrixMarket banner parsing\n";
141  }
142  {
143  // Well formatted banner, passes trivially
144  if (verbose) cout << "Reading first banner\n";
145  typedef Checker<scalar_type, ordinal_type> checker_type;
146  checker_type checker (echo, false, false);
147  RCP<const Comm<int> > comm = rcp (new SerialComm<int>);
148  std::stringstream in;
149  in.str("%%MatrixMarket matrix coordinate real symmetric\n0 0 0\n");
150  RCP<std::istream> inStream = rcpFromRef(in);
151  xs = checker.read (*comm, inStream);
152  if (verbose) {
153  cout << "Banner read " << (!xs ? "un" : "") << "successfully\n";
154  }
155  success = success && xs;
156  }
157 
158  {
159  // Banner with multiple adjacent/consecutive spaces/tabs
160  if (verbose) cout << "Reading second banner\n";
161  typedef Checker<scalar_type, ordinal_type> checker_type;
162  checker_type checker (echo, false, false);
163  RCP<const Comm<int> > comm = rcp (new SerialComm<int>);
164  std::stringstream in;
165  in.str("%%MatrixMarket\tmatrix\t\tcoordinate real symmetric\n0 0 0\n");
166  RCP<std::istream> inStream = rcpFromRef(in);
167  xs = checker.read (*comm, inStream);
168  if (verbose) {
169  cout << "Banner read " << (!xs ? "un" : "") << "successfully\n";
170  }
171  success = success && xs;
172  }
173 
174  {
175  // Bad value in banner. Should throw std::runtime_error
176  if (verbose) cout << "Reading third banner\n";
177  typedef Checker<scalar_type, ordinal_type> checker_type;
178  checker_type checker (echo, false, false);
179  RCP<const Comm<int> > comm = rcp (new SerialComm<int>);
180  std::stringstream in;
181  try {
182  in.str("%%MatrixMarket matrix coordinate real xyz\n0 0 0\n");
183  RCP<std::istream> inStream = rcpFromRef(in);
184  checker.read (*comm, inStream);
185  // The call to read *should* raise an error and the following line
186  // should not be encountered
187  xs = false;
188  }
189  catch (const std::runtime_error& e) {
190  // The error message will include that "xyz" is a bad value. Check
191  // that the string "xyz" is in the error mesage.
192  std::string es(e.what());
193  xs = es.find("xyz") != std::string::npos;
194  }
195  if (verbose) {
196  cout << "Banner read " << (!xs ? "un" : "") << "successfully\n";
197  }
198  success = success && xs;
199  }
200 
201  if (verbose) {
202  if (success) {
203  cout << "Banners parsed successfully\n";
204  } else {
205  cout << "Banners not parsed successfully\n";
206  }
207  }
208  }
209 
210  if (checkOnly) {
211  typedef Checker<scalar_type, ordinal_type> checker_type;
212  checker_type checker (echo, tolerant, debug);
213 
214  RCP<const Comm<int> > comm = rcp (new SerialComm<int>);
215  if (filename != "") {
216  if (verbose) {
217  cout << "Checking syntax of the Matrix Market file \"" << filename
218  << "\"" << endl;
219  }
220  success = success && checker.readFile (*comm, filename);
221  if (verbose) {
222  if (success) {
223  cout << "The given file is a valid Matrix Market file." << endl;
224  }
225  else {
226  cout << "The given file has syntax errors." << endl;
227  }
228  }
229  }
230  else {
231  if (verbose) {
232  cout << "Checking syntax of the first built-in Matrix Market example" << endl
233  << std::flush;// for debug output next
234  }
235  if (debug) {
236  cerr << "First built-in Matrix Market example: " << endl
237  << sampleMatrixMarketFile << endl;
238  }
239  std::istringstream in (sampleMatrixMarketFile);
240  RCP<std::istream> inStream = rcpFromRef (in);
241  success = success && checker.read (*comm, inStream);
242  if (verbose) {
243  if (success) {
244  cout << "The example has valid Matrix Market syntax." << endl;
245  }
246  else {
247  cout << "The example has syntax errors." << endl;
248  }
249  }
250  }
251  }
252  else {
253  typedef Reader<scalar_type, ordinal_type> reader_type;
254  reader_type reader (tolerant, debug);
255  ArrayRCP<ordinal_type> ptr, ind;
256  ArrayRCP<scalar_type> val;
257  ordinal_type numRows, numCols;
258  //
259  // Read the Matrix Market data, either from a file or from a
260  // built-in string.
261  //
262  if (filename != "") {
263  if (verbose) {
264  cout << "Reading the Matrix Market file \"" << filename << "\"" << endl;
265  }
266  success = success && reader.readFile (ptr, ind, val,
267  numRows, numCols, filename);
268  }
269  else {
270  if (verbose) {
271  cout << "Reading the first built-in Matrix Market example" << endl;
272  }
273  if (debug) {
274  cerr << "First built-in Matrix Market example:" << endl
275  << sampleMatrixMarketFile << endl;
276  }
277  std::istringstream inStr (sampleMatrixMarketFile);
278  success = success && reader.read (ptr, ind, val, numRows, numCols, inStr);
279  }
280  TEUCHOS_TEST_FOR_EXCEPTION(! success, std::runtime_error, "Matrix Market "
281  "reader failed to read the given file or input stream.");
282  if (success && verbose) {
283  cout << "Returned from reading the Matrix Market data" << endl
284  << std::flush; // for following debug output
285  }
286  if (debug) {
287  cerr << "CSR output info:" << endl
288  << " ptr.size() = " << ptr.size()
289  << ", ind.size() = " << ind.size()
290  << ", val.size() = " << val.size()
291  << ", numRows = " << numRows
292  << ", numCols = " << numCols
293  << endl;
294  }
295 
296  // Here's the fun part. Output the CSR data to an output stream.
297  // Then read in the output stream. The resulting matrix should be
298  // exactly the same (unless the original file had elements at the
299  // same location that were added together with rounding error).
300  // This is a test for both Writer and Reader.
301  std::ostringstream outStr;
302  if (success && verbose) {
303  cout << "Printing the CSR arrays to a Matrix Market output stream"
304  << endl << std::flush;
305  }
307  writer.write (outStr, ptr (), ind (), val (), numRows, numCols);
308 
309  if (debug && echo) {
310  cerr << "CSR data:" << endl
311  << "- ptr = [";
312  for (ordinal_type i = 0; i < ptr.size(); ++i) {
313  cerr << ptr[i];
314  if (i+1 != ptr.size()) { // don't subtract from zero if unsigned
315  cerr << ", ";
316  }
317  }
318  cerr << "]" << endl
319  << "- ind = [";
320  for (ordinal_type i = 0; i < ind.size(); ++i) {
321  cerr << ind[i];
322  if (i+1 != ind.size()) { // don't subtract from zero if unsigned
323  cerr << ", ";
324  }
325  }
326  cerr << "]" << endl
327  << "- val = [";
328  for (ordinal_type i = 0; i < val.size(); ++i) {
329  cerr << val[i];
330  if (i+1 != val.size()) { // don't subtract from zero if unsigned
331  cerr << ", ";
332  }
333  }
334  cerr << "]" << endl;
335 
336  cerr << "CSR data, converted back to Matrix Market format" << endl;
337  writer.write (cerr, ptr (), ind (), val (), numRows, numCols);
338  cerr << endl;
339  }
340 
341  ArrayRCP<ordinal_type> newptr, newind;
342  ArrayRCP<scalar_type> newval;
343  ordinal_type newNumRows, newNumCols;
344  if (success && verbose) {
345  cout << "Reading the Matrix Market output back into CSR arrays" << endl;
346  }
347  {
348  std::istringstream inStr (outStr.str ());
349  success = success && reader.read (newptr, newind, newval,
350  newNumRows, newNumCols, inStr);
351  }
352  TEUCHOS_TEST_FOR_EXCEPTION(! success, std::logic_error, "Matrix Market "
353  "reader failed to read the output back into CSR arrays.");
354  if (success && verbose) {
355  cout << "Successfully read the Matrix Market output back into CSR arrays"
356  << endl << std::flush;
357  }
358  if (debug) {
359  cerr << "CSR output info:" << endl
360  << " newptr.size() = " << newptr.size()
361  << ", newind.size() = " << newind.size()
362  << ", newval.size() = " << newval.size()
363  << ", newNumRows = " << newNumRows
364  << ", newNumCols = " << newNumCols
365  << endl;
366  }
367 
368  // The old arrays should equal the new arrays.
369  TEUCHOS_TEST_FOR_EXCEPTION(ptr.size () != newptr.size (), std::logic_error,
370  "New ptr array has a different length than old ptr array");
371  TEUCHOS_TEST_FOR_EXCEPTION(ind.size () != newind.size (), std::logic_error,
372  "New ind array has a different length than old ind array");
373  TEUCHOS_TEST_FOR_EXCEPTION(val.size () != newval.size (), std::logic_error,
374  "New val array has a different length than old val array");
375  TEUCHOS_TEST_FOR_EXCEPTION(newNumRows != numRows || newNumCols != numCols,
376  std::logic_error, "New dimensions differ from old dimensions");
377  TEUCHOS_TEST_FOR_EXCEPTION(ptr.size () != numRows+1, std::logic_error,
378  "ptr.size() != numRows+1");
379  TEUCHOS_TEST_FOR_EXCEPTION(newptr.size () != newNumRows+1, std::logic_error,
380  "newptr.size() != newNumRows+1");
381 
382  for (ordinal_type rowIndex = 0; rowIndex < numRows; ++rowIndex) {
383  TEUCHOS_TEST_FOR_EXCEPTION(ptr[rowIndex] != newptr[rowIndex],
384  std::logic_error, "At row index " << rowIndex << ", ptr[rowIndex] = "
385  << ptr[rowIndex] << " != newptr[rowIndex] = " << newptr[rowIndex]
386  << ".");
387  TEUCHOS_TEST_FOR_EXCEPTION(ptr[rowIndex+1] != newptr[rowIndex+1],
388  std::logic_error, "At row index " << rowIndex << ", ptr[rowIndex+1] = "
389  << ptr[rowIndex+1] << " != newptr[rowIndex+1] = " << newptr[rowIndex+1]
390  << ".");
391  for (ordinal_type k = ptr[rowIndex]; k < ptr[rowIndex+1]; ++k) {
392  TEUCHOS_TEST_FOR_EXCEPTION(ind[k] != newind[k], std::logic_error,
393  "At row index " << rowIndex << ", ind[k=" << k << "] = "
394  << ind[k] << " != newind[k] = " << newind[k] << ".");
395  // You may want to relax this inequality if the original
396  // Matrix Market file had multiple entries at the same
397  // location and if adding them together resulted in rounding
398  // error.
399  TEUCHOS_TEST_FOR_EXCEPTION(val[k] != newval[k], std::logic_error,
400  "At row index " << rowIndex << ", val[k=" << k << "] = "
401  << val[k] << " != newval[k] = " << newval[k] << ".");
402  }
403  }
404 
405  // Now test reading symmetric data, if no filename was specified.
406  if (filename == "") {
407  std::istringstream inStr (symmetricMatrixMarketFile);
408  success = success && reader.read (ptr, ind, val, numRows, numCols, inStr);
409  TEUCHOS_TEST_FOR_EXCEPTION(! success, std::logic_error,
410  "Matrix Market reader failed to read the given example string.");
411  if (success && verbose) {
412  cout << "Returned from reading the Matrix Market data" << endl
413  << std::flush; // for following debug output
414  }
415  if (debug) {
416  cerr << "CSR output info:" << endl
417  << " ptr.size() = " << ptr.size()
418  << ", ind.size() = " << ind.size()
419  << ", val.size() = " << val.size()
420  << ", numRows = " << numRows
421  << ", numCols = " << numCols
422  << endl;
423  }
424 
425  // This is a bit of a hack, since we know the contents of the
426  // example. Since we "symmetrize" when reading in symmetric
427  // data, there should be 15 entries in the resulting matrix.
428  const ordinal_type correctNumEntries = 15;
430  val.size() != correctNumEntries,
431  std::logic_error,
432  "Incorrect number of entries after symmetrization: There should be "
433  << correctNumEntries << ", but there are " << val.size() << " entries "
434  "instead.");
435  }
436  } // end of the file / string Reader tests
437 
438  if (success)
439  std::cout << "End Result: TEST PASSED" << endl;
440  else
441  std::cout << "End Result: TEST FAILED" << endl;
442  }
443  TEUCHOS_STANDARD_CATCH_STATEMENTS(verbose, std::cerr, success);
444 
445  return ( success ? EXIT_SUCCESS : EXIT_FAILURE );
446 }
Read a sparse matrix from a Matrix Market file into raw CSR (compressed sparse row) storage...
RCP< T > rcp(const boost::shared_ptr< T > &sptr)
Conversion function that takes in a boost::shared_ptr object and spits out a Teuchos::RCP object...
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
Concrete serial communicator subclass.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
void write(std::ostream &out, const ArrayView< const OrdinalType > &rowptr, const ArrayView< const OrdinalType > &colind, const ArrayView< const ScalarType > &values, const OrdinalType numRows, const OrdinalType numCols)
Write the sparse matrix to the given output stream.
#define TEUCHOS_STANDARD_CATCH_STATEMENTS(VERBOSE, ERR_STREAM, SUCCESS_FLAG)
Simple macro that catches and reports standard exceptions and other exceptions.
A list of parameters of arbitrary type.
Ptr< T > ptr(T *p)
Create a pointer to an object from a raw pointer.
int main(int argc, char *argv[])
Abstract interface for distributed-memory communication.
Nonowning array view.
A MPI utilities class, providing methods for initializing, finalizing, and querying the global MPI se...
Basic command line parser for input from (argc,argv[])
RCP< T > rcpFromRef(T &r)
Return a non-owning weak RCP object from a raw object reference for a defined type.
Smart reference counting pointer class for automatic garbage collection.
Tool for debugging the syntax of a Matrix Market file containing a sparse matrix. ...
Write a sparse matrix from raw CSR (compressed sparse row) storage to a Matrix Market file...
Class that helps parse command line input arguments from (argc,argv[]) and set options.
Reference-counted smart pointer for managing arrays.