|
Tpetra Matrix/Vector Services Version of the Day
|
00001 // @HEADER 00002 // *********************************************************************** 00003 // 00004 // Tpetra: Templated Linear Algebra Services Package 00005 // Copyright (2008) Sandia Corporation 00006 // 00007 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 00008 // the U.S. Government retains certain rights in this software. 00009 // 00010 // Redistribution and use in source and binary forms, with or without 00011 // modification, are permitted provided that the following conditions are 00012 // met: 00013 // 00014 // 1. Redistributions of source code must retain the above copyright 00015 // notice, this list of conditions and the following disclaimer. 00016 // 00017 // 2. Redistributions in binary form must reproduce the above copyright 00018 // notice, this list of conditions and the following disclaimer in the 00019 // documentation and/or other materials provided with the distribution. 00020 // 00021 // 3. Neither the name of the Corporation nor the names of the 00022 // contributors may be used to endorse or promote products derived from 00023 // this software without specific prior written permission. 00024 // 00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00036 // 00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00038 // 00039 // ************************************************************************ 00040 // @HEADER 00041 00042 #ifndef __MatrixMarket_Tpetra_hpp 00043 #define __MatrixMarket_Tpetra_hpp 00044 00056 #include "Tpetra_CrsMatrix.hpp" 00057 #include "MatrixMarket_raw.hpp" 00058 #include "MatrixMarket_Banner.hpp" 00059 #include "MatrixMarket_CoordDataReader.hpp" 00060 #include "MatrixMarket_util.hpp" 00061 00062 #include <algorithm> 00063 #include <fstream> 00064 #include <iostream> 00065 #include <iterator> 00066 #include <vector> 00067 #include <stdexcept> 00068 00069 namespace Tpetra { 00097 namespace MatrixMarket { 00098 00162 template<class SparseMatrixType> 00163 class Reader { 00164 public: 00167 typedef SparseMatrixType sparse_matrix_type; 00168 typedef RCP<sparse_matrix_type> sparse_matrix_ptr; 00169 00172 typedef typename SparseMatrixType::scalar_type scalar_type; 00175 typedef typename SparseMatrixType::local_ordinal_type local_ordinal_type; 00182 typedef typename SparseMatrixType::global_ordinal_type 00183 global_ordinal_type; 00184 00187 typedef typename SparseMatrixType::node_type node_type; 00188 00191 typedef MultiVector<scalar_type, 00192 local_ordinal_type, 00193 global_ordinal_type, 00194 node_type> multivector_type; 00195 00196 typedef RCP<node_type> node_ptr; 00197 typedef Comm<int> comm_type; 00198 typedef RCP<const comm_type> comm_ptr; 00199 typedef Map<local_ordinal_type, global_ordinal_type, node_type> map_type; 00200 typedef RCP<const map_type> map_ptr; 00201 00202 private: 00203 00206 typedef typename ArrayRCP<global_ordinal_type>::size_type size_type; 00207 00220 static RCP<const map_type> 00221 makeRangeMap (const RCP<const comm_type>& pComm, 00222 const RCP<node_type>& pNode, 00223 const global_ordinal_type numRows) 00224 { 00225 // A conventional, uniformly partitioned, contiguous map. 00226 return rcp (new map_type (static_cast<global_size_t> (numRows), 00227 static_cast<global_ordinal_type> (0), 00228 pComm, GloballyDistributed, pNode)); 00229 } 00230 00263 static RCP<const map_type> 00264 makeRowMap (const RCP<const map_type>& pRowMap, 00265 const RCP<const comm_type>& pComm, 00266 const RCP<node_type>& pNode, 00267 const global_ordinal_type numRows) 00268 { 00269 // If the caller didn't provide a map, return a conventional, 00270 // uniformly partitioned, contiguous map. 00271 if (pRowMap.is_null()) { 00272 return rcp (new map_type (static_cast<global_size_t> (numRows), 00273 static_cast<global_ordinal_type> (0), 00274 pComm, GloballyDistributed, pNode)); 00275 } else { 00276 TEUCHOS_TEST_FOR_EXCEPTION(! pRowMap->isDistributed() && pComm->getSize() > 1, 00277 std::invalid_argument, 00278 "The specified row map is not distributed, but " 00279 "the given communicator includes more than one " 00280 "rank (in fact, there are " << pComm->getSize() 00281 << " ranks)."); 00282 TEUCHOS_TEST_FOR_EXCEPTION(pRowMap->getComm() != pComm, 00283 std::invalid_argument, 00284 "The specified row map's communicator (pRowMap->" 00285 "getComm()) is different than the given separately " 00286 "supplied communicator pComm."); 00287 TEUCHOS_TEST_FOR_EXCEPTION(pRowMap->getNode() != pNode, 00288 std::invalid_argument, 00289 "The specified row map's node (pRowMap->getNode()) " 00290 "is different than the given separately supplied " 00291 "node pNode."); 00292 return pRowMap; 00293 } 00294 } 00295 00310 static map_ptr 00311 makeDomainMap (const map_ptr& pRangeMap, 00312 const global_ordinal_type numRows, 00313 const global_ordinal_type numCols) 00314 { 00315 // Abbreviations so that the map creation call isn't too long. 00316 typedef local_ordinal_type LO; 00317 typedef global_ordinal_type GO; 00318 typedef node_type Node; 00319 00320 if (numRows == numCols) { 00321 return pRangeMap; 00322 } else { 00323 comm_ptr pComm = pRangeMap->getComm(); 00324 node_ptr pNode = pRangeMap->getNode(); 00325 return createUniformContigMapWithNode<LO,GO,Node> (numCols, 00326 pComm, 00327 pNode); 00328 } 00329 } 00330 00403 static void 00404 distribute (ArrayRCP<size_t>& myNumEntriesPerRow, 00405 ArrayRCP<size_type>& myRowPtr, 00406 ArrayRCP<global_ordinal_type>& myColInd, 00407 ArrayRCP<scalar_type>& myValues, 00408 const RCP<const map_type>& pRowMap, 00409 ArrayRCP<size_t>& numEntriesPerRow, 00410 ArrayRCP<size_type>& rowPtr, 00411 ArrayRCP<global_ordinal_type>& colInd, 00412 ArrayRCP<scalar_type>& values, 00413 const bool debug=false) 00414 { 00415 using Teuchos::CommRequest; 00416 using Teuchos::receive; 00417 using Teuchos::send; 00418 using std::cerr; 00419 using std::endl; 00420 00421 const bool extraDebug = false; 00422 comm_ptr pComm = pRowMap->getComm(); 00423 const int numProcs = Teuchos::size (*pComm); 00424 const int myRank = Teuchos::rank (*pComm); 00425 const int rootRank = 0; 00426 00427 // Type abbreviations to make the code more concise. 00428 typedef global_ordinal_type GO; 00429 typedef local_ordinal_type LO; 00430 00431 // List of the global indices of my rows. They may or may 00432 // not be contiguous, and the row map need not be one-to-one. 00433 ArrayView<const GO> myRows = pRowMap->getNodeElementList(); 00434 const size_type myNumRows = myRows.size(); 00435 TEUCHOS_TEST_FOR_EXCEPTION(static_cast<size_t>(myNumRows) != 00436 pRowMap->getNodeNumElements(), 00437 std::logic_error, 00438 "pRowMap->getNodeElementList().size() = " 00439 << myNumRows 00440 << " != pRowMap->getNodeNumElements() = " 00441 << pRowMap->getNodeNumElements() << ". " 00442 "Please report this bug to the Tpetra developers."); 00443 TEUCHOS_TEST_FOR_EXCEPTION(myRank == 0 && numEntriesPerRow.size() < myNumRows, 00444 std::logic_error, 00445 "On Proc 0: numEntriesPerRow.size() = " 00446 << numEntriesPerRow.size() 00447 << " != pRowMap->getNodeElementList().size() = " 00448 << myNumRows << ". Please report this bug to the " 00449 "Tpetra developers."); 00450 00451 // Space for my proc's number of entries per row. 00452 // Will be filled in below. 00453 myNumEntriesPerRow = arcp<size_t> (myNumRows); 00454 00455 // Teuchos::receive() returns an int; here is space for it. 00456 int recvResult = 0; 00457 00458 if (myRank != rootRank) 00459 { 00460 // Tell the root how many rows we have. If we're sending 00461 // none, then we don't have anything else to send, nor 00462 // does the root have to receive anything else. 00463 send (*pComm, myNumRows, rootRank); 00464 if (myNumRows != 0) 00465 { 00466 // Now send my rows' global indices. Hopefully the 00467 // cast to int doesn't overflow. This is unlikely, 00468 // since it should fit in a LO, even 00469 // though it is a GO. 00470 send (*pComm, static_cast<int> (myNumRows), 00471 myRows.getRawPtr(), rootRank); 00472 00473 // I (this proc) don't care if my global row indices 00474 // are contiguous, though the root proc does (since 00475 // otherwise it needs to pack noncontiguous data into 00476 // contiguous storage before sending). That's why we 00477 // don't check for contiguousness here. 00478 00479 // Ask the root processor for my part of the array of the 00480 // number of entries per row. 00481 recvResult = receive (*pComm, rootRank, 00482 static_cast<int> (myNumRows), 00483 myNumEntriesPerRow.getRawPtr()); 00484 00485 // Use the resulting array to figure out how many column 00486 // indices and values for which I should ask from the root 00487 // processor. 00488 const size_t myNumEntries = 00489 std::accumulate (myNumEntriesPerRow.begin(), 00490 myNumEntriesPerRow.end(), 00491 static_cast<size_t> (0)); 00492 00493 // Make space for my entries of the sparse matrix. 00494 // Note that they don't have to be sorted by row 00495 // index. Iterating through all my rows requires 00496 // computing a running sum over myNumEntriesPerRow. 00497 myColInd = arcp<GO> (myNumEntries); 00498 myValues = arcp<scalar_type> (myNumEntries); 00499 if (myNumEntries > 0) 00500 { // Ask for that many column indices and values, 00501 // if there are any. 00502 recvResult = receive (*pComm, rootRank, 00503 static_cast<int> (myNumEntries), 00504 myColInd.getRawPtr()); 00505 recvResult = receive (*pComm, rootRank, 00506 static_cast<int> (myNumEntries), 00507 myValues.getRawPtr()); 00508 } 00509 } // If I own at least one row 00510 } // If I am not the root processor 00511 else // I _am_ the root processor 00512 { 00513 if (debug) 00514 cerr << "-- Proc 0: Copying my data from global arrays" << endl; 00515 00516 // Proc 0 (the root processor) still needs to (allocate, 00517 // if not done already) and fill its part of the matrix 00518 // (my*). 00519 for (size_type k = 0; k < myNumRows; ++k) 00520 { 00521 const GO myCurRow = myRows[k]; 00522 const size_t numEntriesInThisRow = numEntriesPerRow[myCurRow]; 00523 //myNumEntriesPerRow[k] = numEntriesPerRow[myCurRow]; 00524 myNumEntriesPerRow[k] = numEntriesInThisRow; 00525 } 00526 if (extraDebug && debug) 00527 { 00528 cerr << "Proc " << Teuchos::rank (*(pRowMap->getComm())) 00529 << ": myNumEntriesPerRow[0.." << (myNumRows-1) << "] = ["; 00530 for (size_type k = 0; k < myNumRows; ++k) 00531 { 00532 cerr << myNumEntriesPerRow[k]; 00533 if (k < myNumRows-1) 00534 cerr << " "; 00535 } 00536 cerr << "]" << endl; 00537 } 00538 // The total number of matrix entries that my proc owns. 00539 const size_t myNumEntries = 00540 std::accumulate (myNumEntriesPerRow.begin(), 00541 myNumEntriesPerRow.end(), 00542 static_cast<size_t> (0)); 00543 if (debug) 00544 cerr << "-- Proc 0: I own " << myNumRows << " rows and " 00545 << myNumEntries << " entries" << endl; 00546 00547 myColInd = arcp<GO> (myNumEntries); 00548 myValues = arcp<scalar_type> (myNumEntries); 00549 00550 // Copy Proc 0's part of the matrix into the my* arrays. 00551 // It's important that myCurPos be updated _before_ k, 00552 // otherwise myCurPos will get the wrong number of entries 00553 // per row (it should be for the row in the just-completed 00554 // iteration, not for the next iteration's row). 00555 size_t myCurPos = 0; 00556 for (size_type k = 0; k < myNumRows; 00557 myCurPos += myNumEntriesPerRow[k], ++k) 00558 { 00559 const size_t curNumEntries = myNumEntriesPerRow[k]; 00560 const GO myRow = myRows[k]; 00561 const size_t curPos = rowPtr[myRow]; 00562 if (extraDebug && debug) 00563 { 00564 cerr << "k = " << k << ", myRow = " << myRow << ": colInd(" 00565 << curPos << "," << curNumEntries << "), myColInd(" 00566 << myCurPos << "," << curNumEntries << ")" << endl; 00567 } 00568 // Only copy if there are entries to copy, in order 00569 // not to construct empty ranges for the ArrayRCP 00570 // views. 00571 if (curNumEntries > 0) 00572 { 00573 ArrayView<GO> colIndView = colInd(curPos, curNumEntries); 00574 ArrayView<GO> myColIndView = 00575 myColInd(myCurPos, curNumEntries); 00576 std::copy (colIndView.begin(), colIndView.end(), 00577 myColIndView.begin()); 00578 00579 ArrayView<scalar_type> valuesView = 00580 values(curPos, curNumEntries); 00581 ArrayView<scalar_type> myValuesView = 00582 myValues(myCurPos, curNumEntries); 00583 std::copy (valuesView.begin(), valuesView.end(), 00584 myValuesView.begin()); 00585 } 00586 } 00587 00588 // Proc 0 processes each other proc p in turn. 00589 for (int p = 1; p < numProcs; ++p) 00590 { 00591 if (debug) 00592 cerr << "-- Proc 0: Processing proc " << p << endl; 00593 00594 size_type theirNumRows = 0; 00595 // Ask Proc p how many rows it has. If it doesn't 00596 // have any, we can move on to the next proc. This 00597 // has to be a standard receive so that we can avoid 00598 // the degenerate case of sending zero data. 00599 recvResult = receive (*pComm, p, &theirNumRows); 00600 if (debug) 00601 cerr << "-- Proc 0: Proc " << p << " owns " 00602 << theirNumRows << " rows" << endl; 00603 if (theirNumRows != 0) 00604 { // Ask Proc p which rows it owns. The resulting 00605 // global row indices are not guaranteed to be 00606 // contiguous or sorted. Global row indices are 00607 // themselves indices into the numEntriesPerRow 00608 // array. 00609 ArrayRCP<GO> theirRows = arcp<GO> (theirNumRows); 00610 recvResult = receive (*pComm, p, 00611 static_cast<int> (theirNumRows), 00612 theirRows.getRawPtr()); 00613 // Extra test to make sure that the rows we 00614 // received are all sensible. This is a good 00615 // idea since we are going to use the global row 00616 // indices we've received to index into the 00617 // numEntriesPerRow array. Better to catch any 00618 // bugs here and print a sensible error message, 00619 // rather than segfault and print a cryptic error 00620 // message. 00621 { 00622 const global_size_t numRows = 00623 pRowMap->getGlobalNumElements(); 00624 bool theirRowsValid = true; 00625 for (size_type k = 0; k < theirNumRows; ++k) 00626 { 00627 // global_ordinal_t is generally a signed type. 00628 if (theirRows[k] < 0) 00629 theirRowsValid = false; 00630 // Signed to unsigned cast should not 00631 // overflow. We cast in order to avoid 00632 // compiler warnings about comparing signed 00633 // and unsigned types, in case 00634 // global_size_t is unsigned. 00635 else if (static_cast<global_size_t> (theirRows[k]) >= numRows) 00636 theirRowsValid = false; 00637 } 00638 if (! theirRowsValid) 00639 { 00640 std::ostringstream os; 00641 std::copy (theirRows.begin(), theirRows.end(), 00642 std::ostream_iterator<GO>(os, " ")); 00643 TEUCHOS_TEST_FOR_EXCEPTION(! theirRowsValid, 00644 std::logic_error, 00645 "Proc " << p << " has at least " 00646 "one invalid row index. Here are" 00647 " all of them: [ " << os.str() 00648 << "]"); 00649 } 00650 } 00651 00652 // Perhaps we could save a little work if we 00653 // check whether Proc p's row indices are 00654 // contiguous. That would make lookups in the 00655 // global data arrays faster. For now, we just 00656 // implement the general case and don't 00657 // prematurely optimize. (Remember that you're 00658 // making Proc 0 read the whole file, so you've 00659 // already lost scalability.) 00660 00661 // Compute the number of entries in each of Proc 00662 // p's rows. (Proc p will compute its row 00663 // pointer array on its own, after it gets the 00664 // data from Proc 0.) 00665 ArrayRCP<size_t> theirNumEntriesPerRow; 00666 theirNumEntriesPerRow = arcp<size_t> (theirNumRows); 00667 for (size_type k = 0; k < theirNumRows; ++k) 00668 theirNumEntriesPerRow[k] = 00669 numEntriesPerRow[theirRows[k]]; 00670 00671 // Tell Proc p the number of entries in each of 00672 // its rows. Hopefully the cast to int doesn't 00673 // overflow. This is unlikely, since it should 00674 // fit in a LO, even though it is 00675 // a GO. 00676 send (*pComm, static_cast<int> (theirNumRows), 00677 theirNumEntriesPerRow.getRawPtr(), p); 00678 00679 // Figure out how many entries Proc p owns. 00680 const size_t theirNumEntries = 00681 std::accumulate (theirNumEntriesPerRow.begin(), 00682 theirNumEntriesPerRow.end(), 00683 static_cast<size_t> (0)); 00684 00685 if (debug) 00686 cerr << "-- Proc 0: Proc " << p << " owns " 00687 << theirNumEntries << " entries" << endl; 00688 00689 // If there are no entries to send, then we're 00690 // done with Proc p. 00691 if (theirNumEntries == 0) 00692 continue; 00693 00694 // Construct (views of) proc p's column indices 00695 // and values. Later, we might like to optimize 00696 // for the (common) contiguous case, for which we 00697 // don't need to copy data into separate "their*" 00698 // arrays (we can just use contiguous views of 00699 // the global arrays). 00700 ArrayRCP<GO> theirColInd = arcp<GO> (theirNumEntries); 00701 ArrayRCP<scalar_type> theirValues = 00702 arcp<scalar_type> (theirNumEntries); 00703 // Copy Proc p's part of the matrix into the 00704 // their* arrays. It's important that 00705 // theirCurPos be updated _before_ k, otherwise 00706 // theirCurPos will get the wrong number of 00707 // entries per row (it should be for the row in 00708 // the just-completed iteration, not for the next 00709 // iteration's row). 00710 size_t theirCurPos = 0; 00711 for (size_type k = 0; k < theirNumRows; 00712 theirCurPos += theirNumEntriesPerRow[k], k++) 00713 { 00714 const size_t curNumEntries = theirNumEntriesPerRow[k]; 00715 const GO theirRow = theirRows[k]; 00716 const size_t curPos = rowPtr[theirRow]; 00717 00718 // Only copy if there are entries to copy, in 00719 // order not to construct empty ranges for 00720 // the ArrayRCP views. 00721 if (curNumEntries > 0) 00722 { 00723 ArrayView<GO> colIndView = 00724 colInd(curPos, curNumEntries); 00725 ArrayView<GO> theirColIndView = 00726 theirColInd(theirCurPos, curNumEntries); 00727 std::copy (colIndView.begin(), colIndView.end(), 00728 theirColIndView.begin()); 00729 00730 ArrayView<scalar_type> valuesView = 00731 values(curPos, curNumEntries); 00732 ArrayView<scalar_type> theirValuesView = 00733 theirValues(theirCurPos, curNumEntries); 00734 std::copy (valuesView.begin(), valuesView.end(), 00735 theirValuesView.begin()); 00736 } 00737 } 00738 // Send Proc p its column indices and values. 00739 // Hopefully the cast to int doesn't overflow. 00740 // This is unlikely, since it should fit in a LO, 00741 // even though it is a GO. 00742 send (*pComm, static_cast<int> (theirNumEntries), 00743 theirColInd.getRawPtr(), p); 00744 send (*pComm, static_cast<int> (theirNumEntries), 00745 theirValues.getRawPtr(), p); 00746 00747 if (debug) 00748 cerr << "-- Proc 0: Finished with proc " << p << endl; 00749 } // If proc p owns at least one row 00750 } // For each proc p not the root proc 0 00751 } // If I'm (not) the root proc 0 00752 00753 // Invalidate the input data to save space, since we don't 00754 // need it anymore. 00755 numEntriesPerRow = null; 00756 rowPtr = null; 00757 colInd = null; 00758 values = null; 00759 00760 if (debug && myRank == 0) 00761 cerr << "-- Proc 0: About to fill in myRowPtr" << endl; 00762 00763 // Allocate and fill in myRowPtr (the row pointer array for 00764 // my rank's rows). We delay this until the end because we 00765 // don't need it to compute anything else in distribute(). 00766 // Each proc can do this work for itself, since it only needs 00767 // myNumEntriesPerRow to do so. 00768 myRowPtr = arcp<size_type> (myNumRows+1); 00769 myRowPtr[0] = 0; 00770 for (size_type k = 1; k < myNumRows+1; ++k) 00771 myRowPtr[k] = myRowPtr[k-1] + myNumEntriesPerRow[k-1]; 00772 if (extraDebug && debug) 00773 { 00774 cerr << "Proc " << Teuchos::rank (*(pRowMap->getComm())) 00775 << ": myRowPtr[0.." << myNumRows << "] = ["; 00776 for (size_type k = 0; k < myNumRows+1; ++k) 00777 { 00778 cerr << myRowPtr[k]; 00779 if (k < myNumRows) 00780 cerr << " "; 00781 } 00782 cerr << "]" << endl << endl; 00783 } 00784 00785 if (debug && myRank == 0) 00786 cerr << "-- Proc 0: Done with distribute" << endl; 00787 } 00788 00799 static sparse_matrix_ptr 00800 makeMatrix (ArrayRCP<size_t>& myNumEntriesPerRow, 00801 ArrayRCP<size_type>& myRowPtr, 00802 ArrayRCP<global_ordinal_type>& myColInd, 00803 ArrayRCP<scalar_type>& myValues, 00804 const map_ptr& pRowMap, 00805 const map_ptr& pRangeMap, 00806 const map_ptr& pDomainMap, 00807 const bool callFillComplete = true) 00808 { 00809 using std::cerr; 00810 using std::endl; 00811 // Typedef to make certain type declarations shorter. 00812 typedef global_ordinal_type GO; 00813 00814 const bool extraDebug = false; 00815 const bool debug = false; 00816 00817 // The row pointer array always has at least one entry, even 00818 // if the matrix has zero rows. myNumEntriesPerRow, myColInd, 00819 // and myValues would all be empty arrays in that degenerate 00820 // case, but the row and domain maps would still be nonnull 00821 // (though they would be trivial maps). 00822 TEUCHOS_TEST_FOR_EXCEPTION(myRowPtr.is_null(), std::logic_error, 00823 "makeMatrix: myRowPtr array is null. " 00824 "Please report this bug to the Tpetra developers."); 00825 TEUCHOS_TEST_FOR_EXCEPTION(pDomainMap.is_null(), std::logic_error, 00826 "makeMatrix: domain map is null. " 00827 "Please report this bug to the Tpetra developers."); 00828 TEUCHOS_TEST_FOR_EXCEPTION(pRangeMap.is_null(), std::logic_error, 00829 "makeMatrix: range map is null. " 00830 "Please report this bug to the Tpetra developers."); 00831 TEUCHOS_TEST_FOR_EXCEPTION(pRowMap.is_null(), std::logic_error, 00832 "makeMatrix: row map is null. " 00833 "Please report this bug to the Tpetra developers."); 00834 00835 // Handy for debugging output; not needed otherwise. 00836 const int myRank = Teuchos::rank (*(pRangeMap->getComm())); 00837 00838 if (extraDebug && debug) 00839 { 00840 cerr << "Proc " << myRank << ":" << endl 00841 << "-- myRowPtr = [ "; 00842 std::copy (myRowPtr.begin(), myRowPtr.end(), 00843 std::ostream_iterator<size_type>(cerr, " ")); 00844 cerr << "]" << endl << "-- myColInd = [ "; 00845 std::copy (myColInd.begin(), myColInd.end(), 00846 std::ostream_iterator<size_type>(cerr, " ")); 00847 cerr << "]" << endl << endl; 00848 } 00849 00850 // Go through all of my columns, and see if any are not in the 00851 // domain map. This is possible if numProcs > 1, otherwise 00852 // not. 00853 if (extraDebug && debug) 00854 { 00855 size_type numRemote = 0; 00856 std::vector<GO> remoteGIDs; 00857 00858 typedef typename ArrayRCP<GO>::const_iterator iter_type; 00859 for (iter_type it = myColInd.begin(); it != myColInd.end(); ++it) 00860 { 00861 if (! pDomainMap->isNodeGlobalElement (*it)) 00862 { 00863 numRemote++; 00864 remoteGIDs.push_back (*it); 00865 } 00866 } 00867 if (numRemote > 0) 00868 { 00869 cerr << "Proc " << myRank << ": " << numRemote 00870 << " remote GIDs = [ " << endl; 00871 std::copy (remoteGIDs.begin(), remoteGIDs.end(), 00872 std::ostream_iterator<GO>(cerr, " ")); 00873 cerr << "]" << endl; 00874 } 00875 } 00876 00877 // Construct the CrsMatrix, using the row map, with the 00878 // constructor specifying the number of nonzeros for each row. 00879 // Create with DynamicProfile, so that the fillComplete() can 00880 // do first-touch reallocation (a NUMA (Non-Uniform Memory 00881 // Access) optimization on multicore CPUs). 00882 sparse_matrix_ptr A = 00883 rcp (new sparse_matrix_type (pRowMap, myNumEntriesPerRow, 00884 DynamicProfile)); 00885 TEUCHOS_TEST_FOR_EXCEPTION(A.is_null(), std::logic_error, 00886 "makeMatrix: Initial allocation of CrsMatrix failed" 00887 ". Please report this bug to the Tpetra developers" 00888 "."); 00889 // List of the global indices of my rows. 00890 // They may or may not be contiguous. 00891 ArrayView<const GO> myRows = pRowMap->getNodeElementList(); 00892 const size_type myNumRows = myRows.size(); 00893 00894 // Add this processor's matrix entries to the CrsMatrix. 00895 for (size_type k = 0; k < myNumRows; ++k) 00896 { 00897 const size_type myCurPos = myRowPtr[k]; 00898 const size_t curNumEntries = myNumEntriesPerRow[k]; 00899 00900 if (extraDebug && debug) 00901 { 00902 cerr << "Proc " << myRank << ": k = " << k 00903 << ", myCurPos = " << myCurPos 00904 << ", curNumEntries = " << curNumEntries 00905 << endl; 00906 } 00907 // Avoid constructing empty views of ArrayRCP objects. 00908 if (curNumEntries > 0) 00909 A->insertGlobalValues (myRows[k], 00910 myColInd(myCurPos, curNumEntries), 00911 myValues(myCurPos, curNumEntries)); 00912 } 00913 // We've entered in all our matrix entries, so we can delete 00914 // the original data. This will save memory when we call 00915 // fillComplete(), so that we never keep more than two copies 00916 // of the matrix's data in memory at once. 00917 myNumEntriesPerRow = null; 00918 myRowPtr = null; 00919 myColInd = null; 00920 myValues = null; 00921 00922 if (callFillComplete) 00923 A->fillComplete (pDomainMap, pRangeMap, DoOptimizeStorage); 00924 return A; 00925 } 00926 00927 private: 00928 00945 static RCP<const Banner> 00946 readBanner (std::istream& in, 00947 size_t& lineNumber, 00948 const comm_ptr& pComm, 00949 const bool tolerant=false, 00950 const bool debug=false) 00951 { 00952 using std::cerr; 00953 using std::endl; 00954 00955 const int myRank = Teuchos::rank (*pComm); 00956 00957 // The pointer will be non-null on return only on MPI Rank 0. 00958 // Using a pointer lets the data persist outside the 00959 // "myRank==0" scopes. 00960 RCP<Banner> pBanner; 00961 if (myRank == 0) 00962 { 00963 typedef Teuchos::ScalarTraits<scalar_type> STS; 00964 00965 std::string line; 00966 // Try to read a line from the input stream. 00967 const bool readFailed = ! getline(in, line); 00968 TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument, 00969 "Failed to get Matrix Market banner line " 00970 "from input."); 00971 // We read a line from the input stream. 00972 lineNumber++; 00973 00974 // Assume that the line we found is the banner line. 00975 try { 00976 pBanner = rcp (new Banner (line, tolerant)); 00977 } catch (std::exception& e) { 00978 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, 00979 "Matrix Market banner line contains syntax " 00980 "error(s): " << e.what()); 00981 } 00982 TEUCHOS_TEST_FOR_EXCEPTION(pBanner->objectType() != "matrix", 00983 std::invalid_argument, 00984 "The Matrix Market file does not contain " 00985 "matrix data. Its banner (first) line says that" 00986 " its object type is \"" << pBanner->matrixType() 00987 << "\", rather than the required \"matrix\"."); 00988 00989 // Validate the data type of the matrix, with respect to 00990 // the Scalar type of the CrsMatrix entries. 00991 TEUCHOS_TEST_FOR_EXCEPTION(! STS::isComplex && 00992 pBanner->dataType() == "complex", 00993 std::invalid_argument, 00994 "The Matrix Market file contains complex-valued " 00995 "data, but you are trying to read it into a " 00996 "matrix containing entries of the real-valued " 00997 "Scalar type \"" 00998 << Teuchos::TypeNameTraits<scalar_type>::name() 00999 << "\"."); 01000 TEUCHOS_TEST_FOR_EXCEPTION(pBanner->dataType() != "real" && 01001 pBanner->dataType() != "complex" && 01002 pBanner->dataType() != "integer", 01003 std::invalid_argument, 01004 "When reading Matrix Market data into a " 01005 "CrsMatrix, the Matrix Market file may not " 01006 "contain a \"pattern\" matrix. A pattern matrix" 01007 " is really just a graph with no weights. It " 01008 "should be stored in a CrsGraph, not a " 01009 "CrsMatrix."); 01010 } 01011 return pBanner; 01012 } 01013 01036 static Tuple<global_ordinal_type, 3> 01037 readCoordDims (std::istream& in, 01038 size_t& lineNumber, 01039 const RCP<const Banner>& pBanner, 01040 const comm_ptr& pComm, 01041 const bool tolerant = false, 01042 const bool debug = false) 01043 { 01044 // Packed coordinate matrix dimensions (numRows, numCols, 01045 // numNonzeros); computed on Rank 0 and broadcasted to all MPI 01046 // ranks. 01047 Tuple<global_ordinal_type, 3> dims; 01048 01049 // Read in the coordinate matrix dimensions from the input 01050 // stream. "success" tells us whether reading in the 01051 // coordinate matrix dimensions succeeded ("Guilty unless 01052 // proven innocent"). 01053 bool success = false; 01054 if (pComm->getRank() == 0) { 01055 TEUCHOS_TEST_FOR_EXCEPTION(pBanner->matrixType() != "coordinate", 01056 std::invalid_argument, "The Tpetra::CrsMatrix Matrix Market reader " 01057 "only accepts \"coordinate\" (sparse) matrix data."); 01058 // Unpacked coordinate matrix dimensions 01059 global_ordinal_type numRows, numCols, numNonzeros; 01060 // Only MPI Rank 0 reads from the input stream 01061 success = readCoordinateDimensions (in, numRows, numCols, 01062 numNonzeros, lineNumber, 01063 tolerant); 01064 // Pack up the data into a Tuple so we can send them with 01065 // one broadcast instead of three. 01066 dims[0] = numRows; 01067 dims[1] = numCols; 01068 dims[2] = numNonzeros; 01069 } 01070 // Only Rank 0 did the reading, so it decides success. 01071 // 01072 // FIXME (mfh 02 Feb 2011) Teuchos::broadcast doesn't know how 01073 // to send bools. For now, we convert to/from int instead, 01074 // using the usual "true is 1, false is 0" encoding. 01075 { 01076 int the_success = success ? 1 : 0; // only matters on MPI Rank 0 01077 Teuchos::broadcast (*pComm, 0, &the_success); 01078 success = (the_success == 1); 01079 } 01080 if (success) { 01081 // Broadcast (numRows, numCols, numNonzeros) from Rank 0 01082 // to all the other MPI ranks. 01083 Teuchos::broadcast (*pComm, 0, dims); 01084 } 01085 else { 01086 // Perhaps in tolerant mode, we could set all the 01087 // dimensions to zero for now, and deduce correct 01088 // dimensions by reading all of the file's entries and 01089 // computing the max(row index) and max(column index). 01090 // However, for now we just error out in that case. 01091 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, 01092 "Error reading Matrix Market sparse matrix: failed to read " 01093 "coordinate matrix dimensions."); 01094 } 01095 return dims; 01096 } 01097 01108 typedef SymmetrizingAdder<Raw::Adder<scalar_type, global_ordinal_type> > 01109 adder_type; 01110 01136 static RCP<adder_type> 01137 makeAdder (const RCP<const Comm<int> >& pComm, 01138 RCP<const Banner>& pBanner, 01139 const Tuple<global_ordinal_type, 3>& dims, 01140 const bool tolerant=false, 01141 const bool debug=false) 01142 { 01143 if (pComm->getRank() == 0) { 01144 typedef Raw::Adder<scalar_type, global_ordinal_type> raw_adder_type; 01145 RCP<raw_adder_type> pRaw = 01146 rcp (new raw_adder_type (dims[0], dims[1], dims[2], tolerant, debug)); 01147 return rcp (new adder_type (pRaw, pBanner->symmType())); 01148 } 01149 else { 01150 return null; 01151 } 01152 } 01153 01154 public: 01155 01180 static sparse_matrix_ptr 01181 readSparseFile (const std::string& filename, 01182 const RCP<const Comm<int> >& pComm, 01183 const RCP<node_type>& pNode, 01184 const bool callFillComplete=true, 01185 const bool tolerant=false, 01186 const bool debug=false) 01187 { 01188 const int myRank = Teuchos::rank (*pComm); 01189 std::ifstream in; 01190 01191 // Only open the file on Rank 0. 01192 if (myRank == 0) 01193 in.open (filename.c_str()); 01194 return readSparse (in, pComm, pNode, callFillComplete, tolerant, debug); 01195 // We can rely on the destructor of the input stream to close 01196 // the file on scope exit, even if readSparse() throws an 01197 // exception. 01198 } 01199 01226 static sparse_matrix_ptr 01227 readSparse (std::istream& in, 01228 const RCP<const Comm<int> >& pComm, 01229 const RCP<node_type>& pNode, 01230 const bool callFillComplete=true, 01231 const bool tolerant=false, 01232 const bool debug=false) 01233 { 01234 using std::cerr; 01235 using std::endl; 01236 typedef Teuchos::ScalarTraits<scalar_type> STS; 01237 01238 const bool extraDebug = false; 01239 const int myRank = Teuchos::rank (*pComm); 01240 01241 // Current line number in the input stream. Various calls 01242 // will modify this depending on the number of lines that are 01243 // read from the input stream. Only Rank 0 modifies this. 01244 size_t lineNumber = 1; 01245 01246 if (debug && myRank == 0) { 01247 cerr << "Matrix Market reader: readSparse:" << endl 01248 << "-- Reading banner line" << endl; 01249 } 01250 01251 // The "Banner" tells you whether the input stream represents 01252 // a sparse matrix, the symmetry type of the matrix, and the 01253 // type of the data it contains. 01254 // 01255 // pBanner will only be nonnull on MPI Rank 0. It will be 01256 // null on all other MPI processes. 01257 RCP<const Banner> pBanner; 01258 if (myRank == 0) 01259 { // Read the Banner line from the input stream. 01260 pBanner = readBanner (in, lineNumber, pComm, tolerant, debug); 01261 // Validate the Banner for the case of a sparse matrix. 01262 01263 // In tolerant mode, we allow the matrix type to be 01264 // anything other than "array" (which would mean that the 01265 // file contains a dense matrix). 01266 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && 01267 pBanner->matrixType() != "coordinate", 01268 std::runtime_error, 01269 "Matrix Market file must contain a " 01270 "\"coordinate\"-format sparse matrix in " 01271 "order to create a Tpetra::CrsMatrix " 01272 "object from it, but the file's matrix " 01273 "type is \"" << pBanner->matrixType() 01274 << "\" instead."); 01275 01276 // In tolerant mode, we allow the matrix type to be 01277 // anything other than "array" (which would mean that the 01278 // file contains a dense matrix). 01279 TEUCHOS_TEST_FOR_EXCEPTION(tolerant && pBanner->matrixType() == "array", 01280 std::runtime_error, 01281 "Matrix Market file must contain a " 01282 "\"coordinate\"-format sparse matrix in " 01283 "order to create a Tpetra::CrsMatrix " 01284 "object from it, but the file's matrix " 01285 "type is \"array\" instead. That means the " 01286 "file contains dense matrix data."); 01287 } 01288 if (debug && myRank == 0) 01289 cerr << "-- Reading dimensions line" << endl; 01290 01291 // Read the matrix dimensions from the Matrix Market metadata. 01292 // dims = (numRows, numCols, numEntries). Rank 0 does the 01293 // reading, but it broadcasts the results to all MPI 01294 // processes. Thus, readCoordDims() is a collective 01295 // operation. 01296 Tuple<global_ordinal_type, 3> dims = 01297 readCoordDims (in, lineNumber, pBanner, pComm, tolerant, debug); 01298 01299 if (debug && myRank == 0) 01300 cerr << "-- Making Adder for collecting matrix data" << endl; 01301 01302 // "Adder" object for collecting all the sparse matrix entries 01303 // from the input stream. This is only nonnull on Rank 0. 01304 RCP<adder_type> pAdder = 01305 makeAdder (pComm, pBanner, dims, tolerant, debug); 01306 01307 if (debug && myRank == 0) 01308 cerr << "-- Reading matrix data" << endl; 01309 // 01310 // Read the matrix entries from the input stream on Rank 0. 01311 // 01312 // We use readSuccess to broadcast the results of the read 01313 // (succeeded or not) to all MPI processes. "Guilty until 01314 // proven innocent" (the read didn't succeed unless we 01315 // proclaim that it did). 01316 bool readSuccess = false; 01317 if (myRank == 0) { 01318 // Reader for "coordinate" format sparse matrix data. 01319 typedef CoordDataReader<adder_type, global_ordinal_type, 01320 scalar_type, STS::isComplex> reader_type; 01321 reader_type reader (pAdder); 01322 01323 // Read the sparse matrix entries. 01324 // 01325 // FIXME (mfh 28 Mar 2011) We should catch exceptions 01326 // here, and broadcast any error messages to all 01327 // processors so that the exception can be rethrown 01328 // everywhere (fail fast and hard, rather than hoping that 01329 // MPI propagates the exceptions quickly). 01330 std::pair<bool, std::vector<size_t> > results = 01331 reader.read (in, lineNumber, tolerant, debug); 01332 01333 readSuccess = results.first; 01334 } 01335 // The broadcast of readSuccess from MPI Rank 0 serves as a 01336 // barrier. Note that Tpetra::CrsMatrix::fillComplete() only 01337 // starts with a barrier in debug mode, so we need some kind 01338 // of barrier or synchronization beforehand. 01339 // 01340 // Currently, Teuchos::broadcast doesn't know how to send 01341 // bools. For now, we convert to/from int instead, using the 01342 // usual "true is 1, false is 0" encoding. 01343 { 01344 // The value of this Boolean only matters on MPI Proc 0. 01345 int the_readSuccess = readSuccess ? 1 : 0; 01346 Teuchos::broadcast (*pComm, 0, &the_readSuccess); 01347 readSuccess = (the_readSuccess == 1); 01348 } 01349 // It would be nice to add a "verbose" flag, so that in 01350 // tolerant mode, we could log any bad line number(s) on MPI 01351 // Rank 0. For now, we just throw if the read fails to 01352 // succeed. 01353 // 01354 // Question: Should we run fillComplete() in tolerant mode, if 01355 // the read did not succeed? 01356 TEUCHOS_TEST_FOR_EXCEPTION(! readSuccess, std::runtime_error, 01357 "Failed to read in the Matrix Market sparse " 01358 "matrix."); 01359 if (debug && myRank == 0) 01360 cerr << "-- Successfully read the Matrix Market data" << endl; 01361 01362 // In tolerant mode, we need to rebroadcast the matrix 01363 // dimensions, since they may be different after reading the 01364 // actual matrix data. We only need to broadcast the number 01365 // of rows and columns. Only Rank 0 needs to know the actual 01366 // global number of entries, since (a) we need to merge 01367 // duplicates on Rank 0 first anyway, and (b) when we 01368 // distribute the entries, each rank other than Rank 0 will 01369 // only need to know how many entries it owns, not the total 01370 // number of entries. 01371 if (tolerant) 01372 { 01373 if (debug && myRank == 0) 01374 { 01375 cerr << "-- Tolerant mode: rebroadcasting matrix dimensions" 01376 << endl 01377 << "----- Dimensions before: " 01378 << dims[0] << " x " << dims[1] 01379 << endl; 01380 } 01381 // Packed coordinate matrix dimensions (numRows, numCols). 01382 Tuple<global_ordinal_type, 2> updatedDims; 01383 if (myRank == 0) 01384 { // If one or more bottom rows of the matrix contain no 01385 // entries, then the Adder will report that the number 01386 // of rows is less than that specified in the 01387 // metadata. We allow this case, and favor the 01388 // metadata so that the zero row(s) will be included. 01389 updatedDims[0] = 01390 std::max (dims[0], pAdder->getAdder()->numRows()); 01391 updatedDims[1] = pAdder->getAdder()->numCols(); 01392 } 01393 Teuchos::broadcast (*pComm, 0, updatedDims); 01394 dims[0] = updatedDims[0]; 01395 dims[1] = updatedDims[1]; 01396 if (debug && myRank == 0) { 01397 cerr << "----- Dimensions after: " << dims[0] << " x " 01398 << dims[1] << endl; 01399 } 01400 } 01401 else 01402 { // In strict mode, we require that the matrix's metadata 01403 // and its actual data agree, at least somewhat. In 01404 // particular, the number of rows must agree, since 01405 // otherwise we cannot distribute the matrix correctly. 01406 01407 // Teuchos::broadcast() doesn't know how to broadcast 01408 // bools, so we use an int with the standard 1 == true, 0 01409 // == false encoding. 01410 int dimsMatch = 1; 01411 if (myRank == 0) 01412 { // If one or more bottom rows of the matrix contain no 01413 // entries, then the Adder will report that the number 01414 // of rows is less than that specified in the 01415 // metadata. We allow this case, and favor the 01416 // metadata, but do not allow the Adder to think there 01417 // are more rows in the matrix than the metadata says. 01418 if (dims[0] < pAdder->getAdder()->numRows()) 01419 dimsMatch = 0; 01420 } 01421 Teuchos::broadcast (*pComm, 0, &dimsMatch); 01422 if (dimsMatch == 0) 01423 { // We're in an error state anyway, so we might as well 01424 // work a little harder to print an informative error 01425 // message. 01426 // 01427 // Broadcast the Adder's idea of the matrix dimensions 01428 // from Proc 0 to all processes. 01429 Tuple<global_ordinal_type, 2> addersDims; 01430 if (myRank == 0) 01431 { 01432 addersDims[0] = pAdder->getAdder()->numRows(); 01433 addersDims[1] = pAdder->getAdder()->numCols(); 01434 } 01435 Teuchos::broadcast (*pComm, 0, addersDims); 01436 TEUCHOS_TEST_FOR_EXCEPTION(dimsMatch == 0, std::runtime_error, 01437 "The matrix metadata says that the matrix is " 01438 << dims[0] << " x " << dims[1] << ", but the " 01439 "actual data says that the matrix is " 01440 << addersDims[0] << " x " << addersDims[1] 01441 << ". That means the data includes more " 01442 "rows than reported in the metadata. This " 01443 "is not allowed when parsing in strict mode." 01444 " Parse the matrix in tolerant mode to " 01445 "ignore the metadata when it disagrees with " 01446 "the data."); 01447 } 01448 } 01449 01450 if (debug && myRank == 0) 01451 cerr << "-- Converting matrix data into CSR format on Proc 0" << endl; 01452 01453 // Now that we've read in all the matrix entries from the 01454 // input stream into the adder on Rank 0, post-process them 01455 // into CSR format (still on Rank 0). This will facilitate 01456 // distributing them to all the processors. 01457 // 01458 // These arrays represent the global matrix data as a CSR 01459 // matrix (with numEntriesPerRow as redundant but convenient 01460 // metadata, since it's computable from rowPtr and vice 01461 // versa). They are valid only on Rank 0. 01462 ArrayRCP<size_t> numEntriesPerRow; 01463 ArrayRCP<size_type> rowPtr; 01464 ArrayRCP<global_ordinal_type> colInd; 01465 ArrayRCP<scalar_type> values; 01466 01467 // Rank 0 first merges duplicate entries, and then converts 01468 // the coordinate-format matrix data to CSR. 01469 if (myRank == 0) 01470 { 01471 typedef Raw::Element<scalar_type, global_ordinal_type> element_type; 01472 typedef typename std::vector<element_type>::const_iterator iter_type; 01473 01474 // Additively merge duplicate matrix entries. 01475 pAdder->getAdder()->merge (); 01476 01477 // Get a temporary const view of the merged matrix entries. 01478 const std::vector<element_type>& entries = 01479 pAdder->getAdder()->getEntries(); 01480 01481 // Number of rows in the matrix. If we are in tolerant 01482 // mode, we've already synchronized dims with the actual 01483 // matrix data. If in strict mode, we should use dims (as 01484 // read from the file's metadata) rather than the matrix 01485 // data to determine the dimensions. (The matrix data 01486 // will claim fewer rows than the metadata, if one or more 01487 // rows have no entries stored in the file.) 01488 const size_type numRows = dims[0]; 01489 01490 // Number of matrix entries (after merging). 01491 const size_type numEntries = entries.size(); 01492 01493 if (debug) 01494 cerr << "----- Proc 0: Matrix has numRows=" << numRows 01495 << " rows and numEntries=" << numEntries 01496 << " entries." << endl; 01497 01498 // Make space for the CSR matrix data. The conversion to 01499 // CSR algorithm is easier if we fill numEntriesPerRow 01500 // with zeros at first. 01501 numEntriesPerRow = arcp<size_t> (numRows); 01502 std::fill (numEntriesPerRow.begin(), numEntriesPerRow.end(), 01503 static_cast<size_t>(0)); 01504 rowPtr = arcp<size_type> (numRows+1); 01505 std::fill (rowPtr.begin(), rowPtr.end(), 01506 static_cast<size_type>(0)); 01507 colInd = arcp<global_ordinal_type> (numEntries); 01508 values = arcp<scalar_type> (numEntries); 01509 01510 // Convert from array-of-structs coordinate format to CSR 01511 // (compressed sparse row) format. 01512 { 01513 global_ordinal_type prvRow = 0; 01514 size_type curPos = 0; 01515 rowPtr[0] = 0; 01516 for (curPos = 0; curPos < numEntries; ++curPos) 01517 { 01518 const element_type& curEntry = entries[curPos]; 01519 const global_ordinal_type curRow = curEntry.rowIndex(); 01520 TEUCHOS_TEST_FOR_EXCEPTION(curRow < prvRow, std::logic_error, 01521 "Row indices out of order, even though " 01522 "they are supposed to be sorted. curRow" 01523 " = " << curRow << ", prvRow = " 01524 << prvRow << ", at curPos = " << curPos 01525 << ". Please report this bug to the " 01526 "Tpetra developers."); 01527 if (curRow > prvRow) 01528 { 01529 for (size_type r = prvRow+1; r <= curRow; ++r) 01530 rowPtr[r] = curPos; 01531 prvRow = curRow; 01532 } 01533 numEntriesPerRow[curRow]++; 01534 colInd[curPos] = curEntry.colIndex(); 01535 values[curPos] = curEntry.value(); 01536 } 01537 // rowPtr has one more entry than numEntriesPerRow. The 01538 // last entry of rowPtr is the number of entries in 01539 // colInd and values. 01540 rowPtr[numRows] = numEntries; 01541 } 01542 if (debug) 01543 { 01544 const size_type maxToDisplay = 100; 01545 01546 cerr << "----- Proc 0: numEntriesPerRow[0.." 01547 << (numEntriesPerRow.size()-1) << "] "; 01548 if (numRows > maxToDisplay) 01549 cerr << "(only showing first and last few entries) "; 01550 cerr << "= ["; 01551 if (numRows > 0) 01552 { 01553 if (numRows > maxToDisplay) 01554 { 01555 for (size_type k = 0; k < 2; ++k) 01556 cerr << numEntriesPerRow[k] << " "; 01557 cerr << "... "; 01558 for (size_type k = numRows-2; k < numRows-1; ++k) 01559 cerr << numEntriesPerRow[k] << " "; 01560 } 01561 else 01562 { 01563 for (size_type k = 0; k < numRows-1; ++k) 01564 cerr << numEntriesPerRow[k] << " "; 01565 } 01566 cerr << numEntriesPerRow[numRows-1]; 01567 } 01568 cerr << "]" << endl; 01569 01570 cerr << "----- Proc 0: rowPtr "; 01571 if (numRows > maxToDisplay) 01572 cerr << "(only showing first and last few entries) "; 01573 cerr << "= ["; 01574 if (numRows > maxToDisplay) 01575 { 01576 for (size_type k = 0; k < 2; ++k) 01577 cerr << rowPtr[k] << " "; 01578 cerr << "... "; 01579 for (size_type k = numRows-2; k < numRows; ++k) 01580 cerr << rowPtr[k] << " "; 01581 } 01582 else 01583 { 01584 for (size_type k = 0; k < numRows; ++k) 01585 cerr << rowPtr[k] << " "; 01586 } 01587 cerr << rowPtr[numRows] << "]" << endl; 01588 } 01589 } // if (myRank == 0) 01590 01591 // Now we're done with the Adder, so we can release the 01592 // reference ("free" it) to save space. This only actually 01593 // does anything on Rank 0, since pAdder is null on all the 01594 // other MPI processes. 01595 pAdder = null; 01596 01597 if (debug && myRank == 0) 01598 cerr << "-- Making range, domain, and row maps" << endl; 01599 01600 // Make the maps that describe the matrix'x range and domain, 01601 // and the distribution of its rows. 01602 map_ptr pRangeMap = makeRangeMap (pComm, pNode, dims[0]); 01603 map_ptr pDomainMap = makeDomainMap (pRangeMap, dims[0], dims[1]); 01604 map_ptr pRowMap = makeRowMap (null, pComm, pNode, dims[0]); 01605 01606 if (debug && myRank == 0) 01607 cerr << "-- Distributing the matrix data" << endl; 01608 01609 // Distribute the matrix data. Each processor has to add the 01610 // rows that it owns. If you try to make Rank 0 call 01611 // insertGlobalValues() for _all_ the rows, not just those it 01612 // owns, then fillComplete() will compute the number of 01613 // columns incorrectly. That's why Rank 0 has to distribute 01614 // the matrix data and why we make all the processors (not 01615 // just Rank 0) call insertGlobalValues() on their own data. 01616 // 01617 // These arrays represent each processor's part of the matrix 01618 // data, in "CSR" format (sort of, since the row indices might 01619 // not be contiguous). 01620 ArrayRCP<size_t> myNumEntriesPerRow; 01621 ArrayRCP<size_type> myRowPtr; 01622 ArrayRCP<global_ordinal_type> myColInd; 01623 ArrayRCP<scalar_type> myValues; 01624 // Distribute the matrix data. 01625 distribute (myNumEntriesPerRow, myRowPtr, myColInd, myValues, pRowMap, 01626 numEntriesPerRow, rowPtr, colInd, values, debug); 01627 01628 if (debug && myRank == 0) 01629 { 01630 cerr << "-- Inserting matrix entries on each processor"; 01631 if (callFillComplete) 01632 cerr << " and calling fillComplete()"; 01633 cerr << endl; 01634 } 01635 // Each processor inserts its part of the matrix data, and 01636 // then they all call fillComplete(). This method invalidates 01637 // the my* distributed matrix data before calling 01638 // fillComplete(), in order to save space. In general, we 01639 // never store more than two copies of the matrix's entries in 01640 // memory at once, which is no worse than what Tpetra 01641 // promises. 01642 sparse_matrix_ptr pMatrix = 01643 makeMatrix (myNumEntriesPerRow, myRowPtr, myColInd, myValues, 01644 pRowMap, pRangeMap, pDomainMap, callFillComplete); 01645 TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::logic_error, 01646 "Reader::makeMatrix() returned a null pointer. " 01647 "Please report this bug to the Tpetra developers."); 01648 01649 // We can't get the dimensions of the matix until after 01650 // fillComplete() is called. Thus, we can't do the sanity 01651 // check (dimensions read from the Matrix Market data, 01652 // vs. dimensions reported by the CrsMatrix) unless the user 01653 // asked makeMatrix() to call fillComplete(). 01654 // 01655 // Note that pMatrix->getGlobalNum{Rows,Cols}() does _not_ do 01656 // what one might think it does, so you have to ask the range 01657 // resp. domain map for the number of rows resp. columns. 01658 if (callFillComplete) 01659 { 01660 if (extraDebug && debug) 01661 { 01662 const global_size_t globalNumRows = 01663 pRangeMap->getGlobalNumElements(); 01664 const global_size_t globalNumCols = 01665 pDomainMap->getGlobalNumElements(); 01666 if (myRank == 0) 01667 { 01668 cerr << "-- Matrix is " 01669 << globalNumRows << " x " << globalNumCols 01670 << " with " << pMatrix->getGlobalNumEntries() 01671 << " entries, and index base " 01672 << pMatrix->getIndexBase() << "." << endl; 01673 } 01674 Teuchos::barrier (*pComm); 01675 for (int p = 0; p < Teuchos::size (*pComm); ++p) 01676 { 01677 if (myRank == p) 01678 { 01679 cerr << "-- Proc " << p << " owns " 01680 << pMatrix->getNodeNumCols() 01681 << " columns, and " 01682 << pMatrix->getNodeNumEntries() 01683 << " entries." << endl; 01684 } 01685 Teuchos::barrier (*pComm); 01686 } 01687 } // if (extraDebug && debug) 01688 } // if (callFillComplete) 01689 01690 if (debug && myRank == 0) 01691 cerr << "-- Done creating the CrsMatrix from the Matrix Market data" 01692 << endl; 01693 01694 return pMatrix; 01695 } 01696 01726 static RCP<multivector_type> 01727 readDenseFile (const std::string& filename, 01728 const RCP<const comm_type>& pComm, 01729 const RCP<node_type>& pNode, 01730 RCP<const map_type>& pMap, 01731 const bool tolerant=false, 01732 const bool debug=false) 01733 { 01734 const int myRank = Teuchos::rank (*pComm); 01735 std::ifstream in; 01736 01737 // Only open the file on Rank 0. 01738 if (myRank == 0) 01739 in.open (filename.c_str()); 01740 return readDense (in, pComm, pNode, pMap, tolerant, debug); 01741 // We can rely on the destructor of the input stream to close 01742 // the file on scope exit, even if readSparse() throws an 01743 // exception. 01744 } 01745 01810 static RCP<multivector_type> 01811 readDense (std::istream& in, 01812 const RCP<const comm_type>& pComm, 01813 const RCP<node_type>& pNode, 01814 RCP<const map_type>& pMap, 01815 const bool tolerant=false, 01816 const bool debug=false) 01817 { 01818 using std::cerr; 01819 using std::endl; 01820 01821 // Abbreviations for typedefs, to make the code more concise. 01822 typedef scalar_type S; 01823 typedef local_ordinal_type LO; 01824 typedef global_ordinal_type GO; 01825 typedef node_type Node; 01826 typedef Teuchos::ScalarTraits<S> STS; 01827 typedef typename STS::magnitudeType M; 01828 typedef Teuchos::ScalarTraits<M> STM; 01829 01830 // Rank 0 is the only (MPI) process allowed to read from the 01831 // input stream. 01832 const int myRank = pComm->getRank (); 01833 01834 if (debug && myRank == 0) { 01835 cerr << "Matrix Market reader: readDense:" << endl; 01836 } 01837 01838 // If pMap is nonnull, check the precondition that its 01839 // communicator resp. node equal pComm resp. pNode. Checking 01840 // now avoids doing a lot of file reading before we detect the 01841 // violated precondition. 01842 TEUCHOS_TEST_FOR_EXCEPTION(! pMap.is_null() && 01843 (pMap->getComm() != pComm || 01844 pMap->getNode() != pNode), 01845 std::invalid_argument, "If you supply a nonnull Map, the Map's " 01846 "communicator and node must equal the supplied communicator resp. " 01847 "node."); 01848 01849 // Rank 0 will read in the matrix dimensions from the file, 01850 // and broadcast them to all ranks in the given communicator. 01851 // There are only 2 dimensions in the matrix, but we use the 01852 // third element of the Tuple to encode the banner's reported 01853 // data type: "real" == 0, "complex" == 1, and "integer" == 0 01854 // (same as "real"). We don't allow pattern matrices (i.e., 01855 // graphs) since they only make sense for sparse data. 01856 Tuple<GO, 3> dims; 01857 dims[0] = 0; 01858 dims[1] = 0; 01859 01860 // Current line number in the input stream. Only valid on 01861 // Rank 0. Various calls will modify this depending on the 01862 // number of lines that are read from the input stream. 01863 size_t lineNumber = 1; 01864 01865 // Only Rank 0 gets to read matrix data from the input stream. 01866 if (myRank == 0) { 01867 if (debug && myRank == 0) { 01868 cerr << "-- Reading banner line (dense)" << endl; 01869 } 01870 01871 // The "Banner" tells you whether the input stream 01872 // represents a dense matrix, the symmetry type of the 01873 // matrix, and the type of the data it contains. 01874 RCP<const Banner> pBanner = 01875 readBanner (in, lineNumber, pComm, tolerant, debug); 01876 TEUCHOS_TEST_FOR_EXCEPTION(pBanner->matrixType() != "array", 01877 std::invalid_argument, "The Matrix Market file does not contain " 01878 "dense matrix data. Its banner (first) line says that its matrix " 01879 "type is \"" << pBanner->matrixType() << "\", rather than the " 01880 "required \"array\"."); 01881 TEUCHOS_TEST_FOR_EXCEPTION(pBanner->dataType() == "pattern", 01882 std::invalid_argument, "The Matrix Market file's banner (first) " 01883 "line claims that the matrix's data type is \"pattern\". This does " 01884 "not make sense for a dense matrix, yet the file reports the matrix " 01885 "as dense. The only valid data types for a dense matrix are " 01886 "\"real\", \"complex\", and \"integer\"."); 01887 // Encode the data type reported by the Banner as the third 01888 // element of the dimensions Tuple. 01889 dims[2] = encodeDataType (pBanner->dataType()); 01890 01891 if (debug && myRank == 0) { 01892 cerr << "-- Reading dimensions line (dense)" << endl; 01893 } 01894 // Keep reading lines from the input stream until we find a 01895 // non-comment line, or until we run out of lines. The 01896 // latter is an error, since every "array" format Matrix 01897 // Market file must have a dimensions line after the banner 01898 // (even if the matrix has zero rows or columns, or zero 01899 // entries). 01900 std::string line; 01901 bool commentLine = true; 01902 while (commentLine) { 01903 // Test whether it is even valid to read from the 01904 // input stream wrapping the line. 01905 TEUCHOS_TEST_FOR_EXCEPTION(in.eof() || in.fail(), std::runtime_error, 01906 "Unable to get array dimensions line (at all) from line " 01907 << lineNumber << " of input stream. The input stream claims that " 01908 "it is " << (in.eof() ? "at end-of-file." : "in a failed state.")); 01909 // Try to get the next line from the input stream. 01910 if (getline(in, line)) { 01911 ++lineNumber; // We did actually read a line. 01912 } 01913 // Is the current line a comment line? Ignore start and 01914 // size; they are only useful for reading the actual 01915 // matrix entries. (We could use them here as an 01916 // optimization, but we've chosen not to.) 01917 size_t start = 0, size = 0; 01918 commentLine = checkCommentLine (line, start, size, lineNumber, tolerant); 01919 } 01920 // 01921 // Read in <numRows> <numCols> from input line. 01922 // 01923 std::istringstream istr (line); 01924 01925 // Test whether it is even valid to read from the input 01926 // stream wrapping the line. 01927 TEUCHOS_TEST_FOR_EXCEPTION(istr.eof() || istr.fail(), std::runtime_error, 01928 "Unable to read any data from line " << lineNumber 01929 << " of input; the line should contain the matrix " 01930 << "dimensions \"<numRows> <numCols>\"."); 01931 // Read in <numRows>. 01932 { 01933 GO theNumRows = 0; 01934 istr >> theNumRows; 01935 TEUCHOS_TEST_FOR_EXCEPTION(istr.fail(), std::runtime_error, 01936 "Failed to get number of rows from line " 01937 << lineNumber << " of input; the line should " 01938 "contain the matrix dimensions \"<numRows> " 01939 "<numCols>\"."); 01940 // Capture the validly read result before checking for eof. 01941 dims[0] = theNumRows; 01942 } 01943 // There should be one more thing to read. 01944 TEUCHOS_TEST_FOR_EXCEPTION(istr.eof(), std::runtime_error, 01945 "No more data after number of rows on line " 01946 << lineNumber << " of input; the line should " 01947 "contain the matrix dimensions \"<numRows> " 01948 "<numCols>\"."); 01949 // Read in <numCols> 01950 { 01951 GO theNumCols = 0; 01952 istr >> theNumCols; 01953 TEUCHOS_TEST_FOR_EXCEPTION(istr.fail(), std::runtime_error, 01954 "Failed to get number of columns from line " 01955 << lineNumber << " of input; the line should " 01956 "contain the matrix dimensions \"<numRows> " 01957 "<numCols>\"."); 01958 // Capture the validly read result. 01959 dims[1] = theNumCols; 01960 } 01961 } // if (myRank == 0) 01962 01963 // Broadcast matrix dimensions and the encoded data type from 01964 // MPI Rank 0 to all the MPI processes. 01965 Teuchos::broadcast (*pComm, 0, dims); 01966 01967 // Tpetra objects want the matrix dimensions in these types. 01968 const global_size_t numRows = static_cast<global_size_t> (dims[0]); 01969 const size_t numCols = static_cast<size_t> (dims[1]); 01970 01971 // Make an "everything on Rank 0" map pRank0Map. We'll use 01972 // this to construct a "distributed" vector X owned entirely 01973 // by Rank 0. Rank 0 will then read all the matrix entries 01974 // and put them in X. 01975 RCP<const map_type> pRank0Map = 01976 createContigMapWithNode<LO, GO, Node> (numRows, 01977 (myRank == 0 ? numRows : 0), 01978 pComm, pNode); 01979 01980 // Make a multivector X owned entirely by Rank 0. 01981 RCP<multivector_type> X = 01982 createMultiVector<S, LO, GO, Node> (pRank0Map, numCols); 01983 01984 // 01985 // On Rank 0, read the Matrix Market data from the input 01986 // stream into the multivector X. 01987 // 01988 if (myRank == 0) { 01989 if (debug && myRank == 0) { 01990 cerr << "-- Reading matrix data (dense)" << endl; 01991 } 01992 01993 // Make sure that we can get a 1-D view of X. 01994 TEUCHOS_TEST_FOR_EXCEPTION(! X->isConstantStride (), std::logic_error, 01995 "Can't get a 1-D view of the entries of the " 01996 "MultiVector X on Rank 0, because the stride " 01997 "between the columns of X is not constant. " 01998 "This shouldn't happen because we just created " 01999 "X and haven't filled it in yet. Please report " 02000 "this bug to the Tpetra developers."); 02001 02002 // Get a writeable 1-D view of the entries of X. Rank 0 02003 // owns all of them. The view will expire at the end of 02004 // scope, so (if necessary) it will be written back to X 02005 // at this time. 02006 ArrayRCP<S> X_view = X->get1dViewNonConst (); 02007 TEUCHOS_TEST_FOR_EXCEPTION(static_cast<global_size_t> (X_view.size()) < numRows * numCols, 02008 std::logic_error, 02009 "The view of X has size " << X_view 02010 << " which is not enough to accommodate the " 02011 "expected number of entries numRows*numCols = " 02012 << numRows << "*" << numCols << " = " 02013 << numRows*numCols << ". Please report this " 02014 "bug to the Tpetra developers."); 02015 const size_t stride = X->getStride (); 02016 02017 // The third element of the dimensions Tuple encodes the data 02018 // type reported by the Banner: "real" == 0, "complex" == 1, 02019 // "integer" == 0 (same as "real"), "pattern" == 2. We do not 02020 // allow dense matrices to be pattern matrices, so dims[2] == 02021 // 0 or 1. We've already checked for this above. 02022 const bool isComplex = (dims[2] == 1); 02023 typename ArrayRCP<S>::size_type count = 0, curRow = 0, curCol = 0; 02024 02025 std::string line; 02026 while (getline(in, line)) { 02027 ++lineNumber; 02028 // Is the current line a comment line? If it's not, 02029 // line.substr(start,size) contains the data. 02030 size_t start = 0, size = 0; 02031 const bool commentLine = 02032 checkCommentLine (line, start, size, lineNumber, tolerant); 02033 if (! commentLine) { 02034 // Make sure we have room in which to put the new matrix 02035 // entry. We check this only after checking for a 02036 // comment line, because there may be one or more 02037 // comment lines at the end of the file. In tolerant 02038 // mode, we simply ignore any extra data. 02039 if (count >= X_view.size()) { 02040 if (tolerant) { 02041 break; 02042 } 02043 else { 02044 TEUCHOS_TEST_FOR_EXCEPTION(count >= X_view.size(), 02045 std::runtime_error, 02046 "The Matrix Market input stream has " 02047 "more data in it than its metadata " 02048 "reported. Current line number is " 02049 << lineNumber << "."); 02050 } 02051 } 02052 std::istringstream istr (line.substr (start, size)); 02053 // Does the line contain anything at all? Can we 02054 // safely read from the input stream wrapping the 02055 // line? 02056 if (istr.eof() || istr.fail()) { 02057 // In tolerant mode, simply ignore the line. 02058 if (tolerant) { 02059 break; 02060 } 02061 // We repeat the full test here so the exception 02062 // message is more informative. 02063 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && (istr.eof() || istr.fail()), 02064 std::runtime_error, "Line " << lineNumber << " of the Matrix " 02065 "Market file is empty, or we cannot read from it for some " 02066 "other reason."); 02067 } 02068 // Current matrix entry to read in. 02069 S val = STS::zero(); 02070 // Real and imaginary parts of the current matrix entry. 02071 // The imaginary part is zero if the matrix is 02072 // real-valued. 02073 M real = STM::zero(), imag = STM::zero(); 02074 02075 // isComplex refers to the input stream's data, not to 02076 // the scalar type S. It's OK to read real-valued data 02077 // into a matrix storing complex-valued data; in that 02078 // case, all entries' imaginary parts are zero. 02079 if (isComplex) { 02080 // STS::real() and STS::imag() return a copy of their 02081 // respective components, not a writeable reference. 02082 // Otherwise we could just assign to them using the 02083 // istream extraction operator (>>). That's why we 02084 // have separate magnitude type "real" and "imag" 02085 // variables. 02086 02087 // Attempt to read the real part of the current matrix 02088 // entry. 02089 istr >> real; 02090 if (istr.fail()) { 02091 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && istr.fail(), 02092 std::runtime_error, 02093 "Failed to get real part of a " 02094 "complex-valued matrix entry " 02095 "from line " << lineNumber 02096 << " of Matrix Market file."); 02097 // In tolerant mode, just skip bad lines. 02098 if (tolerant) { 02099 break; 02100 } 02101 } 02102 else if (istr.eof()) { 02103 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && istr.eof(), 02104 std::runtime_error, 02105 "Missing imaginary part of a " 02106 "complex-valued matrix entry " 02107 "on line " << lineNumber 02108 << " of Matrix Market file."); 02109 // In tolerant mode, let any missing imaginary part 02110 // be 0. 02111 } 02112 else { 02113 // Attempt to read the imaginary part of the current 02114 // matrix entry. 02115 istr >> imag; 02116 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && istr.fail(), 02117 std::runtime_error, 02118 "Failed to get imaginary part " 02119 "of a complex-valued matrix " 02120 "entry from line " << lineNumber 02121 << " of Matrix Market file."); 02122 // In tolerant mode, let any missing 02123 // or corrupted imaginary part be 0. 02124 } 02125 } 02126 else { // Matrix Market file contains real-valued data. 02127 // Attempt to read the current matrix entry. 02128 istr >> real; 02129 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && istr.fail(), 02130 std::runtime_error, 02131 "Failed to get a real-valued matrix " 02132 "entry from line " << lineNumber 02133 << " of Matrix Market file."); 02134 // In tolerant mode, simply ignore the line if 02135 // we failed to read a matrix entry. 02136 if (istr.fail() && tolerant) { 02137 break; 02138 } 02139 } 02140 02141 // In tolerant mode, we simply let pass through whatever 02142 // data we got. 02143 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && istr.fail(), 02144 std::runtime_error, 02145 "Failed to read matrix data from line " 02146 << lineNumber << " of the Matrix Market " 02147 "file."); 02148 02149 // val = S(real, imag), without potential badness 02150 // if S is a real type. 02151 details::assignScalar<S> (val, real, imag); 02152 02153 curRow = count % numRows; 02154 curCol = count / numRows; 02155 X_view[curRow + curCol*stride] = val; 02156 ++count; 02157 } // if not a comment line 02158 } // while there are still lines in the file, get the next one 02159 02160 TEUCHOS_TEST_FOR_EXCEPTION(! tolerant && 02161 static_cast<global_size_t> (count) < numRows * numCols, 02162 std::runtime_error, 02163 "The Matrix Market metadata reports that the " 02164 "dense matrix is " << numRows << " x " 02165 << numCols << ", and thus has " 02166 << numRows*numCols << " total entries, but we " 02167 "only managed to find " << count << " entr" 02168 << (count == 1 ? "y" : "ies") 02169 << " in the file."); 02170 } // if (myRank == 0) 02171 02172 // If there's only one MPI process and the user didn't supply 02173 // a Map (i.e., pMap is null), we're done. Set pMap to the 02174 // Map used to distribute X, and return X. 02175 if (pComm->getSize() == 1 && pMap.is_null()) { 02176 pMap = pRank0Map; 02177 if (debug && myRank == 0) { 02178 cerr << "-- Done reading multivector" << endl; 02179 } 02180 return X; 02181 } 02182 02183 if (debug && myRank == 0) { 02184 cerr << "-- Creating distributed Map and target MultiVector" << endl; 02185 } 02186 02187 // If pMap is null, make a distributed map. We've already 02188 // checked the preconditions above, in the case that pMap was 02189 // not null on input to this method. 02190 if (pMap.is_null()) { 02191 pMap = createUniformContigMapWithNode<LO, GO, Node> (numRows, pComm, pNode); 02192 } 02193 // Make a multivector Y with the distributed map pMap. 02194 RCP<multivector_type> Y = createMultiVector<S, LO, GO, Node> (pMap, numCols); 02195 02196 // Make an Export object that will export X to Y. First 02197 // argument is the source map, second argument is the target 02198 // map. 02199 Export<LO, GO, Node> exporter (pRank0Map, pMap); 02200 02201 if (debug && myRank == 0) { 02202 cerr << "-- Exporting from X (owned by Rank 0) to globally owned Y" 02203 << endl; 02204 } 02205 // Export X into Y. 02206 Y->doExport (*X, exporter, INSERT); 02207 02208 if (debug && myRank == 0) { 02209 cerr << "-- Done reading multivector" << endl; 02210 } 02211 02212 // Y is distributed over all process(es) in the communicator. 02213 return Y; 02214 } 02215 02216 private: 02217 02228 static int 02229 encodeDataType (const std::string& dataType) 02230 { 02231 if (dataType == "real" || dataType == "integer") { 02232 return 0; 02233 } 02234 else if (dataType == "complex") { 02235 return 1; 02236 } 02237 else if (dataType == "pattern") { 02238 return 2; 02239 } 02240 else { 02241 // We should never get here, since Banner validates the 02242 // reported data type and ensures it is one of the accepted 02243 // values. 02244 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error, 02245 "Unrecognized Matrix Market data type \"" << dataType << "\"."); 02246 } 02247 } 02248 }; 02249 02289 template<class SparseMatrixType> 02290 class Writer { 02291 public: 02292 typedef SparseMatrixType sparse_matrix_type; 02293 typedef RCP<sparse_matrix_type> sparse_matrix_ptr; 02294 02297 typedef typename SparseMatrixType::scalar_type scalar_type; 02300 typedef typename SparseMatrixType::local_ordinal_type local_ordinal_type; 02307 typedef typename SparseMatrixType::global_ordinal_type global_ordinal_type; 02310 typedef typename SparseMatrixType::node_type node_type; 02311 02314 typedef MultiVector<scalar_type, 02315 local_ordinal_type, 02316 global_ordinal_type, 02317 node_type> multivector_type; 02320 typedef Map<local_ordinal_type, global_ordinal_type, node_type> map_type; 02321 02353 static void 02354 writeSparseFile (const std::string& filename, 02355 const RCP<const sparse_matrix_type>& pMatrix, 02356 const std::string& matrixName, 02357 const std::string& matrixDescription, 02358 const bool debug=false) 02359 { 02360 const int myRank = Teuchos::rank (*(pMatrix->getComm())); 02361 std::ofstream out; 02362 02363 // Only open the file on Rank 0. 02364 if (myRank == 0) out.open (filename.c_str()); 02365 writeSparse (out, pMatrix, matrixName, matrixDescription, debug); 02366 // We can rely on the destructor of the output stream to close 02367 // the file on scope exit, even if writeSparse() throws an 02368 // exception. 02369 } 02370 02391 static void 02392 writeSparseFile (const std::string& filename, 02393 const RCP<const sparse_matrix_type>& pMatrix, 02394 const bool debug=false) 02395 { 02396 writeSparseFile (filename, pMatrix, "", "", debug); 02397 } 02398 02399 public: 02400 02431 static void 02432 writeSparse (std::ostream& out, 02433 const RCP<const sparse_matrix_type>& pMatrix, 02434 const std::string& matrixName, 02435 const std::string& matrixDescription, 02436 const bool debug=false) 02437 { 02438 using std::cerr; 02439 using std::endl; 02440 typedef typename Teuchos::ScalarTraits<scalar_type> STS; 02441 typedef typename STS::magnitudeType magnitude_type; 02442 typedef typename Teuchos::ScalarTraits<magnitude_type> STM; 02443 typedef typename ArrayView<scalar_type>::size_type size_type; 02444 02445 // Convenient abbreviations. 02446 typedef local_ordinal_type LO; 02447 typedef global_ordinal_type GO; 02448 02449 // Make the output stream write floating-point numbers in 02450 // scientific notation. It will politely put the output 02451 // stream back to its state on input, when this scope 02452 // terminates. 02453 details::SetScientific<scalar_type> sci (out); 02454 02455 RCP<const Comm<int> > pComm = pMatrix->getComm(); 02456 const int myRank = Teuchos::rank (*pComm); 02457 // Whether to print debugging output to stderr. 02458 const bool debugPrint = debug && myRank == 0; 02459 02460 const global_size_t numRows = 02461 pMatrix->getRangeMap()->getGlobalNumElements(); 02462 const global_size_t numCols = 02463 pMatrix->getDomainMap()->getGlobalNumElements(); 02464 if (debugPrint) { 02465 cerr << "writeSparse:" << endl 02466 << "-- Input sparse matrix is:" 02467 << "---- " << numRows << " x " << numCols << " with " 02468 << pMatrix->getGlobalNumEntries() << " entries;" << endl 02469 << "---- " 02470 << (pMatrix->isGloballyIndexed() ? "Globally" : "Locally") 02471 << " indexed." << endl; 02472 } 02473 RCP<const map_type> pRowMap = pMatrix->getRowMap(); 02474 if (debugPrint) { 02475 RCP<const map_type> pColMap = pMatrix->getColMap(); 02476 cerr << "---- Its row map has " << pRowMap->getGlobalNumElements() 02477 << " elements." << endl 02478 << "---- Its col map has " << pColMap->getGlobalNumElements() 02479 << " elements." << endl; 02480 } 02481 // Make the "gather" row map, where Proc 0 owns all rows and 02482 // the other procs own no rows. 02483 const size_t localNumRows = (myRank == 0) ? numRows : 0; 02484 RCP<node_type> pNode = pRowMap->getNode(); 02485 RCP<const map_type> pGatherRowMap = 02486 createContigMapWithNode<LO, GO, node_type> (numRows, localNumRows, 02487 pComm, pNode); 02488 // Since the matrix may in general be non-square, we need to 02489 // make a column map as well. In this case, the column map 02490 // contains all the columns of the original matrix, because we 02491 // are gathering the whole matrix onto Proc 0. 02492 const size_t localNumCols = (myRank == 0) ? numCols : 0; 02493 RCP<const map_type> pGatherColMap = 02494 createContigMapWithNode<LO, GO, node_type> (numCols, localNumCols, 02495 pComm, pNode); 02496 02497 // Current map is the source map, gather map is the target map. 02498 typedef Import<LO, GO, node_type> import_type; 02499 import_type importer (pRowMap, pGatherRowMap); 02500 02501 // Create a new CrsMatrix to hold the result of the import. 02502 // The constructor needs a column map as well as a row map, 02503 // for the case that the matrix is not square. 02504 RCP<sparse_matrix_type> newMatrix = 02505 rcp (new sparse_matrix_type (pGatherRowMap, 02506 pGatherColMap, 02507 size_t(0))); 02508 // Import the sparse matrix onto Proc 0. 02509 newMatrix->doImport (*pMatrix, importer, INSERT); 02510 02511 // fillComplete() needs the domain and range maps for the case 02512 // that the matrix is not square. 02513 { 02514 RCP<const map_type> pGatherDomainMap = 02515 createContigMapWithNode<LO, GO, node_type> (numCols, localNumCols, 02516 pComm, pNode); 02517 RCP<const map_type> pGatherRangeMap = 02518 createContigMapWithNode<LO, GO, node_type> (numRows, localNumRows, 02519 pComm, pNode); 02520 newMatrix->fillComplete (pGatherDomainMap, pGatherRangeMap); 02521 } 02522 02523 if (debugPrint) { 02524 cerr << "-- Output sparse matrix is:" 02525 << "---- " << newMatrix->getRangeMap()->getGlobalNumElements() 02526 << " x " 02527 << newMatrix->getDomainMap()->getGlobalNumElements() 02528 << " with " 02529 << newMatrix->getGlobalNumEntries() << " entries;" << endl 02530 << "---- " 02531 << (newMatrix->isGloballyIndexed() ? "Globally" : "Locally") 02532 << " indexed." << endl 02533 << "---- Its row map has " 02534 << newMatrix->getRowMap()->getGlobalNumElements() 02535 << " elements." << endl 02536 << "---- Its col map has " 02537 << newMatrix->getColMap()->getGlobalNumElements() 02538 << " elements (may be different than the input matrix's column" 02539 << " map, if some columns of the matrix contain no entries)." 02540 << endl; 02541 } 02542 02543 // 02544 // Print the metadata and the matrix entries on Rank 0. 02545 // 02546 if (myRank == 0) { 02547 // Print the Matrix Market banner line. CrsMatrix stores 02548 // data nonsymmetrically ("general"). This implies that 02549 // readSparse() on a symmetrically stored input file, 02550 // followed by writeSparse() on the resulting sparse matrix, 02551 // will result in an output file with a different banner 02552 // line than the original input file. 02553 out << "%%MatrixMarket matrix coordinate " 02554 << (STS::isComplex ? "complex" : "real") 02555 << " general" << endl; 02556 02557 // Print comments (the matrix name and / or description). 02558 if (matrixName != "") 02559 printAsComment (out, matrixName); 02560 if (matrixDescription != "") 02561 printAsComment (out, matrixDescription); 02562 02563 // Print the Matrix Market header (# rows, # columns, # 02564 // nonzeros). Use the range resp. domain map for the 02565 // number of rows resp. columns, since Tpetra::CrsMatrix 02566 // uses the column map for the number of columns. That 02567 // only corresponds to the "linear-algebraic" number of 02568 // columns when the column map is 1-1, which only happens 02569 // if the matrix is (block) diagonal. 02570 out << newMatrix->getRangeMap()->getGlobalNumElements() 02571 << " " 02572 << newMatrix->getDomainMap()->getGlobalNumElements() 02573 << " " 02574 << newMatrix->getGlobalNumEntries() 02575 << endl; 02576 02577 // Index base (0-based or 1-based) for the row map. 02578 const GO rowIndexBase = pGatherRowMap->getIndexBase(); 02579 // Index base (0-based or 1-based) for the column map. 02580 const GO colIndexBase = newMatrix->getColMap()->getIndexBase(); 02581 02582 // 02583 // Print the entries of the matrix. 02584 // 02585 // newMatrix can never be globally indexed, since we 02586 // called fillComplete() on it. We include code for both 02587 // cases (globally or locally indexed) just in case that 02588 // ever changes. 02589 if (newMatrix->isGloballyIndexed()) { 02590 for (GO globalRowIndex = pGatherRowMap->getMinAllGlobalIndex(); 02591 globalRowIndex <= pGatherRowMap->getMaxAllGlobalIndex(); 02592 ++globalRowIndex) 02593 { 02594 ArrayView<const GO> ind; 02595 ArrayView<const scalar_type> val; 02596 02597 newMatrix->getGlobalRowView (globalRowIndex, ind, val); 02598 typename ArrayView<const GO>::const_iterator indIter = 02599 ind.begin(); 02600 typename ArrayView<const scalar_type>::const_iterator valIter = 02601 val.begin(); 02602 for (; indIter != ind.end() && valIter != val.end(); 02603 ++indIter, ++valIter) 02604 { 02605 const GO globalColIndex = *indIter; 02606 02607 // Matrix Market files use 1-based indices. 02608 out << (globalRowIndex + 1 - rowIndexBase) << " " 02609 << (globalColIndex + 1 - colIndexBase) << " "; 02610 if (STS::isComplex) 02611 out << STS::real(*valIter) << " " << STS::imag(*valIter); 02612 else 02613 out << *valIter; 02614 out << endl; 02615 } 02616 } 02617 } 02618 else // newMatrix is locally indexed 02619 { 02620 typedef OrdinalTraits<GO> OTG; 02621 RCP<const map_type> pColMap = newMatrix->getColMap (); 02622 02623 for (LO localRowIndex = pGatherRowMap->getMinLocalIndex(); 02624 localRowIndex <= pGatherRowMap->getMaxLocalIndex(); 02625 ++localRowIndex) 02626 { 02627 // Convert from local to global row index. 02628 const GO globalRowIndex = 02629 pGatherRowMap->getGlobalElement (localRowIndex); 02630 TEUCHOS_TEST_FOR_EXCEPTION(globalRowIndex == OTG::invalid(), 02631 std::logic_error, 02632 "Failed to convert \"local\" row index " 02633 << localRowIndex << " into a global row " 02634 "index. Please report this bug to the " 02635 "Tpetra developers."); 02636 ArrayView<const LO> ind; 02637 ArrayView<const scalar_type> val; 02638 02639 newMatrix->getLocalRowView (localRowIndex, ind, val); 02640 typename ArrayView<const LO>::const_iterator indIter = 02641 ind.begin(); 02642 typename ArrayView<const scalar_type>::const_iterator 02643 valIter = val.begin(); 02644 for (; indIter != ind.end() && valIter != val.end(); 02645 ++indIter, ++valIter) 02646 { 02647 // Convert from local to global index. 02648 const GO globalColIndex = 02649 pColMap->getGlobalElement (*indIter); 02650 TEUCHOS_TEST_FOR_EXCEPTION(globalColIndex == OTG::invalid(), 02651 std::logic_error, 02652 "Failed to convert \"local\" column " 02653 "index " << *indIter << " into a " 02654 "global column index. Please report " 02655 "this bug to the Tpetra developers."); 02656 // Matrix Market files use 1-based indices. 02657 out << (globalRowIndex + 1 - rowIndexBase) << " " 02658 << (globalColIndex + 1 - colIndexBase) << " "; 02659 if (STS::isComplex) 02660 out << STS::real(*valIter) << " " 02661 << STS::imag(*valIter); 02662 else 02663 out << *valIter; 02664 out << endl; 02665 } 02666 } 02667 } 02668 } 02669 } 02670 02693 static void 02694 writeSparse (std::ostream& out, 02695 const RCP<const sparse_matrix_type>& pMatrix, 02696 const bool debug=false) 02697 { 02698 writeSparse (out, pMatrix, "", "", debug); 02699 } 02700 02727 static void 02728 writeDenseFile (const std::string& filename, 02729 const RCP<const multivector_type>& X, 02730 const std::string& matrixName, 02731 const std::string& matrixDescription) 02732 { 02733 const int myRank = Teuchos::rank (*(X->getMap()->getComm())); 02734 std::ofstream out; 02735 02736 if (myRank == 0) // Only open the file on Rank 0. 02737 out.open (filename.c_str()); 02738 02739 writeDense (out, X, matrixName, matrixDescription); 02740 // We can rely on the destructor of the output stream to close 02741 // the file on scope exit, even if writeDense() throws an 02742 // exception. 02743 } 02744 02763 static void 02764 writeDenseFile (const std::string& filename, 02765 const RCP<const multivector_type>& X) 02766 { 02767 writeDenseFile (filename, X, "", ""); 02768 } 02769 02796 static void 02797 writeDense (std::ostream& out, 02798 const RCP<const multivector_type>& X, 02799 const std::string& matrixName, 02800 const std::string& matrixDescription) 02801 { 02802 using Teuchos::rcp; 02803 using Teuchos::rcp_const_cast; 02804 using std::endl; 02805 02806 typedef typename Teuchos::ScalarTraits<scalar_type> STS; 02807 typedef typename STS::magnitudeType magnitude_type; 02808 typedef typename Teuchos::ScalarTraits<magnitude_type> STM; 02809 typedef typename ArrayView<scalar_type>::size_type size_type; 02810 02811 // Convenient abbreviations. 02812 typedef local_ordinal_type LO; 02813 typedef global_ordinal_type GO; 02814 typedef node_type NT; 02815 02816 // If true, when making the "gather" Map, use the same index 02817 // base as X's Map. Otherwise, just use the default index 02818 // base for the gather Map. 02819 const bool keepTheSameIndexBase = false; 02820 02821 // Make the output stream write floating-point numbers in 02822 // scientific notation. It will politely put the output 02823 // stream back to its state on input, when this scope 02824 // terminates. 02825 details::SetScientific<scalar_type> sci (out); 02826 02827 RCP<const Comm<int> > comm = X->getMap()->getComm(); 02828 const int myRank = comm->getRank (); 02829 RCP<const map_type> map = X->getMap(); 02830 const global_size_t numRows = map->getGlobalNumElements(); 02831 // Promote to global_size_t. 02832 const global_size_t numCols = X->getNumVectors(); 02833 02834 // Make the "gather" map, where Proc 0 owns all rows of X, and 02835 // the other procs own no rows. 02836 const size_t localNumRows = (myRank == 0) ? numRows : 0; 02837 RCP<NT> node = map->getNode(); 02838 RCP<const map_type> gatherMap = keepTheSameIndexBase ? 02839 rcp_const_cast<const map_type> (rcp (new map_type (numRows, localNumRows, map->getIndexBase(), comm, node))) : 02840 createContigMapWithNode<LO, GO, NT> (numRows, localNumRows, comm, node); 02841 02842 // Create an Import object to import X's data into a 02843 // multivector Y owned entirely by Proc 0. In the Import 02844 // constructor, X's map is the source map, and the "gather 02845 // map" is the target map. 02846 Import<LO, GO, NT> importer (map, gatherMap); 02847 02848 // Create a new multivector Y to hold the result of the import. 02849 RCP<multivector_type> Y = 02850 createMultiVector<scalar_type, LO, GO, NT> (gatherMap, numCols); 02851 02852 // Import the multivector onto Proc 0. 02853 Y->doImport (*X, importer, INSERT); 02854 02855 // 02856 // Print the matrix in Matrix Market format on Rank 0. 02857 // 02858 if (myRank == 0) { 02859 // Print the Matrix Market banner line. MultiVector 02860 // stores data nonsymmetrically, hence "general". 02861 out << "%%MatrixMarket matrix array " 02862 << (STS::isComplex ? "complex" : "real") 02863 << " general" << endl; 02864 02865 // Print comments (the matrix name and / or description). 02866 if (matrixName != "") { 02867 printAsComment (out, matrixName); 02868 } 02869 if (matrixDescription != "") { 02870 printAsComment (out, matrixDescription); 02871 } 02872 // Print the Matrix Market dimensions header for dense matrices. 02873 out << numRows << " " << numCols << endl; 02874 // 02875 // Get a read-only view of the entries of Y. 02876 // Rank 0 owns all of the entries of Y. 02877 // 02878 // Y must have constant stride, since multivectors have 02879 // constant stride if created with a map and number of 02880 // vectors. This should be the case even if X does not 02881 // have constant stride. However, we check Y, just to 02882 // make sure. 02883 TEUCHOS_TEST_FOR_EXCEPTION(! Y->isConstantStride (), std::logic_error, 02884 "The multivector Y imported onto Rank 0 does not have constant " 02885 "stride. Please report this bug to the Tpetra developers."); 02886 ArrayRCP<const scalar_type> Y_view = Y->get1dView(); 02887 // 02888 // Print the entries of the matrix, in column-major order. 02889 // 02890 const global_size_t stride = Y->getStride (); 02891 for (global_size_t j = 0; j < numCols; ++j) { 02892 for (global_size_t i = 0; i < numRows; ++i) { 02893 const scalar_type Y_ij = Y_view[i + j*stride]; 02894 if (STS::isComplex) { 02895 out << STS::real(Y_ij) << " " << STS::imag(Y_ij) << endl; 02896 } 02897 else { 02898 out << Y_ij << endl; 02899 } 02900 } 02901 } 02902 } // if (myRank == 0) 02903 } 02904 02923 static void 02924 writeDense (std::ostream& out, 02925 const RCP<const multivector_type>& X) 02926 { 02927 writeDense (out, X, "", ""); 02928 } 02929 02930 private: 02954 static void 02955 printAsComment (std::ostream& out, const std::string& str) 02956 { 02957 using std::endl; 02958 std::istringstream istream (str); 02959 std::string line; 02960 02961 while (getline (istream, line)) { 02962 if (! line.empty()) { 02963 // Note that getline() doesn't store '\n', so we have to 02964 // append the endline ourselves. 02965 if (line[0] == '%') { // Line starts with a comment character. 02966 out << line << endl; 02967 } 02968 else { // Line doesn't start with a comment character. 02969 out << "%% " << line << endl; 02970 } 02971 } 02972 } 02973 } 02974 }; // class Writer 02975 02976 } // namespace MatrixMarket 02977 } // namespace Tpetra 02978 02979 #endif // __MatrixMarket_Tpetra_hpp
1.7.4