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