Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MatrixMarket_Tpetra.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 __MatrixMarket_Tpetra_hpp
11 #define __MatrixMarket_Tpetra_hpp
12 
25 #include "Tpetra_CrsMatrix.hpp"
26 #include "Tpetra_Operator.hpp"
27 #include "Tpetra_Vector.hpp"
29 #include "Teuchos_MatrixMarket_Raw_Adder.hpp"
30 #include "Teuchos_MatrixMarket_Raw_Graph_Adder.hpp"
31 #include "Teuchos_MatrixMarket_SymmetrizingAdder.hpp"
32 #include "Teuchos_MatrixMarket_SymmetrizingGraphAdder.hpp"
33 #include "Teuchos_MatrixMarket_assignScalar.hpp"
34 #include "Teuchos_MatrixMarket_Banner.hpp"
35 #include "Teuchos_MatrixMarket_CoordDataReader.hpp"
36 #include "Teuchos_SetScientific.hpp"
37 #include "Teuchos_TimeMonitor.hpp"
38 
39 extern "C" {
40 #include "mmio_Tpetra.h"
41 }
42 #include "Tpetra_Distribution.hpp"
43 
44 #include <algorithm>
45 #include <fstream>
46 #include <iostream>
47 #include <iterator>
48 #include <vector>
49 #include <stdexcept>
50 #include <numeric>
51 
52 namespace Tpetra {
82 namespace MatrixMarket {
138 template <class SparseMatrixType>
139 class Reader {
140  public:
142  typedef SparseMatrixType sparse_matrix_type;
143  typedef Teuchos::RCP<sparse_matrix_type> sparse_matrix_ptr;
144 
147  typedef typename SparseMatrixType::scalar_type scalar_type;
150  typedef typename SparseMatrixType::local_ordinal_type local_ordinal_type;
158  typedef typename SparseMatrixType::global_ordinal_type
161  typedef typename SparseMatrixType::node_type node_type;
162 
166  node_type>
168 
170  typedef MultiVector<scalar_type,
173  node_type>
175 
177  typedef Vector<scalar_type,
180  node_type>
182 
184 
186  using trcp_tcomm_t = Teuchos::RCP<const Teuchos::Comm<int>>;
187 
188  private:
194  typedef Teuchos::ArrayRCP<int>::size_type size_type;
195 
206  static Teuchos::RCP<const map_type>
207  makeRangeMap(const trcp_tcomm_t& pComm,
208  const global_ordinal_type numRows) {
209  // Return a conventional, uniformly partitioned, contiguous map.
210  return rcp(new map_type(static_cast<global_size_t>(numRows),
211  static_cast<global_ordinal_type>(0),
212  pComm, GloballyDistributed));
213  }
214 
242  static Teuchos::RCP<const map_type>
243  makeRowMap(const Teuchos::RCP<const map_type>& pRowMap,
244  const trcp_tcomm_t& pComm,
245  const global_ordinal_type numRows) {
246  // If the caller didn't provide a map, return a conventional,
247  // uniformly partitioned, contiguous map.
248  if (pRowMap.is_null()) {
249  return rcp(new map_type(static_cast<global_size_t>(numRows),
250  static_cast<global_ordinal_type>(0),
251  pComm, GloballyDistributed));
252  } else {
253  TEUCHOS_TEST_FOR_EXCEPTION(!pRowMap->isDistributed() && pComm->getSize() > 1,
254  std::invalid_argument,
255  "The specified row map is not distributed, "
256  "but the given communicator includes more than one process (in "
257  "fact, there are "
258  << pComm->getSize() << " processes).");
259  TEUCHOS_TEST_FOR_EXCEPTION(pRowMap->getComm() != pComm, std::invalid_argument,
260  "The specified row Map's communicator (pRowMap->getComm()) "
261  "differs from the given separately supplied communicator pComm.");
262  return pRowMap;
263  }
264  }
265 
280  static Teuchos::RCP<const map_type>
281  makeDomainMap(const Teuchos::RCP<const map_type>& pRangeMap,
282  const global_ordinal_type numRows,
283  const global_ordinal_type numCols) {
284  // Abbreviations so that the map creation call isn't too long.
285  typedef local_ordinal_type LO;
286  typedef global_ordinal_type GO;
287  typedef node_type NT;
288 
289  if (numRows == numCols) {
290  return pRangeMap;
291  } else {
292  return createUniformContigMapWithNode<LO, GO, NT>(numCols,
293  pRangeMap->getComm());
294  }
295  }
296 
369  static void
370  distribute(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
371  Teuchos::ArrayRCP<size_t>& myRowPtr,
372  Teuchos::ArrayRCP<global_ordinal_type>& myColInd,
373  Teuchos::ArrayRCP<scalar_type>& myValues,
374  const Teuchos::RCP<const map_type>& pRowMap,
375  Teuchos::ArrayRCP<size_t>& numEntriesPerRow,
376  Teuchos::ArrayRCP<size_t>& rowPtr,
377  Teuchos::ArrayRCP<global_ordinal_type>& colInd,
378  Teuchos::ArrayRCP<scalar_type>& values,
379  const bool debug = false) {
380  using std::cerr;
381  using std::endl;
382  using Teuchos::arcp;
383  using Teuchos::ArrayRCP;
384  using Teuchos::ArrayView;
385  using Teuchos::as;
386  using Teuchos::Comm;
387  using Teuchos::CommRequest;
388  using Teuchos::null;
389  using Teuchos::RCP;
390  using Teuchos::receive;
391  using Teuchos::send;
392 
393  const bool extraDebug = false;
394  trcp_tcomm_t pComm = pRowMap->getComm();
395  const int numProcs = pComm->getSize();
396  const int myRank = pComm->getRank();
397  const int rootRank = 0;
398 
399  // Type abbreviations to make the code more concise.
400  typedef global_ordinal_type GO;
401 
402  // List of the global indices of my rows. They may or may
403  // not be contiguous, and the row map need not be one-to-one.
404  ArrayView<const GO> myRows = pRowMap->getLocalElementList();
405  const size_type myNumRows = myRows.size();
406  TEUCHOS_TEST_FOR_EXCEPTION(static_cast<size_t>(myNumRows) !=
407  pRowMap->getLocalNumElements(),
408  std::logic_error,
409  "pRowMap->getLocalElementList().size() = "
410  << myNumRows
411  << " != pRowMap->getLocalNumElements() = "
412  << pRowMap->getLocalNumElements() << ". "
413  "Please report this bug to the Tpetra developers.");
414  TEUCHOS_TEST_FOR_EXCEPTION(myRank == 0 && numEntriesPerRow.size() < myNumRows,
415  std::logic_error,
416  "On Proc 0: numEntriesPerRow.size() = "
417  << numEntriesPerRow.size()
418  << " != pRowMap->getLocalElementList().size() = "
419  << myNumRows << ". Please report this bug to the "
420  "Tpetra developers.");
421 
422  // Space for my proc's number of entries per row. Will be
423  // filled in below.
424  myNumEntriesPerRow = arcp<size_t>(myNumRows);
425 
426  if (myRank != rootRank) {
427  // Tell the root how many rows we have. If we're sending
428  // none, then we don't have anything else to send, nor does
429  // the root have to receive anything else.
430  send(*pComm, myNumRows, rootRank);
431  if (myNumRows != 0) {
432  // Now send my rows' global indices. Hopefully the cast
433  // to int doesn't overflow. This is unlikely, since it
434  // should fit in a LO, even though it is a GO.
435  send(*pComm, static_cast<int>(myNumRows),
436  myRows.getRawPtr(), rootRank);
437 
438  // I (this proc) don't care if my global row indices are
439  // contiguous, though the root proc does (since otherwise
440  // it needs to pack noncontiguous data into contiguous
441  // storage before sending). That's why we don't check
442  // for contiguousness here.
443 
444  // Ask the root process for my part of the array of the
445  // number of entries per row.
446  receive(*pComm, rootRank,
447  static_cast<int>(myNumRows),
448  myNumEntriesPerRow.getRawPtr());
449 
450  // Use the resulting array to figure out how many column
451  // indices and values I should ask from the root process.
452  const local_ordinal_type myNumEntries =
453  std::accumulate(myNumEntriesPerRow.begin(),
454  myNumEntriesPerRow.end(), 0);
455 
456  // Make space for my entries of the sparse matrix. Note
457  // that they don't have to be sorted by row index.
458  // Iterating through all my rows requires computing a
459  // running sum over myNumEntriesPerRow.
460  myColInd = arcp<GO>(myNumEntries);
461  myValues = arcp<scalar_type>(myNumEntries);
462  if (myNumEntries > 0) {
463  // Ask for that many column indices and values, if
464  // there are any.
465  receive(*pComm, rootRank,
466  static_cast<int>(myNumEntries),
467  myColInd.getRawPtr());
468  receive(*pComm, rootRank,
469  static_cast<int>(myNumEntries),
470  myValues.getRawPtr());
471  }
472  } // If I own at least one row
473  } // If I am not the root processor
474  else { // I _am_ the root processor
475  if (debug) {
476  cerr << "-- Proc 0: Copying my data from global arrays" << endl;
477  }
478  // Proc 0 still needs to (allocate, if not done already)
479  // and fill its part of the matrix (my*).
480  for (size_type k = 0; k < myNumRows; ++k) {
481  const GO myCurRow = myRows[k];
482  const local_ordinal_type numEntriesInThisRow = numEntriesPerRow[myCurRow];
483  myNumEntriesPerRow[k] = numEntriesInThisRow;
484  }
485  if (extraDebug && debug) {
486  cerr << "Proc " << pRowMap->getComm()->getRank()
487  << ": myNumEntriesPerRow[0.." << (myNumRows - 1) << "] = [";
488  for (size_type k = 0; k < myNumRows; ++k) {
489  cerr << myNumEntriesPerRow[k];
490  if (k < myNumRows - 1) {
491  cerr << " ";
492  }
493  }
494  cerr << "]" << endl;
495  }
496  // The total number of matrix entries that my proc owns.
497  const local_ordinal_type myNumEntries =
498  std::accumulate(myNumEntriesPerRow.begin(),
499  myNumEntriesPerRow.end(), 0);
500  if (debug) {
501  cerr << "-- Proc 0: I own " << myNumRows << " rows and "
502  << myNumEntries << " entries" << endl;
503  }
504  myColInd = arcp<GO>(myNumEntries);
505  myValues = arcp<scalar_type>(myNumEntries);
506 
507  // Copy Proc 0's part of the matrix into the my* arrays.
508  // It's important that myCurPos be updated _before_ k,
509  // otherwise myCurPos will get the wrong number of entries
510  // per row (it should be for the row in the just-completed
511  // iteration, not for the next iteration's row).
512  local_ordinal_type myCurPos = 0;
513  for (size_type k = 0; k < myNumRows;
514  myCurPos += myNumEntriesPerRow[k], ++k) {
515  const local_ordinal_type curNumEntries = myNumEntriesPerRow[k];
516  const GO myRow = myRows[k];
517  const size_t curPos = rowPtr[myRow];
518  // Only copy if there are entries to copy, in order not
519  // to construct empty ranges for the ArrayRCP views.
520  if (curNumEntries > 0) {
521  ArrayView<GO> colIndView = colInd(curPos, curNumEntries);
522  ArrayView<GO> myColIndView = myColInd(myCurPos, curNumEntries);
523  std::copy(colIndView.begin(), colIndView.end(),
524  myColIndView.begin());
525 
526  ArrayView<scalar_type> valuesView =
527  values(curPos, curNumEntries);
528  ArrayView<scalar_type> myValuesView =
529  myValues(myCurPos, curNumEntries);
530  std::copy(valuesView.begin(), valuesView.end(),
531  myValuesView.begin());
532  }
533  }
534 
535  // Proc 0 processes each other proc p in turn.
536  for (int p = 1; p < numProcs; ++p) {
537  if (debug) {
538  cerr << "-- Proc 0: Processing proc " << p << endl;
539  }
540 
541  size_type theirNumRows = 0;
542  // Ask Proc p how many rows it has. If it doesn't
543  // have any, we can move on to the next proc. This
544  // has to be a standard receive so that we can avoid
545  // the degenerate case of sending zero data.
546  receive(*pComm, p, &theirNumRows);
547  if (debug) {
548  cerr << "-- Proc 0: Proc " << p << " owns "
549  << theirNumRows << " rows" << endl;
550  }
551  if (theirNumRows != 0) {
552  // Ask Proc p which rows it owns. The resulting global
553  // row indices are not guaranteed to be contiguous or
554  // sorted. Global row indices are themselves indices
555  // into the numEntriesPerRow array.
556  ArrayRCP<GO> theirRows = arcp<GO>(theirNumRows);
557  receive(*pComm, p, as<int>(theirNumRows),
558  theirRows.getRawPtr());
559  // Extra test to make sure that the rows we received
560  // are all sensible. This is a good idea since we are
561  // going to use the global row indices we've received
562  // to index into the numEntriesPerRow array. Better to
563  // catch any bugs here and print a sensible error
564  // message, rather than segfault and print a cryptic
565  // error message.
566  {
567  const global_size_t numRows = pRowMap->getGlobalNumElements();
568  const GO indexBase = pRowMap->getIndexBase();
569  bool theirRowsValid = true;
570  for (size_type k = 0; k < theirNumRows; ++k) {
571  if (theirRows[k] < indexBase ||
572  as<global_size_t>(theirRows[k] - indexBase) >= numRows) {
573  theirRowsValid = false;
574  }
575  }
576  if (!theirRowsValid) {
577  TEUCHOS_TEST_FOR_EXCEPTION(
578  !theirRowsValid, std::logic_error,
579  "Proc " << p << " has at least one invalid row index. "
580  "Here are all of them: "
581  << Teuchos::toString(theirRows()) << ". Valid row index "
582  "range (zero-based): [0, "
583  << (numRows - 1) << "].");
584  }
585  }
586 
587  // Perhaps we could save a little work if we check
588  // whether Proc p's row indices are contiguous. That
589  // would make lookups in the global data arrays
590  // faster. For now, we just implement the general
591  // case and don't prematurely optimize. (Remember
592  // that you're making Proc 0 read the whole file, so
593  // you've already lost scalability.)
594 
595  // Compute the number of entries in each of Proc p's
596  // rows. (Proc p will compute its row pointer array
597  // on its own, after it gets the data from Proc 0.)
598  ArrayRCP<size_t> theirNumEntriesPerRow;
599  theirNumEntriesPerRow = arcp<size_t>(theirNumRows);
600  for (size_type k = 0; k < theirNumRows; ++k) {
601  theirNumEntriesPerRow[k] = numEntriesPerRow[theirRows[k]];
602  }
603 
604  // Tell Proc p the number of entries in each of its
605  // rows. Hopefully the cast to int doesn't overflow.
606  // This is unlikely, since it should fit in a LO,
607  // even though it is a GO.
608  send(*pComm, static_cast<int>(theirNumRows),
609  theirNumEntriesPerRow.getRawPtr(), p);
610 
611  // Figure out how many entries Proc p owns.
612  const local_ordinal_type theirNumEntries =
613  std::accumulate(theirNumEntriesPerRow.begin(),
614  theirNumEntriesPerRow.end(), 0);
615 
616  if (debug) {
617  cerr << "-- Proc 0: Proc " << p << " owns "
618  << theirNumEntries << " entries" << endl;
619  }
620 
621  // If there are no entries to send, then we're done
622  // with Proc p.
623  if (theirNumEntries == 0) {
624  continue;
625  }
626 
627  // Construct (views of) proc p's column indices and
628  // values. Later, we might like to optimize for the
629  // (common) contiguous case, for which we don't need to
630  // copy data into separate "their*" arrays (we can just
631  // use contiguous views of the global arrays).
632  ArrayRCP<GO> theirColInd(theirNumEntries);
633  ArrayRCP<scalar_type> theirValues(theirNumEntries);
634  // Copy Proc p's part of the matrix into the their*
635  // arrays. It's important that theirCurPos be updated
636  // _before_ k, otherwise theirCurPos will get the wrong
637  // number of entries per row (it should be for the row
638  // in the just-completed iteration, not for the next
639  // iteration's row).
640  local_ordinal_type theirCurPos = 0;
641  for (size_type k = 0; k < theirNumRows;
642  theirCurPos += theirNumEntriesPerRow[k], k++) {
643  const local_ordinal_type curNumEntries = theirNumEntriesPerRow[k];
644  const GO theirRow = theirRows[k];
645  const local_ordinal_type curPos = rowPtr[theirRow];
646 
647  // Only copy if there are entries to copy, in order
648  // not to construct empty ranges for the ArrayRCP
649  // views.
650  if (curNumEntries > 0) {
651  ArrayView<GO> colIndView =
652  colInd(curPos, curNumEntries);
653  ArrayView<GO> theirColIndView =
654  theirColInd(theirCurPos, curNumEntries);
655  std::copy(colIndView.begin(), colIndView.end(),
656  theirColIndView.begin());
657 
658  ArrayView<scalar_type> valuesView =
659  values(curPos, curNumEntries);
660  ArrayView<scalar_type> theirValuesView =
661  theirValues(theirCurPos, curNumEntries);
662  std::copy(valuesView.begin(), valuesView.end(),
663  theirValuesView.begin());
664  }
665  }
666  // Send Proc p its column indices and values.
667  // Hopefully the cast to int doesn't overflow. This
668  // is unlikely, since it should fit in a LO, even
669  // though it is a GO.
670  send(*pComm, static_cast<int>(theirNumEntries),
671  theirColInd.getRawPtr(), p);
672  send(*pComm, static_cast<int>(theirNumEntries),
673  theirValues.getRawPtr(), p);
674 
675  if (debug) {
676  cerr << "-- Proc 0: Finished with proc " << p << endl;
677  }
678  } // If proc p owns at least one row
679  } // For each proc p not the root proc 0
680  } // If I'm (not) the root proc 0
681 
682  // Invalidate the input data to save space, since we don't
683  // need it anymore.
684  numEntriesPerRow = null;
685  rowPtr = null;
686  colInd = null;
687  values = null;
688 
689  if (debug && myRank == 0) {
690  cerr << "-- Proc 0: About to fill in myRowPtr" << endl;
691  }
692 
693  // Allocate and fill in myRowPtr (the row pointer array for
694  // my rank's rows). We delay this until the end because we
695  // don't need it to compute anything else in distribute().
696  // Each proc can do this work for itself, since it only needs
697  // myNumEntriesPerRow to do so.
698  myRowPtr = arcp<size_t>(myNumRows + 1);
699  myRowPtr[0] = 0;
700  for (size_type k = 1; k < myNumRows + 1; ++k) {
701  myRowPtr[k] = myRowPtr[k - 1] + myNumEntriesPerRow[k - 1];
702  }
703  if (extraDebug && debug) {
704  cerr << "Proc " << Teuchos::rank(*(pRowMap->getComm()))
705  << ": myRowPtr[0.." << myNumRows << "] = [";
706  for (size_type k = 0; k < myNumRows + 1; ++k) {
707  cerr << myRowPtr[k];
708  if (k < myNumRows) {
709  cerr << " ";
710  }
711  }
712  cerr << "]" << endl
713  << endl;
714  }
715 
716  if (debug && myRank == 0) {
717  cerr << "-- Proc 0: Done with distribute" << endl;
718  }
719  }
720 
734  static Teuchos::RCP<sparse_matrix_type>
735  makeMatrix(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
736  Teuchos::ArrayRCP<size_t>& myRowPtr,
737  Teuchos::ArrayRCP<global_ordinal_type>& myColInd,
738  Teuchos::ArrayRCP<scalar_type>& myValues,
739  const Teuchos::RCP<const map_type>& pRowMap,
740  const Teuchos::RCP<const map_type>& pRangeMap,
741  const Teuchos::RCP<const map_type>& pDomainMap,
742  const bool callFillComplete = true) {
743  using std::cerr;
744  using std::endl;
745  using Teuchos::ArrayView;
746  using Teuchos::null;
747  using Teuchos::RCP;
748  using Teuchos::rcp;
749  // Typedef to make certain type declarations shorter.
750  typedef global_ordinal_type GO;
751 
752  // The row pointer array always has at least one entry, even
753  // if the matrix has zero rows. myNumEntriesPerRow, myColInd,
754  // and myValues would all be empty arrays in that degenerate
755  // case, but the row and domain maps would still be nonnull
756  // (though they would be trivial maps).
757  TEUCHOS_TEST_FOR_EXCEPTION(myRowPtr.is_null(), std::logic_error,
758  "makeMatrix: myRowPtr array is null. "
759  "Please report this bug to the Tpetra developers.");
760  TEUCHOS_TEST_FOR_EXCEPTION(pDomainMap.is_null(), std::logic_error,
761  "makeMatrix: domain map is null. "
762  "Please report this bug to the Tpetra developers.");
763  TEUCHOS_TEST_FOR_EXCEPTION(pRangeMap.is_null(), std::logic_error,
764  "makeMatrix: range map is null. "
765  "Please report this bug to the Tpetra developers.");
766  TEUCHOS_TEST_FOR_EXCEPTION(pRowMap.is_null(), std::logic_error,
767  "makeMatrix: row map is null. "
768  "Please report this bug to the Tpetra developers.");
769 
770  // Construct the CrsMatrix, using the row map, with the
771  // constructor specifying the number of nonzeros for each row.
772  RCP<sparse_matrix_type> A =
773  rcp(new sparse_matrix_type(pRowMap, myNumEntriesPerRow()));
774 
775  // List of the global indices of my rows.
776  // They may or may not be contiguous.
777  ArrayView<const GO> myRows = pRowMap->getLocalElementList();
778  const size_type myNumRows = myRows.size();
779 
780  // Add this processor's matrix entries to the CrsMatrix.
781  const GO indexBase = pRowMap->getIndexBase();
782  for (size_type i = 0; i < myNumRows; ++i) {
783  const size_type myCurPos = myRowPtr[i];
784  const local_ordinal_type curNumEntries = myNumEntriesPerRow[i];
785  ArrayView<GO> curColInd = myColInd.view(myCurPos, curNumEntries);
786  ArrayView<scalar_type> curValues = myValues.view(myCurPos, curNumEntries);
787 
788  // Modify the column indices in place to have the right index base.
789  for (size_type k = 0; k < curNumEntries; ++k) {
790  curColInd[k] += indexBase;
791  }
792  // Avoid constructing empty views of ArrayRCP objects.
793  if (curNumEntries > 0) {
794  A->insertGlobalValues(myRows[i], curColInd, curValues);
795  }
796  }
797  // We've entered in all our matrix entries, so we can delete
798  // the original data. This will save memory when we call
799  // fillComplete(), so that we never keep more than two copies
800  // of the matrix's data in memory at once.
801  myNumEntriesPerRow = null;
802  myRowPtr = null;
803  myColInd = null;
804  myValues = null;
805 
806  if (callFillComplete) {
807  A->fillComplete(pDomainMap, pRangeMap);
808  }
809  return A;
810  }
811 
817  static Teuchos::RCP<sparse_matrix_type>
818  makeMatrix(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
819  Teuchos::ArrayRCP<size_t>& myRowPtr,
820  Teuchos::ArrayRCP<global_ordinal_type>& myColInd,
821  Teuchos::ArrayRCP<scalar_type>& myValues,
822  const Teuchos::RCP<const map_type>& pRowMap,
823  const Teuchos::RCP<const map_type>& pRangeMap,
824  const Teuchos::RCP<const map_type>& pDomainMap,
825  const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
826  const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams) {
827  using std::cerr;
828  using std::endl;
829  using Teuchos::ArrayView;
830  using Teuchos::null;
831  using Teuchos::RCP;
832  using Teuchos::rcp;
833  // Typedef to make certain type declarations shorter.
834  typedef global_ordinal_type GO;
835 
836  // The row pointer array always has at least one entry, even
837  // if the matrix has zero rows. myNumEntriesPerRow, myColInd,
838  // and myValues would all be empty arrays in that degenerate
839  // case, but the row and domain maps would still be nonnull
840  // (though they would be trivial maps).
841  TEUCHOS_TEST_FOR_EXCEPTION(
842  myRowPtr.is_null(), std::logic_error,
843  "makeMatrix: myRowPtr array is null. "
844  "Please report this bug to the Tpetra developers.");
845  TEUCHOS_TEST_FOR_EXCEPTION(
846  pDomainMap.is_null(), std::logic_error,
847  "makeMatrix: domain map is null. "
848  "Please report this bug to the Tpetra developers.");
849  TEUCHOS_TEST_FOR_EXCEPTION(
850  pRangeMap.is_null(), std::logic_error,
851  "makeMatrix: range map is null. "
852  "Please report this bug to the Tpetra developers.");
853  TEUCHOS_TEST_FOR_EXCEPTION(
854  pRowMap.is_null(), std::logic_error,
855  "makeMatrix: row map is null. "
856  "Please report this bug to the Tpetra developers.");
857 
858  // Construct the CrsMatrix, using the row map, with the
859  // constructor specifying the number of nonzeros for each row.
860  RCP<sparse_matrix_type> A =
861  rcp(new sparse_matrix_type(pRowMap, myNumEntriesPerRow(),
862  constructorParams));
863 
864  // List of the global indices of my rows.
865  // They may or may not be contiguous.
866  ArrayView<const GO> myRows = pRowMap->getLocalElementList();
867  const size_type myNumRows = myRows.size();
868 
869  // Add this processor's matrix entries to the CrsMatrix.
870  const GO indexBase = pRowMap->getIndexBase();
871  for (size_type i = 0; i < myNumRows; ++i) {
872  const size_type myCurPos = myRowPtr[i];
873  const local_ordinal_type curNumEntries = myNumEntriesPerRow[i];
874  ArrayView<GO> curColInd = myColInd.view(myCurPos, curNumEntries);
875  ArrayView<scalar_type> curValues = myValues.view(myCurPos, curNumEntries);
876 
877  // Modify the column indices in place to have the right index base.
878  for (size_type k = 0; k < curNumEntries; ++k) {
879  curColInd[k] += indexBase;
880  }
881  if (curNumEntries > 0) {
882  A->insertGlobalValues(myRows[i], curColInd, curValues);
883  }
884  }
885  // We've entered in all our matrix entries, so we can delete
886  // the original data. This will save memory when we call
887  // fillComplete(), so that we never keep more than two copies
888  // of the matrix's data in memory at once.
889  myNumEntriesPerRow = null;
890  myRowPtr = null;
891  myColInd = null;
892  myValues = null;
893 
894  A->fillComplete(pDomainMap, pRangeMap, fillCompleteParams);
895  return A;
896  }
897 
902  static Teuchos::RCP<sparse_matrix_type>
903  makeMatrix(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
904  Teuchos::ArrayRCP<size_t>& myRowPtr,
905  Teuchos::ArrayRCP<global_ordinal_type>& myColInd,
906  Teuchos::ArrayRCP<scalar_type>& myValues,
907  const Teuchos::RCP<const map_type>& rowMap,
908  Teuchos::RCP<const map_type>& colMap,
909  const Teuchos::RCP<const map_type>& domainMap,
910  const Teuchos::RCP<const map_type>& rangeMap,
911  const bool callFillComplete = true) {
912  using Teuchos::ArrayView;
913  using Teuchos::as;
914  using Teuchos::null;
915  using Teuchos::RCP;
916  using Teuchos::rcp;
917  typedef global_ordinal_type GO;
918 
919  // Construct the CrsMatrix.
920 
921  RCP<sparse_matrix_type> A; // the matrix to return.
922  if (colMap.is_null()) { // the user didn't provide a column Map
923  A = rcp(new sparse_matrix_type(rowMap, myNumEntriesPerRow));
924  } else { // the user provided a column Map
925  A = rcp(new sparse_matrix_type(rowMap, colMap, myNumEntriesPerRow));
926  }
927 
928  // List of the global indices of my rows.
929  // They may or may not be contiguous.
930  ArrayView<const GO> myRows = rowMap->getLocalElementList();
931  const size_type myNumRows = myRows.size();
932 
933  // Add this process' matrix entries to the CrsMatrix.
934  const GO indexBase = rowMap->getIndexBase();
935  for (size_type i = 0; i < myNumRows; ++i) {
936  const size_type myCurPos = myRowPtr[i];
937  const size_type curNumEntries = as<size_type>(myNumEntriesPerRow[i]);
938  ArrayView<GO> curColInd = myColInd.view(myCurPos, curNumEntries);
939  ArrayView<scalar_type> curValues = myValues.view(myCurPos, curNumEntries);
940 
941  // Modify the column indices in place to have the right index base.
942  for (size_type k = 0; k < curNumEntries; ++k) {
943  curColInd[k] += indexBase;
944  }
945  if (curNumEntries > 0) {
946  A->insertGlobalValues(myRows[i], curColInd, curValues);
947  }
948  }
949  // We've entered in all our matrix entries, so we can delete
950  // the original data. This will save memory when we call
951  // fillComplete(), so that we never keep more than two copies
952  // of the matrix's data in memory at once.
953  myNumEntriesPerRow = null;
954  myRowPtr = null;
955  myColInd = null;
956  myValues = null;
957 
958  if (callFillComplete) {
959  A->fillComplete(domainMap, rangeMap);
960  if (colMap.is_null()) {
961  colMap = A->getColMap();
962  }
963  }
964  return A;
965  }
966 
967  private:
984  static Teuchos::RCP<const Teuchos::MatrixMarket::Banner>
985  readBanner(std::istream& in,
986  size_t& lineNumber,
987  const bool tolerant = false,
988  const bool /* debug */ = false,
989  const bool isGraph = false) {
990  using std::cerr;
991  using std::endl;
992  using Teuchos::RCP;
993  using Teuchos::rcp;
994  using Teuchos::MatrixMarket::Banner;
995  typedef Teuchos::ScalarTraits<scalar_type> STS;
996 
997  RCP<Banner> pBanner; // On output, if successful: the read-in Banner.
998  std::string line; // If read from stream successful: the Banner line
999 
1000  // Try to read a line from the input stream.
1001  const bool readFailed = !getline(in, line);
1002  TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
1003  "Failed to get Matrix Market banner line from input.");
1004 
1005  // We read a line from the input stream.
1006  lineNumber++;
1007 
1008  // Assume that the line we found is the Banner line.
1009  try {
1010  pBanner = rcp(new Banner(line, tolerant));
1011  } catch (std::exception& e) {
1012  TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
1013  "Matrix Market banner line contains syntax error(s): "
1014  << e.what());
1015  }
1016 
1017  TEUCHOS_TEST_FOR_EXCEPTION(pBanner->objectType() != "matrix",
1018  std::invalid_argument,
1019  "The Matrix Market file does not contain "
1020  "matrix data. Its Banner (first) line says that its object type is \""
1021  << pBanner->matrixType() << "\", rather than the required \"matrix\".");
1022 
1023  // Validate the data type of the matrix, with respect to the
1024  // Scalar type of the CrsMatrix entries.
1025  TEUCHOS_TEST_FOR_EXCEPTION(
1026  !STS::isComplex && pBanner->dataType() == "complex",
1027  std::invalid_argument,
1028  "The Matrix Market file contains complex-valued data, but you are "
1029  "trying to read it into a matrix containing entries of the real-"
1030  "valued Scalar type \""
1031  << Teuchos::TypeNameTraits<scalar_type>::name() << "\".");
1032  TEUCHOS_TEST_FOR_EXCEPTION(
1033  !isGraph &&
1034  pBanner->dataType() != "real" &&
1035  pBanner->dataType() != "complex" &&
1036  pBanner->dataType() != "integer",
1037  std::invalid_argument,
1038  "When reading Matrix Market data into a Tpetra::CrsMatrix, the "
1039  "Matrix Market file may not contain a \"pattern\" matrix. A "
1040  "pattern matrix is really just a graph with no weights. It "
1041  "should be stored in a CrsGraph, not a CrsMatrix.");
1042 
1043  TEUCHOS_TEST_FOR_EXCEPTION(
1044  isGraph &&
1045  pBanner->dataType() != "pattern",
1046  std::invalid_argument,
1047  "When reading Matrix Market data into a Tpetra::CrsGraph, the "
1048  "Matrix Market file must contain a \"pattern\" matrix.");
1049 
1050  return pBanner;
1051  }
1052 
1075  static Teuchos::Tuple<global_ordinal_type, 3>
1076  readCoordDims(std::istream& in,
1077  size_t& lineNumber,
1078  const Teuchos::RCP<const Teuchos::MatrixMarket::Banner>& pBanner,
1079  const trcp_tcomm_t& pComm,
1080  const bool tolerant = false,
1081  const bool /* debug */ = false) {
1082  using Teuchos::Tuple;
1083  using Teuchos::MatrixMarket::readCoordinateDimensions;
1084 
1085  // Packed coordinate matrix dimensions (numRows, numCols,
1086  // numNonzeros); computed on Rank 0 and broadcasted to all MPI
1087  // ranks.
1088  Tuple<global_ordinal_type, 3> dims;
1089 
1090  // Read in the coordinate matrix dimensions from the input
1091  // stream. "success" tells us whether reading in the
1092  // coordinate matrix dimensions succeeded ("Guilty unless
1093  // proven innocent").
1094  bool success = false;
1095  if (pComm->getRank() == 0) {
1096  TEUCHOS_TEST_FOR_EXCEPTION(pBanner->matrixType() != "coordinate",
1097  std::invalid_argument,
1098  "The Tpetra::CrsMatrix Matrix Market reader "
1099  "only accepts \"coordinate\" (sparse) matrix data.");
1100  // Unpacked coordinate matrix dimensions
1101  global_ordinal_type numRows, numCols, numNonzeros;
1102  // Only MPI Rank 0 reads from the input stream
1103  success = readCoordinateDimensions(in, numRows, numCols,
1104  numNonzeros, lineNumber,
1105  tolerant);
1106  // Pack up the data into a Tuple so we can send them with
1107  // one broadcast instead of three.
1108  dims[0] = numRows;
1109  dims[1] = numCols;
1110  dims[2] = numNonzeros;
1111  }
1112  // Only Rank 0 did the reading, so it decides success.
1113  //
1114  // FIXME (mfh 02 Feb 2011) Teuchos::broadcast doesn't know how
1115  // to send bools. For now, we convert to/from int instead,
1116  // using the usual "true is 1, false is 0" encoding.
1117  {
1118  int the_success = success ? 1 : 0; // only matters on MPI Rank 0
1119  Teuchos::broadcast(*pComm, 0, &the_success);
1120  success = (the_success == 1);
1121  }
1122  if (success) {
1123  // Broadcast (numRows, numCols, numNonzeros) from Rank 0
1124  // to all the other MPI ranks.
1125  Teuchos::broadcast(*pComm, 0, dims);
1126  } else {
1127  // Perhaps in tolerant mode, we could set all the
1128  // dimensions to zero for now, and deduce correct
1129  // dimensions by reading all of the file's entries and
1130  // computing the max(row index) and max(column index).
1131  // However, for now we just error out in that case.
1132  TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
1133  "Error reading Matrix Market sparse matrix: failed to read "
1134  "coordinate matrix dimensions.");
1135  }
1136  return dims;
1137  }
1138 
1149  typedef Teuchos::MatrixMarket::SymmetrizingAdder<Teuchos::MatrixMarket::Raw::Adder<scalar_type, global_ordinal_type>> adder_type;
1150 
1151  typedef Teuchos::MatrixMarket::SymmetrizingGraphAdder<Teuchos::MatrixMarket::Raw::GraphAdder<global_ordinal_type>> graph_adder_type;
1152 
1178  static Teuchos::RCP<adder_type>
1179  makeAdder(const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1180  Teuchos::RCP<const Teuchos::MatrixMarket::Banner>& pBanner,
1181  const Teuchos::Tuple<global_ordinal_type, 3>& dims,
1182  const bool tolerant = false,
1183  const bool debug = false) {
1184  if (pComm->getRank() == 0) {
1185  typedef Teuchos::MatrixMarket::Raw::Adder<scalar_type,
1187  raw_adder_type;
1188  Teuchos::RCP<raw_adder_type> pRaw =
1189  Teuchos::rcp(new raw_adder_type(dims[0], dims[1], dims[2],
1190  tolerant, debug));
1191  return Teuchos::rcp(new adder_type(pRaw, pBanner->symmType()));
1192  } else {
1193  return Teuchos::null;
1194  }
1195  }
1196 
1222  static Teuchos::RCP<graph_adder_type>
1223  makeGraphAdder(const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1224  Teuchos::RCP<const Teuchos::MatrixMarket::Banner>& pBanner,
1225  const Teuchos::Tuple<global_ordinal_type, 3>& dims,
1226  const bool tolerant = false,
1227  const bool debug = false) {
1228  if (pComm->getRank() == 0) {
1229  typedef Teuchos::MatrixMarket::Raw::GraphAdder<global_ordinal_type> raw_adder_type;
1230  Teuchos::RCP<raw_adder_type> pRaw =
1231  Teuchos::rcp(new raw_adder_type(dims[0], dims[1], dims[2],
1232  tolerant, debug));
1233  return Teuchos::rcp(new graph_adder_type(pRaw, pBanner->symmType()));
1234  } else {
1235  return Teuchos::null;
1236  }
1237  }
1238 
1240  static Teuchos::RCP<sparse_graph_type>
1241  readSparseGraphHelper(std::istream& in,
1242  const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1243  const Teuchos::RCP<const map_type>& rowMap,
1244  Teuchos::RCP<const map_type>& colMap,
1245  const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1246  const bool tolerant,
1247  const bool debug) {
1248  using std::cerr;
1249  using std::endl;
1250  using Teuchos::ptr;
1251  using Teuchos::RCP;
1252  using Teuchos::Tuple;
1253  using Teuchos::MatrixMarket::Banner;
1254 
1255  const int myRank = pComm->getRank();
1256  const int rootRank = 0;
1257 
1258  // Current line number in the input stream. Various calls
1259  // will modify this depending on the number of lines that are
1260  // read from the input stream. Only Rank 0 modifies this.
1261  size_t lineNumber = 1;
1262 
1263  if (debug && myRank == rootRank) {
1264  cerr << "Matrix Market reader: readGraph:" << endl
1265  << "-- Reading banner line" << endl;
1266  }
1267 
1268  // The "Banner" tells you whether the input stream represents
1269  // a sparse matrix, the symmetry type of the matrix, and the
1270  // type of the data it contains.
1271  //
1272  // pBanner will only be nonnull on MPI Rank 0. It will be
1273  // null on all other MPI processes.
1274  RCP<const Banner> pBanner;
1275  {
1276  // We read and validate the Banner on Proc 0, but broadcast
1277  // the validation result to all processes.
1278  // Teuchos::broadcast doesn't currently work with bool, so
1279  // we use int (true -> 1, false -> 0).
1280  int bannerIsCorrect = 1;
1281  std::ostringstream errMsg;
1282 
1283  if (myRank == rootRank) {
1284  // Read the Banner line from the input stream.
1285  try {
1286  pBanner = readBanner(in, lineNumber, tolerant, debug, true);
1287  } catch (std::exception& e) {
1288  errMsg << "Attempt to read the Matrix Market file's Banner line "
1289  "threw an exception: "
1290  << e.what();
1291  bannerIsCorrect = 0;
1292  }
1293 
1294  if (bannerIsCorrect) {
1295  // Validate the Banner for the case of a sparse graph.
1296  // We validate on Proc 0, since it reads the Banner.
1297 
1298  // In intolerant mode, the matrix type must be "coordinate".
1299  if (!tolerant && pBanner->matrixType() != "coordinate") {
1300  bannerIsCorrect = 0;
1301  errMsg << "The Matrix Market input file must contain a "
1302  "\"coordinate\"-format sparse graph in order to create a "
1303  "Tpetra::CrsGraph object from it, but the file's matrix "
1304  "type is \""
1305  << pBanner->matrixType() << "\" instead.";
1306  }
1307  // In tolerant mode, we allow the matrix type to be
1308  // anything other than "array" (which would mean that
1309  // the file contains a dense matrix).
1310  if (tolerant && pBanner->matrixType() == "array") {
1311  bannerIsCorrect = 0;
1312  errMsg << "Matrix Market file must contain a \"coordinate\"-"
1313  "format sparse graph in order to create a Tpetra::CrsGraph "
1314  "object from it, but the file's matrix type is \"array\" "
1315  "instead. That probably means the file contains dense matrix "
1316  "data.";
1317  }
1318  }
1319  } // Proc 0: Done reading the Banner, hopefully successfully.
1320 
1321  // Broadcast from Proc 0 whether the Banner was read correctly.
1322  broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
1323 
1324  // If the Banner is invalid, all processes throw an
1325  // exception. Only Proc 0 gets the exception message, but
1326  // that's OK, since the main point is to "stop the world"
1327  // (rather than throw an exception on one process and leave
1328  // the others hanging).
1329  TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
1330  std::invalid_argument, errMsg.str());
1331  } // Done reading the Banner line and broadcasting success.
1332  if (debug && myRank == rootRank) {
1333  cerr << "-- Reading dimensions line" << endl;
1334  }
1335 
1336  // Read the graph dimensions from the Matrix Market metadata.
1337  // dims = (numRows, numCols, numEntries). Proc 0 does the
1338  // reading, but it broadcasts the results to all MPI
1339  // processes. Thus, readCoordDims() is a collective
1340  // operation. It does a collective check for correctness too.
1341  Tuple<global_ordinal_type, 3> dims =
1342  readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
1343 
1344  if (debug && myRank == rootRank) {
1345  cerr << "-- Making Adder for collecting graph data" << endl;
1346  }
1347 
1348  // "Adder" object for collecting all the sparse graph entries
1349  // from the input stream. This is only nonnull on Proc 0.
1350  // The Adder internally converts the one-based indices (native
1351  // Matrix Market format) into zero-based indices.
1352  RCP<graph_adder_type> pAdder =
1353  makeGraphAdder(pComm, pBanner, dims, tolerant, debug);
1354 
1355  if (debug && myRank == rootRank) {
1356  cerr << "-- Reading graph data" << endl;
1357  }
1358  //
1359  // Read the graph entries from the input stream on Proc 0.
1360  //
1361  {
1362  // We use readSuccess to broadcast the results of the read
1363  // (succeeded or not) to all MPI processes. Since
1364  // Teuchos::broadcast doesn't currently know how to send
1365  // bools, we convert to int (true -> 1, false -> 0).
1366  int readSuccess = 1;
1367  std::ostringstream errMsg; // Exception message (only valid on Proc 0)
1368  if (myRank == rootRank) {
1369  try {
1370  // Reader for "coordinate" format sparse graph data.
1371  typedef Teuchos::MatrixMarket::CoordPatternReader<graph_adder_type,
1373  reader_type;
1374  reader_type reader(pAdder);
1375 
1376  // Read the sparse graph entries.
1377  std::pair<bool, std::vector<size_t>> results =
1378  reader.read(in, lineNumber, tolerant, debug);
1379  readSuccess = results.first ? 1 : 0;
1380  } catch (std::exception& e) {
1381  readSuccess = 0;
1382  errMsg << e.what();
1383  }
1384  }
1385  broadcast(*pComm, rootRank, ptr(&readSuccess));
1386 
1387  // It would be nice to add a "verbose" flag, so that in
1388  // tolerant mode, we could log any bad line number(s) on
1389  // Proc 0. For now, we just throw if the read fails to
1390  // succeed.
1391  //
1392  // Question: If we're in tolerant mode, and if the read did
1393  // not succeed, should we attempt to call fillComplete()?
1394  TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
1395  "Failed to read the Matrix Market sparse graph file: "
1396  << errMsg.str());
1397  } // Done reading the graph entries (stored on Proc 0 for now)
1398 
1399  if (debug && myRank == rootRank) {
1400  cerr << "-- Successfully read the Matrix Market data" << endl;
1401  }
1402 
1403  // In tolerant mode, we need to rebroadcast the graph
1404  // dimensions, since they may be different after reading the
1405  // actual graph data. We only need to broadcast the number
1406  // of rows and columns. Only Rank 0 needs to know the actual
1407  // global number of entries, since (a) we need to merge
1408  // duplicates on Rank 0 first anyway, and (b) when we
1409  // distribute the entries, each rank other than Rank 0 will
1410  // only need to know how many entries it owns, not the total
1411  // number of entries.
1412  if (tolerant) {
1413  if (debug && myRank == rootRank) {
1414  cerr << "-- Tolerant mode: rebroadcasting graph dimensions"
1415  << endl
1416  << "----- Dimensions before: "
1417  << dims[0] << " x " << dims[1]
1418  << endl;
1419  }
1420  // Packed coordinate graph dimensions (numRows, numCols).
1421  Tuple<global_ordinal_type, 2> updatedDims;
1422  if (myRank == rootRank) {
1423  // If one or more bottom rows of the graph contain no
1424  // entries, then the Adder will report that the number
1425  // of rows is less than that specified in the
1426  // metadata. We allow this case, and favor the
1427  // metadata so that the zero row(s) will be included.
1428  updatedDims[0] =
1429  std::max(dims[0], pAdder->getAdder()->numRows());
1430  updatedDims[1] = pAdder->getAdder()->numCols();
1431  }
1432  broadcast(*pComm, rootRank, updatedDims);
1433  dims[0] = updatedDims[0];
1434  dims[1] = updatedDims[1];
1435  if (debug && myRank == rootRank) {
1436  cerr << "----- Dimensions after: " << dims[0] << " x "
1437  << dims[1] << endl;
1438  }
1439  } else {
1440  // In strict mode, we require that the graph's metadata and
1441  // its actual data agree, at least somewhat. In particular,
1442  // the number of rows must agree, since otherwise we cannot
1443  // distribute the graph correctly.
1444 
1445  // Teuchos::broadcast() doesn't know how to broadcast bools,
1446  // so we use an int with the standard 1 == true, 0 == false
1447  // encoding.
1448  int dimsMatch = 1;
1449  if (myRank == rootRank) {
1450  // If one or more bottom rows of the graph contain no
1451  // entries, then the Adder will report that the number of
1452  // rows is less than that specified in the metadata. We
1453  // allow this case, and favor the metadata, but do not
1454  // allow the Adder to think there are more rows in the
1455  // graph than the metadata says.
1456  if (dims[0] < pAdder->getAdder()->numRows()) {
1457  dimsMatch = 0;
1458  }
1459  }
1460  broadcast(*pComm, 0, ptr(&dimsMatch));
1461  if (dimsMatch == 0) {
1462  // We're in an error state anyway, so we might as well
1463  // work a little harder to print an informative error
1464  // message.
1465  //
1466  // Broadcast the Adder's idea of the graph dimensions
1467  // from Proc 0 to all processes.
1468  Tuple<global_ordinal_type, 2> addersDims;
1469  if (myRank == rootRank) {
1470  addersDims[0] = pAdder->getAdder()->numRows();
1471  addersDims[1] = pAdder->getAdder()->numCols();
1472  }
1473  broadcast(*pComm, 0, addersDims);
1474  TEUCHOS_TEST_FOR_EXCEPTION(
1475  dimsMatch == 0, std::runtime_error,
1476  "The graph metadata says that the graph is " << dims[0] << " x "
1477  << dims[1] << ", but the actual data says that the graph is "
1478  << addersDims[0] << " x " << addersDims[1] << ". That means the "
1479  "data includes more rows than reported in the metadata. This "
1480  "is not allowed when parsing in strict mode. Parse the graph in "
1481  "tolerant mode to ignore the metadata when it disagrees with the "
1482  "data.");
1483  }
1484  } // Matrix dimensions (# rows, # cols, # entries) agree.
1485 
1486  // Create a map describing a distribution where the root owns EVERYTHING
1487  RCP<map_type> proc0Map;
1488  global_ordinal_type indexBase;
1489  if (Teuchos::is_null(rowMap)) {
1490  indexBase = 0;
1491  } else {
1492  indexBase = rowMap->getIndexBase();
1493  }
1494  if (myRank == rootRank) {
1495  proc0Map = rcp(new map_type(dims[0], dims[0], indexBase, pComm));
1496  } else {
1497  proc0Map = rcp(new map_type(dims[0], 0, indexBase, pComm));
1498  }
1499 
1500  // Create the graph where the root owns EVERYTHING
1501  std::map<global_ordinal_type, size_t> numEntriesPerRow_map;
1502  if (myRank == rootRank) {
1503  const auto& entries = pAdder()->getAdder()->getEntries();
1504  // This will count duplicates, but it's better than dense.
1505  // An even better approach would use a classic algorithm,
1506  // likely in Saad's old textbook, for converting COO (entries)
1507  // to CSR (the local part of the sparse matrix data structure).
1508  for (const auto& entry : entries) {
1509  const global_ordinal_type gblRow = entry.rowIndex() + indexBase;
1510  ++numEntriesPerRow_map[gblRow];
1511  }
1512  }
1513 
1514  Teuchos::Array<size_t> numEntriesPerRow(proc0Map->getLocalNumElements());
1515  for (const auto& ent : numEntriesPerRow_map) {
1516  const local_ordinal_type lclRow = proc0Map->getLocalElement(ent.first);
1517  numEntriesPerRow[lclRow] = ent.second;
1518  }
1519  // Free anything we don't need before allocating the graph.
1520  // Swapping with an empty data structure is the standard idiom
1521  // for freeing memory used by Standard Library containers.
1522  // (Just resizing to 0 doesn't promise to free memory.)
1523  {
1524  std::map<global_ordinal_type, size_t> empty_map;
1525  std::swap(numEntriesPerRow_map, empty_map);
1526  }
1527 
1528  RCP<sparse_graph_type> proc0Graph =
1529  rcp(new sparse_graph_type(proc0Map, numEntriesPerRow(),
1530  constructorParams));
1531  if (myRank == rootRank) {
1532  typedef Teuchos::MatrixMarket::Raw::GraphElement<global_ordinal_type> element_type;
1533 
1534  // Get the entries
1535  const std::vector<element_type>& entries =
1536  pAdder->getAdder()->getEntries();
1537 
1538  // Insert them one at a time
1539  for (size_t curPos = 0; curPos < entries.size(); curPos++) {
1540  const element_type& curEntry = entries[curPos];
1541  const global_ordinal_type curRow = curEntry.rowIndex() + indexBase;
1542  const global_ordinal_type curCol = curEntry.colIndex() + indexBase;
1543  Teuchos::ArrayView<const global_ordinal_type> colView(&curCol, 1);
1544  proc0Graph->insertGlobalIndices(curRow, colView);
1545  }
1546  }
1547  proc0Graph->fillComplete();
1548 
1549  RCP<sparse_graph_type> distGraph;
1550  if (Teuchos::is_null(rowMap)) {
1551  // Create a map describing the distribution we actually want
1552  RCP<map_type> distMap =
1553  rcp(new map_type(dims[0], 0, pComm, GloballyDistributed));
1554 
1555  // Create the graph with that distribution too
1556  distGraph = rcp(new sparse_graph_type(distMap, colMap, 0, constructorParams));
1557 
1558  // Create an importer/exporter/vandelay to redistribute the graph
1559  typedef Import<local_ordinal_type, global_ordinal_type, node_type> import_type;
1560  import_type importer(proc0Map, distMap);
1561 
1562  // Import the data
1563  distGraph->doImport(*proc0Graph, importer, INSERT);
1564  } else {
1565  distGraph = rcp(new sparse_graph_type(rowMap, colMap, 0, constructorParams));
1566 
1567  // Create an importer/exporter/vandelay to redistribute the graph
1568  typedef Import<local_ordinal_type, global_ordinal_type, node_type> import_type;
1569  import_type importer(proc0Map, rowMap);
1570 
1571  // Import the data
1572  distGraph->doImport(*proc0Graph, importer, INSERT);
1573  }
1574 
1575  return distGraph;
1576  }
1577 
1578  public:
1602  static Teuchos::RCP<sparse_graph_type>
1603  readSparseGraphFile(const std::string& filename,
1604  const trcp_tcomm_t& comm,
1605  const bool callFillComplete = true,
1606  const bool tolerant = false,
1607  const bool debug = false) {
1608  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true);
1609 
1610  return readSparseGraph(in, comm,
1611  callFillComplete,
1612  tolerant, debug);
1613  // We can rely on the destructor of the input stream to close
1614  // the file on scope exit, even if readSparseGraph() throws an
1615  // exception.
1616  }
1617 
1646  static Teuchos::RCP<sparse_graph_type>
1647  readSparseGraphFile(const std::string& filename,
1648  const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1649  const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1650  const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
1651  const bool tolerant = false,
1652  const bool debug = false) {
1653  std::ifstream in = Reader::openInFileOnRankZero(pComm, filename, true);
1654 
1655  return readSparseGraph(in, pComm,
1656  constructorParams,
1657  fillCompleteParams, tolerant, debug);
1658  // We can rely on the destructor of the input stream to close
1659  // the file on scope exit, even if readSparseGraph() throws an
1660  // exception.
1661  }
1662 
1700  static Teuchos::RCP<sparse_graph_type>
1701  readSparseGraphFile(const std::string& filename,
1702  const Teuchos::RCP<const map_type>& rowMap,
1703  Teuchos::RCP<const map_type>& colMap,
1704  const Teuchos::RCP<const map_type>& domainMap,
1705  const Teuchos::RCP<const map_type>& rangeMap,
1706  const bool callFillComplete = true,
1707  const bool tolerant = false,
1708  const bool debug = false) {
1709  TEUCHOS_TEST_FOR_EXCEPTION(rowMap.is_null(), std::invalid_argument,
1710  "Input rowMap must be nonnull.");
1711  trcp_tcomm_t comm = rowMap->getComm();
1712  if (comm.is_null()) {
1713  // If the input communicator is null on some process, then
1714  // that process does not participate in the collective.
1715  return Teuchos::null;
1716  }
1717 
1718  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true);
1719 
1720  return readSparseGraph(in, rowMap, colMap, domainMap, rangeMap,
1721  callFillComplete, tolerant, debug);
1722  }
1723 
1749  static Teuchos::RCP<sparse_graph_type>
1750  readSparseGraph(std::istream& in,
1751  const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1752  const bool callFillComplete = true,
1753  const bool tolerant = false,
1754  const bool debug = false) {
1755  Teuchos::RCP<const map_type> fakeRowMap;
1756  Teuchos::RCP<const map_type> fakeColMap;
1757  Teuchos::RCP<Teuchos::ParameterList> fakeCtorParams;
1758 
1759  Teuchos::RCP<sparse_graph_type> graph =
1760  readSparseGraphHelper(in, pComm,
1761  fakeRowMap, fakeColMap,
1762  fakeCtorParams, tolerant, debug);
1763  if (callFillComplete) {
1764  graph->fillComplete();
1765  }
1766  return graph;
1767  }
1768 
1798  static Teuchos::RCP<sparse_graph_type>
1799  readSparseGraph(std::istream& in,
1800  const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1801  const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1802  const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
1803  const bool tolerant = false,
1804  const bool debug = false) {
1805  Teuchos::RCP<const map_type> fakeRowMap;
1806  Teuchos::RCP<const map_type> fakeColMap;
1807  Teuchos::RCP<sparse_graph_type> graph =
1808  readSparseGraphHelper(in, pComm,
1809  fakeRowMap, fakeColMap,
1810  constructorParams, tolerant, debug);
1811  graph->fillComplete(fillCompleteParams);
1812  return graph;
1813  }
1814 
1855  static Teuchos::RCP<sparse_graph_type>
1856  readSparseGraph(std::istream& in,
1857  const Teuchos::RCP<const map_type>& rowMap,
1858  Teuchos::RCP<const map_type>& colMap,
1859  const Teuchos::RCP<const map_type>& domainMap,
1860  const Teuchos::RCP<const map_type>& rangeMap,
1861  const bool callFillComplete = true,
1862  const bool tolerant = false,
1863  const bool debug = false) {
1864  Teuchos::RCP<sparse_graph_type> graph =
1865  readSparseGraphHelper(in, rowMap->getComm(),
1866  rowMap, colMap, Teuchos::null, tolerant,
1867  debug);
1868  if (callFillComplete) {
1869  graph->fillComplete(domainMap, rangeMap);
1870  }
1871  return graph;
1872  }
1873 
1874 #include "MatrixMarket_TpetraNew.hpp"
1875 
1899  static Teuchos::RCP<sparse_matrix_type>
1900  readSparseFile(const std::string& filename,
1901  const trcp_tcomm_t& comm,
1902  const bool callFillComplete = true,
1903  const bool tolerant = false,
1904  const bool debug = false) {
1905  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true);
1906 
1907  return readSparse(in, comm, callFillComplete, tolerant, debug);
1908  // We can rely on the destructor of the input stream to close
1909  // the file on scope exit, even if readSparse() throws an
1910  // exception.
1911  }
1912 
1941  static Teuchos::RCP<sparse_matrix_type>
1942  readSparseFile(const std::string& filename,
1943  const trcp_tcomm_t& comm,
1944  const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1945  const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
1946  const bool tolerant = false,
1947  const bool debug = false) {
1948  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true);
1949 
1950  return readSparse(in, comm, constructorParams,
1951  fillCompleteParams, tolerant, debug);
1952  }
1953 
1991  static Teuchos::RCP<sparse_matrix_type>
1992  readSparseFile(const std::string& filename,
1993  const Teuchos::RCP<const map_type>& rowMap,
1994  Teuchos::RCP<const map_type>& colMap,
1995  const Teuchos::RCP<const map_type>& domainMap,
1996  const Teuchos::RCP<const map_type>& rangeMap,
1997  const bool callFillComplete = true,
1998  const bool tolerant = false,
1999  const bool debug = false) {
2000  TEUCHOS_TEST_FOR_EXCEPTION(
2001  rowMap.is_null(), std::invalid_argument,
2002  "Row Map must be nonnull.");
2003 
2004  trcp_tcomm_t comm = rowMap->getComm();
2005 
2006  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true);
2007 
2008  return readSparse(in, rowMap, colMap, domainMap, rangeMap,
2009  callFillComplete, tolerant, debug);
2010  }
2011 
2037  static Teuchos::RCP<sparse_matrix_type>
2038  readSparse(std::istream& in,
2039  const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
2040  const bool callFillComplete = true,
2041  const bool tolerant = false,
2042  const bool debug = false) {
2043  using std::cerr;
2044  using std::endl;
2045  using Teuchos::arcp;
2046  using Teuchos::ArrayRCP;
2047  using Teuchos::broadcast;
2048  using Teuchos::null;
2049  using Teuchos::ptr;
2050  using Teuchos::RCP;
2051  using Teuchos::REDUCE_MAX;
2052  using Teuchos::reduceAll;
2053  using Teuchos::Tuple;
2054  using Teuchos::MatrixMarket::Banner;
2055  typedef Teuchos::ScalarTraits<scalar_type> STS;
2056 
2057  const bool extraDebug = false;
2058  const int myRank = pComm->getRank();
2059  const int rootRank = 0;
2060 
2061  // Current line number in the input stream. Various calls
2062  // will modify this depending on the number of lines that are
2063  // read from the input stream. Only Rank 0 modifies this.
2064  size_t lineNumber = 1;
2065 
2066  if (debug && myRank == rootRank) {
2067  cerr << "Matrix Market reader: readSparse:" << endl
2068  << "-- Reading banner line" << endl;
2069  }
2070 
2071  // The "Banner" tells you whether the input stream represents
2072  // a sparse matrix, the symmetry type of the matrix, and the
2073  // type of the data it contains.
2074  //
2075  // pBanner will only be nonnull on MPI Rank 0. It will be
2076  // null on all other MPI processes.
2077  RCP<const Banner> pBanner;
2078  {
2079  // We read and validate the Banner on Proc 0, but broadcast
2080  // the validation result to all processes.
2081  // Teuchos::broadcast doesn't currently work with bool, so
2082  // we use int (true -> 1, false -> 0).
2083  int bannerIsCorrect = 1;
2084  std::ostringstream errMsg;
2085 
2086  if (myRank == rootRank) {
2087  // Read the Banner line from the input stream.
2088  try {
2089  pBanner = readBanner(in, lineNumber, tolerant, debug);
2090  } catch (std::exception& e) {
2091  errMsg << "Attempt to read the Matrix Market file's Banner line "
2092  "threw an exception: "
2093  << e.what();
2094  bannerIsCorrect = 0;
2095  }
2096 
2097  if (bannerIsCorrect) {
2098  // Validate the Banner for the case of a sparse matrix.
2099  // We validate on Proc 0, since it reads the Banner.
2100 
2101  // In intolerant mode, the matrix type must be "coordinate".
2102  if (!tolerant && pBanner->matrixType() != "coordinate") {
2103  bannerIsCorrect = 0;
2104  errMsg << "The Matrix Market input file must contain a "
2105  "\"coordinate\"-format sparse matrix in order to create a "
2106  "Tpetra::CrsMatrix object from it, but the file's matrix "
2107  "type is \""
2108  << pBanner->matrixType() << "\" instead.";
2109  }
2110  // In tolerant mode, we allow the matrix type to be
2111  // anything other than "array" (which would mean that
2112  // the file contains a dense matrix).
2113  if (tolerant && pBanner->matrixType() == "array") {
2114  bannerIsCorrect = 0;
2115  errMsg << "Matrix Market file must contain a \"coordinate\"-"
2116  "format sparse matrix in order to create a Tpetra::CrsMatrix "
2117  "object from it, but the file's matrix type is \"array\" "
2118  "instead. That probably means the file contains dense matrix "
2119  "data.";
2120  }
2121  }
2122  } // Proc 0: Done reading the Banner, hopefully successfully.
2123 
2124  // Broadcast from Proc 0 whether the Banner was read correctly.
2125  broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
2126 
2127  // If the Banner is invalid, all processes throw an
2128  // exception. Only Proc 0 gets the exception message, but
2129  // that's OK, since the main point is to "stop the world"
2130  // (rather than throw an exception on one process and leave
2131  // the others hanging).
2132  TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
2133  std::invalid_argument, errMsg.str());
2134  } // Done reading the Banner line and broadcasting success.
2135  if (debug && myRank == rootRank) {
2136  cerr << "-- Reading dimensions line" << endl;
2137  }
2138 
2139  // Read the matrix dimensions from the Matrix Market metadata.
2140  // dims = (numRows, numCols, numEntries). Proc 0 does the
2141  // reading, but it broadcasts the results to all MPI
2142  // processes. Thus, readCoordDims() is a collective
2143  // operation. It does a collective check for correctness too.
2144  Tuple<global_ordinal_type, 3> dims =
2145  readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
2146 
2147  if (debug && myRank == rootRank) {
2148  cerr << "-- Making Adder for collecting matrix data" << endl;
2149  }
2150 
2151  // "Adder" object for collecting all the sparse matrix entries
2152  // from the input stream. This is only nonnull on Proc 0.
2153  RCP<adder_type> pAdder =
2154  makeAdder(pComm, pBanner, dims, tolerant, debug);
2155 
2156  if (debug && myRank == rootRank) {
2157  cerr << "-- Reading matrix data" << endl;
2158  }
2159  //
2160  // Read the matrix entries from the input stream on Proc 0.
2161  //
2162  {
2163  // We use readSuccess to broadcast the results of the read
2164  // (succeeded or not) to all MPI processes. Since
2165  // Teuchos::broadcast doesn't currently know how to send
2166  // bools, we convert to int (true -> 1, false -> 0).
2167  int readSuccess = 1;
2168  std::ostringstream errMsg; // Exception message (only valid on Proc 0)
2169  if (myRank == rootRank) {
2170  try {
2171  // Reader for "coordinate" format sparse matrix data.
2172  typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
2173  global_ordinal_type, scalar_type, STS::isComplex>
2174  reader_type;
2175  reader_type reader(pAdder);
2176 
2177  // Read the sparse matrix entries.
2178  std::pair<bool, std::vector<size_t>> results =
2179  reader.read(in, lineNumber, tolerant, debug);
2180  readSuccess = results.first ? 1 : 0;
2181  } catch (std::exception& e) {
2182  readSuccess = 0;
2183  errMsg << e.what();
2184  }
2185  }
2186  broadcast(*pComm, rootRank, ptr(&readSuccess));
2187 
2188  // It would be nice to add a "verbose" flag, so that in
2189  // tolerant mode, we could log any bad line number(s) on
2190  // Proc 0. For now, we just throw if the read fails to
2191  // succeed.
2192  //
2193  // Question: If we're in tolerant mode, and if the read did
2194  // not succeed, should we attempt to call fillComplete()?
2195  TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
2196  "Failed to read the Matrix Market sparse matrix file: "
2197  << errMsg.str());
2198  } // Done reading the matrix entries (stored on Proc 0 for now)
2199 
2200  if (debug && myRank == rootRank) {
2201  cerr << "-- Successfully read the Matrix Market data" << endl;
2202  }
2203 
2204  // In tolerant mode, we need to rebroadcast the matrix
2205  // dimensions, since they may be different after reading the
2206  // actual matrix data. We only need to broadcast the number
2207  // of rows and columns. Only Rank 0 needs to know the actual
2208  // global number of entries, since (a) we need to merge
2209  // duplicates on Rank 0 first anyway, and (b) when we
2210  // distribute the entries, each rank other than Rank 0 will
2211  // only need to know how many entries it owns, not the total
2212  // number of entries.
2213  if (tolerant) {
2214  if (debug && myRank == rootRank) {
2215  cerr << "-- Tolerant mode: rebroadcasting matrix dimensions"
2216  << endl
2217  << "----- Dimensions before: "
2218  << dims[0] << " x " << dims[1]
2219  << endl;
2220  }
2221  // Packed coordinate matrix dimensions (numRows, numCols).
2222  Tuple<global_ordinal_type, 2> updatedDims;
2223  if (myRank == rootRank) {
2224  // If one or more bottom rows of the matrix contain no
2225  // entries, then the Adder will report that the number
2226  // of rows is less than that specified in the
2227  // metadata. We allow this case, and favor the
2228  // metadata so that the zero row(s) will be included.
2229  updatedDims[0] =
2230  std::max(dims[0], pAdder->getAdder()->numRows());
2231  updatedDims[1] = pAdder->getAdder()->numCols();
2232  }
2233  broadcast(*pComm, rootRank, updatedDims);
2234  dims[0] = updatedDims[0];
2235  dims[1] = updatedDims[1];
2236  if (debug && myRank == rootRank) {
2237  cerr << "----- Dimensions after: " << dims[0] << " x "
2238  << dims[1] << endl;
2239  }
2240  } else {
2241  // In strict mode, we require that the matrix's metadata and
2242  // its actual data agree, at least somewhat. In particular,
2243  // the number of rows must agree, since otherwise we cannot
2244  // distribute the matrix correctly.
2245 
2246  // Teuchos::broadcast() doesn't know how to broadcast bools,
2247  // so we use an int with the standard 1 == true, 0 == false
2248  // encoding.
2249  int dimsMatch = 1;
2250  if (myRank == rootRank) {
2251  // If one or more bottom rows of the matrix contain no
2252  // entries, then the Adder will report that the number of
2253  // rows is less than that specified in the metadata. We
2254  // allow this case, and favor the metadata, but do not
2255  // allow the Adder to think there are more rows in the
2256  // matrix than the metadata says.
2257  if (dims[0] < pAdder->getAdder()->numRows()) {
2258  dimsMatch = 0;
2259  }
2260  }
2261  broadcast(*pComm, 0, ptr(&dimsMatch));
2262  if (dimsMatch == 0) {
2263  // We're in an error state anyway, so we might as well
2264  // work a little harder to print an informative error
2265  // message.
2266  //
2267  // Broadcast the Adder's idea of the matrix dimensions
2268  // from Proc 0 to all processes.
2269  Tuple<global_ordinal_type, 2> addersDims;
2270  if (myRank == rootRank) {
2271  addersDims[0] = pAdder->getAdder()->numRows();
2272  addersDims[1] = pAdder->getAdder()->numCols();
2273  }
2274  broadcast(*pComm, 0, addersDims);
2275  TEUCHOS_TEST_FOR_EXCEPTION(
2276  dimsMatch == 0, std::runtime_error,
2277  "The matrix metadata says that the matrix is " << dims[0] << " x "
2278  << dims[1] << ", but the actual data says that the matrix is "
2279  << addersDims[0] << " x " << addersDims[1] << ". That means the "
2280  "data includes more rows than reported in the metadata. This "
2281  "is not allowed when parsing in strict mode. Parse the matrix in "
2282  "tolerant mode to ignore the metadata when it disagrees with the "
2283  "data.");
2284  }
2285  } // Matrix dimensions (# rows, # cols, # entries) agree.
2286 
2287  if (debug && myRank == rootRank) {
2288  cerr << "-- Converting matrix data into CSR format on Proc 0" << endl;
2289  }
2290 
2291  // Now that we've read in all the matrix entries from the
2292  // input stream into the adder on Proc 0, post-process them
2293  // into CSR format (still on Proc 0). This will facilitate
2294  // distributing them to all the processors.
2295  //
2296  // These arrays represent the global matrix data as a CSR
2297  // matrix (with numEntriesPerRow as redundant but convenient
2298  // metadata, since it's computable from rowPtr and vice
2299  // versa). They are valid only on Proc 0.
2300  ArrayRCP<size_t> numEntriesPerRow;
2301  ArrayRCP<size_t> rowPtr;
2302  ArrayRCP<global_ordinal_type> colInd;
2303  ArrayRCP<scalar_type> values;
2304 
2305  // Proc 0 first merges duplicate entries, and then converts
2306  // the coordinate-format matrix data to CSR.
2307  {
2308  int mergeAndConvertSucceeded = 1;
2309  std::ostringstream errMsg;
2310 
2311  if (myRank == rootRank) {
2312  try {
2313  typedef Teuchos::MatrixMarket::Raw::Element<scalar_type,
2315  element_type;
2316 
2317  // Number of rows in the matrix. If we are in tolerant
2318  // mode, we've already synchronized dims with the actual
2319  // matrix data. If in strict mode, we should use dims
2320  // (as read from the file's metadata) rather than the
2321  // matrix data to determine the dimensions. (The matrix
2322  // data will claim fewer rows than the metadata, if one
2323  // or more rows have no entries stored in the file.)
2324  const size_type numRows = dims[0];
2325 
2326  // Additively merge duplicate matrix entries.
2327  pAdder->getAdder()->merge();
2328 
2329  // Get a temporary const view of the merged matrix entries.
2330  const std::vector<element_type>& entries =
2331  pAdder->getAdder()->getEntries();
2332 
2333  // Number of matrix entries (after merging).
2334  const size_t numEntries = (size_t)entries.size();
2335 
2336  if (debug) {
2337  cerr << "----- Proc 0: Matrix has numRows=" << numRows
2338  << " rows and numEntries=" << numEntries
2339  << " entries." << endl;
2340  }
2341 
2342  // Make space for the CSR matrix data. Converting to
2343  // CSR is easier if we fill numEntriesPerRow with zeros
2344  // at first.
2345  numEntriesPerRow = arcp<size_t>(numRows);
2346  std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
2347  rowPtr = arcp<size_t>(numRows + 1);
2348  std::fill(rowPtr.begin(), rowPtr.end(), 0);
2349  colInd = arcp<global_ordinal_type>(numEntries);
2350  values = arcp<scalar_type>(numEntries);
2351 
2352  // Convert from array-of-structs coordinate format to CSR
2353  // (compressed sparse row) format.
2354  global_ordinal_type prvRow = 0;
2355  size_t curPos = 0;
2356  rowPtr[0] = 0;
2357  for (curPos = 0; curPos < numEntries; ++curPos) {
2358  const element_type& curEntry = entries[curPos];
2359  const global_ordinal_type curRow = curEntry.rowIndex();
2360  TEUCHOS_TEST_FOR_EXCEPTION(
2361  curRow < prvRow, std::logic_error,
2362  "Row indices are out of order, even though they are supposed "
2363  "to be sorted. curRow = "
2364  << curRow << ", prvRow = "
2365  << prvRow << ", at curPos = " << curPos << ". Please report "
2366  "this bug to the Tpetra developers.");
2367  if (curRow > prvRow) {
2368  for (global_ordinal_type r = prvRow + 1; r <= curRow; ++r) {
2369  rowPtr[r] = curPos;
2370  }
2371  prvRow = curRow;
2372  }
2373  numEntriesPerRow[curRow]++;
2374  colInd[curPos] = curEntry.colIndex();
2375  values[curPos] = curEntry.value();
2376  }
2377  // rowPtr has one more entry than numEntriesPerRow. The
2378  // last entry of rowPtr is the number of entries in
2379  // colInd and values.
2380  rowPtr[numRows] = numEntries;
2381  } // Finished conversion to CSR format
2382  catch (std::exception& e) {
2383  mergeAndConvertSucceeded = 0;
2384  errMsg << "Failed to merge sparse matrix entries and convert to "
2385  "CSR format: "
2386  << e.what();
2387  }
2388 
2389  if (debug && mergeAndConvertSucceeded) {
2390  // Number of rows in the matrix.
2391  const size_type numRows = dims[0];
2392  const size_type maxToDisplay = 100;
2393 
2394  cerr << "----- Proc 0: numEntriesPerRow[0.."
2395  << (numEntriesPerRow.size() - 1) << "] ";
2396  if (numRows > maxToDisplay) {
2397  cerr << "(only showing first and last few entries) ";
2398  }
2399  cerr << "= [";
2400  if (numRows > 0) {
2401  if (numRows > maxToDisplay) {
2402  for (size_type k = 0; k < 2; ++k) {
2403  cerr << numEntriesPerRow[k] << " ";
2404  }
2405  cerr << "... ";
2406  for (size_type k = numRows - 2; k < numRows - 1; ++k) {
2407  cerr << numEntriesPerRow[k] << " ";
2408  }
2409  } else {
2410  for (size_type k = 0; k < numRows - 1; ++k) {
2411  cerr << numEntriesPerRow[k] << " ";
2412  }
2413  }
2414  cerr << numEntriesPerRow[numRows - 1];
2415  } // numRows > 0
2416  cerr << "]" << endl;
2417 
2418  cerr << "----- Proc 0: rowPtr ";
2419  if (numRows > maxToDisplay) {
2420  cerr << "(only showing first and last few entries) ";
2421  }
2422  cerr << "= [";
2423  if (numRows > maxToDisplay) {
2424  for (size_type k = 0; k < 2; ++k) {
2425  cerr << rowPtr[k] << " ";
2426  }
2427  cerr << "... ";
2428  for (size_type k = numRows - 2; k < numRows; ++k) {
2429  cerr << rowPtr[k] << " ";
2430  }
2431  } else {
2432  for (size_type k = 0; k < numRows; ++k) {
2433  cerr << rowPtr[k] << " ";
2434  }
2435  }
2436  cerr << rowPtr[numRows] << "]" << endl;
2437  }
2438  } // if myRank == rootRank
2439  } // Done converting sparse matrix data to CSR format
2440 
2441  // Now we're done with the Adder, so we can release the
2442  // reference ("free" it) to save space. This only actually
2443  // does anything on Rank 0, since pAdder is null on all the
2444  // other MPI processes.
2445  pAdder = null;
2446 
2447  if (debug && myRank == rootRank) {
2448  cerr << "-- Making range, domain, and row maps" << endl;
2449  }
2450 
2451  // Make the maps that describe the matrix's range and domain,
2452  // and the distribution of its rows. Creating a Map is a
2453  // collective operation, so we don't have to do a broadcast of
2454  // a success Boolean.
2455  RCP<const map_type> pRangeMap = makeRangeMap(pComm, dims[0]);
2456  RCP<const map_type> pDomainMap =
2457  makeDomainMap(pRangeMap, dims[0], dims[1]);
2458  RCP<const map_type> pRowMap = makeRowMap(null, pComm, dims[0]);
2459 
2460  if (debug && myRank == rootRank) {
2461  cerr << "-- Distributing the matrix data" << endl;
2462  }
2463 
2464  // Distribute the matrix data. Each processor has to add the
2465  // rows that it owns. If you try to make Proc 0 call
2466  // insertGlobalValues() for _all_ the rows, not just those it
2467  // owns, then fillComplete() will compute the number of
2468  // columns incorrectly. That's why Proc 0 has to distribute
2469  // the matrix data and why we make all the processors (not
2470  // just Proc 0) call insertGlobalValues() on their own data.
2471  //
2472  // These arrays represent each processor's part of the matrix
2473  // data, in "CSR" format (sort of, since the row indices might
2474  // not be contiguous).
2475  ArrayRCP<size_t> myNumEntriesPerRow;
2476  ArrayRCP<size_t> myRowPtr;
2477  ArrayRCP<global_ordinal_type> myColInd;
2478  ArrayRCP<scalar_type> myValues;
2479  // Distribute the matrix data. This is a collective operation.
2480  distribute(myNumEntriesPerRow, myRowPtr, myColInd, myValues, pRowMap,
2481  numEntriesPerRow, rowPtr, colInd, values, debug);
2482 
2483  if (debug && myRank == rootRank) {
2484  cerr << "-- Inserting matrix entries on each processor";
2485  if (callFillComplete) {
2486  cerr << " and calling fillComplete()";
2487  }
2488  cerr << endl;
2489  }
2490  // Each processor inserts its part of the matrix data, and
2491  // then they all call fillComplete(). This method invalidates
2492  // the my* distributed matrix data before calling
2493  // fillComplete(), in order to save space. In general, we
2494  // never store more than two copies of the matrix's entries in
2495  // memory at once, which is no worse than what Tpetra
2496  // promises.
2497  RCP<sparse_matrix_type> pMatrix =
2498  makeMatrix(myNumEntriesPerRow, myRowPtr, myColInd, myValues,
2499  pRowMap, pRangeMap, pDomainMap, callFillComplete);
2500  // Only use a reduce-all in debug mode to check if pMatrix is
2501  // null. Otherwise, just throw an exception. We never expect
2502  // a null pointer here, so we can save a communication.
2503  if (debug) {
2504  int localIsNull = pMatrix.is_null() ? 1 : 0;
2505  int globalIsNull = 0;
2506  reduceAll(*pComm, REDUCE_MAX, localIsNull, ptr(&globalIsNull));
2507  TEUCHOS_TEST_FOR_EXCEPTION(globalIsNull != 0, std::logic_error,
2508  "Reader::makeMatrix() returned a null pointer on at least one "
2509  "process. Please report this bug to the Tpetra developers.");
2510  } else {
2511  TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::logic_error,
2512  "Reader::makeMatrix() returned a null pointer. "
2513  "Please report this bug to the Tpetra developers.");
2514  }
2515 
2516  // We can't get the dimensions of the matrix until after
2517  // fillComplete() is called. Thus, we can't do the sanity
2518  // check (dimensions read from the Matrix Market data,
2519  // vs. dimensions reported by the CrsMatrix) unless the user
2520  // asked makeMatrix() to call fillComplete().
2521  //
2522  // Note that pMatrix->getGlobalNum{Rows,Cols}() does _not_ do
2523  // what one might think it does, so you have to ask the range
2524  // resp. domain map for the number of rows resp. columns.
2525  if (callFillComplete) {
2526  const int numProcs = pComm->getSize();
2527 
2528  if (extraDebug && debug) {
2529  const global_size_t globalNumRows =
2530  pRangeMap->getGlobalNumElements();
2531  const global_size_t globalNumCols =
2532  pDomainMap->getGlobalNumElements();
2533  if (myRank == rootRank) {
2534  cerr << "-- Matrix is "
2535  << globalNumRows << " x " << globalNumCols
2536  << " with " << pMatrix->getGlobalNumEntries()
2537  << " entries, and index base "
2538  << pMatrix->getIndexBase() << "." << endl;
2539  }
2540  pComm->barrier();
2541  for (int p = 0; p < numProcs; ++p) {
2542  if (myRank == p) {
2543  cerr << "-- Proc " << p << " owns "
2544  << pMatrix->getLocalNumCols() << " columns, and "
2545  << pMatrix->getLocalNumEntries() << " entries." << endl;
2546  }
2547  pComm->barrier();
2548  }
2549  } // if (extraDebug && debug)
2550  } // if (callFillComplete)
2551 
2552  if (debug && myRank == rootRank) {
2553  cerr << "-- Done creating the CrsMatrix from the Matrix Market data"
2554  << endl;
2555  }
2556  return pMatrix;
2557  }
2558 
2587  static Teuchos::RCP<sparse_matrix_type>
2588  readSparse(std::istream& in,
2589  const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
2590  const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
2591  const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
2592  const bool tolerant = false,
2593  const bool debug = false) {
2594  using std::cerr;
2595  using std::endl;
2596  using Teuchos::arcp;
2597  using Teuchos::ArrayRCP;
2598  using Teuchos::broadcast;
2599  using Teuchos::null;
2600  using Teuchos::ptr;
2601  using Teuchos::RCP;
2602  using Teuchos::reduceAll;
2603  using Teuchos::Tuple;
2604  using Teuchos::MatrixMarket::Banner;
2605  typedef Teuchos::ScalarTraits<scalar_type> STS;
2606 
2607  const bool extraDebug = false;
2608  const int myRank = pComm->getRank();
2609  const int rootRank = 0;
2610 
2611  // Current line number in the input stream. Various calls
2612  // will modify this depending on the number of lines that are
2613  // read from the input stream. Only Rank 0 modifies this.
2614  size_t lineNumber = 1;
2615 
2616  if (debug && myRank == rootRank) {
2617  cerr << "Matrix Market reader: readSparse:" << endl
2618  << "-- Reading banner line" << endl;
2619  }
2620 
2621  // The "Banner" tells you whether the input stream represents
2622  // a sparse matrix, the symmetry type of the matrix, and the
2623  // type of the data it contains.
2624  //
2625  // pBanner will only be nonnull on MPI Rank 0. It will be
2626  // null on all other MPI processes.
2627  RCP<const Banner> pBanner;
2628  {
2629  // We read and validate the Banner on Proc 0, but broadcast
2630  // the validation result to all processes.
2631  // Teuchos::broadcast doesn't currently work with bool, so
2632  // we use int (true -> 1, false -> 0).
2633  int bannerIsCorrect = 1;
2634  std::ostringstream errMsg;
2635 
2636  if (myRank == rootRank) {
2637  // Read the Banner line from the input stream.
2638  try {
2639  pBanner = readBanner(in, lineNumber, tolerant, debug);
2640  } catch (std::exception& e) {
2641  errMsg << "Attempt to read the Matrix Market file's Banner line "
2642  "threw an exception: "
2643  << e.what();
2644  bannerIsCorrect = 0;
2645  }
2646 
2647  if (bannerIsCorrect) {
2648  // Validate the Banner for the case of a sparse matrix.
2649  // We validate on Proc 0, since it reads the Banner.
2650 
2651  // In intolerant mode, the matrix type must be "coordinate".
2652  if (!tolerant && pBanner->matrixType() != "coordinate") {
2653  bannerIsCorrect = 0;
2654  errMsg << "The Matrix Market input file must contain a "
2655  "\"coordinate\"-format sparse matrix in order to create a "
2656  "Tpetra::CrsMatrix object from it, but the file's matrix "
2657  "type is \""
2658  << pBanner->matrixType() << "\" instead.";
2659  }
2660  // In tolerant mode, we allow the matrix type to be
2661  // anything other than "array" (which would mean that
2662  // the file contains a dense matrix).
2663  if (tolerant && pBanner->matrixType() == "array") {
2664  bannerIsCorrect = 0;
2665  errMsg << "Matrix Market file must contain a \"coordinate\"-"
2666  "format sparse matrix in order to create a Tpetra::CrsMatrix "
2667  "object from it, but the file's matrix type is \"array\" "
2668  "instead. That probably means the file contains dense matrix "
2669  "data.";
2670  }
2671  }
2672  } // Proc 0: Done reading the Banner, hopefully successfully.
2673 
2674  // Broadcast from Proc 0 whether the Banner was read correctly.
2675  broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
2676 
2677  // If the Banner is invalid, all processes throw an
2678  // exception. Only Proc 0 gets the exception message, but
2679  // that's OK, since the main point is to "stop the world"
2680  // (rather than throw an exception on one process and leave
2681  // the others hanging).
2682  TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
2683  std::invalid_argument, errMsg.str());
2684  } // Done reading the Banner line and broadcasting success.
2685  if (debug && myRank == rootRank) {
2686  cerr << "-- Reading dimensions line" << endl;
2687  }
2688 
2689  // Read the matrix dimensions from the Matrix Market metadata.
2690  // dims = (numRows, numCols, numEntries). Proc 0 does the
2691  // reading, but it broadcasts the results to all MPI
2692  // processes. Thus, readCoordDims() is a collective
2693  // operation. It does a collective check for correctness too.
2694  Tuple<global_ordinal_type, 3> dims =
2695  readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
2696 
2697  if (debug && myRank == rootRank) {
2698  cerr << "-- Making Adder for collecting matrix data" << endl;
2699  }
2700 
2701  // "Adder" object for collecting all the sparse matrix entries
2702  // from the input stream. This is only nonnull on Proc 0.
2703  RCP<adder_type> pAdder =
2704  makeAdder(pComm, pBanner, dims, tolerant, debug);
2705 
2706  if (debug && myRank == rootRank) {
2707  cerr << "-- Reading matrix data" << endl;
2708  }
2709  //
2710  // Read the matrix entries from the input stream on Proc 0.
2711  //
2712  {
2713  // We use readSuccess to broadcast the results of the read
2714  // (succeeded or not) to all MPI processes. Since
2715  // Teuchos::broadcast doesn't currently know how to send
2716  // bools, we convert to int (true -> 1, false -> 0).
2717  int readSuccess = 1;
2718  std::ostringstream errMsg; // Exception message (only valid on Proc 0)
2719  if (myRank == rootRank) {
2720  try {
2721  // Reader for "coordinate" format sparse matrix data.
2722  typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
2723  global_ordinal_type, scalar_type, STS::isComplex>
2724  reader_type;
2725  reader_type reader(pAdder);
2726 
2727  // Read the sparse matrix entries.
2728  std::pair<bool, std::vector<size_t>> results =
2729  reader.read(in, lineNumber, tolerant, debug);
2730  readSuccess = results.first ? 1 : 0;
2731  } catch (std::exception& e) {
2732  readSuccess = 0;
2733  errMsg << e.what();
2734  }
2735  }
2736  broadcast(*pComm, rootRank, ptr(&readSuccess));
2737 
2738  // It would be nice to add a "verbose" flag, so that in
2739  // tolerant mode, we could log any bad line number(s) on
2740  // Proc 0. For now, we just throw if the read fails to
2741  // succeed.
2742  //
2743  // Question: If we're in tolerant mode, and if the read did
2744  // not succeed, should we attempt to call fillComplete()?
2745  TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
2746  "Failed to read the Matrix Market sparse matrix file: "
2747  << errMsg.str());
2748  } // Done reading the matrix entries (stored on Proc 0 for now)
2749 
2750  if (debug && myRank == rootRank) {
2751  cerr << "-- Successfully read the Matrix Market data" << endl;
2752  }
2753 
2754  // In tolerant mode, we need to rebroadcast the matrix
2755  // dimensions, since they may be different after reading the
2756  // actual matrix data. We only need to broadcast the number
2757  // of rows and columns. Only Rank 0 needs to know the actual
2758  // global number of entries, since (a) we need to merge
2759  // duplicates on Rank 0 first anyway, and (b) when we
2760  // distribute the entries, each rank other than Rank 0 will
2761  // only need to know how many entries it owns, not the total
2762  // number of entries.
2763  if (tolerant) {
2764  if (debug && myRank == rootRank) {
2765  cerr << "-- Tolerant mode: rebroadcasting matrix dimensions"
2766  << endl
2767  << "----- Dimensions before: "
2768  << dims[0] << " x " << dims[1]
2769  << endl;
2770  }
2771  // Packed coordinate matrix dimensions (numRows, numCols).
2772  Tuple<global_ordinal_type, 2> updatedDims;
2773  if (myRank == rootRank) {
2774  // If one or more bottom rows of the matrix contain no
2775  // entries, then the Adder will report that the number
2776  // of rows is less than that specified in the
2777  // metadata. We allow this case, and favor the
2778  // metadata so that the zero row(s) will be included.
2779  updatedDims[0] =
2780  std::max(dims[0], pAdder->getAdder()->numRows());
2781  updatedDims[1] = pAdder->getAdder()->numCols();
2782  }
2783  broadcast(*pComm, rootRank, updatedDims);
2784  dims[0] = updatedDims[0];
2785  dims[1] = updatedDims[1];
2786  if (debug && myRank == rootRank) {
2787  cerr << "----- Dimensions after: " << dims[0] << " x "
2788  << dims[1] << endl;
2789  }
2790  } else {
2791  // In strict mode, we require that the matrix's metadata and
2792  // its actual data agree, at least somewhat. In particular,
2793  // the number of rows must agree, since otherwise we cannot
2794  // distribute the matrix correctly.
2795 
2796  // Teuchos::broadcast() doesn't know how to broadcast bools,
2797  // so we use an int with the standard 1 == true, 0 == false
2798  // encoding.
2799  int dimsMatch = 1;
2800  if (myRank == rootRank) {
2801  // If one or more bottom rows of the matrix contain no
2802  // entries, then the Adder will report that the number of
2803  // rows is less than that specified in the metadata. We
2804  // allow this case, and favor the metadata, but do not
2805  // allow the Adder to think there are more rows in the
2806  // matrix than the metadata says.
2807  if (dims[0] < pAdder->getAdder()->numRows()) {
2808  dimsMatch = 0;
2809  }
2810  }
2811  broadcast(*pComm, 0, ptr(&dimsMatch));
2812  if (dimsMatch == 0) {
2813  // We're in an error state anyway, so we might as well
2814  // work a little harder to print an informative error
2815  // message.
2816  //
2817  // Broadcast the Adder's idea of the matrix dimensions
2818  // from Proc 0 to all processes.
2819  Tuple<global_ordinal_type, 2> addersDims;
2820  if (myRank == rootRank) {
2821  addersDims[0] = pAdder->getAdder()->numRows();
2822  addersDims[1] = pAdder->getAdder()->numCols();
2823  }
2824  broadcast(*pComm, 0, addersDims);
2825  TEUCHOS_TEST_FOR_EXCEPTION(
2826  dimsMatch == 0, std::runtime_error,
2827  "The matrix metadata says that the matrix is " << dims[0] << " x "
2828  << dims[1] << ", but the actual data says that the matrix is "
2829  << addersDims[0] << " x " << addersDims[1] << ". That means the "
2830  "data includes more rows than reported in the metadata. This "
2831  "is not allowed when parsing in strict mode. Parse the matrix in "
2832  "tolerant mode to ignore the metadata when it disagrees with the "
2833  "data.");
2834  }
2835  } // Matrix dimensions (# rows, # cols, # entries) agree.
2836 
2837  if (debug && myRank == rootRank) {
2838  cerr << "-- Converting matrix data into CSR format on Proc 0" << endl;
2839  }
2840 
2841  // Now that we've read in all the matrix entries from the
2842  // input stream into the adder on Proc 0, post-process them
2843  // into CSR format (still on Proc 0). This will facilitate
2844  // distributing them to all the processors.
2845  //
2846  // These arrays represent the global matrix data as a CSR
2847  // matrix (with numEntriesPerRow as redundant but convenient
2848  // metadata, since it's computable from rowPtr and vice
2849  // versa). They are valid only on Proc 0.
2850  ArrayRCP<size_t> numEntriesPerRow;
2851  ArrayRCP<size_t> rowPtr;
2852  ArrayRCP<global_ordinal_type> colInd;
2853  ArrayRCP<scalar_type> values;
2854 
2855  // Proc 0 first merges duplicate entries, and then converts
2856  // the coordinate-format matrix data to CSR.
2857  {
2858  int mergeAndConvertSucceeded = 1;
2859  std::ostringstream errMsg;
2860 
2861  if (myRank == rootRank) {
2862  try {
2863  typedef Teuchos::MatrixMarket::Raw::Element<scalar_type,
2865  element_type;
2866 
2867  // Number of rows in the matrix. If we are in tolerant
2868  // mode, we've already synchronized dims with the actual
2869  // matrix data. If in strict mode, we should use dims
2870  // (as read from the file's metadata) rather than the
2871  // matrix data to determine the dimensions. (The matrix
2872  // data will claim fewer rows than the metadata, if one
2873  // or more rows have no entries stored in the file.)
2874  const size_type numRows = dims[0];
2875 
2876  // Additively merge duplicate matrix entries.
2877  pAdder->getAdder()->merge();
2878 
2879  // Get a temporary const view of the merged matrix entries.
2880  const std::vector<element_type>& entries =
2881  pAdder->getAdder()->getEntries();
2882 
2883  // Number of matrix entries (after merging).
2884  const size_t numEntries = (size_t)entries.size();
2885 
2886  if (debug) {
2887  cerr << "----- Proc 0: Matrix has numRows=" << numRows
2888  << " rows and numEntries=" << numEntries
2889  << " entries." << endl;
2890  }
2891 
2892  // Make space for the CSR matrix data. Converting to
2893  // CSR is easier if we fill numEntriesPerRow with zeros
2894  // at first.
2895  numEntriesPerRow = arcp<size_t>(numRows);
2896  std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
2897  rowPtr = arcp<size_t>(numRows + 1);
2898  std::fill(rowPtr.begin(), rowPtr.end(), 0);
2899  colInd = arcp<global_ordinal_type>(numEntries);
2900  values = arcp<scalar_type>(numEntries);
2901 
2902  // Convert from array-of-structs coordinate format to CSR
2903  // (compressed sparse row) format.
2904  global_ordinal_type prvRow = 0;
2905  size_t curPos = 0;
2906  rowPtr[0] = 0;
2907  for (curPos = 0; curPos < numEntries; ++curPos) {
2908  const element_type& curEntry = entries[curPos];
2909  const global_ordinal_type curRow = curEntry.rowIndex();
2910  TEUCHOS_TEST_FOR_EXCEPTION(
2911  curRow < prvRow, std::logic_error,
2912  "Row indices are out of order, even though they are supposed "
2913  "to be sorted. curRow = "
2914  << curRow << ", prvRow = "
2915  << prvRow << ", at curPos = " << curPos << ". Please report "
2916  "this bug to the Tpetra developers.");
2917  if (curRow > prvRow) {
2918  for (global_ordinal_type r = prvRow + 1; r <= curRow; ++r) {
2919  rowPtr[r] = curPos;
2920  }
2921  prvRow = curRow;
2922  }
2923  numEntriesPerRow[curRow]++;
2924  colInd[curPos] = curEntry.colIndex();
2925  values[curPos] = curEntry.value();
2926  }
2927  // rowPtr has one more entry than numEntriesPerRow. The
2928  // last entry of rowPtr is the number of entries in
2929  // colInd and values.
2930  rowPtr[numRows] = numEntries;
2931  } // Finished conversion to CSR format
2932  catch (std::exception& e) {
2933  mergeAndConvertSucceeded = 0;
2934  errMsg << "Failed to merge sparse matrix entries and convert to "
2935  "CSR format: "
2936  << e.what();
2937  }
2938 
2939  if (debug && mergeAndConvertSucceeded) {
2940  // Number of rows in the matrix.
2941  const size_type numRows = dims[0];
2942  const size_type maxToDisplay = 100;
2943 
2944  cerr << "----- Proc 0: numEntriesPerRow[0.."
2945  << (numEntriesPerRow.size() - 1) << "] ";
2946  if (numRows > maxToDisplay) {
2947  cerr << "(only showing first and last few entries) ";
2948  }
2949  cerr << "= [";
2950  if (numRows > 0) {
2951  if (numRows > maxToDisplay) {
2952  for (size_type k = 0; k < 2; ++k) {
2953  cerr << numEntriesPerRow[k] << " ";
2954  }
2955  cerr << "... ";
2956  for (size_type k = numRows - 2; k < numRows - 1; ++k) {
2957  cerr << numEntriesPerRow[k] << " ";
2958  }
2959  } else {
2960  for (size_type k = 0; k < numRows - 1; ++k) {
2961  cerr << numEntriesPerRow[k] << " ";
2962  }
2963  }
2964  cerr << numEntriesPerRow[numRows - 1];
2965  } // numRows > 0
2966  cerr << "]" << endl;
2967 
2968  cerr << "----- Proc 0: rowPtr ";
2969  if (numRows > maxToDisplay) {
2970  cerr << "(only showing first and last few entries) ";
2971  }
2972  cerr << "= [";
2973  if (numRows > maxToDisplay) {
2974  for (size_type k = 0; k < 2; ++k) {
2975  cerr << rowPtr[k] << " ";
2976  }
2977  cerr << "... ";
2978  for (size_type k = numRows - 2; k < numRows; ++k) {
2979  cerr << rowPtr[k] << " ";
2980  }
2981  } else {
2982  for (size_type k = 0; k < numRows; ++k) {
2983  cerr << rowPtr[k] << " ";
2984  }
2985  }
2986  cerr << rowPtr[numRows] << "]" << endl;
2987  }
2988  } // if myRank == rootRank
2989  } // Done converting sparse matrix data to CSR format
2990 
2991  // Now we're done with the Adder, so we can release the
2992  // reference ("free" it) to save space. This only actually
2993  // does anything on Rank 0, since pAdder is null on all the
2994  // other MPI processes.
2995  pAdder = null;
2996 
2997  if (debug && myRank == rootRank) {
2998  cerr << "-- Making range, domain, and row maps" << endl;
2999  }
3000 
3001  // Make the maps that describe the matrix's range and domain,
3002  // and the distribution of its rows. Creating a Map is a
3003  // collective operation, so we don't have to do a broadcast of
3004  // a success Boolean.
3005  RCP<const map_type> pRangeMap = makeRangeMap(pComm, dims[0]);
3006  RCP<const map_type> pDomainMap =
3007  makeDomainMap(pRangeMap, dims[0], dims[1]);
3008  RCP<const map_type> pRowMap = makeRowMap(null, pComm, dims[0]);
3009 
3010  if (debug && myRank == rootRank) {
3011  cerr << "-- Distributing the matrix data" << endl;
3012  }
3013 
3014  // Distribute the matrix data. Each processor has to add the
3015  // rows that it owns. If you try to make Proc 0 call
3016  // insertGlobalValues() for _all_ the rows, not just those it
3017  // owns, then fillComplete() will compute the number of
3018  // columns incorrectly. That's why Proc 0 has to distribute
3019  // the matrix data and why we make all the processors (not
3020  // just Proc 0) call insertGlobalValues() on their own data.
3021  //
3022  // These arrays represent each processor's part of the matrix
3023  // data, in "CSR" format (sort of, since the row indices might
3024  // not be contiguous).
3025  ArrayRCP<size_t> myNumEntriesPerRow;
3026  ArrayRCP<size_t> myRowPtr;
3027  ArrayRCP<global_ordinal_type> myColInd;
3028  ArrayRCP<scalar_type> myValues;
3029  // Distribute the matrix data. This is a collective operation.
3030  distribute(myNumEntriesPerRow, myRowPtr, myColInd, myValues, pRowMap,
3031  numEntriesPerRow, rowPtr, colInd, values, debug);
3032 
3033  if (debug && myRank == rootRank) {
3034  cerr << "-- Inserting matrix entries on each process "
3035  "and calling fillComplete()"
3036  << endl;
3037  }
3038  // Each processor inserts its part of the matrix data, and
3039  // then they all call fillComplete(). This method invalidates
3040  // the my* distributed matrix data before calling
3041  // fillComplete(), in order to save space. In general, we
3042  // never store more than two copies of the matrix's entries in
3043  // memory at once, which is no worse than what Tpetra
3044  // promises.
3045  Teuchos::RCP<sparse_matrix_type> pMatrix =
3046  makeMatrix(myNumEntriesPerRow, myRowPtr, myColInd, myValues,
3047  pRowMap, pRangeMap, pDomainMap, constructorParams,
3048  fillCompleteParams);
3049  // Only use a reduce-all in debug mode to check if pMatrix is
3050  // null. Otherwise, just throw an exception. We never expect
3051  // a null pointer here, so we can save a communication.
3052  if (debug) {
3053  int localIsNull = pMatrix.is_null() ? 1 : 0;
3054  int globalIsNull = 0;
3055  reduceAll(*pComm, Teuchos::REDUCE_MAX, localIsNull, ptr(&globalIsNull));
3056  TEUCHOS_TEST_FOR_EXCEPTION(globalIsNull != 0, std::logic_error,
3057  "Reader::makeMatrix() returned a null pointer on at least one "
3058  "process. Please report this bug to the Tpetra developers.");
3059  } else {
3060  TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::logic_error,
3061  "Reader::makeMatrix() returned a null pointer. "
3062  "Please report this bug to the Tpetra developers.");
3063  }
3064 
3065  // Sanity check for dimensions (read from the Matrix Market
3066  // data, vs. dimensions reported by the CrsMatrix).
3067  //
3068  // Note that pMatrix->getGlobalNum{Rows,Cols}() does _not_ do
3069  // what one might think it does, so you have to ask the range
3070  // resp. domain map for the number of rows resp. columns.
3071  if (extraDebug && debug) {
3072  const int numProcs = pComm->getSize();
3073  const global_size_t globalNumRows =
3074  pRangeMap->getGlobalNumElements();
3075  const global_size_t globalNumCols =
3076  pDomainMap->getGlobalNumElements();
3077  if (myRank == rootRank) {
3078  cerr << "-- Matrix is "
3079  << globalNumRows << " x " << globalNumCols
3080  << " with " << pMatrix->getGlobalNumEntries()
3081  << " entries, and index base "
3082  << pMatrix->getIndexBase() << "." << endl;
3083  }
3084  pComm->barrier();
3085  for (int p = 0; p < numProcs; ++p) {
3086  if (myRank == p) {
3087  cerr << "-- Proc " << p << " owns "
3088  << pMatrix->getLocalNumCols() << " columns, and "
3089  << pMatrix->getLocalNumEntries() << " entries." << endl;
3090  }
3091  pComm->barrier();
3092  }
3093  } // if (extraDebug && debug)
3094 
3095  if (debug && myRank == rootRank) {
3096  cerr << "-- Done creating the CrsMatrix from the Matrix Market data"
3097  << endl;
3098  }
3099  return pMatrix;
3100  }
3101 
3142  static Teuchos::RCP<sparse_matrix_type>
3143  readSparse(std::istream& in,
3144  const Teuchos::RCP<const map_type>& rowMap,
3145  Teuchos::RCP<const map_type>& colMap,
3146  const Teuchos::RCP<const map_type>& domainMap,
3147  const Teuchos::RCP<const map_type>& rangeMap,
3148  const bool callFillComplete = true,
3149  const bool tolerant = false,
3150  const bool debug = false) {
3151  using std::cerr;
3152  using std::endl;
3153  using Teuchos::arcp;
3154  using Teuchos::ArrayRCP;
3155  using Teuchos::ArrayView;
3156  using Teuchos::as;
3157  using Teuchos::broadcast;
3158  using Teuchos::Comm;
3159  using Teuchos::null;
3160  using Teuchos::ptr;
3161  using Teuchos::RCP;
3162  using Teuchos::reduceAll;
3163  using Teuchos::Tuple;
3164  using Teuchos::MatrixMarket::Banner;
3165  typedef Teuchos::ScalarTraits<scalar_type> STS;
3166 
3167  RCP<const Comm<int>> pComm = rowMap->getComm();
3168  const int myRank = pComm->getRank();
3169  const int rootRank = 0;
3170  const bool extraDebug = false;
3171 
3172  // Fast checks for invalid input. We can't check other
3173  // attributes of the Maps until we've read in the matrix
3174  // dimensions.
3175  TEUCHOS_TEST_FOR_EXCEPTION(
3176  rowMap.is_null(), std::invalid_argument,
3177  "Row Map must be nonnull.");
3178  TEUCHOS_TEST_FOR_EXCEPTION(
3179  rangeMap.is_null(), std::invalid_argument,
3180  "Range Map must be nonnull.");
3181  TEUCHOS_TEST_FOR_EXCEPTION(
3182  domainMap.is_null(), std::invalid_argument,
3183  "Domain Map must be nonnull.");
3184  TEUCHOS_TEST_FOR_EXCEPTION(
3185  rowMap->getComm().getRawPtr() != pComm.getRawPtr(),
3186  std::invalid_argument,
3187  "The specified row Map's communicator (rowMap->getComm())"
3188  "differs from the given separately supplied communicator pComm.");
3189  TEUCHOS_TEST_FOR_EXCEPTION(
3190  domainMap->getComm().getRawPtr() != pComm.getRawPtr(),
3191  std::invalid_argument,
3192  "The specified domain Map's communicator (domainMap->getComm())"
3193  "differs from the given separately supplied communicator pComm.");
3194  TEUCHOS_TEST_FOR_EXCEPTION(
3195  rangeMap->getComm().getRawPtr() != pComm.getRawPtr(),
3196  std::invalid_argument,
3197  "The specified range Map's communicator (rangeMap->getComm())"
3198  "differs from the given separately supplied communicator pComm.");
3199 
3200  // Current line number in the input stream. Various calls
3201  // will modify this depending on the number of lines that are
3202  // read from the input stream. Only Rank 0 modifies this.
3203  size_t lineNumber = 1;
3204 
3205  if (debug && myRank == rootRank) {
3206  cerr << "Matrix Market reader: readSparse:" << endl
3207  << "-- Reading banner line" << endl;
3208  }
3209 
3210  // The "Banner" tells you whether the input stream represents
3211  // a sparse matrix, the symmetry type of the matrix, and the
3212  // type of the data it contains.
3213  //
3214  // pBanner will only be nonnull on MPI Rank 0. It will be
3215  // null on all other MPI processes.
3216  RCP<const Banner> pBanner;
3217  {
3218  // We read and validate the Banner on Proc 0, but broadcast
3219  // the validation result to all processes.
3220  // Teuchos::broadcast doesn't currently work with bool, so
3221  // we use int (true -> 1, false -> 0).
3222  int bannerIsCorrect = 1;
3223  std::ostringstream errMsg;
3224 
3225  if (myRank == rootRank) {
3226  // Read the Banner line from the input stream.
3227  try {
3228  pBanner = readBanner(in, lineNumber, tolerant, debug);
3229  } catch (std::exception& e) {
3230  errMsg << "Attempt to read the Matrix Market file's Banner line "
3231  "threw an exception: "
3232  << e.what();
3233  bannerIsCorrect = 0;
3234  }
3235 
3236  if (bannerIsCorrect) {
3237  // Validate the Banner for the case of a sparse matrix.
3238  // We validate on Proc 0, since it reads the Banner.
3239 
3240  // In intolerant mode, the matrix type must be "coordinate".
3241  if (!tolerant && pBanner->matrixType() != "coordinate") {
3242  bannerIsCorrect = 0;
3243  errMsg << "The Matrix Market input file must contain a "
3244  "\"coordinate\"-format sparse matrix in order to create a "
3245  "Tpetra::CrsMatrix object from it, but the file's matrix "
3246  "type is \""
3247  << pBanner->matrixType() << "\" instead.";
3248  }
3249  // In tolerant mode, we allow the matrix type to be
3250  // anything other than "array" (which would mean that
3251  // the file contains a dense matrix).
3252  if (tolerant && pBanner->matrixType() == "array") {
3253  bannerIsCorrect = 0;
3254  errMsg << "Matrix Market file must contain a \"coordinate\"-"
3255  "format sparse matrix in order to create a Tpetra::CrsMatrix "
3256  "object from it, but the file's matrix type is \"array\" "
3257  "instead. That probably means the file contains dense matrix "
3258  "data.";
3259  }
3260  }
3261  } // Proc 0: Done reading the Banner, hopefully successfully.
3262 
3263  // Broadcast from Proc 0 whether the Banner was read correctly.
3264  broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
3265 
3266  // If the Banner is invalid, all processes throw an
3267  // exception. Only Proc 0 gets the exception message, but
3268  // that's OK, since the main point is to "stop the world"
3269  // (rather than throw an exception on one process and leave
3270  // the others hanging).
3271  TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
3272  std::invalid_argument, errMsg.str());
3273  } // Done reading the Banner line and broadcasting success.
3274  if (debug && myRank == rootRank) {
3275  cerr << "-- Reading dimensions line" << endl;
3276  }
3277 
3278  // Read the matrix dimensions from the Matrix Market metadata.
3279  // dims = (numRows, numCols, numEntries). Proc 0 does the
3280  // reading, but it broadcasts the results to all MPI
3281  // processes. Thus, readCoordDims() is a collective
3282  // operation. It does a collective check for correctness too.
3283  Tuple<global_ordinal_type, 3> dims =
3284  readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
3285 
3286  if (debug && myRank == rootRank) {
3287  cerr << "-- Making Adder for collecting matrix data" << endl;
3288  }
3289 
3290  // "Adder" object for collecting all the sparse matrix entries
3291  // from the input stream. This is only nonnull on Proc 0.
3292  // The Adder internally converts the one-based indices (native
3293  // Matrix Market format) into zero-based indices.
3294  RCP<adder_type> pAdder =
3295  makeAdder(pComm, pBanner, dims, tolerant, debug);
3296 
3297  if (debug && myRank == rootRank) {
3298  cerr << "-- Reading matrix data" << endl;
3299  }
3300  //
3301  // Read the matrix entries from the input stream on Proc 0.
3302  //
3303  {
3304  // We use readSuccess to broadcast the results of the read
3305  // (succeeded or not) to all MPI processes. Since
3306  // Teuchos::broadcast doesn't currently know how to send
3307  // bools, we convert to int (true -> 1, false -> 0).
3308  int readSuccess = 1;
3309  std::ostringstream errMsg; // Exception message (only valid on Proc 0)
3310  if (myRank == rootRank) {
3311  try {
3312  // Reader for "coordinate" format sparse matrix data.
3313  typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
3314  global_ordinal_type, scalar_type, STS::isComplex>
3315  reader_type;
3316  reader_type reader(pAdder);
3317 
3318  // Read the sparse matrix entries.
3319  std::pair<bool, std::vector<size_t>> results =
3320  reader.read(in, lineNumber, tolerant, debug);
3321  readSuccess = results.first ? 1 : 0;
3322  } catch (std::exception& e) {
3323  readSuccess = 0;
3324  errMsg << e.what();
3325  }
3326  }
3327  broadcast(*pComm, rootRank, ptr(&readSuccess));
3328 
3329  // It would be nice to add a "verbose" flag, so that in
3330  // tolerant mode, we could log any bad line number(s) on
3331  // Proc 0. For now, we just throw if the read fails to
3332  // succeed.
3333  //
3334  // Question: If we're in tolerant mode, and if the read did
3335  // not succeed, should we attempt to call fillComplete()?
3336  TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
3337  "Failed to read the Matrix Market sparse matrix file: "
3338  << errMsg.str());
3339  } // Done reading the matrix entries (stored on Proc 0 for now)
3340 
3341  if (debug && myRank == rootRank) {
3342  cerr << "-- Successfully read the Matrix Market data" << endl;
3343  }
3344 
3345  // In tolerant mode, we need to rebroadcast the matrix
3346  // dimensions, since they may be different after reading the
3347  // actual matrix data. We only need to broadcast the number
3348  // of rows and columns. Only Rank 0 needs to know the actual
3349  // global number of entries, since (a) we need to merge
3350  // duplicates on Rank 0 first anyway, and (b) when we
3351  // distribute the entries, each rank other than Rank 0 will
3352  // only need to know how many entries it owns, not the total
3353  // number of entries.
3354  if (tolerant) {
3355  if (debug && myRank == rootRank) {
3356  cerr << "-- Tolerant mode: rebroadcasting matrix dimensions"
3357  << endl
3358  << "----- Dimensions before: "
3359  << dims[0] << " x " << dims[1]
3360  << endl;
3361  }
3362  // Packed coordinate matrix dimensions (numRows, numCols).
3363  Tuple<global_ordinal_type, 2> updatedDims;
3364  if (myRank == rootRank) {
3365  // If one or more bottom rows of the matrix contain no
3366  // entries, then the Adder will report that the number
3367  // of rows is less than that specified in the
3368  // metadata. We allow this case, and favor the
3369  // metadata so that the zero row(s) will be included.
3370  updatedDims[0] =
3371  std::max(dims[0], pAdder->getAdder()->numRows());
3372  updatedDims[1] = pAdder->getAdder()->numCols();
3373  }
3374  broadcast(*pComm, rootRank, updatedDims);
3375  dims[0] = updatedDims[0];
3376  dims[1] = updatedDims[1];
3377  if (debug && myRank == rootRank) {
3378  cerr << "----- Dimensions after: " << dims[0] << " x "
3379  << dims[1] << endl;
3380  }
3381  } else {
3382  // In strict mode, we require that the matrix's metadata and
3383  // its actual data agree, at least somewhat. In particular,
3384  // the number of rows must agree, since otherwise we cannot
3385  // distribute the matrix correctly.
3386 
3387  // Teuchos::broadcast() doesn't know how to broadcast bools,
3388  // so we use an int with the standard 1 == true, 0 == false
3389  // encoding.
3390  int dimsMatch = 1;
3391  if (myRank == rootRank) {
3392  // If one or more bottom rows of the matrix contain no
3393  // entries, then the Adder will report that the number of
3394  // rows is less than that specified in the metadata. We
3395  // allow this case, and favor the metadata, but do not
3396  // allow the Adder to think there are more rows in the
3397  // matrix than the metadata says.
3398  if (dims[0] < pAdder->getAdder()->numRows()) {
3399  dimsMatch = 0;
3400  }
3401  }
3402  broadcast(*pComm, 0, ptr(&dimsMatch));
3403  if (dimsMatch == 0) {
3404  // We're in an error state anyway, so we might as well
3405  // work a little harder to print an informative error
3406  // message.
3407  //
3408  // Broadcast the Adder's idea of the matrix dimensions
3409  // from Proc 0 to all processes.
3410  Tuple<global_ordinal_type, 2> addersDims;
3411  if (myRank == rootRank) {
3412  addersDims[0] = pAdder->getAdder()->numRows();
3413  addersDims[1] = pAdder->getAdder()->numCols();
3414  }
3415  broadcast(*pComm, 0, addersDims);
3416  TEUCHOS_TEST_FOR_EXCEPTION(
3417  dimsMatch == 0, std::runtime_error,
3418  "The matrix metadata says that the matrix is " << dims[0] << " x "
3419  << dims[1] << ", but the actual data says that the matrix is "
3420  << addersDims[0] << " x " << addersDims[1] << ". That means the "
3421  "data includes more rows than reported in the metadata. This "
3422  "is not allowed when parsing in strict mode. Parse the matrix in "
3423  "tolerant mode to ignore the metadata when it disagrees with the "
3424  "data.");
3425  }
3426  } // Matrix dimensions (# rows, # cols, # entries) agree.
3427 
3428  if (debug && myRank == rootRank) {
3429  cerr << "-- Converting matrix data into CSR format on Proc 0" << endl;
3430  }
3431 
3432  // Now that we've read in all the matrix entries from the
3433  // input stream into the adder on Proc 0, post-process them
3434  // into CSR format (still on Proc 0). This will facilitate
3435  // distributing them to all the processors.
3436  //
3437  // These arrays represent the global matrix data as a CSR
3438  // matrix (with numEntriesPerRow as redundant but convenient
3439  // metadata, since it's computable from rowPtr and vice
3440  // versa). They are valid only on Proc 0.
3441  ArrayRCP<size_t> numEntriesPerRow;
3442  ArrayRCP<size_t> rowPtr;
3443  ArrayRCP<global_ordinal_type> colInd;
3444  ArrayRCP<scalar_type> values;
3445 
3446  // Proc 0 first merges duplicate entries, and then converts
3447  // the coordinate-format matrix data to CSR.
3448  {
3449  int mergeAndConvertSucceeded = 1;
3450  std::ostringstream errMsg;
3451 
3452  if (myRank == rootRank) {
3453  try {
3454  typedef Teuchos::MatrixMarket::Raw::Element<scalar_type,
3456  element_type;
3457 
3458  // Number of rows in the matrix. If we are in tolerant
3459  // mode, we've already synchronized dims with the actual
3460  // matrix data. If in strict mode, we should use dims
3461  // (as read from the file's metadata) rather than the
3462  // matrix data to determine the dimensions. (The matrix
3463  // data will claim fewer rows than the metadata, if one
3464  // or more rows have no entries stored in the file.)
3465  const size_type numRows = dims[0];
3466 
3467  // Additively merge duplicate matrix entries.
3468  pAdder->getAdder()->merge();
3469 
3470  // Get a temporary const view of the merged matrix entries.
3471  const std::vector<element_type>& entries =
3472  pAdder->getAdder()->getEntries();
3473 
3474  // Number of matrix entries (after merging).
3475  const size_t numEntries = (size_t)entries.size();
3476 
3477  if (debug) {
3478  cerr << "----- Proc 0: Matrix has numRows=" << numRows
3479  << " rows and numEntries=" << numEntries
3480  << " entries." << endl;
3481  }
3482 
3483  // Make space for the CSR matrix data. Converting to
3484  // CSR is easier if we fill numEntriesPerRow with zeros
3485  // at first.
3486  numEntriesPerRow = arcp<size_t>(numRows);
3487  std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
3488  rowPtr = arcp<size_t>(numRows + 1);
3489  std::fill(rowPtr.begin(), rowPtr.end(), 0);
3490  colInd = arcp<global_ordinal_type>(numEntries);
3491  values = arcp<scalar_type>(numEntries);
3492 
3493  // Convert from array-of-structs coordinate format to CSR
3494  // (compressed sparse row) format.
3495  global_ordinal_type prvRow = 0;
3496  size_t curPos = 0;
3497  rowPtr[0] = 0;
3498  for (curPos = 0; curPos < numEntries; ++curPos) {
3499  const element_type& curEntry = entries[curPos];
3500  const global_ordinal_type curRow = curEntry.rowIndex();
3501  TEUCHOS_TEST_FOR_EXCEPTION(
3502  curRow < prvRow, std::logic_error,
3503  "Row indices are out of order, even though they are supposed "
3504  "to be sorted. curRow = "
3505  << curRow << ", prvRow = "
3506  << prvRow << ", at curPos = " << curPos << ". Please report "
3507  "this bug to the Tpetra developers.");
3508  if (curRow > prvRow) {
3509  prvRow = curRow;
3510  }
3511  numEntriesPerRow[curRow]++;
3512  colInd[curPos] = curEntry.colIndex();
3513  values[curPos] = curEntry.value();
3514  }
3515 
3516  rowPtr[0] = 0;
3517  for (global_ordinal_type row = 1; row <= numRows; ++row) {
3518  rowPtr[row] = numEntriesPerRow[row - 1] + rowPtr[row - 1];
3519  }
3520  } // Finished conversion to CSR format
3521  catch (std::exception& e) {
3522  mergeAndConvertSucceeded = 0;
3523  errMsg << "Failed to merge sparse matrix entries and convert to "
3524  "CSR format: "
3525  << e.what();
3526  }
3527 
3528  if (debug && mergeAndConvertSucceeded) {
3529  // Number of rows in the matrix.
3530  const size_type numRows = dims[0];
3531  const size_type maxToDisplay = 100;
3532 
3533  cerr << "----- Proc 0: numEntriesPerRow[0.."
3534  << (numEntriesPerRow.size() - 1) << "] ";
3535  if (numRows > maxToDisplay) {
3536  cerr << "(only showing first and last few entries) ";
3537  }
3538  cerr << "= [";
3539  if (numRows > 0) {
3540  if (numRows > maxToDisplay) {
3541  for (size_type k = 0; k < 2; ++k) {
3542  cerr << numEntriesPerRow[k] << " ";
3543  }
3544  cerr << "... ";
3545  for (size_type k = numRows - 2; k < numRows - 1; ++k) {
3546  cerr << numEntriesPerRow[k] << " ";
3547  }
3548  } else {
3549  for (size_type k = 0; k < numRows - 1; ++k) {
3550  cerr << numEntriesPerRow[k] << " ";
3551  }
3552  }
3553  cerr << numEntriesPerRow[numRows - 1];
3554  } // numRows > 0
3555  cerr << "]" << endl;
3556 
3557  cerr << "----- Proc 0: rowPtr ";
3558  if (numRows > maxToDisplay) {
3559  cerr << "(only showing first and last few entries) ";
3560  }
3561  cerr << "= [";
3562  if (numRows > maxToDisplay) {
3563  for (size_type k = 0; k < 2; ++k) {
3564  cerr << rowPtr[k] << " ";
3565  }
3566  cerr << "... ";
3567  for (size_type k = numRows - 2; k < numRows; ++k) {
3568  cerr << rowPtr[k] << " ";
3569  }
3570  } else {
3571  for (size_type k = 0; k < numRows; ++k) {
3572  cerr << rowPtr[k] << " ";
3573  }
3574  }
3575  cerr << rowPtr[numRows] << "]" << endl;
3576 
3577  cerr << "----- Proc 0: colInd = [";
3578  for (size_t k = 0; k < rowPtr[numRows]; ++k) {
3579  cerr << colInd[k] << " ";
3580  }
3581  cerr << "]" << endl;
3582  }
3583  } // if myRank == rootRank
3584  } // Done converting sparse matrix data to CSR format
3585 
3586  // Now we're done with the Adder, so we can release the
3587  // reference ("free" it) to save space. This only actually
3588  // does anything on Rank 0, since pAdder is null on all the
3589  // other MPI processes.
3590  pAdder = null;
3591 
3592  // Verify details of the Maps. Don't count the global number
3593  // of entries in the row Map, since that number doesn't
3594  // correctly count overlap.
3595  if (debug && myRank == rootRank) {
3596  cerr << "-- Verifying Maps" << endl;
3597  }
3598  TEUCHOS_TEST_FOR_EXCEPTION(
3599  as<global_ordinal_type>(dims[0]) != rangeMap->getMaxAllGlobalIndex() + 1 - rangeMap->getIndexBase(),
3600  std::invalid_argument,
3601  "The range Map has " << rangeMap->getMaxAllGlobalIndex()
3602  << " max entry, but the matrix has a global number of rows " << dims[0]
3603  << ".");
3604  TEUCHOS_TEST_FOR_EXCEPTION(
3605  as<global_ordinal_type>(dims[1]) != domainMap->getMaxAllGlobalIndex() + 1 - domainMap->getIndexBase(),
3606  std::invalid_argument,
3607  "The domain Map has " << domainMap->getMaxAllGlobalIndex()
3608  << " max entry, but the matrix has a global number of columns "
3609  << dims[1] << ".");
3610 
3611  // Create a row Map which is entirely owned on Proc 0.
3612  RCP<Teuchos::FancyOStream> err = debug ? Teuchos::getFancyOStream(Teuchos::rcpFromRef(cerr)) : null;
3613 
3614  RCP<const map_type> gatherRowMap = Details::computeGatherMap(rowMap, err, debug);
3615  ArrayView<const global_ordinal_type> myRows =
3616  gatherRowMap->getLocalElementList();
3617  const size_type myNumRows = myRows.size();
3618  const global_ordinal_type indexBase = gatherRowMap->getIndexBase();
3619 
3620  ArrayRCP<size_t> gatherNumEntriesPerRow = arcp<size_t>(myNumRows);
3621  for (size_type i_ = 0; i_ < myNumRows; i_++) {
3622  gatherNumEntriesPerRow[i_] = numEntriesPerRow[myRows[i_] - indexBase];
3623  }
3624 
3625  // Create a matrix using this Map, and fill in on Proc 0. We
3626  // know how many entries there are in each row, so we can use
3627  // static profile.
3628  RCP<sparse_matrix_type> A_proc0 =
3629  rcp(new sparse_matrix_type(gatherRowMap, gatherNumEntriesPerRow()));
3630  if (myRank == rootRank) {
3631  if (debug) {
3632  cerr << "-- Proc 0: Filling gather matrix" << endl;
3633  }
3634  if (debug) {
3635  cerr << "---- Rows: " << Teuchos::toString(myRows) << endl;
3636  }
3637 
3638  // Add Proc 0's matrix entries to the CrsMatrix.
3639  for (size_type i_ = 0; i_ < myNumRows; ++i_) {
3640  size_type i = myRows[i_] - indexBase;
3641 
3642  const size_type curPos = as<size_type>(rowPtr[i]);
3643  const local_ordinal_type curNumEntries = numEntriesPerRow[i];
3644  ArrayView<global_ordinal_type> curColInd =
3645  colInd.view(curPos, curNumEntries);
3646  ArrayView<scalar_type> curValues =
3647  values.view(curPos, curNumEntries);
3648 
3649  // Modify the column indices in place to have the right index base.
3650  for (size_type k = 0; k < curNumEntries; ++k) {
3651  curColInd[k] += indexBase;
3652  }
3653  if (debug) {
3654  cerr << "------ Columns: " << Teuchos::toString(curColInd) << endl;
3655  cerr << "------ Values: " << Teuchos::toString(curValues) << endl;
3656  }
3657  // Avoid constructing empty views of ArrayRCP objects.
3658  if (curNumEntries > 0) {
3659  A_proc0->insertGlobalValues(myRows[i_], curColInd, curValues);
3660  }
3661  }
3662  // Now we can save space by deallocating numEntriesPerRow,
3663  // rowPtr, colInd, and values, since we've already put those
3664  // data in the matrix.
3665  numEntriesPerRow = null;
3666  rowPtr = null;
3667  colInd = null;
3668  values = null;
3669  } // if myRank == rootRank
3670 
3671  RCP<sparse_matrix_type> A;
3673  export_type exp(gatherRowMap, rowMap);
3674 
3675  // Communicate the precise number of nonzeros per row, which was already
3676  // calculated above.
3677  typedef local_ordinal_type LO;
3678  typedef global_ordinal_type GO;
3679  typedef Tpetra::MultiVector<GO, LO, GO, node_type> mv_type_go;
3680  mv_type_go target_nnzPerRow(rowMap, 1);
3681  mv_type_go source_nnzPerRow(gatherRowMap, 1);
3682  Teuchos::ArrayRCP<GO> srcData = source_nnzPerRow.getDataNonConst(0);
3683  for (int i = 0; i < myNumRows; i++)
3684  srcData[i] = gatherNumEntriesPerRow[i];
3685  srcData = Teuchos::null;
3686  target_nnzPerRow.doExport(source_nnzPerRow, exp, Tpetra::INSERT);
3687  Teuchos::ArrayRCP<GO> targetData = target_nnzPerRow.getDataNonConst(0);
3688  ArrayRCP<size_t> targetData_size_t = arcp<size_t>(targetData.size());
3689  for (int i = 0; i < targetData.size(); i++)
3690  targetData_size_t[i] = targetData[i];
3691 
3692  if (colMap.is_null()) {
3693  A = rcp(new sparse_matrix_type(rowMap, targetData_size_t()));
3694  } else {
3695  A = rcp(new sparse_matrix_type(rowMap, colMap, targetData_size_t()));
3696  }
3697  A->doExport(*A_proc0, exp, INSERT);
3698  if (callFillComplete) {
3699  A->fillComplete(domainMap, rangeMap);
3700  }
3701 
3702  // We can't get the dimensions of the matrix until after
3703  // fillComplete() is called. Thus, we can't do the sanity
3704  // check (dimensions read from the Matrix Market data,
3705  // vs. dimensions reported by the CrsMatrix) unless the user
3706  // asked us to call fillComplete().
3707  //
3708  // Note that pMatrix->getGlobalNum{Rows,Cols}() does _not_ do
3709  // what one might think it does, so you have to ask the range
3710  // resp. domain map for the number of rows resp. columns.
3711  if (callFillComplete) {
3712  const int numProcs = pComm->getSize();
3713 
3714  if (extraDebug && debug) {
3715  const global_size_t globalNumRows = rangeMap->getGlobalNumElements();
3716  const global_size_t globalNumCols = domainMap->getGlobalNumElements();
3717  if (myRank == rootRank) {
3718  cerr << "-- Matrix is "
3719  << globalNumRows << " x " << globalNumCols
3720  << " with " << A->getGlobalNumEntries()
3721  << " entries, and index base "
3722  << A->getIndexBase() << "." << endl;
3723  }
3724  pComm->barrier();
3725  for (int p = 0; p < numProcs; ++p) {
3726  if (myRank == p) {
3727  cerr << "-- Proc " << p << " owns "
3728  << A->getLocalNumCols() << " columns, and "
3729  << A->getLocalNumEntries() << " entries." << endl;
3730  }
3731  pComm->barrier();
3732  }
3733  } // if (extraDebug && debug)
3734  } // if (callFillComplete)
3735 
3736  if (debug && myRank == rootRank) {
3737  cerr << "-- Done creating the CrsMatrix from the Matrix Market data"
3738  << endl;
3739  }
3740  return A;
3741  }
3742 
3772  static Teuchos::RCP<multivector_type>
3773  readDenseFile(const std::string& filename,
3774  const trcp_tcomm_t& comm,
3775  Teuchos::RCP<const map_type>& map,
3776  const bool tolerant = false,
3777  const bool debug = false,
3778  const bool binary = false) {
3779  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true, binary ? std::ios::binary : std::ios::in);
3780 
3781  return readDense(in, comm, map, tolerant, debug, binary);
3782  }
3783 
3794  static std::ifstream openInFileOnRankZero(
3795  const trcp_tcomm_t& comm,
3796  const std::string& filename, const bool safe = true,
3797  std::ios_base::openmode mode = std::ios_base::in) {
3798  // Input stream.
3799  std::ifstream in;
3800 
3801  // Placeholder for broadcasting in-stream state.
3802  int all_should_stop = false;
3803 
3804  // Try to open the in-stream on root rank.
3805  if (comm->getRank() == 0) {
3806  in.open(filename, mode);
3807  all_should_stop = !in && safe;
3808  }
3809 
3810  // Broadcast state and possibly throw.
3811  if (comm) Teuchos::broadcast(*comm, 0, &all_should_stop);
3812 
3813  TEUCHOS_TEST_FOR_EXCEPTION(
3814  all_should_stop,
3815  std::runtime_error,
3816  "Could not open input file '" + filename + "' on root rank 0.");
3817 
3818  return in;
3819  }
3820 
3850  static Teuchos::RCP<vector_type>
3851  readVectorFile(const std::string& filename,
3852  const trcp_tcomm_t& comm,
3853  Teuchos::RCP<const map_type>& map,
3854  const bool tolerant = false,
3855  const bool debug = false) {
3856  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true);
3857 
3858  return readVector(in, comm, map, tolerant, debug);
3859  }
3860 
3928 
3929  static Teuchos::RCP<multivector_type>
3930  readDense(std::istream& in,
3931  const trcp_tcomm_t& comm,
3932  Teuchos::RCP<const map_type>& map,
3933  const bool tolerant = false,
3934  const bool debug = false,
3935  const bool binary = false) {
3936  Teuchos::RCP<Teuchos::FancyOStream> err =
3937  Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
3938  return readDenseImpl<scalar_type>(in, comm, map, err, tolerant, debug, binary);
3939  }
3940 
3942  static Teuchos::RCP<vector_type>
3943  readVector(std::istream& in,
3944  const trcp_tcomm_t& comm,
3945  Teuchos::RCP<const map_type>& map,
3946  const bool tolerant = false,
3947  const bool debug = false) {
3948  Teuchos::RCP<Teuchos::FancyOStream> err =
3949  Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
3950  return readVectorImpl<scalar_type>(in, comm, map, err, tolerant, debug);
3951  }
3952 
3974  static Teuchos::RCP<const map_type>
3975  readMapFile(const std::string& filename,
3976  const trcp_tcomm_t& comm,
3977  const bool tolerant = false,
3978  const bool debug = false,
3979  const bool binary = false) {
3980  std::ifstream in = Reader::openInFileOnRankZero(comm, filename, true, binary ? std::ios::binary : std::ios::in);
3981 
3982  return readMap(in, comm, tolerant, debug, binary);
3983  }
3984 
3985  private:
3986  template <class MultiVectorScalarType>
3987  static Teuchos::RCP<Tpetra::MultiVector<MultiVectorScalarType,
3990  node_type>>
3991  readDenseImpl(std::istream& in,
3992  const trcp_tcomm_t& comm,
3993  Teuchos::RCP<const map_type>& map,
3994  const Teuchos::RCP<Teuchos::FancyOStream>& err,
3995  const bool tolerant = false,
3996  const bool debug = false,
3997  const bool binary = false) {
3998  using std::endl;
3999  using Teuchos::ArrayRCP;
4000  using Teuchos::as;
4001  using Teuchos::broadcast;
4002  using Teuchos::outArg;
4003  using Teuchos::RCP;
4004  using Teuchos::Tuple;
4005  using Teuchos::MatrixMarket::Banner;
4006  using Teuchos::MatrixMarket::checkCommentLine;
4007  typedef MultiVectorScalarType ST;
4008  typedef local_ordinal_type LO;
4009  typedef global_ordinal_type GO;
4010  typedef node_type NT;
4011  typedef Teuchos::ScalarTraits<ST> STS;
4012  typedef typename STS::magnitudeType MT;
4013  typedef Teuchos::ScalarTraits<MT> STM;
4015 
4016  // Rank 0 is the only (MPI) process allowed to read from the
4017  // input stream.
4018  const int myRank = comm->getRank();
4019 
4020  if (!err.is_null()) {
4021  err->pushTab();
4022  }
4023  if (debug) {
4024  *err << myRank << ": readDenseImpl" << endl;
4025  }
4026  if (!err.is_null()) {
4027  err->pushTab();
4028  }
4029 
4030  // mfh 17 Feb 2013: It's not strictly necessary that the Comm
4031  // instances be identical and that the Node instances be
4032  // identical. The essential condition is more complicated to
4033  // test and isn't the same for all Node types. Thus, we just
4034  // leave it up to the user.
4035 
4036  // // If map is nonnull, check the precondition that its
4037  // // communicator resp. node equal comm resp. node. Checking
4038  // // now avoids doing a lot of file reading before we detect the
4039  // // violated precondition.
4040  // TEUCHOS_TEST_FOR_EXCEPTION(
4041  // ! map.is_null() && (map->getComm() != comm || map->getNode () != node,
4042  // std::invalid_argument, "If you supply a nonnull Map, the Map's "
4043  // "communicator and node must equal the supplied communicator resp. "
4044  // "node.");
4045 
4046  // Process 0 will read in the matrix dimensions from the file,
4047  // and broadcast them to all ranks in the given communicator.
4048  // There are only 2 dimensions in the matrix, but we use the
4049  // third element of the Tuple to encode the banner's reported
4050  // data type: "real" == 0, "complex" == 1, and "integer" == 0
4051  // (same as "real"). We don't allow pattern matrices (i.e.,
4052  // graphs) since they only make sense for sparse data.
4053  Tuple<GO, 3> dims;
4054  dims[0] = 0;
4055  dims[1] = 0;
4056  dims[2] = 0;
4057 
4058  // Current line number in the input stream. Only valid on
4059  // Proc 0. Various calls will modify this depending on the
4060  // number of lines that are read from the input stream.
4061  size_t lineNumber = 1;
4062 
4063  // Capture errors and their messages on Proc 0.
4064  std::ostringstream exMsg;
4065  int localBannerReadSuccess = 1;
4066  int localDimsReadSuccess = 1;
4067 
4068  // Only Proc 0 gets to read matrix data from the input stream.
4069  if (myRank == 0) {
4070  if (!binary) {
4071  if (debug) {
4072  *err << myRank << ": readDenseImpl: Reading banner line (dense)" << endl;
4073  }
4074 
4075  // The "Banner" tells you whether the input stream
4076  // represents a dense matrix, the symmetry type of the
4077  // matrix, and the type of the data it contains.
4078  RCP<const Banner> pBanner;
4079  try {
4080  pBanner = readBanner(in, lineNumber, tolerant, debug);
4081  } catch (std::exception& e) {
4082  exMsg << e.what();
4083  localBannerReadSuccess = 0;
4084  }
4085  // Make sure the input stream is the right kind of data.
4086  if (localBannerReadSuccess) {
4087  if (pBanner->matrixType() != "array") {
4088  exMsg << "The Matrix Market file does not contain dense matrix "
4089  "data. Its banner (first) line says that its matrix type is \""
4090  << pBanner->matrixType() << "\", rather that the required "
4091  "\"array\".";
4092  localBannerReadSuccess = 0;
4093  } else if (pBanner->dataType() == "pattern") {
4094  exMsg << "The Matrix Market file's banner (first) "
4095  "line claims that the matrix's data type is \"pattern\". This does "
4096  "not make sense for a dense matrix, yet the file reports the matrix "
4097  "as dense. The only valid data types for a dense matrix are "
4098  "\"real\", \"complex\", and \"integer\".";
4099  localBannerReadSuccess = 0;
4100  } else {
4101  // Encode the data type reported by the Banner as the
4102  // third element of the dimensions Tuple.
4103  dims[2] = encodeDataType(pBanner->dataType());
4104  }
4105  } // if we successfully read the banner line
4106 
4107  // At this point, we've successfully read the banner line.
4108  // Now read the dimensions line.
4109  if (localBannerReadSuccess) {
4110  if (debug) {
4111  *err << myRank << ": readDenseImpl: Reading dimensions line (dense)" << endl;
4112  }
4113  // Keep reading lines from the input stream until we find
4114  // a non-comment line, or until we run out of lines. The
4115  // latter is an error, since every "array" format Matrix
4116  // Market file must have a dimensions line after the
4117  // banner (even if the matrix has zero rows or columns, or
4118  // zero entries).
4119  std::string line;
4120  bool commentLine = true;
4121 
4122  while (commentLine) {
4123  // Test whether it is even valid to read from the input
4124  // stream wrapping the line.
4125  if (in.eof() || in.fail()) {
4126  exMsg << "Unable to get array dimensions line (at all) from line "
4127  << lineNumber << " of input stream. The input stream "
4128  << "claims that it is "
4129  << (in.eof() ? "at end-of-file." : "in a failed state.");
4130  localDimsReadSuccess = 0;
4131  } else {
4132  // Try to get the next line from the input stream.
4133  if (getline(in, line)) {
4134  ++lineNumber; // We did actually read a line.
4135  }
4136  // Is the current line a comment line? Ignore start
4137  // and size; they are only useful for reading the
4138  // actual matrix entries. (We could use them here as
4139  // an optimization, but we've chosen not to.)
4140  size_t start = 0, size = 0;
4141  commentLine = checkCommentLine(line, start, size, lineNumber, tolerant);
4142  } // whether we failed to read the line at all
4143  } // while the line we just read is a comment line
4144 
4145  //
4146  // Get <numRows> <numCols> from the line we just read.
4147  //
4148  std::istringstream istr(line);
4149 
4150  // Test whether it is even valid to read from the input
4151  // stream wrapping the line.
4152  if (istr.eof() || istr.fail()) {
4153  exMsg << "Unable to read any data from line " << lineNumber
4154  << " of input; the line should contain the matrix dimensions "
4155  << "\"<numRows> <numCols>\".";
4156  localDimsReadSuccess = 0;
4157  } else { // It's valid to read from the line.
4158  GO theNumRows = 0;
4159  istr >> theNumRows; // Read in the number of rows.
4160  if (istr.fail()) {
4161  exMsg << "Failed to get number of rows from line "
4162  << lineNumber << " of input; the line should contains the "
4163  << "matrix dimensions \"<numRows> <numCols>\".";
4164  localDimsReadSuccess = 0;
4165  } else { // We successfully read the number of rows
4166  dims[0] = theNumRows; // Save the number of rows
4167  if (istr.eof()) { // Do we still have data to read?
4168  exMsg << "No more data after number of rows on line "
4169  << lineNumber << " of input; the line should contain the "
4170  << "matrix dimensions \"<numRows> <numCols>\".";
4171  localDimsReadSuccess = 0;
4172  } else { // Still data left to read; read in number of columns.
4173  GO theNumCols = 0;
4174  istr >> theNumCols; // Read in the number of columns
4175  if (istr.fail()) {
4176  exMsg << "Failed to get number of columns from line "
4177  << lineNumber << " of input; the line should contain "
4178  << "the matrix dimensions \"<numRows> <numCols>\".";
4179  localDimsReadSuccess = 0;
4180  } else { // We successfully read the number of columns
4181  dims[1] = theNumCols; // Save the number of columns
4182  } // if istr.fail ()
4183  } // if istr.eof ()
4184  } // if we read the number of rows
4185  } // if the input stream wrapping the dims line was (in)valid
4186  } // if we successfully read the banner line
4187  } // not in binary format
4188  else { // in binary format
4189  global_size_t numRows, numCols;
4190  in.read(reinterpret_cast<char*>(&numRows), sizeof(numRows));
4191  in.read(reinterpret_cast<char*>(&numCols), sizeof(numCols));
4192  dims[0] = Teuchos::as<GO>(numRows);
4193  dims[1] = Teuchos::as<GO>(numCols);
4194  if ((typeid(ST) == typeid(double)) || Teuchos::ScalarTraits<ST>::isOrdinal) {
4195  dims[2] = 0;
4196  } else if (Teuchos::ScalarTraits<ST>::isComplex) {
4197  dims[2] = 1;
4198  } else {
4199  TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
4200  "Unrecognized Matrix Market data type. "
4201  "We should never get here. "
4202  "Please report this bug to the Tpetra developers.");
4203  }
4204  localDimsReadSuccess = true;
4205  localDimsReadSuccess = true;
4206  } // in binary format
4207  } // if (myRank == 0)
4208 
4209  // Broadcast the matrix dimensions, the encoded data type, and
4210  // whether or not Proc 0 succeeded in reading the banner and
4211  // dimensions.
4212  Tuple<GO, 5> bannerDimsReadResult;
4213  if (myRank == 0) {
4214  bannerDimsReadResult[0] = dims[0]; // numRows
4215  bannerDimsReadResult[1] = dims[1]; // numCols
4216  bannerDimsReadResult[2] = dims[2]; // encoded data type
4217  bannerDimsReadResult[3] = localBannerReadSuccess;
4218  bannerDimsReadResult[4] = localDimsReadSuccess;
4219  }
4220  // Broadcast matrix dimensions and the encoded data type from
4221  // Proc 0 to all the MPI processes.
4222  broadcast(*comm, 0, bannerDimsReadResult);
4223 
4224  TEUCHOS_TEST_FOR_EXCEPTION(
4225  bannerDimsReadResult[3] == 0, std::runtime_error,
4226  "Failed to read banner line: " << exMsg.str());
4227  TEUCHOS_TEST_FOR_EXCEPTION(
4228  bannerDimsReadResult[4] == 0, std::runtime_error,
4229  "Failed to read matrix dimensions line: " << exMsg.str());
4230  if (myRank != 0) {
4231  dims[0] = bannerDimsReadResult[0];
4232  dims[1] = bannerDimsReadResult[1];
4233  dims[2] = bannerDimsReadResult[2];
4234  }
4235 
4236  // Tpetra objects want the matrix dimensions in these types.
4237  const global_size_t numRows = static_cast<global_size_t>(dims[0]);
4238  const size_t numCols = static_cast<size_t>(dims[1]);
4239 
4240  // Make a "Proc 0 owns everything" Map that we will use to
4241  // read in the multivector entries in the correct order on
4242  // Proc 0. This must be a collective
4243  RCP<const map_type> proc0Map; // "Proc 0 owns everything" Map
4244  if (map.is_null()) {
4245  // The user didn't supply a Map. Make a contiguous
4246  // distributed Map for them, using the read-in multivector
4247  // dimensions.
4248  map = createUniformContigMapWithNode<LO, GO, NT>(numRows, comm);
4249  const size_t localNumRows = (myRank == 0) ? numRows : 0;
4250  // At this point, map exists and has a nonnull node.
4251  proc0Map = createContigMapWithNode<LO, GO, NT>(numRows, localNumRows,
4252  comm);
4253  } else { // The user supplied a Map.
4254  proc0Map = Details::computeGatherMap<map_type>(map, err, debug);
4255  }
4256 
4257  // Make a multivector X owned entirely by Proc 0.
4258  RCP<MV> X = createMultiVector<ST, LO, GO, NT>(proc0Map, numCols);
4259 
4260  //
4261  // On Proc 0, read the Matrix Market data from the input
4262  // stream into the multivector X.
4263  //
4264  int localReadDataSuccess = 1;
4265  if (myRank == 0) {
4266  if (!binary) {
4267  try {
4268  if (debug) {
4269  *err << myRank << ": readDenseImpl: Reading matrix data (dense)"
4270  << endl;
4271  }
4272 
4273  // Make sure that we can get a 1-D view of X.
4274  TEUCHOS_TEST_FOR_EXCEPTION(
4275  !X->isConstantStride(), std::logic_error,
4276  "Can't get a 1-D view of the entries of the MultiVector X on "
4277  "Process 0, because the stride between the columns of X is not "
4278  "constant. This shouldn't happen because we just created X and "
4279  "haven't filled it in yet. Please report this bug to the Tpetra "
4280  "developers.");
4281 
4282  // Get a writeable 1-D view of the entries of X. Rank 0
4283  // owns all of them. The view will expire at the end of
4284  // scope, so (if necessary) it will be written back to X
4285  // at this time.
4286  ArrayRCP<ST> X_view = X->get1dViewNonConst();
4287  TEUCHOS_TEST_FOR_EXCEPTION(
4288  as<global_size_t>(X_view.size()) < numRows * numCols,
4289  std::logic_error,
4290  "The view of X has size " << X_view.size() << " which is not enough to "
4291  "accommodate the expected number of entries numRows*numCols = "
4292  << numRows << "*" << numCols << " = " << numRows * numCols << ". "
4293  "Please report this bug to the Tpetra developers.");
4294  const size_t stride = X->getStride();
4295 
4296  // The third element of the dimensions Tuple encodes the data
4297  // type reported by the Banner: "real" == 0, "complex" == 1,
4298  // "integer" == 0 (same as "real"), "pattern" == 2. We do not
4299  // allow dense matrices to be pattern matrices, so dims[2] ==
4300  // 0 or 1. We've already checked for this above.
4301  const bool isComplex = (dims[2] == 1);
4302  size_type count = 0, curRow = 0, curCol = 0;
4303 
4304  std::string line;
4305  while (getline(in, line)) {
4306  ++lineNumber;
4307  // Is the current line a comment line? If it's not,
4308  // line.substr(start,size) contains the data.
4309  size_t start = 0, size = 0;
4310  const bool commentLine =
4311  checkCommentLine(line, start, size, lineNumber, tolerant);
4312  if (!commentLine) {
4313  // Make sure we have room in which to put the new matrix
4314  // entry. We check this only after checking for a
4315  // comment line, because there may be one or more
4316  // comment lines at the end of the file. In tolerant
4317  // mode, we simply ignore any extra data.
4318  if (count >= X_view.size()) {
4319  if (tolerant) {
4320  break;
4321  } else {
4322  TEUCHOS_TEST_FOR_EXCEPTION(
4323  count >= X_view.size(),
4324  std::runtime_error,
4325  "The Matrix Market input stream has more data in it than "
4326  "its metadata reported. Current line number is "
4327  << lineNumber << ".");
4328  }
4329  }
4330 
4331  // mfh 19 Dec 2012: Ignore everything up to the initial
4332  // colon. writeDense() has the option to print out the
4333  // global row index in front of each entry, followed by
4334  // a colon and space.
4335  {
4336  const size_t pos = line.substr(start, size).find(':');
4337  if (pos != std::string::npos) {
4338  start = pos + 1;
4339  }
4340  }
4341  std::istringstream istr(line.substr(start, size));
4342  // Does the line contain anything at all? Can we
4343  // safely read from the input stream wrapping the
4344  // line?
4345  if (istr.eof() || istr.fail()) {
4346  // In tolerant mode, simply ignore the line.
4347  if (tolerant) {
4348  break;
4349  }
4350  // We repeat the full test here so the exception
4351  // message is more informative.
4352  TEUCHOS_TEST_FOR_EXCEPTION(
4353  !tolerant && (istr.eof() || istr.fail()),
4354  std::runtime_error,
4355  "Line " << lineNumber << " of the Matrix Market file is "
4356  "empty, or we cannot read from it for some other reason.");
4357  }
4358  // Current matrix entry to read in.
4359  ST val = STS::zero();
4360  // Real and imaginary parts of the current matrix entry.
4361  // The imaginary part is zero if the matrix is real-valued.
4362  MT real = STM::zero(), imag = STM::zero();
4363 
4364  // isComplex refers to the input stream's data, not to
4365  // the scalar type S. It's OK to read real-valued
4366  // data into a matrix storing complex-valued data; in
4367  // that case, all entries' imaginary parts are zero.
4368  if (isComplex) {
4369  // STS::real() and STS::imag() return a copy of
4370  // their respective components, not a writeable
4371  // reference. Otherwise we could just assign to
4372  // them using the istream extraction operator (>>).
4373  // That's why we have separate magnitude type "real"
4374  // and "imag" variables.
4375 
4376  // Attempt to read the real part of the current entry.
4377  istr >> real;
4378  if (istr.fail()) {
4379  TEUCHOS_TEST_FOR_EXCEPTION(
4380  !tolerant && istr.eof(), std::runtime_error,
4381  "Failed to get the real part of a complex-valued matrix "
4382  "entry from line "
4383  << lineNumber << " of the Matrix Market "
4384  "file.");
4385  // In tolerant mode, just skip bad lines.
4386  if (tolerant) {
4387  break;
4388  }
4389  } else if (istr.eof()) {
4390  TEUCHOS_TEST_FOR_EXCEPTION(
4391  !tolerant && istr.eof(), std::runtime_error,
4392  "Missing imaginary part of a complex-valued matrix entry "
4393  "on line "
4394  << lineNumber << " of the Matrix Market file.");
4395  // In tolerant mode, let any missing imaginary part be 0.
4396  } else {
4397  // Attempt to read the imaginary part of the current
4398  // matrix entry.
4399  istr >> imag;
4400  TEUCHOS_TEST_FOR_EXCEPTION(
4401  !tolerant && istr.fail(), std::runtime_error,
4402  "Failed to get the imaginary part of a complex-valued "
4403  "matrix entry from line "
4404  << lineNumber << " of the "
4405  "Matrix Market file.");
4406  // In tolerant mode, let any missing or corrupted
4407  // imaginary part be 0.
4408  }
4409  } else { // Matrix Market file contains real-valued data.
4410  // Attempt to read the current matrix entry.
4411  istr >> real;
4412  TEUCHOS_TEST_FOR_EXCEPTION(
4413  !tolerant && istr.fail(), std::runtime_error,
4414  "Failed to get a real-valued matrix entry from line "
4415  << lineNumber << " of the Matrix Market file.");
4416  // In tolerant mode, simply ignore the line if
4417  // we failed to read a matrix entry.
4418  if (istr.fail() && tolerant) {
4419  break;
4420  }
4421  }
4422  // In tolerant mode, we simply let pass through whatever
4423  // data we got.
4424  TEUCHOS_TEST_FOR_EXCEPTION(
4425  !tolerant && istr.fail(), std::runtime_error,
4426  "Failed to read matrix data from line " << lineNumber
4427  << " of the Matrix Market file.");
4428 
4429  // Assign val = ST(real, imag).
4430  Teuchos::MatrixMarket::details::assignScalar<ST>(val, real, imag);
4431 
4432  curRow = count % numRows;
4433  curCol = count / numRows;
4434  X_view[curRow + curCol * stride] = val;
4435  ++count;
4436  } // if not a comment line
4437  } // while there are still lines in the file, get the next one
4438 
4439  TEUCHOS_TEST_FOR_EXCEPTION(
4440  !tolerant && static_cast<global_size_t>(count) < numRows * numCols,
4441  std::runtime_error,
4442  "The Matrix Market metadata reports that the dense matrix is "
4443  << numRows << " x " << numCols << ", and thus has "
4444  << numRows * numCols << " total entries, but we only found " << count
4445  << " entr" << (count == 1 ? "y" : "ies") << " in the file.");
4446  } catch (std::exception& e) {
4447  exMsg << e.what();
4448  localReadDataSuccess = 0;
4449  }
4450  } // not binary file
4451  else {
4452  if (debug) {
4453  *err << myRank << ": readDenseImpl: Reading matrix data (dense)"
4454  << endl;
4455  }
4456 
4457  // Make sure that we can get a 1-D view of X.
4458  TEUCHOS_TEST_FOR_EXCEPTION(
4459  !X->isConstantStride(), std::logic_error,
4460  "Can't get a 1-D view of the entries of the MultiVector X on "
4461  "Process 0, because the stride between the columns of X is not "
4462  "constant. This shouldn't happen because we just created X and "
4463  "haven't filled it in yet. Please report this bug to the Tpetra "
4464  "developers.");
4465 
4466  // Get a writeable 1-D view of the entries of X. Rank 0
4467  // owns all of them. The view will expire at the end of
4468  // scope, so (if necessary) it will be written back to X
4469  // at this time.
4470  auto X_view = X->getLocalViewHost(Access::OverwriteAll);
4471 
4472  TEUCHOS_TEST_FOR_EXCEPTION(
4473  as<global_size_t>(X_view.extent(0)) < numRows,
4474  std::logic_error,
4475  "The view of X has " << X_view.extent(0) << " rows which is not enough to "
4476  "accommodate the expected number of entries numRows = "
4477  << numRows << ". "
4478  "Please report this bug to the Tpetra developers.");
4479  TEUCHOS_TEST_FOR_EXCEPTION(
4480  as<global_size_t>(X_view.extent(1)) < numCols,
4481  std::logic_error,
4482  "The view of X has " << X_view.extent(1) << " colums which is not enough to "
4483  "accommodate the expected number of entries numRows = "
4484  << numCols << ". "
4485  "Please report this bug to the Tpetra developers.");
4486 
4487  for (size_t curRow = 0; curRow < numRows; ++curRow) {
4488  for (size_t curCol = 0; curCol < numCols; ++curCol) {
4489  if (Teuchos::ScalarTraits<ST>::isOrdinal) {
4490  global_size_t val;
4491  in.read(reinterpret_cast<char*>(&val), sizeof(val));
4492  X_view(curRow, curCol) = val;
4493  } else {
4494  double val;
4495  in.read(reinterpret_cast<char*>(&val), sizeof(val));
4496  X_view(curRow, curCol) = val;
4497  }
4498  }
4499  }
4500  } // binary
4501  } // if (myRank == 0)
4502 
4503  if (debug) {
4504  *err << myRank << ": readDenseImpl: done reading data" << endl;
4505  }
4506 
4507  // Synchronize on whether Proc 0 successfully read the data.
4508  int globalReadDataSuccess = localReadDataSuccess;
4509  broadcast(*comm, 0, outArg(globalReadDataSuccess));
4510  TEUCHOS_TEST_FOR_EXCEPTION(
4511  globalReadDataSuccess == 0, std::runtime_error,
4512  "Failed to read the multivector's data: " << exMsg.str());
4513 
4514  // If there's only one MPI process and the user didn't supply
4515  // a Map (i.e., pMap is null), we're done. Set pMap to the
4516  // Map used to distribute X, and return X.
4517  if (comm->getSize() == 1 && map.is_null()) {
4518  map = proc0Map;
4519  if (!err.is_null()) {
4520  err->popTab();
4521  }
4522  if (debug) {
4523  *err << myRank << ": readDenseImpl: done" << endl;
4524  }
4525  if (!err.is_null()) {
4526  err->popTab();
4527  }
4528  return X;
4529  }
4530 
4531  if (debug) {
4532  *err << myRank << ": readDenseImpl: Creating target MV" << endl;
4533  }
4534 
4535  // Make a multivector Y with the distributed map pMap.
4536  RCP<MV> Y = createMultiVector<ST, LO, GO, NT>(map, numCols);
4537 
4538  if (debug) {
4539  *err << myRank << ": readDenseImpl: Creating Export" << endl;
4540  }
4541 
4542  // Make an Export object that will export X to Y. First
4543  // argument is the source map, second argument is the target
4544  // map.
4545  Export<LO, GO, NT> exporter(proc0Map, map, err);
4546 
4547  if (debug) {
4548  *err << myRank << ": readDenseImpl: Exporting" << endl;
4549  }
4550  // Export X into Y.
4551  Y->doExport(*X, exporter, INSERT);
4552 
4553  if (!err.is_null()) {
4554  err->popTab();
4555  }
4556  if (debug) {
4557  *err << myRank << ": readDenseImpl: done" << endl;
4558  }
4559  if (!err.is_null()) {
4560  err->popTab();
4561  }
4562 
4563  // Y is distributed over all process(es) in the communicator.
4564  return Y;
4565  }
4566 
4567  template <class VectorScalarType>
4568  static Teuchos::RCP<Tpetra::Vector<VectorScalarType,
4571  node_type>>
4572  readVectorImpl(std::istream& in,
4573  const trcp_tcomm_t& comm,
4574  Teuchos::RCP<const map_type>& map,
4575  const Teuchos::RCP<Teuchos::FancyOStream>& err,
4576  const bool tolerant = false,
4577  const bool debug = false) {
4578  using std::endl;
4579  using Teuchos::as;
4580  using Teuchos::broadcast;
4581  using Teuchos::outArg;
4582  using Teuchos::RCP;
4583  using Teuchos::Tuple;
4584  using Teuchos::MatrixMarket::Banner;
4585  using Teuchos::MatrixMarket::checkCommentLine;
4586  typedef VectorScalarType ST;
4587  typedef local_ordinal_type LO;
4588  typedef global_ordinal_type GO;
4589  typedef node_type NT;
4590  typedef Teuchos::ScalarTraits<ST> STS;
4591  typedef typename STS::magnitudeType MT;
4592  typedef Teuchos::ScalarTraits<MT> STM;
4593  typedef Tpetra::Vector<ST, LO, GO, NT> MV;
4594 
4595  // Rank 0 is the only (MPI) process allowed to read from the
4596  // input stream.
4597  const int myRank = comm->getRank();
4598 
4599  if (!err.is_null()) {
4600  err->pushTab();
4601  }
4602  if (debug) {
4603  *err << myRank << ": readVectorImpl" << endl;
4604  }
4605  if (!err.is_null()) {
4606  err->pushTab();
4607  }
4608 
4609  // mfh 17 Feb 2013: It's not strictly necessary that the Comm
4610  // instances be identical and that the Node instances be
4611  // identical. The essential condition is more complicated to
4612  // test and isn't the same for all Node types. Thus, we just
4613  // leave it up to the user.
4614 
4615  // // If map is nonnull, check the precondition that its
4616  // // communicator resp. node equal comm resp. node. Checking
4617  // // now avoids doing a lot of file reading before we detect the
4618  // // violated precondition.
4619  // TEUCHOS_TEST_FOR_EXCEPTION(
4620  // ! map.is_null() && (map->getComm() != comm || map->getNode () != node,
4621  // std::invalid_argument, "If you supply a nonnull Map, the Map's "
4622  // "communicator and node must equal the supplied communicator resp. "
4623  // "node.");
4624 
4625  // Process 0 will read in the matrix dimensions from the file,
4626  // and broadcast them to all ranks in the given communicator.
4627  // There are only 2 dimensions in the matrix, but we use the
4628  // third element of the Tuple to encode the banner's reported
4629  // data type: "real" == 0, "complex" == 1, and "integer" == 0
4630  // (same as "real"). We don't allow pattern matrices (i.e.,
4631  // graphs) since they only make sense for sparse data.
4632  Tuple<GO, 3> dims;
4633  dims[0] = 0;
4634  dims[1] = 0;
4635 
4636  // Current line number in the input stream. Only valid on
4637  // Proc 0. Various calls will modify this depending on the
4638  // number of lines that are read from the input stream.
4639  size_t lineNumber = 1;
4640 
4641  // Capture errors and their messages on Proc 0.
4642  std::ostringstream exMsg;
4643  int localBannerReadSuccess = 1;
4644  int localDimsReadSuccess = 1;
4645 
4646  // Only Proc 0 gets to read matrix data from the input stream.
4647  if (myRank == 0) {
4648  if (debug) {
4649  *err << myRank << ": readVectorImpl: Reading banner line (dense)" << endl;
4650  }
4651 
4652  // The "Banner" tells you whether the input stream
4653  // represents a dense matrix, the symmetry type of the
4654  // matrix, and the type of the data it contains.
4655  RCP<const Banner> pBanner;
4656  try {
4657  pBanner = readBanner(in, lineNumber, tolerant, debug);
4658  } catch (std::exception& e) {
4659  exMsg << e.what();
4660  localBannerReadSuccess = 0;
4661  }
4662  // Make sure the input stream is the right kind of data.
4663  if (localBannerReadSuccess) {
4664  if (pBanner->matrixType() != "array") {
4665  exMsg << "The Matrix Market file does not contain dense matrix "
4666  "data. Its banner (first) line says that its matrix type is \""
4667  << pBanner->matrixType() << "\", rather that the required "
4668  "\"array\".";
4669  localBannerReadSuccess = 0;
4670  } else if (pBanner->dataType() == "pattern") {
4671  exMsg << "The Matrix Market file's banner (first) "
4672  "line claims that the matrix's data type is \"pattern\". This does "
4673  "not make sense for a dense matrix, yet the file reports the matrix "
4674  "as dense. The only valid data types for a dense matrix are "
4675  "\"real\", \"complex\", and \"integer\".";
4676  localBannerReadSuccess = 0;
4677  } else {
4678  // Encode the data type reported by the Banner as the
4679  // third element of the dimensions Tuple.
4680  dims[2] = encodeDataType(pBanner->dataType());
4681  }
4682  } // if we successfully read the banner line
4683 
4684  // At this point, we've successfully read the banner line.
4685  // Now read the dimensions line.
4686  if (localBannerReadSuccess) {
4687  if (debug) {
4688  *err << myRank << ": readVectorImpl: Reading dimensions line (dense)" << endl;
4689  }
4690  // Keep reading lines from the input stream until we find
4691  // a non-comment line, or until we run out of lines. The
4692  // latter is an error, since every "array" format Matrix
4693  // Market file must have a dimensions line after the
4694  // banner (even if the matrix has zero rows or columns, or
4695  // zero entries).
4696  std::string line;
4697  bool commentLine = true;
4698 
4699  while (commentLine) {
4700  // Test whether it is even valid to read from the input
4701  // stream wrapping the line.
4702  if (in.eof() || in.fail()) {
4703  exMsg << "Unable to get array dimensions line (at all) from line "
4704  << lineNumber << " of input stream. The input stream "
4705  << "claims that it is "
4706  << (in.eof() ? "at end-of-file." : "in a failed state.");
4707  localDimsReadSuccess = 0;
4708  } else {
4709  // Try to get the next line from the input stream.
4710  if (getline(in, line)) {
4711  ++lineNumber; // We did actually read a line.
4712  }
4713  // Is the current line a comment line? Ignore start
4714  // and size; they are only useful for reading the
4715  // actual matrix entries. (We could use them here as
4716  // an optimization, but we've chosen not to.)
4717  size_t start = 0, size = 0;
4718  commentLine = checkCommentLine(line, start, size, lineNumber, tolerant);
4719  } // whether we failed to read the line at all
4720  } // while the line we just read is a comment line
4721 
4722  //
4723  // Get <numRows> <numCols> from the line we just read.
4724  //
4725  std::istringstream istr(line);
4726 
4727  // Test whether it is even valid to read from the input
4728  // stream wrapping the line.
4729  if (istr.eof() || istr.fail()) {
4730  exMsg << "Unable to read any data from line " << lineNumber
4731  << " of input; the line should contain the matrix dimensions "
4732  << "\"<numRows> <numCols>\".";
4733  localDimsReadSuccess = 0;
4734  } else { // It's valid to read from the line.
4735  GO theNumRows = 0;
4736  istr >> theNumRows; // Read in the number of rows.
4737  if (istr.fail()) {
4738  exMsg << "Failed to get number of rows from line "
4739  << lineNumber << " of input; the line should contains the "
4740  << "matrix dimensions \"<numRows> <numCols>\".";
4741  localDimsReadSuccess = 0;
4742  } else { // We successfully read the number of rows
4743  dims[0] = theNumRows; // Save the number of rows
4744  if (istr.eof()) { // Do we still have data to read?
4745  exMsg << "No more data after number of rows on line "
4746  << lineNumber << " of input; the line should contain the "
4747  << "matrix dimensions \"<numRows> <numCols>\".";
4748  localDimsReadSuccess = 0;
4749  } else { // Still data left to read; read in number of columns.
4750  GO theNumCols = 0;
4751  istr >> theNumCols; // Read in the number of columns
4752  if (istr.fail()) {
4753  exMsg << "Failed to get number of columns from line "
4754  << lineNumber << " of input; the line should contain "
4755  << "the matrix dimensions \"<numRows> <numCols>\".";
4756  localDimsReadSuccess = 0;
4757  } else { // We successfully read the number of columns
4758  dims[1] = theNumCols; // Save the number of columns
4759  } // if istr.fail ()
4760  } // if istr.eof ()
4761  } // if we read the number of rows
4762  } // if the input stream wrapping the dims line was (in)valid
4763  } // if we successfully read the banner line
4764  } // if (myRank == 0)
4765 
4766  // Check if file has a Vector
4767  if (dims[1] != 1) {
4768  exMsg << "File does not contain a 1D Vector";
4769  localDimsReadSuccess = 0;
4770  }
4771 
4772  // Broadcast the matrix dimensions, the encoded data type, and
4773  // whether or not Proc 0 succeeded in reading the banner and
4774  // dimensions.
4775  Tuple<GO, 5> bannerDimsReadResult;
4776  if (myRank == 0) {
4777  bannerDimsReadResult[0] = dims[0]; // numRows
4778  bannerDimsReadResult[1] = dims[1]; // numCols
4779  bannerDimsReadResult[2] = dims[2]; // encoded data type
4780  bannerDimsReadResult[3] = localBannerReadSuccess;
4781  bannerDimsReadResult[4] = localDimsReadSuccess;
4782  }
4783 
4784  // Broadcast matrix dimensions and the encoded data type from
4785  // Proc 0 to all the MPI processes.
4786  broadcast(*comm, 0, bannerDimsReadResult);
4787 
4788  TEUCHOS_TEST_FOR_EXCEPTION(
4789  bannerDimsReadResult[3] == 0, std::runtime_error,
4790  "Failed to read banner line: " << exMsg.str());
4791  TEUCHOS_TEST_FOR_EXCEPTION(
4792  bannerDimsReadResult[4] == 0, std::runtime_error,
4793  "Failed to read matrix dimensions line: " << exMsg.str());
4794  if (myRank != 0) {
4795  dims[0] = bannerDimsReadResult[0];
4796  dims[1] = bannerDimsReadResult[1];
4797  dims[2] = bannerDimsReadResult[2];
4798  }
4799 
4800  // Tpetra objects want the matrix dimensions in these types.
4801  const global_size_t numRows = static_cast<global_size_t>(dims[0]);
4802  const size_t numCols = static_cast<size_t>(dims[1]);
4803 
4804  // Make a "Proc 0 owns everything" Map that we will use to
4805  // read in the multivector entries in the correct order on
4806  // Proc 0. This must be a collective
4807  RCP<const map_type> proc0Map; // "Proc 0 owns everything" Map
4808  if (map.is_null()) {
4809  // The user didn't supply a Map. Make a contiguous
4810  // distributed Map for them, using the read-in multivector
4811  // dimensions.
4812  map = createUniformContigMapWithNode<LO, GO, NT>(numRows, comm);
4813  const size_t localNumRows = (myRank == 0) ? numRows : 0;
4814  // At this point, map exists and has a nonnull node.
4815  proc0Map = createContigMapWithNode<LO, GO, NT>(numRows, localNumRows,
4816  comm);
4817  } else { // The user supplied a Map.
4818  proc0Map = Details::computeGatherMap<map_type>(map, err, debug);
4819  }
4820 
4821  // Make a multivector X owned entirely by Proc 0.
4822  RCP<MV> X = createVector<ST, LO, GO, NT>(proc0Map);
4823 
4824  //
4825  // On Proc 0, read the Matrix Market data from the input
4826  // stream into the multivector X.
4827  //
4828  int localReadDataSuccess = 1;
4829  if (myRank == 0) {
4830  try {
4831  if (debug) {
4832  *err << myRank << ": readVectorImpl: Reading matrix data (dense)"
4833  << endl;
4834  }
4835 
4836  // Make sure that we can get a 1-D view of X.
4837  TEUCHOS_TEST_FOR_EXCEPTION(
4838  !X->isConstantStride(), std::logic_error,
4839  "Can't get a 1-D view of the entries of the MultiVector X on "
4840  "Process 0, because the stride between the columns of X is not "
4841  "constant. This shouldn't happen because we just created X and "
4842  "haven't filled it in yet. Please report this bug to the Tpetra "
4843  "developers.");
4844 
4845  // Get a writeable 1-D view of the entries of X. Rank 0
4846  // owns all of them. The view will expire at the end of
4847  // scope, so (if necessary) it will be written back to X
4848  // at this time.
4849  Teuchos::ArrayRCP<ST> X_view = X->get1dViewNonConst();
4850  TEUCHOS_TEST_FOR_EXCEPTION(
4851  as<global_size_t>(X_view.size()) < numRows * numCols,
4852  std::logic_error,
4853  "The view of X has size " << X_view << " which is not enough to "
4854  "accommodate the expected number of entries numRows*numCols = "
4855  << numRows << "*" << numCols << " = " << numRows * numCols << ". "
4856  "Please report this bug to the Tpetra developers.");
4857  const size_t stride = X->getStride();
4858 
4859  // The third element of the dimensions Tuple encodes the data
4860  // type reported by the Banner: "real" == 0, "complex" == 1,
4861  // "integer" == 0 (same as "real"), "pattern" == 2. We do not
4862  // allow dense matrices to be pattern matrices, so dims[2] ==
4863  // 0 or 1. We've already checked for this above.
4864  const bool isComplex = (dims[2] == 1);
4865  size_type count = 0, curRow = 0, curCol = 0;
4866 
4867  std::string line;
4868  while (getline(in, line)) {
4869  ++lineNumber;
4870  // Is the current line a comment line? If it's not,
4871  // line.substr(start,size) contains the data.
4872  size_t start = 0, size = 0;
4873  const bool commentLine =
4874  checkCommentLine(line, start, size, lineNumber, tolerant);
4875  if (!commentLine) {
4876  // Make sure we have room in which to put the new matrix
4877  // entry. We check this only after checking for a
4878  // comment line, because there may be one or more
4879  // comment lines at the end of the file. In tolerant
4880  // mode, we simply ignore any extra data.
4881  if (count >= X_view.size()) {
4882  if (tolerant) {
4883  break;
4884  } else {
4885  TEUCHOS_TEST_FOR_EXCEPTION(
4886  count >= X_view.size(),
4887  std::runtime_error,
4888  "The Matrix Market input stream has more data in it than "
4889  "its metadata reported. Current line number is "
4890  << lineNumber << ".");
4891  }
4892  }
4893 
4894  // mfh 19 Dec 2012: Ignore everything up to the initial
4895  // colon. writeDense() has the option to print out the
4896  // global row index in front of each entry, followed by
4897  // a colon and space.
4898  {
4899  const size_t pos = line.substr(start, size).find(':');
4900  if (pos != std::string::npos) {
4901  start = pos + 1;
4902  }
4903  }
4904  std::istringstream istr(line.substr(start, size));
4905  // Does the line contain anything at all? Can we
4906  // safely read from the input stream wrapping the
4907  // line?
4908  if (istr.eof() || istr.fail()) {
4909  // In tolerant mode, simply ignore the line.
4910  if (tolerant) {
4911  break;
4912  }
4913  // We repeat the full test here so the exception
4914  // message is more informative.
4915  TEUCHOS_TEST_FOR_EXCEPTION(
4916  !tolerant && (istr.eof() || istr.fail()),
4917  std::runtime_error,
4918  "Line " << lineNumber << " of the Matrix Market file is "
4919  "empty, or we cannot read from it for some other reason.");
4920  }
4921  // Current matrix entry to read in.
4922  ST val = STS::zero();
4923  // Real and imaginary parts of the current matrix entry.
4924  // The imaginary part is zero if the matrix is real-valued.
4925  MT real = STM::zero(), imag = STM::zero();
4926 
4927  // isComplex refers to the input stream's data, not to
4928  // the scalar type S. It's OK to read real-valued
4929  // data into a matrix storing complex-valued data; in
4930  // that case, all entries' imaginary parts are zero.
4931  if (isComplex) {
4932  // STS::real() and STS::imag() return a copy of
4933  // their respective components, not a writeable
4934  // reference. Otherwise we could just assign to
4935  // them using the istream extraction operator (>>).
4936  // That's why we have separate magnitude type "real"
4937  // and "imag" variables.
4938 
4939  // Attempt to read the real part of the current entry.
4940  istr >> real;
4941  if (istr.fail()) {
4942  TEUCHOS_TEST_FOR_EXCEPTION(
4943  !tolerant && istr.eof(), std::runtime_error,
4944  "Failed to get the real part of a complex-valued matrix "
4945  "entry from line "
4946  << lineNumber << " of the Matrix Market "
4947  "file.");
4948  // In tolerant mode, just skip bad lines.
4949  if (tolerant) {
4950  break;
4951  }
4952  } else if (istr.eof()) {
4953  TEUCHOS_TEST_FOR_EXCEPTION(
4954  !tolerant && istr.eof(), std::runtime_error,
4955  "Missing imaginary part of a complex-valued matrix entry "
4956  "on line "
4957  << lineNumber << " of the Matrix Market file.");
4958  // In tolerant mode, let any missing imaginary part be 0.
4959  } else {
4960  // Attempt to read the imaginary part of the current
4961  // matrix entry.
4962  istr >> imag;
4963  TEUCHOS_TEST_FOR_EXCEPTION(
4964  !tolerant && istr.fail(), std::runtime_error,
4965  "Failed to get the imaginary part of a complex-valued "
4966  "matrix entry from line "
4967  << lineNumber << " of the "
4968  "Matrix Market file.");
4969  // In tolerant mode, let any missing or corrupted
4970  // imaginary part be 0.
4971  }
4972  } else { // Matrix Market file contains real-valued data.
4973  // Attempt to read the current matrix entry.
4974  istr >> real;
4975  TEUCHOS_TEST_FOR_EXCEPTION(
4976  !tolerant && istr.fail(), std::runtime_error,
4977  "Failed to get a real-valued matrix entry from line "
4978  << lineNumber << " of the Matrix Market file.");
4979  // In tolerant mode, simply ignore the line if
4980  // we failed to read a matrix entry.
4981  if (istr.fail() && tolerant) {
4982  break;
4983  }
4984  }
4985  // In tolerant mode, we simply let pass through whatever
4986  // data we got.
4987  TEUCHOS_TEST_FOR_EXCEPTION(
4988  !tolerant && istr.fail(), std::runtime_error,
4989  "Failed to read matrix data from line " << lineNumber
4990  << " of the Matrix Market file.");
4991 
4992  // Assign val = ST(real, imag).
4993  Teuchos::MatrixMarket::details::assignScalar<ST>(val, real, imag);
4994 
4995  curRow = count % numRows;
4996  curCol = count / numRows;
4997  X_view[curRow + curCol * stride] = val;
4998  ++count;
4999  } // if not a comment line
5000  } // while there are still lines in the file, get the next one
5001 
5002  TEUCHOS_TEST_FOR_EXCEPTION(
5003  !tolerant && static_cast<global_size_t>(count) < numRows * numCols,
5004  std::runtime_error,
5005  "The Matrix Market metadata reports that the dense matrix is "
5006  << numRows << " x " << numCols << ", and thus has "
5007  << numRows * numCols << " total entries, but we only found " << count
5008  << " entr" << (count == 1 ? "y" : "ies") << " in the file.");
5009  } catch (std::exception& e) {
5010  exMsg << e.what();
5011  localReadDataSuccess = 0;
5012  }
5013  } // if (myRank == 0)
5014 
5015  if (debug) {
5016  *err << myRank << ": readVectorImpl: done reading data" << endl;
5017  }
5018 
5019  // Synchronize on whether Proc 0 successfully read the data.
5020  int globalReadDataSuccess = localReadDataSuccess;
5021  broadcast(*comm, 0, outArg(globalReadDataSuccess));
5022  TEUCHOS_TEST_FOR_EXCEPTION(
5023  globalReadDataSuccess == 0, std::runtime_error,
5024  "Failed to read the multivector's data: " << exMsg.str());
5025 
5026  // If there's only one MPI process and the user didn't supply
5027  // a Map (i.e., pMap is null), we're done. Set pMap to the
5028  // Map used to distribute X, and return X.
5029  if (comm->getSize() == 1 && map.is_null()) {
5030  map = proc0Map;
5031  if (!err.is_null()) {
5032  err->popTab();
5033  }
5034  if (debug) {
5035  *err << myRank << ": readVectorImpl: done" << endl;
5036  }
5037  if (!err.is_null()) {
5038  err->popTab();
5039  }
5040  return X;
5041  }
5042 
5043  if (debug) {
5044  *err << myRank << ": readVectorImpl: Creating target MV" << endl;
5045  }
5046 
5047  // Make a multivector Y with the distributed map pMap.
5048  RCP<MV> Y = createVector<ST, LO, GO, NT>(map);
5049 
5050  if (debug) {
5051  *err << myRank << ": readVectorImpl: Creating Export" << endl;
5052  }
5053 
5054  // Make an Export object that will export X to Y. First
5055  // argument is the source map, second argument is the target
5056  // map.
5057  Export<LO, GO, NT> exporter(proc0Map, map, err);
5058 
5059  if (debug) {
5060  *err << myRank << ": readVectorImpl: Exporting" << endl;
5061  }
5062  // Export X into Y.
5063  Y->doExport(*X, exporter, INSERT);
5064 
5065  if (!err.is_null()) {
5066  err->popTab();
5067  }
5068  if (debug) {
5069  *err << myRank << ": readVectorImpl: done" << endl;
5070  }
5071  if (!err.is_null()) {
5072  err->popTab();
5073  }
5074 
5075  // Y is distributed over all process(es) in the communicator.
5076  return Y;
5077  }
5078 
5079  public:
5100  static Teuchos::RCP<const map_type>
5101  readMap(std::istream& in,
5102  const trcp_tcomm_t& comm,
5103  const bool tolerant = false,
5104  const bool debug = false,
5105  const bool binary = false) {
5106  Teuchos::RCP<Teuchos::FancyOStream> err =
5107  Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
5108  return readMap(in, comm, err, tolerant, debug, binary);
5109  }
5110 
5137  static Teuchos::RCP<const map_type>
5138  readMap(std::istream& in,
5139  const trcp_tcomm_t& comm,
5140  const Teuchos::RCP<Teuchos::FancyOStream>& err,
5141  const bool tolerant = false,
5142  const bool debug = false,
5143  const bool binary = false) {
5144  using std::endl;
5145  using Teuchos::arcp;
5146  using Teuchos::Array;
5147  using Teuchos::ArrayRCP;
5148  using Teuchos::as;
5149  using Teuchos::broadcast;
5150  using Teuchos::Comm;
5151  using Teuchos::CommRequest;
5152  using Teuchos::inOutArg;
5153  using Teuchos::ireceive;
5154  using Teuchos::isend;
5155  using Teuchos::outArg;
5156  using Teuchos::RCP;
5157  using Teuchos::receive;
5158  using Teuchos::REDUCE_MIN;
5159  using Teuchos::reduceAll;
5160  using Teuchos::SerialComm;
5161  using Teuchos::toString;
5162  using Teuchos::wait;
5163  typedef Tpetra::global_size_t GST;
5164  typedef ptrdiff_t int_type; // Can hold int and GO
5165  typedef local_ordinal_type LO;
5166  typedef global_ordinal_type GO;
5167  typedef node_type NT;
5169 
5170  const int numProcs = comm->getSize();
5171  const int myRank = comm->getRank();
5172 
5173  if (err.is_null()) {
5174  err->pushTab();
5175  }
5176  if (debug) {
5177  std::ostringstream os;
5178  os << myRank << ": readMap: " << endl;
5179  *err << os.str();
5180  }
5181  if (err.is_null()) {
5182  err->pushTab();
5183  }
5184 
5185  // Tag for receive-size / send-size messages. writeMap used
5186  // tags 1337 and 1338; we count up from there.
5187  const int sizeTag = 1339;
5188  // Tag for receive-data / send-data messages.
5189  const int dataTag = 1340;
5190 
5191  // These are for sends on Process 0, and for receives on all
5192  // other processes. sizeReq is for the {receive,send}-size
5193  // message, and dataReq is for the message containing the
5194  // actual GIDs to belong to the receiving process.
5195  RCP<CommRequest<int>> sizeReq;
5196  RCP<CommRequest<int>> dataReq;
5197 
5198  // Each process will have to receive the number of GIDs to
5199  // expect. Thus, we can post the receives now, and cancel
5200  // them if something should go wrong in the meantime.
5201  ArrayRCP<int_type> numGidsToRecv(1);
5202  numGidsToRecv[0] = 0;
5203  if (myRank != 0) {
5204  sizeReq = ireceive<int, int_type>(numGidsToRecv, 0, sizeTag, *comm);
5205  }
5206 
5207  int readSuccess = 1;
5208  std::ostringstream exMsg;
5209  RCP<MV> data; // Will only be valid on Proc 0
5210  if (myRank == 0) {
5211  // If we want to reuse readDenseImpl, we have to make a
5212  // communicator that only contains Proc 0. Otherwise,
5213  // readDenseImpl will redistribute the data to all
5214  // processes. While we eventually want that, neither we nor
5215  // readDenseImpl know the correct Map to use at the moment.
5216  // That depends on the second column of the multivector.
5217  RCP<const Comm<int>> proc0Comm(new SerialComm<int>());
5218  try {
5219  RCP<const map_type> dataMap;
5220  // This is currently the only place where we use the
5221  // 'tolerant' argument. Later, if we want to be clever,
5222  // we could have tolerant mode allow PIDs out of order.
5223  data = readDenseImpl<GO>(in, proc0Comm, dataMap, err, tolerant, debug, binary);
5224  (void)dataMap; // Silence "unused" warnings
5225  if (data.is_null()) {
5226  readSuccess = 0;
5227  exMsg << "readDenseImpl() returned null." << endl;
5228  }
5229  } catch (std::exception& e) {
5230  readSuccess = 0;
5231  exMsg << e.what() << endl;
5232  }
5233  }
5234 
5235  // Map from PID to all the GIDs for that PID.
5236  // Only populated on Process 0.
5237  std::map<int, Array<GO>> pid2gids;
5238 
5239  // The index base must be the global minimum GID.
5240  // We will compute this on Process 0 and broadcast,
5241  // so that all processes can set up the Map.
5242  int_type globalNumGIDs = 0;
5243 
5244  // The index base must be the global minimum GID.
5245  // We will compute this on Process 0 and broadcast,
5246  // so that all processes can set up the Map.
5247  GO indexBase = 0;
5248 
5249  // Process 0: If the above read of the MultiVector succeeded,
5250  // extract the GIDs and PIDs into pid2gids, and find the
5251  // global min GID.
5252  if (myRank == 0 && readSuccess == 1) {
5253  if (data->getNumVectors() == 2) { // Map format 1.0
5254  ArrayRCP<const GO> GIDs = data->getData(0);
5255  ArrayRCP<const GO> PIDs = data->getData(1); // convert to int
5256  globalNumGIDs = GIDs.size();
5257 
5258  // Start computing the global min GID, while collecting
5259  // the GIDs for each PID.
5260  if (globalNumGIDs > 0) {
5261  const int pid = static_cast<int>(PIDs[0]);
5262 
5263  if (pid < 0 || pid >= numProcs) {
5264  readSuccess = 0;
5265  exMsg << "Tpetra::MatrixMarket::readMap: "
5266  << "Encountered invalid PID " << pid << "." << endl;
5267  } else {
5268  const GO gid = GIDs[0];
5269  pid2gids[pid].push_back(gid);
5270  indexBase = gid; // the current min GID
5271  }
5272  }
5273  if (readSuccess == 1) {
5274  // Collect the rest of the GIDs for each PID, and compute
5275  // the global min GID.
5276  for (size_type k = 1; k < globalNumGIDs; ++k) {
5277  const int pid = static_cast<int>(PIDs[k]);
5278  if (pid < 0 || pid >= numProcs) {
5279  readSuccess = 0;
5280  exMsg << "Tpetra::MatrixMarket::readMap: "
5281  << "Encountered invalid PID " << pid << "." << endl;
5282  } else {
5283  const int_type gid = GIDs[k];
5284  pid2gids[pid].push_back(gid);
5285  if (gid < indexBase) {
5286  indexBase = gid; // the current min GID
5287  }
5288  }
5289  }
5290  }
5291  } else if (data->getNumVectors() == 1) { // Map format 2.0
5292  if (data->getGlobalLength() % 2 != static_cast<GST>(0)) {
5293  readSuccess = 0;
5294  exMsg << "Tpetra::MatrixMarket::readMap: Input data has the "
5295  "wrong format (for Map format 2.0). The global number of rows "
5296  "in the MultiVector must be even (divisible by 2)."
5297  << endl;
5298  } else {
5299  ArrayRCP<const GO> theData = data->getData(0);
5300  globalNumGIDs = static_cast<GO>(data->getGlobalLength()) /
5301  static_cast<GO>(2);
5302 
5303  // Start computing the global min GID, while
5304  // collecting the GIDs for each PID.
5305  if (globalNumGIDs > 0) {
5306  const int pid = static_cast<int>(theData[1]);
5307  if (pid < 0 || pid >= numProcs) {
5308  readSuccess = 0;
5309  exMsg << "Tpetra::MatrixMarket::readMap: "
5310  << "Encountered invalid PID " << pid << "." << endl;
5311  } else {
5312  const GO gid = theData[0];
5313  pid2gids[pid].push_back(gid);
5314  indexBase = gid; // the current min GID
5315  }
5316  }
5317  // Collect the rest of the GIDs for each PID, and
5318  // compute the global min GID.
5319  for (int_type k = 1; k < globalNumGIDs; ++k) {
5320  const int pid = static_cast<int>(theData[2 * k + 1]);
5321  if (pid < 0 || pid >= numProcs) {
5322  readSuccess = 0;
5323  exMsg << "Tpetra::MatrixMarket::readMap: "
5324  << "Encountered invalid PID " << pid << "." << endl;
5325  } else {
5326  const GO gid = theData[2 * k];
5327  pid2gids[pid].push_back(gid);
5328  if (gid < indexBase) {
5329  indexBase = gid; // the current min GID
5330  }
5331  }
5332  } // for each GID
5333  } // if the amount of data is correct
5334  } else {
5335  readSuccess = 0;
5336  exMsg << "Tpetra::MatrixMarket::readMap: Input data must have "
5337  "either 1 column (for the new Map format 2.0) or 2 columns (for "
5338  "the old Map format 1.0).";
5339  }
5340  } // myRank is zero
5341 
5342  // Broadcast the indexBase, the global number of GIDs, and the
5343  // current success status. Use int_type for all of these.
5344  {
5345  int_type readResults[3];
5346  readResults[0] = static_cast<int_type>(indexBase);
5347  readResults[1] = static_cast<int_type>(globalNumGIDs);
5348  readResults[2] = static_cast<int_type>(readSuccess);
5349  broadcast<int, int_type>(*comm, 0, 3, readResults);
5350 
5351  indexBase = static_cast<GO>(readResults[0]);
5352  globalNumGIDs = static_cast<int_type>(readResults[1]);
5353  readSuccess = static_cast<int>(readResults[2]);
5354  }
5355 
5356  // Unwinding the stack will invoke sizeReq's destructor, which
5357  // will cancel the receive-size request on all processes that
5358  // posted it.
5359  TEUCHOS_TEST_FOR_EXCEPTION(
5360  readSuccess != 1, std::runtime_error,
5361  "Tpetra::MatrixMarket::readMap: Reading the Map failed with the "
5362  "following exception message: "
5363  << exMsg.str());
5364 
5365  if (myRank == 0) {
5366  // Proc 0: Send each process' number of GIDs to that process.
5367  for (int p = 1; p < numProcs; ++p) {
5368  ArrayRCP<int_type> numGidsToSend(1);
5369 
5370  auto it = pid2gids.find(p);
5371  if (it == pid2gids.end()) {
5372  numGidsToSend[0] = 0;
5373  } else {
5374  numGidsToSend[0] = it->second.size();
5375  }
5376  sizeReq = isend<int, int_type>(numGidsToSend, p, sizeTag, *comm);
5377  wait<int>(*comm, outArg(sizeReq));
5378  }
5379  } else {
5380  // Wait on the receive-size message to finish.
5381  wait<int>(*comm, outArg(sizeReq));
5382  }
5383 
5384  // Allocate / get the array for my GIDs.
5385  // Only Process 0 will have its actual GIDs at this point.
5386  ArrayRCP<GO> myGids;
5387  int_type myNumGids = 0;
5388  if (myRank == 0) {
5389  GO* myGidsRaw = NULL;
5390 
5391  typename std::map<int, Array<GO>>::iterator it = pid2gids.find(0);
5392  if (it != pid2gids.end()) {
5393  myGidsRaw = it->second.getRawPtr();
5394  myNumGids = it->second.size();
5395  // Nonowning ArrayRCP just views the Array.
5396  myGids = arcp<GO>(myGidsRaw, 0, myNumGids, false);
5397  }
5398  } else { // myRank != 0
5399  myNumGids = numGidsToRecv[0];
5400  myGids = arcp<GO>(myNumGids);
5401  }
5402 
5403  if (myRank != 0) {
5404  // Post receive for data, now that we know how much data we
5405  // will receive. Only post receive if my process actually
5406  // has nonzero GIDs.
5407  if (myNumGids > 0) {
5408  dataReq = ireceive<int, GO>(myGids, 0, dataTag, *comm);
5409  }
5410  }
5411 
5412  for (int p = 1; p < numProcs; ++p) {
5413  if (myRank == 0) {
5414  ArrayRCP<GO> sendGids; // to send to Process p
5415  GO* sendGidsRaw = NULL;
5416  int_type numSendGids = 0;
5417 
5418  typename std::map<int, Array<GO>>::iterator it = pid2gids.find(p);
5419  if (it != pid2gids.end()) {
5420  numSendGids = it->second.size();
5421  sendGidsRaw = it->second.getRawPtr();
5422  sendGids = arcp<GO>(sendGidsRaw, 0, numSendGids, false);
5423  }
5424  // Only send if that process actually has nonzero GIDs.
5425  if (numSendGids > 0) {
5426  dataReq = isend<int, GO>(sendGids, p, dataTag, *comm);
5427  }
5428  wait<int>(*comm, outArg(dataReq));
5429  } else if (myRank == p) {
5430  // Wait on my receive of GIDs to finish.
5431  wait<int>(*comm, outArg(dataReq));
5432  }
5433  } // for each process rank p in 1, 2, ..., numProcs-1
5434 
5435  if (debug) {
5436  std::ostringstream os;
5437  os << myRank << ": readMap: creating Map" << endl;
5438  *err << os.str();
5439  }
5440  const GST INVALID = Teuchos::OrdinalTraits<GST>::invalid();
5441  RCP<const map_type> newMap;
5442 
5443  // Create the Map; test whether the constructor threw. This
5444  // avoids deadlock and makes error reporting more readable.
5445 
5446  int lclSuccess = 1;
5447  int gblSuccess = 0; // output argument
5448  std::ostringstream errStrm;
5449  try {
5450  newMap = rcp(new map_type(INVALID, myGids(), indexBase, comm));
5451  } catch (std::exception& e) {
5452  lclSuccess = 0;
5453  errStrm << "Process " << comm->getRank() << " threw an exception: "
5454  << e.what() << std::endl;
5455  } catch (...) {
5456  lclSuccess = 0;
5457  errStrm << "Process " << comm->getRank() << " threw an exception "
5458  "not a subclass of std::exception"
5459  << std::endl;
5460  }
5461  Teuchos::reduceAll<int, int>(*comm, Teuchos::REDUCE_MIN,
5462  lclSuccess, Teuchos::outArg(gblSuccess));
5463  if (gblSuccess != 1) {
5464  Tpetra::Details::gathervPrint(std::cerr, errStrm.str(), *comm);
5465  }
5466  TEUCHOS_TEST_FOR_EXCEPTION(gblSuccess != 1, std::runtime_error, "Map constructor failed!");
5467 
5468  if (err.is_null()) {
5469  err->popTab();
5470  }
5471  if (debug) {
5472  std::ostringstream os;
5473  os << myRank << ": readMap: done" << endl;
5474  *err << os.str();
5475  }
5476  if (err.is_null()) {
5477  err->popTab();
5478  }
5479  return newMap;
5480  }
5481 
5482  private:
5493  static int
5494  encodeDataType(const std::string& dataType) {
5495  if (dataType == "real" || dataType == "integer") {
5496  return 0;
5497  } else if (dataType == "complex") {
5498  return 1;
5499  } else if (dataType == "pattern") {
5500  return 2;
5501  } else {
5502  // We should never get here, since Banner validates the
5503  // reported data type and ensures it is one of the accepted
5504  // values.
5505  TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
5506  "Unrecognized Matrix Market data type \"" << dataType
5507  << "\". We should never get here. "
5508  "Please report this bug to the Tpetra developers.");
5509  }
5510  }
5511 
5512  public:
5542  static Teuchos::RCP<sparse_matrix_type>
5543  readSparsePerRank(const std::string& filename_prefix,
5544  const std::string& filename_suffix,
5545  const Teuchos::RCP<const map_type>& rowMap,
5546  Teuchos::RCP<const map_type>& colMap,
5547  const Teuchos::RCP<const map_type>& domainMap,
5548  const Teuchos::RCP<const map_type>& rangeMap,
5549  const bool callFillComplete = true,
5550  const bool tolerant = false,
5551  const int ranksToReadAtOnce = 8,
5552  const bool debug = false) {
5553  using ST = scalar_type;
5554  using LO = local_ordinal_type;
5555  using GO = global_ordinal_type;
5556  using STS = typename Teuchos::ScalarTraits<ST>;
5557  using Teuchos::arcp;
5558  using Teuchos::ArrayRCP;
5559  using Teuchos::RCP;
5560  using Teuchos::rcp;
5561 
5562  // Sanity Checks
5563  // Fast checks for invalid input. We can't check other
5564  // attributes of the Maps until we've read in the matrix
5565  // dimensions.
5566  TEUCHOS_TEST_FOR_EXCEPTION(
5567  rowMap.is_null(), std::invalid_argument,
5568  "Row Map must be nonnull.");
5569  Teuchos::RCP<const Teuchos::Comm<int>> comm = rowMap->getComm();
5570  TEUCHOS_TEST_FOR_EXCEPTION(comm.is_null(), std::invalid_argument,
5571  "The input row map's communicator (Teuchos::Comm object) is null.");
5572  TEUCHOS_TEST_FOR_EXCEPTION(
5573  rangeMap.is_null(), std::invalid_argument,
5574  "Range Map must be nonnull.");
5575  TEUCHOS_TEST_FOR_EXCEPTION(
5576  domainMap.is_null(), std::invalid_argument,
5577  "Domain Map must be nonnull.");
5578  TEUCHOS_TEST_FOR_EXCEPTION(
5579  domainMap->getComm().getRawPtr() != comm.getRawPtr(),
5580  std::invalid_argument,
5581  "The specified domain Map's communicator (domainMap->getComm())"
5582  "differs from row Map's communicator");
5583  TEUCHOS_TEST_FOR_EXCEPTION(
5584  rangeMap->getComm().getRawPtr() != comm.getRawPtr(),
5585  std::invalid_argument,
5586  "The specified range Map's communicator (rangeMap->getComm())"
5587  "differs from row Map's communicator");
5588 
5589  // Setup
5590  const int myRank = comm->getRank();
5591  const int numProc = comm->getSize();
5592  std::string filename = filename_prefix + std::to_string(myRank) + filename_suffix;
5593 
5594  // Bounds check the writing limits
5595  int rank_limit = std::min(std::max(ranksToReadAtOnce, 1), numProc);
5596 
5597  // Data structures for constructor
5598  ArrayRCP<size_t> numEntriesPerRow;
5599  ArrayRCP<size_t> rowPtr;
5600  ArrayRCP<global_ordinal_type> colInd;
5601  ArrayRCP<scalar_type> values;
5602  std::ostringstream errMsg;
5603 
5605  // Start the reading of the banners to get
5606  // local row / nnz counts and then read the
5607  // data. We'll pack everything into big ol'
5608  // rowptr/colind/values ArrayRCPs
5609  bool success = true;
5610  int bannerIsCorrect = 1, readSuccess = 1;
5611  LO numRows, numCols, numNonzeros;
5612  for (int base_rank = 0; base_rank < numProc; base_rank += rank_limit) {
5613  int stop = std::min(base_rank + rank_limit, numProc);
5614 
5615  // Is my rank in this batch?
5616  if (base_rank <= myRank && myRank < stop) {
5617  // My turn to read
5618  std::ifstream in(filename);
5619  using Teuchos::MatrixMarket::Banner;
5620  size_t lineNumber = 1;
5621  RCP<const Banner> pBanner;
5622  try {
5623  pBanner = readBanner(in, lineNumber, tolerant, debug);
5624  } catch (std::exception& e) {
5625  errMsg << "Attempt to read the Matrix Market file's Banner line "
5626  "threw an exception: "
5627  << e.what();
5628  bannerIsCorrect = 0;
5629  }
5630  if (bannerIsCorrect) {
5631  // Validate the Banner for the case of a sparse matrix.
5632  // We validate on Proc 0, since it reads the Banner.
5633 
5634  // In intolerant mode, the matrix type must be "coordinate".
5635  if (!tolerant && pBanner->matrixType() != "coordinate") {
5636  bannerIsCorrect = 0;
5637  errMsg << "The Matrix Market input file must contain a "
5638  "\"coordinate\"-format sparse matrix in order to create a "
5639  "Tpetra::CrsMatrix object from it, but the file's matrix "
5640  "type is \""
5641  << pBanner->matrixType() << "\" instead.";
5642  }
5643  // In tolerant mode, we allow the matrix type to be
5644  // anything other than "array" (which would mean that
5645  // the file contains a dense matrix).
5646  if (tolerant && pBanner->matrixType() == "array") {
5647  bannerIsCorrect = 0;
5648  errMsg << "Matrix Market file must contain a \"coordinate\"-"
5649  "format sparse matrix in order to create a Tpetra::CrsMatrix "
5650  "object from it, but the file's matrix type is \"array\" "
5651  "instead. That probably means the file contains dense matrix "
5652  "data.";
5653  }
5654  }
5655 
5656  // Unpacked coordinate matrix dimensions
5657  using Teuchos::MatrixMarket::readCoordinateDimensions;
5658  success = readCoordinateDimensions(in, numRows, numCols,
5659  numNonzeros, lineNumber,
5660  tolerant);
5661 
5662  // Sanity checking of headers
5663  TEUCHOS_TEST_FOR_EXCEPTION(numRows != (LO)rowMap->getLocalNumElements(), std::invalid_argument,
5664  "# rows in file does not match rowmap.");
5665  TEUCHOS_TEST_FOR_EXCEPTION(!colMap.is_null() && numCols != (LO)colMap->getLocalNumElements(), std::invalid_argument,
5666  "# rows in file does not match colmap.");
5667 
5668  // Read the data
5669  typedef Teuchos::MatrixMarket::Raw::Adder<scalar_type, global_ordinal_type> raw_adder_type;
5670  bool tolerant_required = true;
5671  Teuchos::RCP<raw_adder_type> pRaw =
5672  Teuchos::rcp(new raw_adder_type(numRows, numCols, numNonzeros, tolerant_required, debug));
5673  RCP<adder_type> pAdder = Teuchos::rcp(new adder_type(pRaw, pBanner->symmType()));
5674 
5675  if (debug) {
5676  std::cerr << "-- Reading matrix data" << std::endl;
5677  }
5678 
5679  try {
5680  // Reader for "coordinate" format sparse matrix data.
5681  typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
5682  global_ordinal_type, scalar_type, STS::isComplex>
5683  reader_type;
5684  reader_type reader(pAdder);
5685 
5686  // Read the sparse matrix entries.
5687  std::pair<bool, std::vector<size_t>> results = reader.read(in, lineNumber, tolerant_required, debug);
5688 
5689  readSuccess = results.first ? 1 : 0;
5690  } catch (std::exception& e) {
5691  readSuccess = 0;
5692  errMsg << e.what();
5693  }
5694 
5696  // Create the CSR Arrays
5697  typedef Teuchos::MatrixMarket::Raw::Element<scalar_type, global_ordinal_type> element_type;
5698 
5699  // Additively merge duplicate matrix entries.
5700  pAdder->getAdder()->merge();
5701 
5702  // Get a temporary const view of the merged matrix entries.
5703  const std::vector<element_type>& entries = pAdder->getAdder()->getEntries();
5704 
5705  // Number of matrix entries (after merging).
5706  const size_t numEntries = (size_t)entries.size();
5707 
5708  if (debug) {
5709  std::cerr << "----- Proc " << myRank << ": Matrix has numRows=" << numRows
5710  << " rows and numEntries=" << numEntries
5711  << " entries." << std::endl;
5712  }
5713 
5714  // Make space for the CSR matrix data. Converting to
5715  // CSR is easier if we fill numEntriesPerRow with zeros
5716  // at first.
5717  numEntriesPerRow = arcp<size_t>(numRows);
5718  std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
5719  rowPtr = arcp<size_t>(numRows + 1);
5720  std::fill(rowPtr.begin(), rowPtr.end(), 0);
5721  colInd = arcp<global_ordinal_type>(numEntries);
5722  values = arcp<scalar_type>(numEntries);
5723 
5724  // Convert from array-of-structs coordinate format to CSR
5725  // (compressed sparse row) format.
5726  global_ordinal_type l_prvRow = 0;
5727  size_t curPos = 0;
5728  LO INVALID = Teuchos::OrdinalTraits<LO>::invalid();
5729  rowPtr[0] = 0;
5730  LO indexBase = rowMap->getIndexBase();
5731  for (curPos = 0; curPos < numEntries; ++curPos) {
5732  const element_type& curEntry = entries[curPos];
5733  const global_ordinal_type curRow = curEntry.rowIndex() + indexBase;
5734  LO l_curRow = rowMap->getLocalElement(curRow);
5735 
5736  TEUCHOS_TEST_FOR_EXCEPTION(l_curRow == INVALID, std::logic_error,
5737  "Current global row " << curRow << " is invalid.");
5738 
5739  TEUCHOS_TEST_FOR_EXCEPTION(l_curRow < l_prvRow, std::logic_error,
5740  "Row indices are out of order, even though they are supposed "
5741  "to be sorted. curRow = "
5742  << l_curRow << ", prvRow = "
5743  << l_prvRow << ", at curPos = " << curPos << ". Please report "
5744  "this bug to the Tpetra developers.");
5745  if (l_curRow > l_prvRow) {
5746  for (LO r = l_prvRow + 1; r <= l_curRow; ++r) {
5747  rowPtr[r] = curPos;
5748  }
5749  l_prvRow = l_curRow;
5750  }
5751  numEntriesPerRow[l_curRow]++;
5752  colInd[curPos] = curEntry.colIndex() + indexBase;
5753  values[curPos] = curEntry.value();
5754  }
5755  // rowPtr has one more entry than numEntriesPerRow. The
5756  // last entry of rowPtr is the number of entries in
5757  // colInd and values.
5758  rowPtr[numRows] = numEntries;
5759 
5760  } // end base_rank <= myRank < stop
5761 
5762  // Barrier between batches to keep the filesystem happy
5763  comm->barrier();
5764 
5765  } // end outer rank loop
5766 
5767  // Call the matrix constructor and fill. This isn't particularly efficient
5768  RCP<sparse_matrix_type> A;
5769  if (colMap.is_null()) {
5770  A = rcp(new sparse_matrix_type(rowMap, numEntriesPerRow()));
5771  for (size_t i = 0; i < rowMap->getLocalNumElements(); i++) {
5772  GO g_row = rowMap->getGlobalElement(i);
5773  size_t start = rowPtr[i];
5774  size_t size = rowPtr[i + 1] - rowPtr[i];
5775  if (size > 0) {
5776  A->insertGlobalValues(g_row, size, &values[start], &colInd[start]);
5777  }
5778  }
5779  } else {
5780  throw std::runtime_error("Reading with a column map is not yet implemented");
5781  }
5782  RCP<const map_type> myDomainMap = domainMap.is_null() ? rowMap : domainMap;
5783  RCP<const map_type> myRangeMap = rangeMap.is_null() ? rowMap : rangeMap;
5784 
5785  A->fillComplete(myDomainMap, myRangeMap);
5786 
5787  if (!readSuccess)
5788  success = false;
5789  TEUCHOS_TEST_FOR_EXCEPTION(success == false, std::runtime_error,
5790  "Read failed.");
5791 
5792  return A;
5793  } // end readSparsePerRank
5794 
5795 }; // class Reader
5796 
5825 template <class SparseMatrixType>
5826 class Writer {
5827  public:
5829  typedef SparseMatrixType sparse_matrix_type;
5830  typedef Teuchos::RCP<sparse_matrix_type> sparse_matrix_ptr;
5831 
5833  typedef typename SparseMatrixType::scalar_type scalar_type;
5835  typedef typename SparseMatrixType::local_ordinal_type local_ordinal_type;
5841  typedef typename SparseMatrixType::global_ordinal_type global_ordinal_type;
5843  typedef typename SparseMatrixType::node_type node_type;
5844 
5846  typedef MultiVector<scalar_type,
5849  node_type>
5855 
5858 
5860  using trcp_tcomm_t = Teuchos::RCP<const Teuchos::Comm<int>>;
5861 
5893  static void
5894  writeSparseFile(const std::string& filename,
5895  const sparse_matrix_type& matrix,
5896  const std::string& matrixName,
5897  const std::string& matrixDescription,
5898  const bool debug = false) {
5899  trcp_tcomm_t comm = matrix.getComm();
5900  TEUCHOS_TEST_FOR_EXCEPTION(comm.is_null(), std::invalid_argument,
5901  "The input matrix's communicator (Teuchos::Comm object) is null.");
5902  const int myRank = comm->getRank();
5903 
5904  auto out = Writer::openOutFileOnRankZero(comm, filename, myRank, true);
5905 
5906  writeSparse(out, matrix, matrixName, matrixDescription, debug);
5907  // We can rely on the destructor of the output stream to close
5908  // the file on scope exit, even if writeSparse() throws an
5909  // exception.
5910  }
5911 
5913  static void
5914  writeSparseFile(const std::string& filename,
5915  const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
5916  const std::string& matrixName,
5917  const std::string& matrixDescription,
5918  const bool debug = false) {
5919  TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::invalid_argument,
5920  "The input matrix is null.");
5921  writeSparseFile(filename, *pMatrix, matrixName,
5922  matrixDescription, debug);
5923  }
5924 
5944  static void
5945  writeSparseFile(const std::string& filename,
5946  const sparse_matrix_type& matrix,
5947  const bool debug = false) {
5948  writeSparseFile(filename, matrix, "", "", debug);
5949  }
5950 
5952  static void
5953  writeSparseFile(const std::string& filename,
5954  const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
5955  const bool debug = false) {
5956  writeSparseFile(filename, *pMatrix, "", "", debug);
5957  }
5958 
5989  static void
5990  writeSparse(std::ostream& out,
5991  const sparse_matrix_type& matrix,
5992  const std::string& matrixName,
5993  const std::string& matrixDescription,
5994  const bool debug = false) {
5995  using std::cerr;
5996  using std::endl;
5997  using Teuchos::ArrayView;
5998  using Teuchos::Comm;
5999  using Teuchos::FancyOStream;
6000  using Teuchos::getFancyOStream;
6001  using Teuchos::null;
6002  using Teuchos::RCP;
6003  using Teuchos::rcpFromRef;
6004  using ST = scalar_type;
6005  using LO = local_ordinal_type;
6006  using GO = global_ordinal_type;
6007  using STS = typename Teuchos::ScalarTraits<ST>;
6008 
6009  // Make the output stream write floating-point numbers in
6010  // scientific notation. It will politely put the output
6011  // stream back to its state on input, when this scope
6012  // terminates.
6013  Teuchos::SetScientific<ST> sci(out);
6014 
6015  // Get the matrix's communicator.
6016  trcp_tcomm_t comm = matrix.getComm();
6017  TEUCHOS_TEST_FOR_EXCEPTION(
6018  comm.is_null(), std::invalid_argument,
6019  "The input matrix's communicator (Teuchos::Comm object) is null.");
6020  const int myRank = comm->getRank();
6021 
6022  // Optionally, make a stream for debugging output.
6023  RCP<FancyOStream> err =
6024  debug ? getFancyOStream(rcpFromRef(std::cerr)) : null;
6025  if (debug) {
6026  std::ostringstream os;
6027  os << myRank << ": writeSparse" << endl;
6028  *err << os.str();
6029  comm->barrier();
6030  os << "-- " << myRank << ": past barrier" << endl;
6031  *err << os.str();
6032  }
6033 
6034  // Whether to print debugging output to stderr.
6035  const bool debugPrint = debug && myRank == 0;
6036 
6037  RCP<const map_type> rowMap = matrix.getRowMap();
6038  RCP<const map_type> colMap = matrix.getColMap();
6039  RCP<const map_type> domainMap = matrix.getDomainMap();
6040  RCP<const map_type> rangeMap = matrix.getRangeMap();
6041 
6042  const global_size_t numRows = rangeMap->getMaxAllGlobalIndex() + 1 - rangeMap->getIndexBase();
6043  const global_size_t numCols = domainMap->getMaxAllGlobalIndex() + 1 - domainMap->getIndexBase();
6044 
6045  if (debug && myRank == 0) {
6046  std::ostringstream os;
6047  os << "-- Input sparse matrix is:"
6048  << "---- " << numRows << " x " << numCols << endl
6049  << "---- "
6050  << (matrix.isGloballyIndexed() ? "Globally" : "Locally")
6051  << " indexed." << endl
6052  << "---- Its row map has " << rowMap->getGlobalNumElements()
6053  << " elements." << endl
6054  << "---- Its col map has " << colMap->getGlobalNumElements()
6055  << " elements." << endl;
6056  *err << os.str();
6057  }
6058  // Make the "gather" row map, where Proc 0 owns all rows and
6059  // the other procs own no rows.
6060  const size_t localNumRows = (myRank == 0) ? numRows : 0;
6061  if (debug) {
6062  std::ostringstream os;
6063  os << "-- " << myRank << ": making gatherRowMap" << endl;
6064  *err << os.str();
6065  }
6066  RCP<const map_type> gatherRowMap =
6067  Details::computeGatherMap(rowMap, err, debug);
6068 
6069  // Since the matrix may in general be non-square, we need to
6070  // make a column map as well. In this case, the column map
6071  // contains all the columns of the original matrix, because we
6072  // are gathering the whole matrix onto Proc 0. We call
6073  // computeGatherMap to preserve the original order of column
6074  // indices over all the processes.
6075  const size_t localNumCols = (myRank == 0) ? numCols : 0;
6076  RCP<const map_type> gatherColMap =
6077  Details::computeGatherMap(colMap, err, debug);
6078 
6079  // Current map is the source map, gather map is the target map.
6080  typedef Import<LO, GO, node_type> import_type;
6081  import_type importer(rowMap, gatherRowMap);
6082 
6083  // Create a new CrsMatrix to hold the result of the import.
6084  // The constructor needs a column map as well as a row map,
6085  // for the case that the matrix is not square.
6086  RCP<sparse_matrix_type> newMatrix =
6087  rcp(new sparse_matrix_type(gatherRowMap, gatherColMap,
6088  static_cast<size_t>(0)));
6089  // Import the sparse matrix onto Proc 0.
6090  newMatrix->doImport(matrix, importer, INSERT);
6091 
6092  // fillComplete() needs the domain and range maps for the case
6093  // that the matrix is not square.
6094  {
6095  RCP<const map_type> gatherDomainMap =
6096  rcp(new map_type(numCols, localNumCols,
6097  domainMap->getIndexBase(),
6098  comm));
6099  RCP<const map_type> gatherRangeMap =
6100  rcp(new map_type(numRows, localNumRows,
6101  rangeMap->getIndexBase(),
6102  comm));
6103  newMatrix->fillComplete(gatherDomainMap, gatherRangeMap);
6104  }
6105 
6106  if (debugPrint) {
6107  cerr << "-- Output sparse matrix is:"
6108  << "---- " << newMatrix->getRangeMap()->getGlobalNumElements()
6109  << " x "
6110  << newMatrix->getDomainMap()->getGlobalNumElements()
6111  << " with "
6112  << newMatrix->getGlobalNumEntries() << " entries;" << endl
6113  << "---- "
6114  << (newMatrix->isGloballyIndexed() ? "Globally" : "Locally")
6115  << " indexed." << endl
6116  << "---- Its row map has "
6117  << newMatrix->getRowMap()->getGlobalNumElements()
6118  << " elements, with index base "
6119  << newMatrix->getRowMap()->getIndexBase() << "." << endl
6120  << "---- Its col map has "
6121  << newMatrix->getColMap()->getGlobalNumElements()
6122  << " elements, with index base "
6123  << newMatrix->getColMap()->getIndexBase() << "." << endl
6124  << "---- Element count of output matrix's column Map may differ "
6125  << "from that of the input matrix's column Map, if some columns "
6126  << "of the matrix contain no entries." << endl;
6127  }
6128 
6129  //
6130  // Print the metadata and the matrix entries on Rank 0.
6131  //
6132  if (myRank == 0) {
6133  // Print the Matrix Market banner line. CrsMatrix stores
6134  // data nonsymmetrically ("general"). This implies that
6135  // readSparse() on a symmetrically stored input file,
6136  // followed by writeSparse() on the resulting sparse matrix,
6137  // will result in an output file with a different banner
6138  // line than the original input file.
6139  out << "%%MatrixMarket matrix coordinate "
6140  << (STS::isComplex ? "complex" : "real")
6141  << " general" << endl;
6142 
6143  // Print comments (the matrix name and / or description).
6144  if (matrixName != "") {
6145  printAsComment(out, matrixName);
6146  }
6147  if (matrixDescription != "") {
6148  printAsComment(out, matrixDescription);
6149  }
6150 
6151  // Print the Matrix Market header (# rows, # columns, #
6152  // nonzeros). Use the range resp. domain map for the number
6153  // of rows resp. columns, since Tpetra::CrsMatrix uses the
6154  // column map for the number of columns. That only
6155  // corresponds to the "linear-algebraic" number of columns
6156  // when the column map is uniquely owned (a.k.a. one-to-one),
6157  // which only happens if the matrix is (block) diagonal.
6158  out << newMatrix->getRangeMap()->getMaxAllGlobalIndex() + 1 - newMatrix->getRangeMap()->getIndexBase() << " "
6159  << newMatrix->getDomainMap()->getMaxAllGlobalIndex() + 1 - newMatrix->getDomainMap()->getIndexBase() << " "
6160  << newMatrix->getGlobalNumEntries() << endl;
6161 
6162  // The Matrix Market format expects one-based row and column
6163  // indices. We'll convert the indices on output from
6164  // whatever index base they use to one-based indices.
6165  const GO rowIndexBase = gatherRowMap->getIndexBase();
6166  const GO colIndexBase = newMatrix->getColMap()->getIndexBase();
6167  //
6168  // Print the entries of the matrix.
6169  //
6170  // newMatrix can never be globally indexed, since we called
6171  // fillComplete() on it. We include code for both cases
6172  // (globally or locally indexed) just in case that ever
6173  // changes.
6174  if (newMatrix->isGloballyIndexed()) {
6175  // We know that the "gather" row Map is contiguous, so we
6176  // don't need to get the list of GIDs.
6177  const GO minAllGlobalIndex = gatherRowMap->getMinAllGlobalIndex();
6178  const GO maxAllGlobalIndex = gatherRowMap->getMaxAllGlobalIndex();
6179  for (GO globalRowIndex = minAllGlobalIndex;
6180  globalRowIndex <= maxAllGlobalIndex; // inclusive range
6181  ++globalRowIndex) {
6182  typename sparse_matrix_type::global_inds_host_view_type ind;
6183  typename sparse_matrix_type::values_host_view_type val;
6184  newMatrix->getGlobalRowView(globalRowIndex, ind, val);
6185  for (size_t ii = 0; ii < ind.extent(0); ii++) {
6186  const GO globalColIndex = ind(ii);
6187  // Convert row and column indices to 1-based.
6188  // This works because the global index type is signed.
6189  out << (globalRowIndex + 1 - rowIndexBase) << " "
6190  << (globalColIndex + 1 - colIndexBase) << " ";
6191  if (STS::isComplex) {
6192  out << STS::real(val(ii)) << " " << STS::imag(val(ii));
6193  } else {
6194  out << val(ii);
6195  }
6196  out << endl;
6197  } // For each entry in the current row
6198  } // For each row of the "gather" matrix
6199  } else { // newMatrix is locally indexed
6200  using OTG = Teuchos::OrdinalTraits<GO>;
6201  for (LO localRowIndex = gatherRowMap->getMinLocalIndex();
6202  localRowIndex <= gatherRowMap->getMaxLocalIndex();
6203  ++localRowIndex) {
6204  // Convert from local to global row index.
6205  const GO globalRowIndex =
6206  gatherRowMap->getGlobalElement(localRowIndex);
6207  TEUCHOS_TEST_FOR_EXCEPTION(
6208  globalRowIndex == OTG::invalid(), std::logic_error,
6209  "Failed to convert the supposed local row index "
6210  << localRowIndex << " into a global row index. "
6211  "Please report this bug to the Tpetra developers.");
6212  typename sparse_matrix_type::local_inds_host_view_type ind;
6213  typename sparse_matrix_type::values_host_view_type val;
6214  newMatrix->getLocalRowView(localRowIndex, ind, val);
6215  for (size_t ii = 0; ii < ind.extent(0); ii++) {
6216  // Convert the column index from local to global.
6217  const GO globalColIndex =
6218  newMatrix->getColMap()->getGlobalElement(ind(ii));
6219  TEUCHOS_TEST_FOR_EXCEPTION(
6220  globalColIndex == OTG::invalid(), std::logic_error,
6221  "On local row " << localRowIndex << " of the sparse matrix: "
6222  "Failed to convert the supposed local column index "
6223  << ind(ii) << " into a global column index. Please report "
6224  "this bug to the Tpetra developers.");
6225  // Convert row and column indices to 1-based.
6226  // This works because the global index type is signed.
6227  out << (globalRowIndex + 1 - rowIndexBase) << " "
6228  << (globalColIndex + 1 - colIndexBase) << " ";
6229  if (STS::isComplex) {
6230  out << STS::real(val(ii)) << " " << STS::imag(val(ii));
6231  } else {
6232  out << val(ii);
6233  }
6234  out << endl;
6235  } // For each entry in the current row
6236  } // For each row of the "gather" matrix
6237  } // Whether the "gather" matrix is locally or globally indexed
6238  } // If my process' rank is 0
6239  }
6240 
6242  static void
6243  writeSparse(std::ostream& out,
6244  const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
6245  const std::string& matrixName,
6246  const std::string& matrixDescription,
6247  const bool debug = false) {
6248  TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::invalid_argument,
6249  "The input matrix is null.");
6250  writeSparse(out, *pMatrix, matrixName, matrixDescription, debug);
6251  }
6252 
6283  static void
6284  writeSparseGraph(std::ostream& out,
6285  const crs_graph_type& graph,
6286  const std::string& graphName,
6287  const std::string& graphDescription,
6288  const bool debug = false) {
6289  using std::cerr;
6290  using std::endl;
6291  using Teuchos::ArrayView;
6292  using Teuchos::Comm;
6293  using Teuchos::FancyOStream;
6294  using Teuchos::getFancyOStream;
6295  using Teuchos::null;
6296  using Teuchos::RCP;
6297  using Teuchos::rcpFromRef;
6298  typedef local_ordinal_type LO;
6299  typedef global_ordinal_type GO;
6300 
6301  // Get the graph's communicator. Processes on which the
6302  // graph's Map or communicator is null don't participate in
6303  // this operation. This function shouldn't even be called on
6304  // those processes.
6305  auto rowMap = graph.getRowMap();
6306  if (rowMap.is_null()) {
6307  return;
6308  }
6309  auto comm = rowMap->getComm();
6310  if (comm.is_null()) {
6311  return;
6312  }
6313  const int myRank = comm->getRank();
6314 
6315  // Optionally, make a stream for debugging output.
6316  RCP<FancyOStream> err =
6317  debug ? getFancyOStream(rcpFromRef(std::cerr)) : null;
6318  if (debug) {
6319  std::ostringstream os;
6320  os << myRank << ": writeSparseGraph" << endl;
6321  *err << os.str();
6322  comm->barrier();
6323  os << "-- " << myRank << ": past barrier" << endl;
6324  *err << os.str();
6325  }
6326 
6327  // Whether to print debugging output to stderr.
6328  const bool debugPrint = debug && myRank == 0;
6329 
6330  // We've already gotten the rowMap above.
6331  auto colMap = graph.getColMap();
6332  auto domainMap = graph.getDomainMap();
6333  auto rangeMap = graph.getRangeMap();
6334 
6335  const global_size_t numRows = rangeMap->getGlobalNumElements();
6336  const global_size_t numCols = domainMap->getGlobalNumElements();
6337 
6338  if (debug && myRank == 0) {
6339  std::ostringstream os;
6340  os << "-- Input sparse graph is:"
6341  << "---- " << numRows << " x " << numCols << " with "
6342  << graph.getGlobalNumEntries() << " entries;" << endl
6343  << "---- "
6344  << (graph.isGloballyIndexed() ? "Globally" : "Locally")
6345  << " indexed." << endl
6346  << "---- Its row Map has " << rowMap->getGlobalNumElements()
6347  << " elements." << endl
6348  << "---- Its col Map has " << colMap->getGlobalNumElements()
6349  << " elements." << endl;
6350  *err << os.str();
6351  }
6352  // Make the "gather" row map, where Proc 0 owns all rows and
6353  // the other procs own no rows.
6354  const size_t localNumRows = (myRank == 0) ? numRows : 0;
6355  if (debug) {
6356  std::ostringstream os;
6357  os << "-- " << myRank << ": making gatherRowMap" << endl;
6358  *err << os.str();
6359  }
6360  auto gatherRowMap = Details::computeGatherMap(rowMap, err, debug);
6361 
6362  // Since the graph may in general be non-square, we need to
6363  // make a column map as well. In this case, the column map
6364  // contains all the columns of the original graph, because we
6365  // are gathering the whole graph onto Proc 0. We call
6366  // computeGatherMap to preserve the original order of column
6367  // indices over all the processes.
6368  const size_t localNumCols = (myRank == 0) ? numCols : 0;
6369  auto gatherColMap = Details::computeGatherMap(colMap, err, debug);
6370 
6371  // Current map is the source map, gather map is the target map.
6372  Import<LO, GO, node_type> importer(rowMap, gatherRowMap);
6373 
6374  // Create a new CrsGraph to hold the result of the import.
6375  // The constructor needs a column map as well as a row map,
6376  // for the case that the graph is not square.
6377  crs_graph_type newGraph(gatherRowMap, gatherColMap,
6378  static_cast<size_t>(0));
6379  // Import the sparse graph onto Proc 0.
6380  newGraph.doImport(graph, importer, INSERT);
6381 
6382  // fillComplete() needs the domain and range maps for the case
6383  // that the graph is not square.
6384  {
6385  RCP<const map_type> gatherDomainMap =
6386  rcp(new map_type(numCols, localNumCols,
6387  domainMap->getIndexBase(),
6388  comm));
6389  RCP<const map_type> gatherRangeMap =
6390  rcp(new map_type(numRows, localNumRows,
6391  rangeMap->getIndexBase(),
6392  comm));
6393  newGraph.fillComplete(gatherDomainMap, gatherRangeMap);
6394  }
6395 
6396  if (debugPrint) {
6397  cerr << "-- Output sparse graph is:"
6398  << "---- " << newGraph.getRangeMap()->getGlobalNumElements()
6399  << " x "
6400  << newGraph.getDomainMap()->getGlobalNumElements()
6401  << " with "
6402  << newGraph.getGlobalNumEntries() << " entries;" << endl
6403  << "---- "
6404  << (newGraph.isGloballyIndexed() ? "Globally" : "Locally")
6405  << " indexed." << endl
6406  << "---- Its row map has "
6407  << newGraph.getRowMap()->getGlobalNumElements()
6408  << " elements, with index base "
6409  << newGraph.getRowMap()->getIndexBase() << "." << endl
6410  << "---- Its col map has "
6411  << newGraph.getColMap()->getGlobalNumElements()
6412  << " elements, with index base "
6413  << newGraph.getColMap()->getIndexBase() << "." << endl
6414  << "---- Element count of output graph's column Map may differ "
6415  << "from that of the input matrix's column Map, if some columns "
6416  << "of the matrix contain no entries." << endl;
6417  }
6418 
6419  //
6420  // Print the metadata and the graph entries on Process 0 of
6421  // the graph's communicator.
6422  //
6423  if (myRank == 0) {
6424  // Print the Matrix Market banner line. CrsGraph stores
6425  // data nonsymmetrically ("general"). This implies that
6426  // readSparseGraph() on a symmetrically stored input file,
6427  // followed by writeSparseGraph() on the resulting sparse
6428  // graph, will result in an output file with a different
6429  // banner line than the original input file.
6430  out << "%%MatrixMarket matrix coordinate pattern general" << endl;
6431 
6432  // Print comments (the graph name and / or description).
6433  if (graphName != "") {
6434  printAsComment(out, graphName);
6435  }
6436  if (graphDescription != "") {
6437  printAsComment(out, graphDescription);
6438  }
6439 
6440  // Print the Matrix Market header (# rows, # columns, #
6441  // stored entries). Use the range resp. domain map for the
6442  // number of rows resp. columns, since Tpetra::CrsGraph uses
6443  // the column map for the number of columns. That only
6444  // corresponds to the "linear-algebraic" number of columns
6445  // when the column map is uniquely owned
6446  // (a.k.a. one-to-one), which only happens if the graph is
6447  // block diagonal (one block per process).
6448  out << newGraph.getRangeMap()->getMaxAllGlobalIndex() + 1 - newGraph.getRangeMap()->getIndexBase() << " "
6449  << newGraph.getDomainMap()->getMaxAllGlobalIndex() + 1 - newGraph.getDomainMap()->getIndexBase() << " "
6450  << newGraph.getGlobalNumEntries() << endl;
6451 
6452  // The Matrix Market format expects one-based row and column
6453  // indices. We'll convert the indices on output from
6454  // whatever index base they use to one-based indices.
6455  const GO rowIndexBase = gatherRowMap->getIndexBase();
6456  const GO colIndexBase = newGraph.getColMap()->getIndexBase();
6457  //
6458  // Print the entries of the graph.
6459  //
6460  // newGraph can never be globally indexed, since we called
6461  // fillComplete() on it. We include code for both cases
6462  // (globally or locally indexed) just in case that ever
6463  // changes.
6464  if (newGraph.isGloballyIndexed()) {
6465  // We know that the "gather" row Map is contiguous, so we
6466  // don't need to get the list of GIDs.
6467  const GO minAllGlobalIndex = gatherRowMap->getMinAllGlobalIndex();
6468  const GO maxAllGlobalIndex = gatherRowMap->getMaxAllGlobalIndex();
6469  for (GO globalRowIndex = minAllGlobalIndex;
6470  globalRowIndex <= maxAllGlobalIndex; // inclusive range
6471  ++globalRowIndex) {
6472  typename crs_graph_type::global_inds_host_view_type ind;
6473  newGraph.getGlobalRowView(globalRowIndex, ind);
6474  for (size_t ii = 0; ii < ind.extent(0); ii++) {
6475  const GO globalColIndex = ind(ii);
6476  // Convert row and column indices to 1-based.
6477  // This works because the global index type is signed.
6478  out << (globalRowIndex + 1 - rowIndexBase) << " "
6479  << (globalColIndex + 1 - colIndexBase) << " ";
6480  out << endl;
6481  } // For each entry in the current row
6482  } // For each row of the "gather" graph
6483  } else { // newGraph is locally indexed
6484  typedef Teuchos::OrdinalTraits<GO> OTG;
6485  for (LO localRowIndex = gatherRowMap->getMinLocalIndex();
6486  localRowIndex <= gatherRowMap->getMaxLocalIndex();
6487  ++localRowIndex) {
6488  // Convert from local to global row index.
6489  const GO globalRowIndex =
6490  gatherRowMap->getGlobalElement(localRowIndex);
6491  TEUCHOS_TEST_FOR_EXCEPTION(globalRowIndex == OTG::invalid(), std::logic_error,
6492  "Failed "
6493  "to convert the supposed local row index "
6494  << localRowIndex << " into a global row index. Please report this bug to the "
6495  "Tpetra developers.");
6496  typename crs_graph_type::local_inds_host_view_type ind;
6497  newGraph.getLocalRowView(localRowIndex, ind);
6498  for (size_t ii = 0; ii < ind.extent(0); ii++) {
6499  // Convert the column index from local to global.
6500  const GO globalColIndex =
6501  newGraph.getColMap()->getGlobalElement(ind(ii));
6502  TEUCHOS_TEST_FOR_EXCEPTION(
6503  globalColIndex == OTG::invalid(), std::logic_error,
6504  "On local row " << localRowIndex << " of the sparse graph: "
6505  "Failed to convert the supposed local column index "
6506  << ind(ii) << " into a global column index. Please report "
6507  "this bug to the Tpetra developers.");
6508  // Convert row and column indices to 1-based.
6509  // This works because the global index type is signed.
6510  out << (globalRowIndex + 1 - rowIndexBase) << " "
6511  << (globalColIndex + 1 - colIndexBase) << " ";
6512  out << endl;
6513  } // For each entry in the current row
6514  } // For each row of the "gather" graph
6515  } // Whether the "gather" graph is locally or globally indexed
6516  } // If my process' rank is 0
6517  }
6518 
6524  static void
6525  writeSparseGraph(std::ostream& out,
6526  const crs_graph_type& graph,
6527  const bool debug = false) {
6528  writeSparseGraph(out, graph, "", "", debug);
6529  }
6530 
6565  static void
6566  writeSparseGraphFile(const std::string& filename,
6567  const crs_graph_type& graph,
6568  const std::string& graphName,
6569  const std::string& graphDescription,
6570  const bool debug = false) {
6571  auto comm = graph.getComm();
6572  if (comm.is_null()) {
6573  // Processes on which the communicator is null shouldn't
6574  // even call this function. The convention is that
6575  // processes on which the object's communicator is null do
6576  // not participate in collective operations involving the
6577  // object.
6578  return;
6579  }
6580  const int myRank = comm->getRank();
6581 
6582  auto out = Writer::openOutFileOnRankZero(comm, filename, myRank, true);
6583 
6584  writeSparseGraph(out, graph, graphName, graphDescription, debug);
6585  // We can rely on the destructor of the output stream to close
6586  // the file on scope exit, even if writeSparseGraph() throws
6587  // an exception.
6588  }
6589 
6594  static void
6595  writeSparseGraphFile(const std::string& filename,
6596  const crs_graph_type& graph,
6597  const bool debug = false) {
6598  writeSparseGraphFile(filename, graph, "", "", debug);
6599  }
6600 
6609  static void
6610  writeSparseGraphFile(const std::string& filename,
6611  const Teuchos::RCP<const crs_graph_type>& pGraph,
6612  const std::string& graphName,
6613  const std::string& graphDescription,
6614  const bool debug = false) {
6615  writeSparseGraphFile(filename, *pGraph, graphName, graphDescription, debug);
6616  }
6617 
6627  static void
6628  writeSparseGraphFile(const std::string& filename,
6629  const Teuchos::RCP<const crs_graph_type>& pGraph,
6630  const bool debug = false) {
6631  writeSparseGraphFile(filename, *pGraph, "", "", debug);
6632  }
6633 
6656  static void
6657  writeSparse(std::ostream& out,
6658  const sparse_matrix_type& matrix,
6659  const bool debug = false) {
6660  writeSparse(out, matrix, "", "", debug);
6661  }
6662 
6664  static void
6665  writeSparse(std::ostream& out,
6666  const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
6667  const bool debug = false) {
6668  writeSparse(out, *pMatrix, "", "", debug);
6669  }
6670 
6699  static void
6700  writeDenseFile(const std::string& filename,
6701  const multivector_type& X,
6702  const std::string& matrixName,
6703  const std::string& matrixDescription,
6704  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
6705  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
6706  trcp_tcomm_t comm = Writer::getComm(X.getMap());
6707  const int myRank = Writer::getRank(comm);
6708 
6709  auto out = Writer::openOutFileOnRankZero(comm, filename, myRank, true);
6710 
6711  writeDense(out, X, matrixName, matrixDescription, err, dbg);
6712  // We can rely on the destructor of the output stream to close
6713  // the file on scope exit, even if writeDense() throws an
6714  // exception.
6715  }
6716 
6722  static void
6723  writeDenseFile(const std::string& filename,
6724  const Teuchos::RCP<const multivector_type>& X,
6725  const std::string& matrixName,
6726  const std::string& matrixDescription,
6727  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
6728  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
6729  TEUCHOS_TEST_FOR_EXCEPTION(
6730  X.is_null(), std::invalid_argument,
6731  "Tpetra::MatrixMarket::"
6732  "writeDenseFile: The input MultiVector X is null.");
6733  writeDenseFile(filename, *X, matrixName, matrixDescription, err, dbg);
6734  }
6735 
6741  static void
6742  writeDenseFile(const std::string& filename,
6743  const multivector_type& X,
6744  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
6745  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
6746  writeDenseFile(filename, X, "", "", err, dbg);
6747  }
6748 
6754  static void
6755  writeDenseFile(const std::string& filename,
6756  const Teuchos::RCP<const multivector_type>& X,
6757  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
6758  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
6759  TEUCHOS_TEST_FOR_EXCEPTION(
6760  X.is_null(), std::invalid_argument,
6761  "Tpetra::MatrixMarket::"
6762  "writeDenseFile: The input MultiVector X is null.");
6763  writeDenseFile(filename, *X, err, dbg);
6764  }
6765 
6796  static void
6797  writeDense(std::ostream& out,
6798  const multivector_type& X,
6799  const std::string& matrixName,
6800  const std::string& matrixDescription,
6801  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
6802  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
6803  using std::endl;
6804  using Teuchos::Comm;
6805  using Teuchos::outArg;
6806  using Teuchos::REDUCE_MAX;
6807  using Teuchos::reduceAll;
6808 
6809  trcp_tcomm_t comm = Writer::getComm(X.getMap());
6810  const int myRank = Writer::getRank(comm);
6811 
6812  // If the caller provides a nonnull debug output stream, we
6813  // print debugging output to it. This is a local thing; we
6814  // don't have to check across processes.
6815  const bool debug = !dbg.is_null();
6816  if (debug) {
6817  dbg->pushTab();
6818  std::ostringstream os;
6819  os << myRank << ": writeDense" << endl;
6820  *dbg << os.str();
6821  dbg->pushTab();
6822  }
6823  // Print the Matrix Market header.
6824  writeDenseHeader(out, X, matrixName, matrixDescription, err, dbg);
6825 
6826  // Print each column one at a time. This is a (perhaps)
6827  // temporary fix for Bug 6288.
6828  const size_t numVecs = X.getNumVectors();
6829  for (size_t j = 0; j < numVecs; ++j) {
6830  writeDenseColumn(out, *(X.getVector(j)), err, dbg);
6831  }
6832 
6833  if (debug) {
6834  dbg->popTab();
6835  std::ostringstream os;
6836  os << myRank << ": writeDense: Done" << endl;
6837  *dbg << os.str();
6838  dbg->popTab();
6839  }
6840  }
6841 
6842  private:
6848  static std::ofstream openOutFileOnRankZero(
6849  const trcp_tcomm_t& comm,
6850  const std::string& filename, const int rank, const bool safe = true,
6851  const std::ios_base::openmode mode = std::ios_base::out) {
6852  // Placeholder for the output stream.
6853  std::ofstream out;
6854 
6855  // State that will make all ranks throw if the root rank wasn't able to open the stream (using @c int for broadcasting).
6856  int all_should_stop = 0;
6857 
6858  // Try to open the file and update the state.
6859  if (rank == 0) {
6860  out.open(filename, mode);
6861  all_should_stop = !out && safe;
6862  }
6863 
6864  // Broadcast the stream state and throw from all ranks if needed.
6865  if (comm) Teuchos::broadcast(*comm, 0, &all_should_stop);
6866 
6867  TEUCHOS_TEST_FOR_EXCEPTION(
6868  all_should_stop,
6869  std::runtime_error,
6870  "Could not open output file '" + filename + "' on root rank 0.");
6871 
6872  return out;
6873  }
6874 
6900  static void
6901  writeDenseHeader(std::ostream& out,
6902  const multivector_type& X,
6903  const std::string& matrixName,
6904  const std::string& matrixDescription,
6905  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
6906  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
6907  using std::endl;
6908  using Teuchos::Comm;
6909  using Teuchos::outArg;
6910  using Teuchos::RCP;
6911  using Teuchos::REDUCE_MAX;
6912  using Teuchos::reduceAll;
6913  typedef Teuchos::ScalarTraits<scalar_type> STS;
6914  const char prefix[] = "Tpetra::MatrixMarket::writeDenseHeader: ";
6915 
6916  trcp_tcomm_t comm = Writer::getComm(X.getMap());
6917  const int myRank = Writer::getRank(comm);
6918  int lclErr = 0; // whether this MPI process has seen an error
6919  int gblErr = 0; // whether we know if some MPI process has seen an error
6920 
6921  // If the caller provides a nonnull debug output stream, we
6922  // print debugging output to it. This is a local thing; we
6923  // don't have to check across processes.
6924  const bool debug = !dbg.is_null();
6925 
6926  if (debug) {
6927  dbg->pushTab();
6928  std::ostringstream os;
6929  os << myRank << ": writeDenseHeader" << endl;
6930  *dbg << os.str();
6931  dbg->pushTab();
6932  }
6933 
6934  //
6935  // Process 0: Write the MatrixMarket header.
6936  //
6937  if (myRank == 0) {
6938  try {
6939  // Print the Matrix Market header. MultiVector stores data
6940  // nonsymmetrically, hence "general" in the banner line.
6941  // Print first to a temporary string output stream, and then
6942  // write it to the main output stream, so that at least the
6943  // header output has transactional semantics. We can't
6944  // guarantee transactional semantics for the whole output,
6945  // since that would not be memory scalable. (This could be
6946  // done in the file system by using a temporary file; we
6947  // don't do this, but users could.)
6948  std::ostringstream hdr;
6949  {
6950  std::string dataType;
6951  if (STS::isComplex) {
6952  dataType = "complex";
6953  } else if (STS::isOrdinal) {
6954  dataType = "integer";
6955  } else {
6956  dataType = "real";
6957  }
6958  hdr << "%%MatrixMarket matrix array " << dataType << " general"
6959  << endl;
6960  }
6961 
6962  // Print comments (the matrix name and / or description).
6963  if (matrixName != "") {
6964  printAsComment(hdr, matrixName);
6965  }
6966  if (matrixDescription != "") {
6967  printAsComment(hdr, matrixDescription);
6968  }
6969  // Print the Matrix Market dimensions header for dense matrices.
6970  hdr << X.getGlobalLength() << " " << X.getNumVectors() << endl;
6971 
6972  // Write the MatrixMarket header to the output stream.
6973  out << hdr.str();
6974  } catch (std::exception& e) {
6975  if (!err.is_null()) {
6976  *err << prefix << "While writing the Matrix Market header, "
6977  "Process 0 threw an exception: "
6978  << e.what() << endl;
6979  }
6980  lclErr = 1;
6981  }
6982  } // if I am Process 0
6983 
6984  // Establish global agreement on the error state. It wouldn't
6985  // be good for other processes to keep going, if Process 0
6986  // finds out that it can't write to the given output stream.
6987  reduceAll<int, int>(*comm, REDUCE_MAX, lclErr, outArg(gblErr));
6988  TEUCHOS_TEST_FOR_EXCEPTION(
6989  gblErr == 1, std::runtime_error, prefix << "Some error occurred "
6990  "which prevented this method from completing.");
6991 
6992  if (debug) {
6993  dbg->popTab();
6994  *dbg << myRank << ": writeDenseHeader: Done" << endl;
6995  dbg->popTab();
6996  }
6997  }
6998 
7016  static void
7017  writeDenseColumn(std::ostream& out,
7018  const multivector_type& X,
7019  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
7020  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
7021  using std::endl;
7022  using Teuchos::arcp;
7023  using Teuchos::Array;
7024  using Teuchos::ArrayRCP;
7025  using Teuchos::ArrayView;
7026  using Teuchos::Comm;
7027  using Teuchos::CommRequest;
7028  using Teuchos::ireceive;
7029  using Teuchos::isend;
7030  using Teuchos::outArg;
7031  using Teuchos::RCP;
7032  using Teuchos::REDUCE_MAX;
7033  using Teuchos::reduceAll;
7034  using Teuchos::TypeNameTraits;
7035  using Teuchos::wait;
7036  typedef Teuchos::ScalarTraits<scalar_type> STS;
7037 
7038  const Comm<int>& comm = *(X.getMap()->getComm());
7039  const int myRank = comm.getRank();
7040  const int numProcs = comm.getSize();
7041  int lclErr = 0; // whether this MPI process has seen an error
7042  int gblErr = 0; // whether we know if some MPI process has seen an error
7043 
7044  // If the caller provides a nonnull debug output stream, we
7045  // print debugging output to it. This is a local thing; we
7046  // don't have to check across processes.
7047  const bool debug = !dbg.is_null();
7048 
7049  if (debug) {
7050  dbg->pushTab();
7051  std::ostringstream os;
7052  os << myRank << ": writeDenseColumn" << endl;
7053  *dbg << os.str();
7054  dbg->pushTab();
7055  }
7056 
7057  // Make the output stream write floating-point numbers in
7058  // scientific notation. It will politely put the output
7059  // stream back to its state on input, when this scope
7060  // terminates.
7061  Teuchos::SetScientific<scalar_type> sci(out);
7062 
7063  const size_t myNumRows = X.getLocalLength();
7064  const size_t numCols = X.getNumVectors();
7065  // Use a different tag for the "size" messages than for the
7066  // "data" messages, in order to help us debug any mix-ups.
7067  const int sizeTag = 1337;
7068  const int dataTag = 1338;
7069 
7070  // Process 0 pipelines nonblocking receives with file output.
7071  //
7072  // Constraints:
7073  // - Process 0 can't post a receive for another process'
7074  // actual data, until it posts and waits on the receive
7075  // from that process with the amount of data to receive.
7076  // (We could just post receives with a max data size, but
7077  // I feel uncomfortable about that.)
7078  // - The C++ standard library doesn't allow nonblocking
7079  // output to an std::ostream. (Thus, we have to start a
7080  // receive or send before starting the write, and hope
7081  // that MPI completes it in the background.)
7082  //
7083  // Process 0: Post receive-size receives from Processes 1 and 2.
7084  // Process 1: Post send-size send to Process 0.
7085  // Process 2: Post send-size send to Process 0.
7086  //
7087  // All processes: Pack my entries.
7088  //
7089  // Process 1:
7090  // - Post send-data send to Process 0.
7091  // - Wait on my send-size send to Process 0.
7092  //
7093  // Process 0:
7094  // - Print MatrixMarket header.
7095  // - Print my entries.
7096  // - Wait on receive-size receive from Process 1.
7097  // - Post receive-data receive from Process 1.
7098  //
7099  // For each process p = 1, 2, ... numProcs-1:
7100  // If I am Process 0:
7101  // - Post receive-size receive from Process p + 2
7102  // - Wait on receive-size receive from Process p + 1
7103  // - Post receive-data receive from Process p + 1
7104  // - Wait on receive-data receive from Process p
7105  // - Write data from Process p.
7106  // Else if I am Process p:
7107  // - Wait on my send-data send.
7108  // Else if I am Process p+1:
7109  // - Post send-data send to Process 0.
7110  // - Wait on my send-size send.
7111  // Else if I am Process p+2:
7112  // - Post send-size send to Process 0.
7113  //
7114  // Pipelining has three goals here:
7115  // 1. Overlap communication (the receives) with file I/O
7116  // 2. Give Process 0 a chance to prepost some receives,
7117  // before sends show up, by packing local data before
7118  // posting sends
7119  // 3. Don't post _all_ receives or _all_ sends, because that
7120  // wouldn't be memory scalable. (Just because we can't
7121  // see how much memory MPI consumes, doesn't mean that it
7122  // doesn't consume any!)
7123 
7124  // These are used on every process. sendReqSize[0] holds the
7125  // number of rows on this process, and sendReqBuf holds this
7126  // process' data. Process 0 packs into sendReqBuf, but
7127  // doesn't send; it only uses that for printing. All other
7128  // processes send both of these to Process 0.
7129  RCP<CommRequest<int>> sendReqSize, sendReqData;
7130 
7131  // These are used only on Process 0, for received data. Keep
7132  // 3 of each, and treat the arrays as circular buffers. When
7133  // receiving from Process p, the corresponding array index
7134  // here is p % 3.
7135  Array<ArrayRCP<size_t>> recvSizeBufs(3);
7136  Array<ArrayRCP<scalar_type>> recvDataBufs(3);
7137  Array<RCP<CommRequest<int>>> recvSizeReqs(3);
7138  Array<RCP<CommRequest<int>>> recvDataReqs(3);
7139 
7140  // Buffer for nonblocking send of the "send size."
7141  ArrayRCP<size_t> sendDataSize(1);
7142  sendDataSize[0] = myNumRows;
7143 
7144  if (myRank == 0) {
7145  if (debug) {
7146  std::ostringstream os;
7147  os << myRank << ": Post receive-size receives from "
7148  "Procs 1 and 2: tag = "
7149  << sizeTag << endl;
7150  *dbg << os.str();
7151  }
7152  // Process 0: Post receive-size receives from Processes 1 and 2.
7153  recvSizeBufs[0].resize(1);
7154  // Set these three to an invalid value as a flag. If we
7155  // don't get these messages, then the invalid value will
7156  // remain, so we can test for it.
7157  (recvSizeBufs[0])[0] = Teuchos::OrdinalTraits<size_t>::invalid();
7158  recvSizeBufs[1].resize(1);
7159  (recvSizeBufs[1])[0] = Teuchos::OrdinalTraits<size_t>::invalid();
7160  recvSizeBufs[2].resize(1);
7161  (recvSizeBufs[2])[0] = Teuchos::OrdinalTraits<size_t>::invalid();
7162  if (numProcs > 1) {
7163  recvSizeReqs[1] =
7164  ireceive<int, size_t>(recvSizeBufs[1], 1, sizeTag, comm);
7165  }
7166  if (numProcs > 2) {
7167  recvSizeReqs[2] =
7168  ireceive<int, size_t>(recvSizeBufs[2], 2, sizeTag, comm);
7169  }
7170  } else if (myRank == 1 || myRank == 2) {
7171  if (debug) {
7172  std::ostringstream os;
7173  os << myRank << ": Post send-size send: size = "
7174  << sendDataSize[0] << ", tag = " << sizeTag << endl;
7175  *dbg << os.str();
7176  }
7177  // Prime the pipeline by having Processes 1 and 2 start
7178  // their send-size sends. We don't want _all_ the processes
7179  // to start their send-size sends, because that wouldn't be
7180  // memory scalable.
7181  sendReqSize = isend<int, size_t>(sendDataSize, 0, sizeTag, comm);
7182  } else {
7183  if (debug) {
7184  std::ostringstream os;
7185  os << myRank << ": Not posting my send-size send yet" << endl;
7186  *dbg << os.str();
7187  }
7188  }
7189 
7190  //
7191  // Pack my entries, in column-major order.
7192  //
7193  if (debug) {
7194  std::ostringstream os;
7195  os << myRank << ": Pack my entries" << endl;
7196  *dbg << os.str();
7197  }
7198  ArrayRCP<scalar_type> sendDataBuf;
7199  try {
7200  sendDataBuf = arcp<scalar_type>(myNumRows * numCols);
7201  X.get1dCopy(sendDataBuf(), myNumRows);
7202  } catch (std::exception& e) {
7203  lclErr = 1;
7204  if (!err.is_null()) {
7205  std::ostringstream os;
7206  os << "Process " << myRank << ": Attempt to pack my MultiVector "
7207  "entries threw an exception: "
7208  << e.what() << endl;
7209  *err << os.str();
7210  }
7211  }
7212  if (debug) {
7213  std::ostringstream os;
7214  os << myRank << ": Done packing my entries" << endl;
7215  *dbg << os.str();
7216  }
7217 
7218  //
7219  // Process 1: post send-data send to Process 0.
7220  //
7221  if (myRank == 1) {
7222  if (debug) {
7223  *dbg << myRank << ": Post send-data send: tag = " << dataTag
7224  << endl;
7225  }
7226  sendReqData = isend<int, scalar_type>(sendDataBuf, 0, dataTag, comm);
7227  }
7228 
7229  //
7230  // Process 0: Write my entries.
7231  //
7232  if (myRank == 0) {
7233  if (debug) {
7234  std::ostringstream os;
7235  os << myRank << ": Write my entries" << endl;
7236  *dbg << os.str();
7237  }
7238 
7239  // Write Process 0's data to the output stream.
7240  // Matrix Market prints dense matrices in column-major order.
7241  const size_t printNumRows = myNumRows;
7242  ArrayView<const scalar_type> printData = sendDataBuf();
7243  const size_t printStride = printNumRows;
7244  if (static_cast<size_t>(printData.size()) < printStride * numCols) {
7245  lclErr = 1;
7246  if (!err.is_null()) {
7247  std::ostringstream os;
7248  os << "Process " << myRank << ": My MultiVector data's size "
7249  << printData.size() << " does not match my local dimensions "
7250  << printStride << " x " << numCols << "." << endl;
7251  *err << os.str();
7252  }
7253  } else {
7254  // Matrix Market dense format wants one number per line.
7255  // It wants each complex number as two real numbers (real
7256  // resp. imaginary parts) with a space between.
7257  for (size_t col = 0; col < numCols; ++col) {
7258  for (size_t row = 0; row < printNumRows; ++row) {
7259  if (STS::isComplex) {
7260  out << STS::real(printData[row + col * printStride]) << " "
7261  << STS::imag(printData[row + col * printStride]) << endl;
7262  } else {
7263  out << printData[row + col * printStride] << endl;
7264  }
7265  }
7266  }
7267  }
7268  }
7269 
7270  if (myRank == 0) {
7271  // Wait on receive-size receive from Process 1.
7272  const int recvRank = 1;
7273  const int circBufInd = recvRank % 3;
7274  if (debug) {
7275  std::ostringstream os;
7276  os << myRank << ": Wait on receive-size receive from Process "
7277  << recvRank << endl;
7278  *dbg << os.str();
7279  }
7280  if (numProcs > 1) {
7281  wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
7282 
7283  // We received the number of rows of data. (The data
7284  // come in two columns.)
7285  size_t recvNumRows = (recvSizeBufs[circBufInd])[0];
7286  if (recvNumRows == Teuchos::OrdinalTraits<size_t>::invalid()) {
7287  lclErr = 1;
7288  if (!err.is_null()) {
7289  std::ostringstream os;
7290  os << myRank << ": Result of receive-size receive from Process "
7291  << recvRank << " is Teuchos::OrdinalTraits<size_t>::invalid() "
7292  << "= " << Teuchos::OrdinalTraits<size_t>::invalid() << ". "
7293  "This should never happen, and suggests that the receive never "
7294  "got posted. Please report this bug to the Tpetra developers."
7295  << endl;
7296  *err << os.str();
7297  }
7298 
7299  // If we're going to continue after error, set the
7300  // number of rows to receive to a reasonable size. This
7301  // may cause MPI_ERR_TRUNCATE if the sending process is
7302  // sending more than 0 rows, but that's better than MPI
7303  // overflowing due to the huge positive value that is
7304  // Teuchos::OrdinalTraits<size_t>::invalid().
7305  recvNumRows = 0;
7306  }
7307 
7308  // Post receive-data receive from Process 1.
7309  recvDataBufs[circBufInd].resize(recvNumRows * numCols);
7310  if (debug) {
7311  std::ostringstream os;
7312  os << myRank << ": Post receive-data receive from Process "
7313  << recvRank << ": tag = " << dataTag << ", buffer size = "
7314  << recvDataBufs[circBufInd].size() << endl;
7315  *dbg << os.str();
7316  }
7317  if (!recvSizeReqs[circBufInd].is_null()) {
7318  lclErr = 1;
7319  if (!err.is_null()) {
7320  std::ostringstream os;
7321  os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
7322  "null, before posting the receive-data receive from Process "
7323  << recvRank << ". This should never happen. Please report "
7324  "this bug to the Tpetra developers."
7325  << endl;
7326  *err << os.str();
7327  }
7328  }
7329  recvDataReqs[circBufInd] =
7330  ireceive<int, scalar_type>(recvDataBufs[circBufInd],
7331  recvRank, dataTag, comm);
7332  } // numProcs > 1
7333  } else if (myRank == 1) {
7334  // Wait on my send-size send.
7335  if (debug) {
7336  std::ostringstream os;
7337  os << myRank << ": Wait on my send-size send" << endl;
7338  *dbg << os.str();
7339  }
7340  wait<int>(comm, outArg(sendReqSize));
7341  }
7342 
7343  //
7344  // Pipeline loop
7345  //
7346  for (int p = 1; p < numProcs; ++p) {
7347  if (myRank == 0) {
7348  if (p + 2 < numProcs) {
7349  // Post receive-size receive from Process p + 2.
7350  const int recvRank = p + 2;
7351  const int circBufInd = recvRank % 3;
7352  if (debug) {
7353  std::ostringstream os;
7354  os << myRank << ": Post receive-size receive from Process "
7355  << recvRank << ": tag = " << sizeTag << endl;
7356  *dbg << os.str();
7357  }
7358  if (!recvSizeReqs[circBufInd].is_null()) {
7359  lclErr = 1;
7360  if (!err.is_null()) {
7361  std::ostringstream os;
7362  os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
7363  << "null, for the receive-size receive from Process "
7364  << recvRank << "! This may mean that this process never "
7365  << "finished waiting for the receive from Process "
7366  << (recvRank - 3) << "." << endl;
7367  *err << os.str();
7368  }
7369  }
7370  recvSizeReqs[circBufInd] =
7371  ireceive<int, size_t>(recvSizeBufs[circBufInd],
7372  recvRank, sizeTag, comm);
7373  }
7374 
7375  if (p + 1 < numProcs) {
7376  const int recvRank = p + 1;
7377  const int circBufInd = recvRank % 3;
7378 
7379  // Wait on receive-size receive from Process p + 1.
7380  if (debug) {
7381  std::ostringstream os;
7382  os << myRank << ": Wait on receive-size receive from Process "
7383  << recvRank << endl;
7384  *dbg << os.str();
7385  }
7386  wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
7387 
7388  // We received the number of rows of data. (The data
7389  // come in two columns.)
7390  size_t recvNumRows = (recvSizeBufs[circBufInd])[0];
7391  if (recvNumRows == Teuchos::OrdinalTraits<size_t>::invalid()) {
7392  lclErr = 1;
7393  if (!err.is_null()) {
7394  std::ostringstream os;
7395  os << myRank << ": Result of receive-size receive from Process "
7396  << recvRank << " is Teuchos::OrdinalTraits<size_t>::invalid() "
7397  << "= " << Teuchos::OrdinalTraits<size_t>::invalid() << ". "
7398  "This should never happen, and suggests that the receive never "
7399  "got posted. Please report this bug to the Tpetra developers."
7400  << endl;
7401  *err << os.str();
7402  }
7403  // If we're going to continue after error, set the
7404  // number of rows to receive to a reasonable size.
7405  // This may cause MPI_ERR_TRUNCATE if the sending
7406  // process sends more than 0 rows, but that's better
7407  // than MPI overflowing due to the huge positive value
7408  // Teuchos::OrdinalTraits<size_t>::invalid().
7409  recvNumRows = 0;
7410  }
7411 
7412  // Post receive-data receive from Process p + 1.
7413  recvDataBufs[circBufInd].resize(recvNumRows * numCols);
7414  if (debug) {
7415  std::ostringstream os;
7416  os << myRank << ": Post receive-data receive from Process "
7417  << recvRank << ": tag = " << dataTag << ", buffer size = "
7418  << recvDataBufs[circBufInd].size() << endl;
7419  *dbg << os.str();
7420  }
7421  if (!recvDataReqs[circBufInd].is_null()) {
7422  lclErr = 1;
7423  if (!err.is_null()) {
7424  std::ostringstream os;
7425  os << myRank << ": recvDataReqs[" << circBufInd << "] is not "
7426  << "null, for the receive-data receive from Process "
7427  << recvRank << "! This may mean that this process never "
7428  << "finished waiting for the receive from Process "
7429  << (recvRank - 3) << "." << endl;
7430  *err << os.str();
7431  }
7432  }
7433  recvDataReqs[circBufInd] =
7434  ireceive<int, scalar_type>(recvDataBufs[circBufInd],
7435  recvRank, dataTag, comm);
7436  }
7437 
7438  // Wait on receive-data receive from Process p.
7439  const int recvRank = p;
7440  const int circBufInd = recvRank % 3;
7441  if (debug) {
7442  std::ostringstream os;
7443  os << myRank << ": Wait on receive-data receive from Process "
7444  << recvRank << endl;
7445  *dbg << os.str();
7446  }
7447  wait<int>(comm, outArg(recvDataReqs[circBufInd]));
7448 
7449  // Write Process p's data. Number of rows lives in
7450  // recvSizeBufs[circBufInd], and the actual data live in
7451  // recvDataBufs[circBufInd]. Do this after posting receives,
7452  // in order to expose overlap of comm. with file I/O.
7453  if (debug) {
7454  std::ostringstream os;
7455  os << myRank << ": Write entries from Process " << recvRank
7456  << endl;
7457  *dbg << os.str() << endl;
7458  }
7459  size_t printNumRows = (recvSizeBufs[circBufInd])[0];
7460  if (printNumRows == Teuchos::OrdinalTraits<size_t>::invalid()) {
7461  lclErr = 1;
7462  if (!err.is_null()) {
7463  std::ostringstream os;
7464  os << myRank << ": Result of receive-size receive from Process "
7465  << recvRank << " was Teuchos::OrdinalTraits<size_t>::"
7466  "invalid() = "
7467  << Teuchos::OrdinalTraits<size_t>::invalid()
7468  << ". This should never happen, and suggests that its "
7469  "receive-size receive was never posted. "
7470  "Please report this bug to the Tpetra developers."
7471  << endl;
7472  *err << os.str();
7473  }
7474  // If we're going to continue after error, set the
7475  // number of rows to print to a reasonable size.
7476  printNumRows = 0;
7477  }
7478  if (printNumRows > 0 && recvDataBufs[circBufInd].is_null()) {
7479  lclErr = 1;
7480  if (!err.is_null()) {
7481  std::ostringstream os;
7482  os << myRank << ": Result of receive-size receive from Proc "
7483  << recvRank << " was " << printNumRows << " > 0, but "
7484  "recvDataBufs["
7485  << circBufInd << "] is null. This should "
7486  "never happen. Please report this bug to the Tpetra "
7487  "developers."
7488  << endl;
7489  *err << os.str();
7490  }
7491  // If we're going to continue after error, set the
7492  // number of rows to print to a reasonable size.
7493  printNumRows = 0;
7494  }
7495 
7496  // Write the received data to the output stream.
7497  // Matrix Market prints dense matrices in column-major order.
7498  ArrayView<const scalar_type> printData = (recvDataBufs[circBufInd])();
7499  const size_t printStride = printNumRows;
7500  // Matrix Market dense format wants one number per line.
7501  // It wants each complex number as two real numbers (real
7502  // resp. imaginary parts) with a space between.
7503  for (size_t col = 0; col < numCols; ++col) {
7504  for (size_t row = 0; row < printNumRows; ++row) {
7505  if (STS::isComplex) {
7506  out << STS::real(printData[row + col * printStride]) << " "
7507  << STS::imag(printData[row + col * printStride]) << endl;
7508  } else {
7509  out << printData[row + col * printStride] << endl;
7510  }
7511  }
7512  }
7513  } else if (myRank == p) { // Process p
7514  // Wait on my send-data send.
7515  if (debug) {
7516  std::ostringstream os;
7517  os << myRank << ": Wait on my send-data send" << endl;
7518  *dbg << os.str();
7519  }
7520  wait<int>(comm, outArg(sendReqData));
7521  } else if (myRank == p + 1) { // Process p + 1
7522  // Post send-data send to Process 0.
7523  if (debug) {
7524  std::ostringstream os;
7525  os << myRank << ": Post send-data send: tag = " << dataTag
7526  << endl;
7527  *dbg << os.str();
7528  }
7529  sendReqData = isend<int, scalar_type>(sendDataBuf, 0, dataTag, comm);
7530  // Wait on my send-size send.
7531  if (debug) {
7532  std::ostringstream os;
7533  os << myRank << ": Wait on my send-size send" << endl;
7534  *dbg << os.str();
7535  }
7536  wait<int>(comm, outArg(sendReqSize));
7537  } else if (myRank == p + 2) { // Process p + 2
7538  // Post send-size send to Process 0.
7539  if (debug) {
7540  std::ostringstream os;
7541  os << myRank << ": Post send-size send: size = "
7542  << sendDataSize[0] << ", tag = " << sizeTag << endl;
7543  *dbg << os.str();
7544  }
7545  sendReqSize = isend<int, size_t>(sendDataSize, 0, sizeTag, comm);
7546  }
7547  }
7548 
7549  // Establish global agreement on the error state.
7550  reduceAll<int, int>(comm, REDUCE_MAX, lclErr, outArg(gblErr));
7551  TEUCHOS_TEST_FOR_EXCEPTION(
7552  gblErr == 1, std::runtime_error,
7553  "Tpetra::MatrixMarket::writeDense "
7554  "experienced some kind of error and was unable to complete.");
7555 
7556  if (debug) {
7557  dbg->popTab();
7558  *dbg << myRank << ": writeDenseColumn: Done" << endl;
7559  dbg->popTab();
7560  }
7561  }
7562 
7563  public:
7569  static void
7570  writeDense(std::ostream& out,
7571  const Teuchos::RCP<const multivector_type>& X,
7572  const std::string& matrixName,
7573  const std::string& matrixDescription,
7574  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
7575  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
7576  TEUCHOS_TEST_FOR_EXCEPTION(
7577  X.is_null(), std::invalid_argument,
7578  "Tpetra::MatrixMarket::"
7579  "writeDense: The input MultiVector X is null.");
7580  writeDense(out, *X, matrixName, matrixDescription, err, dbg);
7581  }
7582 
7588  static void
7589  writeDense(std::ostream& out,
7590  const multivector_type& X,
7591  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
7592  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
7593  writeDense(out, X, "", "", err, dbg);
7594  }
7595 
7601  static void
7602  writeDense(std::ostream& out,
7603  const Teuchos::RCP<const multivector_type>& X,
7604  const Teuchos::RCP<Teuchos::FancyOStream>& err = Teuchos::null,
7605  const Teuchos::RCP<Teuchos::FancyOStream>& dbg = Teuchos::null) {
7606  TEUCHOS_TEST_FOR_EXCEPTION(
7607  X.is_null(), std::invalid_argument,
7608  "Tpetra::MatrixMarket::"
7609  "writeDense: The input MultiVector X is null.");
7610  writeDense(out, *X, "", "", err, dbg);
7611  }
7612 
7632  static void
7633  writeMap(std::ostream& out, const map_type& map, const bool debug = false) {
7634  Teuchos::RCP<Teuchos::FancyOStream> err =
7635  Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
7636  writeMap(out, map, err, debug);
7637  }
7638 
7647  static void
7648  writeMap(std::ostream& out,
7649  const map_type& map,
7650  const Teuchos::RCP<Teuchos::FancyOStream>& err,
7651  const bool debug = false) {
7652  using std::endl;
7653  using Teuchos::Array;
7654  using Teuchos::ArrayRCP;
7655  using Teuchos::ArrayView;
7656  using Teuchos::Comm;
7657  using Teuchos::CommRequest;
7658  using Teuchos::ireceive;
7659  using Teuchos::isend;
7660  using Teuchos::RCP;
7661  using Teuchos::TypeNameTraits;
7662  using Teuchos::wait;
7663  typedef global_ordinal_type GO;
7664  typedef int pid_type;
7665 
7666  // Treat the Map as a 1-column "multivector." This differs
7667  // from the previous two-column format, in which column 0 held
7668  // the GIDs, and column 1 held the corresponding PIDs. It
7669  // differs because printing that format requires knowing the
7670  // entire first column -- that is, all the GIDs -- in advance.
7671  // Sending messages from each process one at a time saves
7672  // memory, but it means that Process 0 doesn't ever have all
7673  // the GIDs at once.
7674  //
7675  // We pack the entries as ptrdiff_t, since this should be the
7676  // biggest signed built-in integer type that can hold any GO
7677  // or pid_type (= int) quantity without overflow. Test this
7678  // assumption at run time.
7679  typedef ptrdiff_t int_type;
7680  TEUCHOS_TEST_FOR_EXCEPTION(
7681  sizeof(GO) > sizeof(int_type), std::logic_error,
7682  "The global ordinal type GO=" << TypeNameTraits<GO>::name()
7683  << " is too big for ptrdiff_t. sizeof(GO) = " << sizeof(GO)
7684  << " > sizeof(ptrdiff_t) = " << sizeof(ptrdiff_t) << ".");
7685  TEUCHOS_TEST_FOR_EXCEPTION(
7686  sizeof(pid_type) > sizeof(int_type), std::logic_error,
7687  "The (MPI) process rank type pid_type=" << TypeNameTraits<pid_type>::name() << " is too big for ptrdiff_t. "
7688  "sizeof(pid_type) = "
7689  << sizeof(pid_type) << " > sizeof(ptrdiff_t)"
7690  " = "
7691  << sizeof(ptrdiff_t) << ".");
7692 
7693  const Comm<int>& comm = *(map.getComm());
7694  const int myRank = comm.getRank();
7695  const int numProcs = comm.getSize();
7696 
7697  if (!err.is_null()) {
7698  err->pushTab();
7699  }
7700  if (debug) {
7701  std::ostringstream os;
7702  os << myRank << ": writeMap" << endl;
7703  *err << os.str();
7704  }
7705  if (!err.is_null()) {
7706  err->pushTab();
7707  }
7708 
7709  const size_t myNumRows = map.getLocalNumElements();
7710  // Use a different tag for the "size" messages than for the
7711  // "data" messages, in order to help us debug any mix-ups.
7712  const int sizeTag = 1337;
7713  const int dataTag = 1338;
7714 
7715  // Process 0 pipelines nonblocking receives with file output.
7716  //
7717  // Constraints:
7718  // - Process 0 can't post a receive for another process'
7719  // actual data, until it posts and waits on the receive
7720  // from that process with the amount of data to receive.
7721  // (We could just post receives with a max data size, but
7722  // I feel uncomfortable about that.)
7723  // - The C++ standard library doesn't allow nonblocking
7724  // output to an std::ostream.
7725  //
7726  // Process 0: Post receive-size receives from Processes 1 and 2.
7727  // Process 1: Post send-size send to Process 0.
7728  // Process 2: Post send-size send to Process 0.
7729  //
7730  // All processes: Pack my GIDs and PIDs.
7731  //
7732  // Process 1:
7733  // - Post send-data send to Process 0.
7734  // - Wait on my send-size send to Process 0.
7735  //
7736  // Process 0:
7737  // - Print MatrixMarket header.
7738  // - Print my GIDs and PIDs.
7739  // - Wait on receive-size receive from Process 1.
7740  // - Post receive-data receive from Process 1.
7741  //
7742  // For each process p = 1, 2, ... numProcs-1:
7743  // If I am Process 0:
7744  // - Post receive-size receive from Process p + 2
7745  // - Wait on receive-size receive from Process p + 1
7746  // - Post receive-data receive from Process p + 1
7747  // - Wait on receive-data receive from Process p
7748  // - Write data from Process p.
7749  // Else if I am Process p:
7750  // - Wait on my send-data send.
7751  // Else if I am Process p+1:
7752  // - Post send-data send to Process 0.
7753  // - Wait on my send-size send.
7754  // Else if I am Process p+2:
7755  // - Post send-size send to Process 0.
7756  //
7757  // Pipelining has three goals here:
7758  // 1. Overlap communication (the receives) with file I/O
7759  // 2. Give Process 0 a chance to prepost some receives,
7760  // before sends show up, by packing local data before
7761  // posting sends
7762  // 3. Don't post _all_ receives or _all_ sends, because that
7763  // wouldn't be memory scalable. (Just because we can't
7764  // see how much memory MPI consumes, doesn't mean that it
7765  // doesn't consume any!)
7766 
7767  // These are used on every process. sendReqSize[0] holds the
7768  // number of rows on this process, and sendReqBuf holds this
7769  // process' data. Process 0 packs into sendReqBuf, but
7770  // doesn't send; it only uses that for printing. All other
7771  // processes send both of these to Process 0.
7772  RCP<CommRequest<int>> sendReqSize, sendReqData;
7773 
7774  // These are used only on Process 0, for received data. Keep
7775  // 3 of each, and treat the arrays as circular buffers. When
7776  // receiving from Process p, the corresponding array index
7777  // here is p % 3.
7778  Array<ArrayRCP<int_type>> recvSizeBufs(3);
7779  Array<ArrayRCP<int_type>> recvDataBufs(3);
7780  Array<RCP<CommRequest<int>>> recvSizeReqs(3);
7781  Array<RCP<CommRequest<int>>> recvDataReqs(3);
7782 
7783  // Buffer for nonblocking send of the "send size."
7784  ArrayRCP<int_type> sendDataSize(1);
7785  sendDataSize[0] = myNumRows;
7786 
7787  if (myRank == 0) {
7788  if (debug) {
7789  std::ostringstream os;
7790  os << myRank << ": Post receive-size receives from "
7791  "Procs 1 and 2: tag = "
7792  << sizeTag << endl;
7793  *err << os.str();
7794  }
7795  // Process 0: Post receive-size receives from Processes 1 and 2.
7796  recvSizeBufs[0].resize(1);
7797  (recvSizeBufs[0])[0] = -1; // error flag
7798  recvSizeBufs[1].resize(1);
7799  (recvSizeBufs[1])[0] = -1; // error flag
7800  recvSizeBufs[2].resize(1);
7801  (recvSizeBufs[2])[0] = -1; // error flag
7802  if (numProcs > 1) {
7803  recvSizeReqs[1] =
7804  ireceive<int, int_type>(recvSizeBufs[1], 1, sizeTag, comm);
7805  }
7806  if (numProcs > 2) {
7807  recvSizeReqs[2] =
7808  ireceive<int, int_type>(recvSizeBufs[2], 2, sizeTag, comm);
7809  }
7810  } else if (myRank == 1 || myRank == 2) {
7811  if (debug) {
7812  std::ostringstream os;
7813  os << myRank << ": Post send-size send: size = "
7814  << sendDataSize[0] << ", tag = " << sizeTag << endl;
7815  *err << os.str();
7816  }
7817  // Prime the pipeline by having Processes 1 and 2 start
7818  // their send-size sends. We don't want _all_ the processes
7819  // to start their send-size sends, because that wouldn't be
7820  // memory scalable.
7821  sendReqSize = isend<int, int_type>(sendDataSize, 0, sizeTag, comm);
7822  } else {
7823  if (debug) {
7824  std::ostringstream os;
7825  os << myRank << ": Not posting my send-size send yet" << endl;
7826  *err << os.str();
7827  }
7828  }
7829 
7830  //
7831  // Pack my GIDs and PIDs. Each (GID,PID) pair gets packed
7832  // consecutively, for better locality.
7833  //
7834 
7835  if (debug) {
7836  std::ostringstream os;
7837  os << myRank << ": Pack my GIDs and PIDs" << endl;
7838  *err << os.str();
7839  }
7840 
7841  ArrayRCP<int_type> sendDataBuf(myNumRows * 2);
7842 
7843  if (map.isContiguous()) {
7844  const int_type myMinGblIdx =
7845  static_cast<int_type>(map.getMinGlobalIndex());
7846  for (size_t k = 0; k < myNumRows; ++k) {
7847  const int_type gid = myMinGblIdx + static_cast<int_type>(k);
7848  const int_type pid = static_cast<int_type>(myRank);
7849  sendDataBuf[2 * k] = gid;
7850  sendDataBuf[2 * k + 1] = pid;
7851  }
7852  } else {
7853  ArrayView<const GO> myGblInds = map.getLocalElementList();
7854  for (size_t k = 0; k < myNumRows; ++k) {
7855  const int_type gid = static_cast<int_type>(myGblInds[k]);
7856  const int_type pid = static_cast<int_type>(myRank);
7857  sendDataBuf[2 * k] = gid;
7858  sendDataBuf[2 * k + 1] = pid;
7859  }
7860  }
7861 
7862  if (debug) {
7863  std::ostringstream os;
7864  os << myRank << ": Done packing my GIDs and PIDs" << endl;
7865  *err << os.str();
7866  }
7867 
7868  if (myRank == 1) {
7869  // Process 1: post send-data send to Process 0.
7870  if (debug) {
7871  *err << myRank << ": Post send-data send: tag = " << dataTag
7872  << endl;
7873  }
7874  sendReqData = isend<int, int_type>(sendDataBuf, 0, dataTag, comm);
7875  }
7876 
7877  if (myRank == 0) {
7878  if (debug) {
7879  *err << myRank << ": Write MatrixMarket header" << endl;
7880  }
7881 
7882  // Process 0: Write the MatrixMarket header.
7883  // Description section explains each column.
7884  std::ostringstream hdr;
7885 
7886  // Print the Matrix Market header. MultiVector stores data
7887  // nonsymmetrically, hence "general" in the banner line.
7888  hdr << "%%MatrixMarket matrix array integer general" << endl
7889  << "% Format: Version 2.0" << endl
7890  << "%" << endl
7891  << "% This file encodes a Tpetra::Map." << endl
7892  << "% It is stored as a dense vector, with twice as many " << endl
7893  << "% entries as the global number of GIDs (global indices)." << endl
7894  << "% (GID, PID) pairs are stored contiguously, where the PID " << endl
7895  << "% is the rank of the process owning that GID." << endl
7896  << (2 * map.getGlobalNumElements()) << " " << 1 << endl;
7897  out << hdr.str();
7898 
7899  if (debug) {
7900  std::ostringstream os;
7901  os << myRank << ": Write my GIDs and PIDs" << endl;
7902  *err << os.str();
7903  }
7904 
7905  // Write Process 0's data to the output stream.
7906  // Matrix Market prints dense matrices in column-major order.
7907  const int_type printNumRows = myNumRows;
7908  ArrayView<const int_type> printData = sendDataBuf();
7909  for (int_type k = 0; k < printNumRows; ++k) {
7910  const int_type gid = printData[2 * k];
7911  const int_type pid = printData[2 * k + 1];
7912  out << gid << endl
7913  << pid << endl;
7914  }
7915  }
7916 
7917  if (myRank == 0) {
7918  // Wait on receive-size receive from Process 1.
7919  const int recvRank = 1;
7920  const int circBufInd = recvRank % 3;
7921  if (debug) {
7922  std::ostringstream os;
7923  os << myRank << ": Wait on receive-size receive from Process "
7924  << recvRank << endl;
7925  *err << os.str();
7926  }
7927  if (numProcs > 1) {
7928  wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
7929 
7930  // We received the number of rows of data. (The data
7931  // come in two columns.)
7932  const int_type recvNumRows = (recvSizeBufs[circBufInd])[0];
7933  if (debug && recvNumRows == -1) {
7934  std::ostringstream os;
7935  os << myRank << ": Result of receive-size receive from Process "
7936  << recvRank << " is -1. This should never happen, and "
7937  "suggests that the receive never got posted. Please report "
7938  "this bug to the Tpetra developers."
7939  << endl;
7940  *err << os.str();
7941  }
7942 
7943  // Post receive-data receive from Process 1.
7944  recvDataBufs[circBufInd].resize(recvNumRows * 2);
7945  if (debug) {
7946  std::ostringstream os;
7947  os << myRank << ": Post receive-data receive from Process "
7948  << recvRank << ": tag = " << dataTag << ", buffer size = "
7949  << recvDataBufs[circBufInd].size() << endl;
7950  *err << os.str();
7951  }
7952  if (!recvSizeReqs[circBufInd].is_null()) {
7953  std::ostringstream os;
7954  os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
7955  "null, before posting the receive-data receive from Process "
7956  << recvRank << ". This should never happen. Please report "
7957  "this bug to the Tpetra developers."
7958  << endl;
7959  *err << os.str();
7960  }
7961  recvDataReqs[circBufInd] =
7962  ireceive<int, int_type>(recvDataBufs[circBufInd],
7963  recvRank, dataTag, comm);
7964  } // numProcs > 1
7965  } else if (myRank == 1) {
7966  // Wait on my send-size send.
7967  if (debug) {
7968  std::ostringstream os;
7969  os << myRank << ": Wait on my send-size send" << endl;
7970  *err << os.str();
7971  }
7972  wait<int>(comm, outArg(sendReqSize));
7973  }
7974 
7975  //
7976  // Pipeline loop
7977  //
7978  for (int p = 1; p < numProcs; ++p) {
7979  if (myRank == 0) {
7980  if (p + 2 < numProcs) {
7981  // Post receive-size receive from Process p + 2.
7982  const int recvRank = p + 2;
7983  const int circBufInd = recvRank % 3;
7984  if (debug) {
7985  std::ostringstream os;
7986  os << myRank << ": Post receive-size receive from Process "
7987  << recvRank << ": tag = " << sizeTag << endl;
7988  *err << os.str();
7989  }
7990  if (!recvSizeReqs[circBufInd].is_null()) {
7991  std::ostringstream os;
7992  os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
7993  << "null, for the receive-size receive from Process "
7994  << recvRank << "! This may mean that this process never "
7995  << "finished waiting for the receive from Process "
7996  << (recvRank - 3) << "." << endl;
7997  *err << os.str();
7998  }
7999  recvSizeReqs[circBufInd] =
8000  ireceive<int, int_type>(recvSizeBufs[circBufInd],
8001  recvRank, sizeTag, comm);
8002  }
8003 
8004  if (p + 1 < numProcs) {
8005  const int recvRank = p + 1;
8006  const int circBufInd = recvRank % 3;
8007 
8008  // Wait on receive-size receive from Process p + 1.
8009  if (debug) {
8010  std::ostringstream os;
8011  os << myRank << ": Wait on receive-size receive from Process "
8012  << recvRank << endl;
8013  *err << os.str();
8014  }
8015  wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
8016 
8017  // We received the number of rows of data. (The data
8018  // come in two columns.)
8019  const int_type recvNumRows = (recvSizeBufs[circBufInd])[0];
8020  if (debug && recvNumRows == -1) {
8021  std::ostringstream os;
8022  os << myRank << ": Result of receive-size receive from Process "
8023  << recvRank << " is -1. This should never happen, and "
8024  "suggests that the receive never got posted. Please report "
8025  "this bug to the Tpetra developers."
8026  << endl;
8027  *err << os.str();
8028  }
8029 
8030  // Post receive-data receive from Process p + 1.
8031  recvDataBufs[circBufInd].resize(recvNumRows * 2);
8032  if (debug) {
8033  std::ostringstream os;
8034  os << myRank << ": Post receive-data receive from Process "
8035  << recvRank << ": tag = " << dataTag << ", buffer size = "
8036  << recvDataBufs[circBufInd].size() << endl;
8037  *err << os.str();
8038  }
8039  if (!recvDataReqs[circBufInd].is_null()) {
8040  std::ostringstream os;
8041  os << myRank << ": recvDataReqs[" << circBufInd << "] is not "
8042  << "null, for the receive-data receive from Process "
8043  << recvRank << "! This may mean that this process never "
8044  << "finished waiting for the receive from Process "
8045  << (recvRank - 3) << "." << endl;
8046  *err << os.str();
8047  }
8048  recvDataReqs[circBufInd] =
8049  ireceive<int, int_type>(recvDataBufs[circBufInd],
8050  recvRank, dataTag, comm);
8051  }
8052 
8053  // Wait on receive-data receive from Process p.
8054  const int recvRank = p;
8055  const int circBufInd = recvRank % 3;
8056  if (debug) {
8057  std::ostringstream os;
8058  os << myRank << ": Wait on receive-data receive from Process "
8059  << recvRank << endl;
8060  *err << os.str();
8061  }
8062  wait<int>(comm, outArg(recvDataReqs[circBufInd]));
8063 
8064  // Write Process p's data. Number of rows lives in
8065  // recvSizeBufs[circBufInd], and the actual data live in
8066  // recvDataBufs[circBufInd]. Do this after posting receives,
8067  // in order to expose overlap of comm. with file I/O.
8068  if (debug) {
8069  std::ostringstream os;
8070  os << myRank << ": Write GIDs and PIDs from Process "
8071  << recvRank << endl;
8072  *err << os.str() << endl;
8073  }
8074  const int_type printNumRows = (recvSizeBufs[circBufInd])[0];
8075  if (debug && printNumRows == -1) {
8076  std::ostringstream os;
8077  os << myRank << ": Result of receive-size receive from Process "
8078  << recvRank << " was -1. This should never happen, and "
8079  "suggests that its receive-size receive was never posted. "
8080  "Please report this bug to the Tpetra developers."
8081  << endl;
8082  *err << os.str();
8083  }
8084  if (debug && printNumRows > 0 && recvDataBufs[circBufInd].is_null()) {
8085  std::ostringstream os;
8086  os << myRank << ": Result of receive-size receive from Proc "
8087  << recvRank << " was " << printNumRows << " > 0, but "
8088  "recvDataBufs["
8089  << circBufInd << "] is null. This should "
8090  "never happen. Please report this bug to the Tpetra "
8091  "developers."
8092  << endl;
8093  *err << os.str();
8094  }
8095  ArrayView<const int_type> printData = (recvDataBufs[circBufInd])();
8096  for (int_type k = 0; k < printNumRows; ++k) {
8097  const int_type gid = printData[2 * k];
8098  const int_type pid = printData[2 * k + 1];
8099  out << gid << endl
8100  << pid << endl;
8101  }
8102  } else if (myRank == p) { // Process p
8103  // Wait on my send-data send.
8104  if (debug) {
8105  std::ostringstream os;
8106  os << myRank << ": Wait on my send-data send" << endl;
8107  *err << os.str();
8108  }
8109  wait<int>(comm, outArg(sendReqData));
8110  } else if (myRank == p + 1) { // Process p + 1
8111  // Post send-data send to Process 0.
8112  if (debug) {
8113  std::ostringstream os;
8114  os << myRank << ": Post send-data send: tag = " << dataTag
8115  << endl;
8116  *err << os.str();
8117  }
8118  sendReqData = isend<int, int_type>(sendDataBuf, 0, dataTag, comm);
8119  // Wait on my send-size send.
8120  if (debug) {
8121  std::ostringstream os;
8122  os << myRank << ": Wait on my send-size send" << endl;
8123  *err << os.str();
8124  }
8125  wait<int>(comm, outArg(sendReqSize));
8126  } else if (myRank == p + 2) { // Process p + 2
8127  // Post send-size send to Process 0.
8128  if (debug) {
8129  std::ostringstream os;
8130  os << myRank << ": Post send-size send: size = "
8131  << sendDataSize[0] << ", tag = " << sizeTag << endl;
8132  *err << os.str();
8133  }
8134  sendReqSize = isend<int, int_type>(sendDataSize, 0, sizeTag, comm);
8135  }
8136  }
8137 
8138  if (!err.is_null()) {
8139  err->popTab();
8140  }
8141  if (debug) {
8142  *err << myRank << ": writeMap: Done" << endl;
8143  }
8144  if (!err.is_null()) {
8145  err->popTab();
8146  }
8147  }
8148 
8150  static void
8151  writeMapFile(const std::string& filename,
8152  const map_type& map) {
8153  const int myRank = map.getComm()->getRank();
8154 
8155  auto out = Writer::openOutFileOnRankZero(map.getComm(), filename, myRank, true);
8156 
8157  writeMap(out, map);
8158  // We can rely on the destructor of the output stream to close
8159  // the file on scope exit, even if writeDense() throws an
8160  // exception.
8161  }
8162 
8163  private:
8187  static void
8188  printAsComment(std::ostream& out, const std::string& str) {
8189  using std::endl;
8190  std::istringstream inpstream(str);
8191  std::string line;
8192 
8193  while (getline(inpstream, line)) {
8194  if (!line.empty()) {
8195  // Note that getline() doesn't store '\n', so we have to
8196  // append the endline ourselves.
8197  if (line[0] == '%') { // Line starts with a comment character.
8198  out << line << endl;
8199  } else { // Line doesn't start with a comment character.
8200  out << "%% " << line << endl;
8201  }
8202  }
8203  }
8204  }
8205 
8206  public:
8225  static void
8226  writeOperator(const std::string& fileName, operator_type const& A) {
8227  Teuchos::ParameterList pl;
8228  writeOperator(fileName, A, pl);
8229  }
8230 
8251  static void
8252  writeOperator(std::ostream& out, const operator_type& A) {
8253  Teuchos::ParameterList pl;
8254  writeOperator(out, A, pl);
8255  }
8256 
8293  static void
8294  writeOperator(const std::string& fileName,
8295  const operator_type& A,
8296  const Teuchos::ParameterList& params) {
8297  std::ofstream out;
8298  std::string tmpFile = "__TMP__" + fileName;
8299  const int myRank = A.getDomainMap()->getComm()->getRank();
8300  bool precisionChanged = false;
8301  int oldPrecision;
8302  // The number of nonzero entries in a Tpetra::Operator is
8303  // unknown until probing is completed. In order to write a
8304  // MatrixMarket header, we write the matrix to a temporary
8305  // file.
8306  //
8307  // FIXME (mfh 23 May 2015) IT WASN'T MY IDEA TO WRITE TO A
8308  // TEMPORARY FILE.
8309  if (myRank == 0) {
8310  if (std::ifstream(tmpFile))
8311  TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error,
8312  "writeOperator: temporary file " << tmpFile << " already exists");
8313  out.open(tmpFile.c_str());
8314  if (params.isParameter("precision")) {
8315  oldPrecision = out.precision(params.get<int>("precision"));
8316  precisionChanged = true;
8317  }
8318  }
8319 
8320  const std::string header = writeOperatorImpl(out, A, params);
8321 
8322  if (myRank == 0) {
8323  if (precisionChanged)
8324  out.precision(oldPrecision);
8325  out.close();
8326  out.open(fileName.c_str(), std::ios::binary);
8327  bool printMatrixMarketHeader = true;
8328  if (params.isParameter("print MatrixMarket header"))
8329  printMatrixMarketHeader = params.get<bool>("print MatrixMarket header");
8330  if (printMatrixMarketHeader && myRank == 0) {
8331  // Write header to final file.
8332  out << header;
8333  }
8334  // Append matrix from temporary to final file.
8335  std::ifstream src(tmpFile, std::ios_base::binary);
8336  out << src.rdbuf();
8337  src.close();
8338  // Delete the temporary file.
8339  remove(tmpFile.c_str());
8340  }
8341  }
8342 
8381  static void
8382  writeOperator(std::ostream& out,
8383  const operator_type& A,
8384  const Teuchos::ParameterList& params) {
8385  const int myRank = A.getDomainMap()->getComm()->getRank();
8386 
8387  // The number of nonzero entries in a Tpetra::Operator is
8388  // unknown until probing is completed. In order to write a
8389  // MatrixMarket header, we write the matrix to a temporary
8390  // output stream.
8391  //
8392  // NOTE (mfh 23 May 2015): Writing to a temporary output
8393  // stream may double the memory usage, depending on whether
8394  // 'out' is a file stream or an in-memory output stream (e.g.,
8395  // std::ostringstream). It might be wise to use a temporary
8396  // file instead. However, please look carefully at POSIX
8397  // functions for safe creation of temporary files. Don't just
8398  // prepend "__TMP__" to the filename and hope for the best.
8399  // Furthermore, it should be valid to call the std::ostream
8400  // overload of this method even when Process 0 does not have
8401  // access to a file system.
8402  std::ostringstream tmpOut;
8403  if (myRank == 0) {
8404  if (params.isParameter("precision") && params.isType<int>("precision")) {
8405  (void)tmpOut.precision(params.get<int>("precision"));
8406  }
8407  }
8408 
8409  const std::string header = writeOperatorImpl(tmpOut, A, params);
8410 
8411  if (myRank == 0) {
8412  bool printMatrixMarketHeader = true;
8413  if (params.isParameter("print MatrixMarket header") &&
8414  params.isType<bool>("print MatrixMarket header")) {
8415  printMatrixMarketHeader = params.get<bool>("print MatrixMarket header");
8416  }
8417  if (printMatrixMarketHeader && myRank == 0) {
8418  out << header; // write header to final output stream
8419  }
8420  // Append matrix from temporary output stream to final output stream.
8421  //
8422  // NOTE (mfh 23 May 2015) This might use a lot of memory.
8423  // However, we should not use temporary files in this
8424  // method. Since it does not access the file system (unlike
8425  // the overload that takes a file name), it should not
8426  // require the file system at all.
8427  //
8428  // If memory usage becomes a problem, one thing we could do
8429  // is write the entries of the Operator one column (or a few
8430  // columns) at a time. The Matrix Market sparse format does
8431  // not impose an order on its entries, so it would be OK to
8432  // write them in that order.
8433  out << tmpOut.str();
8434  }
8435  }
8436 
8437  private:
8445  static std::string
8446  writeOperatorImpl(std::ostream& os,
8447  const operator_type& A,
8448  const Teuchos::ParameterList& params) {
8449  using Teuchos::Array;
8450  using Teuchos::ArrayRCP;
8451  using Teuchos::RCP;
8452  using Teuchos::rcp;
8453 
8454  typedef local_ordinal_type LO;
8455  typedef global_ordinal_type GO;
8456  typedef scalar_type Scalar;
8457  typedef Teuchos::OrdinalTraits<LO> TLOT;
8458  typedef Teuchos::OrdinalTraits<GO> TGOT;
8459  typedef Tpetra::Import<LO, GO, node_type> import_type;
8460  typedef Tpetra::MultiVector<GO, LO, GO, node_type> mv_type_go;
8461 
8462  const map_type& domainMap = *(A.getDomainMap());
8463  RCP<const map_type> rangeMap = A.getRangeMap();
8464  trcp_tcomm_t comm = rangeMap->getComm();
8465  const int myRank = comm->getRank();
8466  const size_t numProcs = comm->getSize();
8467 
8468  size_t numMVs = 10;
8469  if (params.isParameter("probing size"))
8470  numMVs = params.get<int>("probing size");
8471 
8472  GO globalNnz = 0;
8473  GO minColGid = domainMap.getMinAllGlobalIndex();
8474  GO maxColGid = domainMap.getMaxAllGlobalIndex();
8475  // Rather than replicating the domainMap on all processors, we instead
8476  // iterate from the min GID to the max GID. If the map is gappy,
8477  // there will be invalid GIDs, i.e., GIDs no one has. This will require
8478  // unnecessary matvecs against potentially zero vectors.
8479  GO numGlobElts = maxColGid - minColGid + TGOT::one();
8480  GO numChunks = numGlobElts / numMVs;
8481  GO rem = numGlobElts % numMVs;
8482  GO indexBase = rangeMap->getIndexBase();
8483 
8484  int offsetToUseInPrinting = 1 - indexBase; // default is 1-based indexing
8485  if (params.isParameter("zero-based indexing")) {
8486  if (params.get<bool>("zero-based indexing") == true)
8487  offsetToUseInPrinting = -indexBase; // If 0-based, use as-is. If 1-based, subtract 1.
8488  }
8489 
8490  // Create map that replicates the range map on pid 0 and is empty for all other pids
8491  size_t numLocalRangeEntries = rangeMap->getLocalNumElements();
8492 
8493  // Create contiguous source map
8494  RCP<const map_type> allGidsMap = rcp(new map_type(TGOT::invalid(), numLocalRangeEntries,
8495  indexBase, comm));
8496  // Create vector based on above map. Populate it with GIDs corresponding to this pid's GIDs in rangeMap.
8497  mv_type_go allGids(allGidsMap, 1);
8498  Teuchos::ArrayRCP<GO> allGidsData = allGids.getDataNonConst(0);
8499 
8500  for (size_t i = 0; i < numLocalRangeEntries; i++)
8501  allGidsData[i] = rangeMap->getGlobalElement(i);
8502  allGidsData = Teuchos::null;
8503 
8504  // Create target map that is nontrivial only on pid 0
8505  GO numTargetMapEntries = TGOT::zero();
8506  Teuchos::Array<GO> importGidList;
8507  if (myRank == 0) {
8508  numTargetMapEntries = rangeMap->getGlobalNumElements();
8509  importGidList.reserve(numTargetMapEntries);
8510  for (GO j = 0; j < numTargetMapEntries; ++j) importGidList.push_back(j + indexBase);
8511  } else {
8512  importGidList.reserve(numTargetMapEntries);
8513  }
8514  RCP<map_type> importGidMap = rcp(new map_type(TGOT::invalid(), importGidList(), indexBase, comm));
8515 
8516  // Import all rangeMap GIDs to pid 0
8517  import_type gidImporter(allGidsMap, importGidMap);
8518  mv_type_go importedGids(importGidMap, 1);
8519  importedGids.doImport(allGids, gidImporter, INSERT);
8520 
8521  // The following import map will be non-trivial only on pid 0.
8522  ArrayRCP<const GO> importedGidsData = importedGids.getData(0);
8523  RCP<const map_type> importMap = rcp(new map_type(TGOT::invalid(), importedGidsData(), indexBase, comm));
8524 
8525  // Importer from original range map to pid 0
8526  import_type importer(rangeMap, importMap);
8527  // Target vector on pid 0
8528  RCP<mv_type> colsOnPid0 = rcp(new mv_type(importMap, numMVs));
8529 
8530  RCP<mv_type> ei = rcp(new mv_type(A.getDomainMap(), numMVs)); // probing vector
8531  RCP<mv_type> colsA = rcp(new mv_type(A.getRangeMap(), numMVs)); // columns of A revealed by probing
8532 
8533  Array<GO> globalColsArray, localColsArray;
8534  globalColsArray.reserve(numMVs);
8535  localColsArray.reserve(numMVs);
8536 
8537  ArrayRCP<ArrayRCP<Scalar>> eiData(numMVs);
8538  for (size_t i = 0; i < numMVs; ++i)
8539  eiData[i] = ei->getDataNonConst(i);
8540 
8541  // //////////////////////////////////////
8542  // Discover A by chunks
8543  // //////////////////////////////////////
8544  for (GO k = 0; k < numChunks; ++k) {
8545  for (size_t j = 0; j < numMVs; ++j) {
8546  // GO curGlobalCol = maxColGid - numMVs + j + TGOT::one();
8547  GO curGlobalCol = minColGid + k * numMVs + j;
8548  globalColsArray.push_back(curGlobalCol);
8549  // TODO extract the g2l map outside of this loop loop
8550  LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
8551  if (curLocalCol != TLOT::invalid()) {
8552  eiData[j][curLocalCol] = TGOT::one();
8553  localColsArray.push_back(curLocalCol);
8554  }
8555  }
8556 
8557  // drop host views before apply
8558  for (size_t i = 0; i < numMVs; ++i)
8559  eiData[i] = Teuchos::null;
8560  // probe
8561  A.apply(*ei, *colsA);
8562 
8563  colsOnPid0->doImport(*colsA, importer, INSERT);
8564 
8565  if (myRank == 0)
8566  globalNnz += writeColumns(os, *colsOnPid0, numMVs, importedGidsData(),
8567  globalColsArray, offsetToUseInPrinting);
8568 
8569  // reconstruct dropped eiData
8570  for (size_t i = 0; i < numMVs; ++i)
8571  eiData[i] = ei->getDataNonConst(i);
8572  for (size_t j = 0; j < numMVs; ++j) {
8573  GO curGlobalCol = minColGid + k * numMVs + j;
8574  // TODO extract the g2l map outside of this loop loop
8575  LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
8576  if (curLocalCol != TLOT::invalid()) {
8577  eiData[j][curLocalCol] = TGOT::one();
8578  }
8579  }
8580 
8581  // zero out the ei's
8582  for (size_t j = 0; j < numMVs; ++j) {
8583  for (int i = 0; i < localColsArray.size(); ++i)
8584  eiData[j][localColsArray[i]] = TGOT::zero();
8585  }
8586  globalColsArray.clear();
8587  localColsArray.clear();
8588  }
8589 
8590  // //////////////////////////////////////
8591  // Handle leftover part of A
8592  // //////////////////////////////////////
8593  if (rem > 0) {
8594  for (int j = 0; j < rem; ++j) {
8595  GO curGlobalCol = maxColGid - rem + j + TGOT::one();
8596  globalColsArray.push_back(curGlobalCol);
8597  // TODO extract the g2l map outside of this loop loop
8598  LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
8599  if (curLocalCol != TLOT::invalid()) {
8600  eiData[j][curLocalCol] = TGOT::one();
8601  localColsArray.push_back(curLocalCol);
8602  }
8603  }
8604 
8605  // drop host views before apply
8606  for (size_t i = 0; i < numMVs; ++i)
8607  eiData[i] = Teuchos::null;
8608  // probe
8609  A.apply(*ei, *colsA);
8610 
8611  colsOnPid0->doImport(*colsA, importer, INSERT);
8612  if (myRank == 0)
8613  globalNnz += writeColumns(os, *colsOnPid0, rem, importedGidsData(),
8614  globalColsArray, offsetToUseInPrinting);
8615 
8616  // reconstruct dropped eiData
8617  for (size_t i = 0; i < numMVs; ++i)
8618  eiData[i] = ei->getDataNonConst(i);
8619  for (int j = 0; j < rem; ++j) {
8620  GO curGlobalCol = maxColGid - rem + j + TGOT::one();
8621  // TODO extract the g2l map outside of this loop loop
8622  LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
8623  if (curLocalCol != TLOT::invalid()) {
8624  eiData[j][curLocalCol] = TGOT::one();
8625  }
8626  }
8627 
8628  // zero out the ei's
8629  for (int j = 0; j < rem; ++j) {
8630  for (int i = 0; i < localColsArray.size(); ++i)
8631  eiData[j][localColsArray[i]] = TGOT::zero();
8632  }
8633  globalColsArray.clear();
8634  localColsArray.clear();
8635  }
8636 
8637  // Return the Matrix Market header. It includes the header
8638  // line (that starts with "%%"), some comments, and the triple
8639  // of matrix dimensions and number of nonzero entries. We
8640  // don't actually print this here, because we don't know the
8641  // number of nonzero entries until after probing.
8642  std::ostringstream oss;
8643  if (myRank == 0) {
8644  oss << "%%MatrixMarket matrix coordinate ";
8645  if (Teuchos::ScalarTraits<typename operator_type::scalar_type>::isComplex) {
8646  oss << "complex";
8647  } else {
8648  oss << "real";
8649  }
8650  oss << " general" << std::endl;
8651  oss << "% Tpetra::Operator" << std::endl;
8652  std::time_t now = std::time(NULL);
8653  oss << "% time stamp: " << ctime(&now);
8654  oss << "% written from " << numProcs << " processes" << std::endl;
8655  size_t numRows = rangeMap->getGlobalNumElements();
8656  size_t numCols = domainMap.getGlobalNumElements();
8657  oss << numRows << " " << numCols << " " << globalNnz << std::endl;
8658  }
8659 
8660  return oss.str();
8661  }
8662 
8663  static global_ordinal_type
8664  writeColumns(std::ostream& os, mv_type const& colsA, size_t const& numCols,
8665  Teuchos::ArrayView<const global_ordinal_type> const& rowGids,
8666  Teuchos::Array<global_ordinal_type> const& colsArray,
8667  global_ordinal_type const& indexBase) {
8668  typedef global_ordinal_type GO;
8669  typedef scalar_type Scalar;
8670  typedef Teuchos::ScalarTraits<Scalar> STS;
8671 
8672  GO nnz = 0;
8673  const Scalar zero = STS::zero();
8674  const size_t numRows = colsA.getGlobalLength();
8675  for (size_t j = 0; j < numCols; ++j) {
8676  Teuchos::ArrayRCP<const Scalar> const curCol = colsA.getData(j);
8677  const GO J = colsArray[j];
8678  for (size_t i = 0; i < numRows; ++i) {
8679  const Scalar val = curCol[i];
8680  if (val != zero) {
8681  os << rowGids[i] + indexBase << " " << J + indexBase << " " << val << std::endl;
8682  ++nnz;
8683  }
8684  }
8685  }
8686 
8687  return nnz;
8688  }
8689 
8690  public:
8697 
8698  static void
8699  writeSparsePerRank(const std::string& filename_prefix,
8700  const std::string& filename_suffix,
8701  const sparse_matrix_type& matrix,
8702  const std::string& matrixName,
8703  const std::string& matrixDescription,
8704  const int ranksToWriteAtOnce = 8,
8705  const bool debug = false) {
8706  using ST = scalar_type;
8707  // using LO = local_ordinal_type;
8708  using GO = global_ordinal_type;
8709  using STS = typename Teuchos::ScalarTraits<ST>;
8710  using Teuchos::RCP;
8711 
8712  // Sanity Checks
8713  trcp_tcomm_t comm = matrix.getComm();
8714  TEUCHOS_TEST_FOR_EXCEPTION(comm.is_null(), std::invalid_argument,
8715  "The input matrix's communicator (Teuchos::Comm object) is null.");
8716  TEUCHOS_TEST_FOR_EXCEPTION(matrix.isGloballyIndexed() || !matrix.isFillComplete(), std::invalid_argument,
8717  "The input matrix must not be GloballyIndexed and must be fillComplete.");
8718 
8719  // Setup
8720  const int myRank = comm->getRank();
8721  const int numProc = comm->getSize();
8722  std::string filename = filename_prefix + std::to_string(myRank) + filename_suffix;
8723  RCP<const map_type> rowMap = matrix.getRowMap();
8724  RCP<const map_type> colMap = matrix.getColMap();
8725  size_t local_nnz = matrix.getLocalNumEntries();
8726  size_t local_num_rows = rowMap->getLocalNumElements();
8727  size_t local_num_cols = colMap->getLocalNumElements();
8728  const GO rowIndexBase = rowMap->getIndexBase();
8729  const GO colIndexBase = colMap->getIndexBase();
8730 
8731  // Bounds check the writing limits
8732  int rank_limit = std::min(std::max(ranksToWriteAtOnce, 1), numProc);
8733 
8734  // Start the writing
8735  for (int base_rank = 0; base_rank < numProc; base_rank += rank_limit) {
8736  int stop = std::min(base_rank + rank_limit, numProc);
8737 
8738  if (base_rank <= myRank && myRank < stop) {
8739  // My turn to write
8740  std::ofstream out(filename);
8741 
8742  // MatrixMarket Header
8743  out << "%%MatrixMarket matrix coordinate "
8744  << (STS::isComplex ? "complex" : "real")
8745  << " general" << std::endl;
8746 
8747  // Print comments (the matrix name and / or description).
8748  if (matrixName != "") {
8749  printAsComment(out, matrixName);
8750  }
8751  if (matrixDescription != "") {
8752  printAsComment(out, matrixDescription);
8753  }
8754 
8755  // Print the Matrix Market header (# local rows, # local columns, #
8756  // local enonzeros). This will *not* be read correctly by a generic matrix
8757  // market reader since we'll be writing out GIDs here and local row/col counts
8758  out << local_num_rows << " " << local_num_cols << " " << local_nnz << std::endl;
8759 
8760  {
8761  // Make the output stream write floating-point numbers in
8762  // scientific notation. It will politely put the output
8763  // stream back to its state on input, when this scope
8764  // terminates.
8765  Teuchos::SetScientific<ST> sci(out);
8766 
8767  for (size_t l_row = 0; l_row < local_num_rows; l_row++) {
8768  GO g_row = rowMap->getGlobalElement(l_row);
8769 
8770  typename sparse_matrix_type::local_inds_host_view_type indices;
8771  typename sparse_matrix_type::values_host_view_type values;
8772  matrix.getLocalRowView(l_row, indices, values);
8773  for (size_t ii = 0; ii < indices.extent(0); ii++) {
8774  const GO g_col = colMap->getGlobalElement(indices(ii));
8775  // Convert row and column indices to 1-based.
8776  // This works because the global index type is signed.
8777  out << (g_row + 1 - rowIndexBase) << " "
8778  << (g_col + 1 - colIndexBase) << " ";
8779  if (STS::isComplex) {
8780  out << STS::real(values(ii)) << " " << STS::imag(values(ii));
8781  } else {
8782  out << values(ii);
8783  }
8784  out << std::endl;
8785  } // For each entry in the current row
8786  } // For each row of the matrix
8787  } // end Teuchos::SetScientfic scoping
8788 
8789  out.close();
8790  } // end if base_rank <= myRank < stop
8791 
8792  // Barrier after each writing "batch" to make sure we're not hammering the file system
8793  // too aggressively
8794  comm->barrier();
8795 
8796  } // end outer loop
8797 
8798  } // end writeSparsePerRank
8799 
8801  template <typename T>
8802  static inline trcp_tcomm_t getComm(const Teuchos::RCP<T>& obj) {
8803  return obj.is_null() ? Teuchos::null : obj->getComm();
8804  }
8805 
8807  static inline int getRank(const trcp_tcomm_t& comm) {
8808  return comm.is_null() ? 0 : comm->getRank();
8809  }
8810 
8811 }; // class Writer
8812 
8813 } // namespace MatrixMarket
8814 } // namespace Tpetra
8815 
8816 #endif // __MatrixMarket_Tpetra_hpp
static std::ifstream openInFileOnRankZero(const trcp_tcomm_t &comm, const std::string &filename, const bool safe=true, std::ios_base::openmode mode=std::ios_base::in)
Open an input file stream safely on rank zero.
Communication plan for data redistribution from a uniquely-owned to a (possibly) multiply-owned distr...
Teuchos::RCP< const map_type > getRowMap() const override
Returns the Map that describes the row distribution in this graph.
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getDomainMap() const =0
The Map associated with the domain of this operator, which must be compatible with X...
static Teuchos::RCP< multivector_type > readDense(std::istream &in, const trcp_tcomm_t &comm, Teuchos::RCP< const map_type > &map, const bool tolerant=false, const bool debug=false, const bool binary=false)
Read dense matrix (as a MultiVector) from the given Matrix Market input stream.
static void writeSparseGraphFile(const std::string &filename, const Teuchos::RCP< const crs_graph_type > &pGraph, const bool debug=false)
Print the sparse graph in Matrix Market format to the given file (by filename), with no comments...
static Teuchos::RCP< sparse_matrix_type > readSparseFile(const std::string &filename, const trcp_tcomm_t &comm, const Teuchos::RCP< Teuchos::ParameterList > &constructorParams, const Teuchos::RCP< Teuchos::ParameterList > &fillCompleteParams, const bool tolerant=false, const bool debug=false)
Read sparse matrix from the given Matrix Market file.
static Teuchos::RCP< vector_type > readVectorFile(const std::string &filename, const trcp_tcomm_t &comm, Teuchos::RCP< const map_type > &map, const bool tolerant=false, const bool debug=false)
Read a Vector from the given Matrix Market file.
static void writeOperator(std::ostream &out, const operator_type &A)
Write a Tpetra::Operator to an output stream.
Teuchos::RCP< const map_type > getColMap() const override
Returns the Map that describes the column distribution in this graph.
static void writeSparseFile(const std::string &filename, const Teuchos::RCP< const sparse_matrix_type > &pMatrix, const std::string &matrixName, const std::string &matrixDescription, const bool debug=false)
Only for backwards compatibility; prefer the overload above.
SparseMatrixType::node_type node_type
The fourth template parameter of CrsMatrix and MultiVector.
static void writeDense(std::ostream &out, const multivector_type &X, const std::string &matrixName, const std::string &matrixDescription, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with matrix name and description.
bool isContiguous() const
True if this Map is distributed contiguously, else false.
void doImport(const SrcDistObject &source, const Import< LocalOrdinal, GlobalOrdinal, Node > &importer, const CombineMode CM, const bool restrictedMode=false)
Import data into this object using an Import object (&quot;forward mode&quot;).
size_t getNumVectors() const
Number of columns in the multivector.
Declaration of a function that prints strings from each process.
size_t getLocalNumElements() const
The number of elements belonging to the calling process.
SparseMatrixType::local_ordinal_type local_ordinal_type
SparseMatrixType::global_ordinal_type global_ordinal_type
One or more distributed dense vectors.
Teuchos::RCP< const map_type > getDomainMap() const override
Returns the Map associated with the domain of this graph.
SparseMatrixType::local_ordinal_type local_ordinal_type
Type of the local indices of the sparse matrix.
static Teuchos::RCP< const map_type > readMapFile(const std::string &filename, const trcp_tcomm_t &comm, const bool tolerant=false, const bool debug=false, const bool binary=false)
Read Map (as a MultiVector) from the given Matrix Market file.
static void writeDenseFile(const std::string &filename, const Teuchos::RCP< const multivector_type > &X, const std::string &matrixName, const std::string &matrixDescription, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with matrix name and description.
SparseMatrixType::scalar_type scalar_type
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const
Accessors for the Teuchos::Comm and Kokkos Node objects.
static Teuchos::RCP< sparse_graph_type > readSparseGraphFile(const std::string &filename, const Teuchos::RCP< const map_type > &rowMap, Teuchos::RCP< const map_type > &colMap, const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse graph from the given Matrix Market file, with provided Maps.
MultiVector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > multivector_type
Specialization of Tpetra::MultiVector that matches SparseMatrixType.
static Teuchos::RCP< sparse_graph_type > readSparseGraphFile(const std::string &filename, const trcp_tcomm_t &comm, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse graph from the given Matrix Market file.
static Teuchos::RCP< const map_type > readMap(std::istream &in, const trcp_tcomm_t &comm, const bool tolerant=false, const bool debug=false, const bool binary=false)
Read Map (as a MultiVector) from the given input stream.
bool isGloballyIndexed() const override
Whether the graph&#39;s column indices are stored as global indices.
static void writeMap(std::ostream &out, const map_type &map, const bool debug=false)
Print the Map to the given output stream.
static void writeDenseFile(const std::string &filename, const multivector_type &X, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with no matrix name or description.
static void writeSparse(std::ostream &out, const sparse_matrix_type &matrix, const bool debug=false)
Print the sparse matrix in Matrix Market format.
static Teuchos::RCP< sparse_matrix_type > readSparse(std::istream &in, const Teuchos::RCP< const Teuchos::Comm< int >> &pComm, const Teuchos::RCP< Teuchos::ParameterList > &constructorParams, const Teuchos::RCP< Teuchos::ParameterList > &fillCompleteParams, const bool tolerant=false, const bool debug=false)
Read sparse matrix from the given Matrix Market input stream.
static trcp_tcomm_t getComm(const Teuchos::RCP< T > &obj)
Return obj MPI communicator or Teuchos::null.
static Teuchos::RCP< const map_type > readMap(std::istream &in, const trcp_tcomm_t &comm, const Teuchos::RCP< Teuchos::FancyOStream > &err, const bool tolerant=false, const bool debug=false, const bool binary=false)
Read Map (as a MultiVector) from the given input stream, with optional debugging output stream...
static void writeDenseFile(const std::string &filename, const Teuchos::RCP< const multivector_type > &X, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with no matrix name or description.
SparseMatrixType::global_ordinal_type global_ordinal_type
Type of indices as read from the Matrix Market file.
static Teuchos::RCP< sparse_matrix_type > readSparse(std::istream &in, const Teuchos::RCP< const map_type > &rowMap, Teuchos::RCP< const map_type > &colMap, const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse matrix from the given Matrix Market input stream, with provided Maps. ...
void gathervPrint(std::ostream &out, const std::string &s, const Teuchos::Comm< int > &comm)
On Process 0 in the given communicator, print strings from each process in that communicator, in rank order.
static Teuchos::RCP< sparse_matrix_type > readSparseFile(const std::string &filename, const trcp_tcomm_t &comm, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse matrix from the given Matrix Market file.
static void writeSparseGraph(std::ostream &out, const crs_graph_type &graph, const bool debug=false)
Print the sparse graph in Matrix Market format to the given output stream, with no comments...
Teuchos::ArrayView< const global_ordinal_type > getLocalElementList() const
Return a NONOWNING view of the global indices owned by this process.
static Teuchos::RCP< sparse_matrix_type > readSparsePerRank(const std::string &filename_prefix, const std::string &filename_suffix, const Teuchos::RCP< const map_type > &rowMap, Teuchos::RCP< const map_type > &colMap, const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const bool callFillComplete=true, const bool tolerant=false, const int ranksToReadAtOnce=8, const bool debug=false)
Read a Tpetra::CrsMatrix from a file per rank setup.
size_t global_size_t
Global size_t object.
SparseMatrixType::node_type node_type
The Kokkos Node type; fourth template parameter of Tpetra::CrsMatrix.
static void writeSparsePerRank(const std::string &filename_prefix, const std::string &filename_suffix, const sparse_matrix_type &matrix, const std::string &matrixName, const std::string &matrixDescription, const int ranksToWriteAtOnce=8, const bool debug=false)
Write a Tpetra::CrsMatrix to a file per rank.
static void writeOperator(const std::string &fileName, const operator_type &A, const Teuchos::ParameterList &params)
Write a Tpetra::Operator to a file, with options.
Map< local_ordinal_type, global_ordinal_type, node_type > map_type
Specialization of Tpetra::Map that matches SparseMatrixType.
static Teuchos::RCP< multivector_type > readDenseFile(const std::string &filename, const trcp_tcomm_t &comm, Teuchos::RCP< const map_type > &map, const bool tolerant=false, const bool debug=false, const bool binary=false)
Read dense matrix (as a MultiVector) from the given Matrix Market file.
Insert new values that don&#39;t currently exist.
SparseMatrixType::scalar_type scalar_type
Type of the entries of the sparse matrix.
static void writeMapFile(const std::string &filename, const map_type &map)
Write the Map to the given file.
Abstract interface for operators (e.g., matrices and preconditioners).
static Teuchos::RCP< sparse_graph_type > readSparseGraph(std::istream &in, const Teuchos::RCP< const Teuchos::Comm< int >> &pComm, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse graph from the given Matrix Market input stream.
static void writeSparseGraphFile(const std::string &filename, const Teuchos::RCP< const crs_graph_type > &pGraph, const std::string &graphName, const std::string &graphDescription, const bool debug=false)
Print the sparse graph in Matrix Market format to the given file (by filename), taking the graph by T...
Teuchos::RCP< const Teuchos::Comm< int >> trcp_tcomm_t
Type of the MPI communicator.
static void writeSparse(std::ostream &out, const sparse_matrix_type &matrix, const std::string &matrixName, const std::string &matrixDescription, const bool debug=false)
Print the sparse matrix in Matrix Market format, with comments.
CrsGraph< local_ordinal_type, global_ordinal_type, node_type > crs_graph_type
Specialization of Tpetra::CrsGraph that matches SparseMatrixType.
static Teuchos::RCP< vector_type > readVector(std::istream &in, const trcp_tcomm_t &comm, Teuchos::RCP< const map_type > &map, const bool tolerant=false, const bool debug=false)
Read Vector from the given Matrix Market input stream.
static void writeDense(std::ostream &out, const Teuchos::RCP< const multivector_type > &X, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with no matrix name or description.
static Teuchos::RCP< sparse_matrix_type > readSparseFile(const std::string &filename, const Teuchos::RCP< const map_type > &rowMap, Teuchos::RCP< const map_type > &colMap, const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse matrix from the given Matrix Market file, with provided Maps.
Communication plan for data redistribution from a (possibly) multiply-owned to a uniquely-owned distr...
static void writeSparseFile(const std::string &filename, const sparse_matrix_type &matrix, const std::string &matrixName, const std::string &matrixDescription, const bool debug=false)
Print the sparse matrix in Matrix Market format, with comments.
From a distributed map build a map with all GIDs on the root node.
static int getRank(const trcp_tcomm_t &comm)
Return MPI rank or 0.
static void writeSparseFile(const std::string &filename, const sparse_matrix_type &matrix, const bool debug=false)
Print the sparse matrix in Matrix Market format.
Teuchos::RCP< const Teuchos::Comm< int >> trcp_tcomm_t
Type of the MPI communicator.
static void writeMap(std::ostream &out, const map_type &map, const Teuchos::RCP< Teuchos::FancyOStream > &err, const bool debug=false)
Print the Map to the given output stream out.
static void writeSparseGraphFile(const std::string &filename, const crs_graph_type &graph, const bool debug=false)
Print the sparse graph in Matrix Market format to the given file (by filename), with no comments...
Teuchos::RCP< const map_type > getRangeMap() const override
Returns the Map associated with the domain of this graph.
static void writeDense(std::ostream &out, const multivector_type &X, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with no matrix name or description.
static void writeSparse(std::ostream &out, const Teuchos::RCP< const sparse_matrix_type > &pMatrix, const std::string &matrixName, const std::string &matrixDescription, const bool debug=false)
Only for backwards compatibility; prefer the overload above.
static void writeSparseFile(const std::string &filename, const Teuchos::RCP< const sparse_matrix_type > &pMatrix, const bool debug=false)
Only for backwards compatibility; prefer the overload above.
Teuchos::RCP< const Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > getVector(const size_t j) const
Return a Vector which is a const view of column j.
static Teuchos::RCP< sparse_graph_type > readSparseGraph(std::istream &in, const Teuchos::RCP< const Teuchos::Comm< int >> &pComm, const Teuchos::RCP< Teuchos::ParameterList > &constructorParams, const Teuchos::RCP< Teuchos::ParameterList > &fillCompleteParams, const bool tolerant=false, const bool debug=false)
Read sparse graph from the given Matrix Market input stream.
MultiVector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > multivector_type
The MultiVector specialization associated with SparseMatrixType.
void start()
Start the deep_copy counter.
static void writeSparseGraphFile(const std::string &filename, const crs_graph_type &graph, const std::string &graphName, const std::string &graphDescription, const bool debug=false)
Print the sparse graph in Matrix Market format to the given file (by filename).
global_ordinal_type getMinGlobalIndex() const
The minimum global index owned by the calling process.
A distributed graph accessed by rows (adjacency lists) and stored sparsely.
Vector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > vector_type
The Vector specialization associated with SparseMatrixType.
static Teuchos::RCP< sparse_graph_type > readSparseGraphFile(const std::string &filename, const Teuchos::RCP< const Teuchos::Comm< int >> &pComm, const Teuchos::RCP< Teuchos::ParameterList > &constructorParams, const Teuchos::RCP< Teuchos::ParameterList > &fillCompleteParams, const bool tolerant=false, const bool debug=false)
Read sparse graph from the given Matrix Market file.
A parallel distribution of indices over processes.
static Teuchos::RCP< sparse_matrix_type > readSparse(std::istream &in, const Teuchos::RCP< const Teuchos::Comm< int >> &pComm, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse matrix from the given Matrix Market input stream.
void fillComplete(const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Tell the graph that you are done changing its structure.
void getLocalRowView(const LocalOrdinal lclRow, local_inds_host_view_type &lclColInds) const override
Get a const view of the given local row&#39;s local column indices.
A distributed dense vector.
static void writeSparseGraph(std::ostream &out, const crs_graph_type &graph, const std::string &graphName, const std::string &graphDescription, const bool debug=false)
Print the sparse graph in Matrix Market format to the given output stream.
CrsGraph< local_ordinal_type, global_ordinal_type, node_type > sparse_graph_type
The CrsGraph specialization associated with SparseMatrixType.
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const override
Returns the communicator.
SparseMatrixType sparse_matrix_type
This class&#39; template parameter; a specialization of CrsMatrix.
virtual Teuchos::RCP< const map_type > getMap() const
The Map describing the parallel distribution of this object.
SparseMatrixType sparse_matrix_type
Template parameter of this class; specialization of CrsMatrix.
static Teuchos::RCP< sparse_graph_type > readSparseGraph(std::istream &in, const Teuchos::RCP< const map_type > &rowMap, Teuchos::RCP< const map_type > &colMap, const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const bool callFillComplete=true, const bool tolerant=false, const bool debug=false)
Read sparse matrix from the given Matrix Market input stream, with provided Maps. ...
static void writeDenseFile(const std::string &filename, const multivector_type &X, const std::string &matrixName, const std::string &matrixDescription, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with matrix name and description.
static void writeOperator(std::ostream &out, const operator_type &A, const Teuchos::ParameterList &params)
Write a Tpetra::Operator to an output stream, with options.
global_size_t getGlobalNumEntries() const override
Returns the global number of entries in the graph.
Matrix Market file reader for CrsMatrix and MultiVector.
Matrix Market file writer for CrsMatrix and MultiVector.
static void writeSparse(std::ostream &out, const Teuchos::RCP< const sparse_matrix_type > &pMatrix, const bool debug=false)
Only for backwards compatibility; prefer the overload above.
void getGlobalRowView(const global_ordinal_type gblRow, global_inds_host_view_type &gblColInds) const override
Get a const view of the given global row&#39;s global column indices.
void stop()
Stop the deep_copy counter.
static void writeDense(std::ostream &out, const Teuchos::RCP< const multivector_type > &X, const std::string &matrixName, const std::string &matrixDescription, const Teuchos::RCP< Teuchos::FancyOStream > &err=Teuchos::null, const Teuchos::RCP< Teuchos::FancyOStream > &dbg=Teuchos::null)
Print the multivector in Matrix Market format, with matrix name and or description.
global_size_t getGlobalNumElements() const
The number of elements in this Map.
static void writeOperator(const std::string &fileName, operator_type const &A)
Write a Tpetra::Operator to a file.