Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
MatrixMarket_Tpetra_def.hpp
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_DEF_HPP
11#define MATRIXMARKET_TPETRA_DEF_HPP
12
13#include "MatrixMarket_Tpetra_decl.hpp"
14#include "Teuchos_OrdinalTraits.hpp"
16#include "Tpetra_CrsMatrix.hpp"
17#include "Tpetra_Operator.hpp"
18#include "Tpetra_Vector.hpp"
20#include "Teuchos_MatrixMarket_Raw_Adder.hpp"
21#include "Teuchos_MatrixMarket_Raw_Graph_Adder.hpp"
22#include "Teuchos_MatrixMarket_SymmetrizingAdder.hpp"
23#include "Teuchos_MatrixMarket_SymmetrizingGraphAdder.hpp"
24#include "Teuchos_MatrixMarket_assignScalar.hpp"
25#include "Teuchos_MatrixMarket_Banner.hpp"
26#include "Teuchos_MatrixMarket_CoordDataReader.hpp"
27#include "Teuchos_SetScientific.hpp"
28#include "Teuchos_TimeMonitor.hpp"
29
30extern "C" {
31#include "mmio_Tpetra.h"
32}
33#include "Tpetra_Distribution.hpp"
34
35#include <algorithm>
36#include <fstream>
37#include <iostream>
38#include <iterator>
39#include <vector>
40#include <stdexcept>
41#include <numeric>
42
43namespace Tpetra {
44
45template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
46Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>
47MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeRangeMap(const trcp_tcomm_t& pComm,
48 const global_ordinal_type numRows) {
49 // Return a conventional, uniformly partitioned, contiguous map.
50 return rcp(new map_type(static_cast<global_size_t>(numRows),
51 static_cast<global_ordinal_type>(0),
52 pComm, GloballyDistributed));
53}
54
55template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
56Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>
57MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeRowMap(const Teuchos::RCP<const map_type>& pRowMap,
58 const trcp_tcomm_t& pComm,
59 const global_ordinal_type numRows) {
60 // If the caller didn't provide a map, return a conventional,
61 // uniformly partitioned, contiguous map.
62 if (pRowMap.is_null()) {
63 return rcp(new map_type(static_cast<global_size_t>(numRows),
64 static_cast<global_ordinal_type>(0),
65 pComm, GloballyDistributed));
66 } else {
67 TEUCHOS_TEST_FOR_EXCEPTION(!pRowMap->isDistributed() && pComm->getSize() > 1,
68 std::invalid_argument,
69 "The specified row map is not distributed, "
70 "but the given communicator includes more than one process (in "
71 "fact, there are "
72 << pComm->getSize() << " processes).");
73 TEUCHOS_TEST_FOR_EXCEPTION(pRowMap->getComm() != pComm, std::invalid_argument,
74 "The specified row Map's communicator (pRowMap->getComm()) "
75 "differs from the given separately supplied communicator pComm.");
76 return pRowMap;
77 }
78}
79
80template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
81Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>
82MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeDomainMap(const Teuchos::RCP<const map_type>& pRangeMap,
83 const global_ordinal_type numRows,
84 const global_ordinal_type numCols) {
85 // Abbreviations so that the map creation call isn't too long.
86 typedef local_ordinal_type LO;
87 typedef global_ordinal_type GO;
88 typedef node_type NT;
89
90 if (numRows == numCols) {
91 return pRangeMap;
92 } else {
94 pRangeMap->getComm());
95 }
96}
97
98template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
99void MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::distribute(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
100 Teuchos::ArrayRCP<size_t>& myRowPtr,
101 Teuchos::ArrayRCP<global_ordinal_type>& myColInd,
102 Teuchos::ArrayRCP<scalar_type>& myValues,
103 const Teuchos::RCP<const map_type>& pRowMap,
104 Teuchos::ArrayRCP<size_t>& numEntriesPerRow,
105 Teuchos::ArrayRCP<size_t>& rowPtr,
106 Teuchos::ArrayRCP<global_ordinal_type>& colInd,
107 Teuchos::ArrayRCP<scalar_type>& values,
108 const bool debug) {
109 using std::cerr;
110 using std::endl;
111 using Teuchos::arcp;
112 using Teuchos::ArrayRCP;
113 using Teuchos::ArrayView;
114 using Teuchos::as;
115 using Teuchos::Comm;
116 using Teuchos::CommRequest;
117 using Teuchos::null;
118 using Teuchos::RCP;
119 using Teuchos::receive;
120 using Teuchos::send;
121
122 const bool extraDebug = false;
123 trcp_tcomm_t pComm = pRowMap->getComm();
124 const int numProcs = pComm->getSize();
125 const int myRank = pComm->getRank();
126 const int rootRank = 0;
127
128 // Type abbreviations to make the code more concise.
129 typedef global_ordinal_type GO;
130
131 // List of the global indices of my rows. They may or may
132 // not be contiguous, and the row map need not be one-to-one.
133 ArrayView<const GO> myRows = pRowMap->getLocalElementList();
134 const size_type myNumRows = myRows.size();
135 TEUCHOS_TEST_FOR_EXCEPTION(static_cast<size_t>(myNumRows) !=
136 pRowMap->getLocalNumElements(),
137 std::logic_error,
138 "pRowMap->getLocalElementList().size() = "
139 << myNumRows
140 << " != pRowMap->getLocalNumElements() = "
141 << pRowMap->getLocalNumElements() << ". "
142 "Please report this bug to the Tpetra developers.");
143 TEUCHOS_TEST_FOR_EXCEPTION(myRank == 0 && numEntriesPerRow.size() < myNumRows,
144 std::logic_error,
145 "On Proc 0: numEntriesPerRow.size() = "
146 << numEntriesPerRow.size()
147 << " != pRowMap->getLocalElementList().size() = "
148 << myNumRows << ". Please report this bug to the "
149 "Tpetra developers.");
150
151 // Space for my proc's number of entries per row. Will be
152 // filled in below.
153 myNumEntriesPerRow = arcp<size_t>(myNumRows);
154
155 if (myRank != rootRank) {
156 // Tell the root how many rows we have. If we're sending
157 // none, then we don't have anything else to send, nor does
158 // the root have to receive anything else.
159 send(*pComm, myNumRows, rootRank);
160 if (myNumRows != 0) {
161 // Now send my rows' global indices. Hopefully the cast
162 // to int doesn't overflow. This is unlikely, since it
163 // should fit in a LO, even though it is a GO.
164 send(*pComm, static_cast<int>(myNumRows),
165 myRows.getRawPtr(), rootRank);
166
167 // I (this proc) don't care if my global row indices are
168 // contiguous, though the root proc does (since otherwise
169 // it needs to pack noncontiguous data into contiguous
170 // storage before sending). That's why we don't check
171 // for contiguousness here.
172
173 // Ask the root process for my part of the array of the
174 // number of entries per row.
175 receive(*pComm, rootRank,
176 static_cast<int>(myNumRows),
177 myNumEntriesPerRow.getRawPtr());
178
179 // Use the resulting array to figure out how many column
180 // indices and values I should ask from the root process.
181 const size_t myNumEntries =
182 std::accumulate(myNumEntriesPerRow.begin(),
183 myNumEntriesPerRow.end(), size_t(0));
184 TEUCHOS_TEST_FOR_EXCEPTION(
185 myNumEntries > static_cast<size_t>(std::numeric_limits<int>::max()),
186 std::overflow_error,
187 "MatrixMarketReader::distribute: myNumEntries exceeds int range");
188
189 // Make space for my entries of the sparse matrix. Note
190 // that they don't have to be sorted by row index.
191 // Iterating through all my rows requires computing a
192 // running sum over myNumEntriesPerRow.
193 myColInd = arcp<GO>(myNumEntries);
194 myValues = arcp<scalar_type>(myNumEntries);
195 if (myNumEntries > 0) {
196 // Ask for that many column indices and values, if
197 // there are any.
198 receive(*pComm, rootRank,
199 static_cast<int>(myNumEntries),
200 myColInd.getRawPtr());
201 receive(*pComm, rootRank,
202 static_cast<int>(myNumEntries),
203 myValues.getRawPtr());
204 }
205 } // If I own at least one row
206 } // If I am not the root processor
207 else { // I _am_ the root processor
208 if (debug) {
209 cerr << "-- Proc 0: Copying my data from global arrays" << endl;
210 }
211 // Proc 0 still needs to (allocate, if not done already)
212 // and fill its part of the matrix (my*).
213 for (size_type k = 0; k < myNumRows; ++k) {
214 const GO myCurRow = myRows[k];
215 const local_ordinal_type numEntriesInThisRow = numEntriesPerRow[myCurRow];
216 myNumEntriesPerRow[k] = numEntriesInThisRow;
217 }
218 if (extraDebug && debug) {
219 cerr << "Proc " << pRowMap->getComm()->getRank()
220 << ": myNumEntriesPerRow[0.." << (myNumRows - 1) << "] = [";
221 for (size_type k = 0; k < myNumRows; ++k) {
222 cerr << myNumEntriesPerRow[k];
223 if (k < myNumRows - 1) {
224 cerr << " ";
225 }
226 }
227 cerr << "]" << endl;
228 }
229 // The total number of matrix entries that my proc owns.
230 const size_t myNumEntries =
231 std::accumulate(myNumEntriesPerRow.begin(),
232 myNumEntriesPerRow.end(), size_t(0));
233 TEUCHOS_TEST_FOR_EXCEPTION(
234 myNumEntries > static_cast<size_t>(std::numeric_limits<int>::max()),
235 std::overflow_error,
236 "MatrixMarketReader::distribute: myNumEntries exceeds int range");
237 if (debug) {
238 cerr << "-- Proc 0: I own " << myNumRows << " rows and "
239 << myNumEntries << " entries" << endl;
240 }
241 myColInd = arcp<GO>(myNumEntries);
242 myValues = arcp<scalar_type>(myNumEntries);
243
244 // Copy Proc 0's part of the matrix into the my* arrays.
245 // It's important that myCurPos be updated _before_ k,
246 // otherwise myCurPos will get the wrong number of entries
247 // per row (it should be for the row in the just-completed
248 // iteration, not for the next iteration's row).
249 local_ordinal_type myCurPos = 0;
250 for (size_type k = 0; k < myNumRows;
251 myCurPos += myNumEntriesPerRow[k], ++k) {
252 const local_ordinal_type curNumEntries = myNumEntriesPerRow[k];
253 const GO myRow = myRows[k];
254 const size_t curPos = rowPtr[myRow];
255 // Only copy if there are entries to copy, in order not
256 // to construct empty ranges for the ArrayRCP views.
257 if (curNumEntries > 0) {
258 ArrayView<GO> colIndView = colInd(curPos, curNumEntries);
259 ArrayView<GO> myColIndView = myColInd(myCurPos, curNumEntries);
260 std::copy(colIndView.begin(), colIndView.end(),
261 myColIndView.begin());
262
263 ArrayView<scalar_type> valuesView =
264 values(curPos, curNumEntries);
265 ArrayView<scalar_type> myValuesView =
266 myValues(myCurPos, curNumEntries);
267 std::copy(valuesView.begin(), valuesView.end(),
268 myValuesView.begin());
269 }
270 }
271
272 // Proc 0 processes each other proc p in turn.
273 for (int p = 1; p < numProcs; ++p) {
274 if (debug) {
275 cerr << "-- Proc 0: Processing proc " << p << endl;
276 }
277
278 size_type theirNumRows = 0;
279 // Ask Proc p how many rows it has. If it doesn't
280 // have any, we can move on to the next proc. This
281 // has to be a standard receive so that we can avoid
282 // the degenerate case of sending zero data.
283 receive(*pComm, p, &theirNumRows);
284 if (debug) {
285 cerr << "-- Proc 0: Proc " << p << " owns "
286 << theirNumRows << " rows" << endl;
287 }
288 if (theirNumRows != 0) {
289 // Ask Proc p which rows it owns. The resulting global
290 // row indices are not guaranteed to be contiguous or
291 // sorted. Global row indices are themselves indices
292 // into the numEntriesPerRow array.
293 ArrayRCP<GO> theirRows = arcp<GO>(theirNumRows);
294 receive(*pComm, p, as<int>(theirNumRows),
295 theirRows.getRawPtr());
296 // Extra test to make sure that the rows we received
297 // are all sensible. This is a good idea since we are
298 // going to use the global row indices we've received
299 // to index into the numEntriesPerRow array. Better to
300 // catch any bugs here and print a sensible error
301 // message, rather than segfault and print a cryptic
302 // error message.
303 {
304 const global_size_t numRows = pRowMap->getGlobalNumElements();
305 const GO indexBase = pRowMap->getIndexBase();
306 bool theirRowsValid = true;
307 for (size_type k = 0; k < theirNumRows; ++k) {
308 if (theirRows[k] < indexBase ||
309 as<global_size_t>(theirRows[k] - indexBase) >= numRows) {
310 theirRowsValid = false;
311 }
312 }
313 if (!theirRowsValid) {
314 TEUCHOS_TEST_FOR_EXCEPTION(
315 !theirRowsValid, std::logic_error,
316 "Proc " << p << " has at least one invalid row index. "
317 "Here are all of them: "
318 << Teuchos::toString(theirRows()) << ". Valid row index "
319 "range (zero-based): [0, "
320 << (numRows - 1) << "].");
321 }
322 }
323
324 // Perhaps we could save a little work if we check
325 // whether Proc p's row indices are contiguous. That
326 // would make lookups in the global data arrays
327 // faster. For now, we just implement the general
328 // case and don't prematurely optimize. (Remember
329 // that you're making Proc 0 read the whole file, so
330 // you've already lost scalability.)
331
332 // Compute the number of entries in each of Proc p's
333 // rows. (Proc p will compute its row pointer array
334 // on its own, after it gets the data from Proc 0.)
335 ArrayRCP<size_t> theirNumEntriesPerRow;
336 theirNumEntriesPerRow = arcp<size_t>(theirNumRows);
337 for (size_type k = 0; k < theirNumRows; ++k) {
338 theirNumEntriesPerRow[k] = numEntriesPerRow[theirRows[k]];
339 }
340
341 // Tell Proc p the number of entries in each of its
342 // rows. Hopefully the cast to int doesn't overflow.
343 // This is unlikely, since it should fit in a LO,
344 // even though it is a GO.
345 send(*pComm, static_cast<int>(theirNumRows),
346 theirNumEntriesPerRow.getRawPtr(), p);
347
348 // Figure out how many entries Proc p owns.
349 const size_t theirNumEntries =
350 std::accumulate(theirNumEntriesPerRow.begin(),
351 theirNumEntriesPerRow.end(), size_t(0));
352 TEUCHOS_TEST_FOR_EXCEPTION(
353 theirNumEntries > static_cast<size_t>(std::numeric_limits<int>::max()),
354 std::overflow_error,
355 "MatrixMarketReader::distribute: theirNumEntries exceeds int range");
356
357 if (debug) {
358 cerr << "-- Proc 0: Proc " << p << " owns "
359 << theirNumEntries << " entries" << endl;
360 }
361
362 // If there are no entries to send, then we're done
363 // with Proc p.
364 if (theirNumEntries == 0) {
365 continue;
366 }
367
368 // Construct (views of) proc p's column indices and
369 // values. Later, we might like to optimize for the
370 // (common) contiguous case, for which we don't need to
371 // copy data into separate "their*" arrays (we can just
372 // use contiguous views of the global arrays).
373 ArrayRCP<GO> theirColInd(theirNumEntries);
374 ArrayRCP<scalar_type> theirValues(theirNumEntries);
375 // Copy Proc p's part of the matrix into the their*
376 // arrays. It's important that theirCurPos be updated
377 // _before_ k, otherwise theirCurPos will get the wrong
378 // number of entries per row (it should be for the row
379 // in the just-completed iteration, not for the next
380 // iteration's row).
381 local_ordinal_type theirCurPos = 0;
382 for (size_type k = 0; k < theirNumRows;
383 theirCurPos += theirNumEntriesPerRow[k], k++) {
384 const local_ordinal_type curNumEntries = theirNumEntriesPerRow[k];
385 const GO theirRow = theirRows[k];
386 const local_ordinal_type curPos = rowPtr[theirRow];
387
388 // Only copy if there are entries to copy, in order
389 // not to construct empty ranges for the ArrayRCP
390 // views.
391 if (curNumEntries > 0) {
392 ArrayView<GO> colIndView =
393 colInd(curPos, curNumEntries);
394 ArrayView<GO> theirColIndView =
395 theirColInd(theirCurPos, curNumEntries);
396 std::copy(colIndView.begin(), colIndView.end(),
397 theirColIndView.begin());
398
399 ArrayView<scalar_type> valuesView =
400 values(curPos, curNumEntries);
401 ArrayView<scalar_type> theirValuesView =
402 theirValues(theirCurPos, curNumEntries);
403 std::copy(valuesView.begin(), valuesView.end(),
404 theirValuesView.begin());
405 }
406 }
407 // Send Proc p its column indices and values.
408 // Hopefully the cast to int doesn't overflow. This
409 // is unlikely, since it should fit in a LO, even
410 // though it is a GO.
411 send(*pComm, static_cast<int>(theirNumEntries),
412 theirColInd.getRawPtr(), p);
413 send(*pComm, static_cast<int>(theirNumEntries),
414 theirValues.getRawPtr(), p);
415
416 if (debug) {
417 cerr << "-- Proc 0: Finished with proc " << p << endl;
418 }
419 } // If proc p owns at least one row
420 } // For each proc p not the root proc 0
421 } // If I'm (not) the root proc 0
422
423 // Invalidate the input data to save space, since we don't
424 // need it anymore.
425 numEntriesPerRow = null;
426 rowPtr = null;
427 colInd = null;
428 values = null;
429
430 if (debug && myRank == 0) {
431 cerr << "-- Proc 0: About to fill in myRowPtr" << endl;
432 }
433
434 // Allocate and fill in myRowPtr (the row pointer array for
435 // my rank's rows). We delay this until the end because we
436 // don't need it to compute anything else in distribute().
437 // Each proc can do this work for itself, since it only needs
438 // myNumEntriesPerRow to do so.
439 myRowPtr = arcp<size_t>(myNumRows + 1);
440 myRowPtr[0] = 0;
441 for (size_type k = 1; k < myNumRows + 1; ++k) {
442 myRowPtr[k] = myRowPtr[k - 1] + myNumEntriesPerRow[k - 1];
443 }
444 if (extraDebug && debug) {
445 cerr << "Proc " << Teuchos::rank(*(pRowMap->getComm()))
446 << ": myRowPtr[0.." << myNumRows << "] = [";
447 for (size_type k = 0; k < myNumRows + 1; ++k) {
448 cerr << myRowPtr[k];
449 if (k < myNumRows) {
450 cerr << " ";
451 }
452 }
453 cerr << "]" << endl
454 << endl;
455 }
456
457 if (debug && myRank == 0) {
458 cerr << "-- Proc 0: Done with distribute" << endl;
459 }
460}
461
462template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
463Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
464MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeMatrix(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
465 Teuchos::ArrayRCP<size_t>& myRowPtr,
466 Teuchos::ArrayRCP<global_ordinal_type>& myColInd,
467 Teuchos::ArrayRCP<scalar_type>& myValues,
468 const Teuchos::RCP<const map_type>& pRowMap,
469 const Teuchos::RCP<const map_type>& pRangeMap,
470 const Teuchos::RCP<const map_type>& pDomainMap,
471 const bool callFillComplete) {
472 using std::cerr;
473 using std::endl;
474 using Teuchos::ArrayView;
475 using Teuchos::null;
476 using Teuchos::RCP;
477 using Teuchos::rcp;
478 // Typedef to make certain type declarations shorter.
479 typedef global_ordinal_type GO;
480
481 // The row pointer array always has at least one entry, even
482 // if the matrix has zero rows. myNumEntriesPerRow, myColInd,
483 // and myValues would all be empty arrays in that degenerate
484 // case, but the row and domain maps would still be nonnull
485 // (though they would be trivial maps).
486 TEUCHOS_TEST_FOR_EXCEPTION(myRowPtr.is_null(), std::logic_error,
487 "makeMatrix: myRowPtr array is null. "
488 "Please report this bug to the Tpetra developers.");
489 TEUCHOS_TEST_FOR_EXCEPTION(pDomainMap.is_null(), std::logic_error,
490 "makeMatrix: domain map is null. "
491 "Please report this bug to the Tpetra developers.");
492 TEUCHOS_TEST_FOR_EXCEPTION(pRangeMap.is_null(), std::logic_error,
493 "makeMatrix: range map is null. "
494 "Please report this bug to the Tpetra developers.");
495 TEUCHOS_TEST_FOR_EXCEPTION(pRowMap.is_null(), std::logic_error,
496 "makeMatrix: row map is null. "
497 "Please report this bug to the Tpetra developers.");
498
499 // Construct the CrsMatrix, using the row map, with the
500 // constructor specifying the number of nonzeros for each row.
501 RCP<sparse_matrix_type> A =
502 rcp(new sparse_matrix_type(pRowMap, myNumEntriesPerRow()));
503
504 // List of the global indices of my rows.
505 // They may or may not be contiguous.
506 ArrayView<const GO> myRows = pRowMap->getLocalElementList();
507 const size_type myNumRows = myRows.size();
508
509 // Add this processor's matrix entries to the CrsMatrix.
510 const GO indexBase = pRowMap->getIndexBase();
511 for (size_type i = 0; i < myNumRows; ++i) {
512 const size_type myCurPos = myRowPtr[i];
513 const local_ordinal_type curNumEntries = myNumEntriesPerRow[i];
514 ArrayView<GO> curColInd = myColInd.view(myCurPos, curNumEntries);
515 ArrayView<scalar_type> curValues = myValues.view(myCurPos, curNumEntries);
516
517 // Modify the column indices in place to have the right index base.
518 for (size_type k = 0; k < curNumEntries; ++k) {
519 curColInd[k] += indexBase;
520 }
521 // Avoid constructing empty views of ArrayRCP objects.
522 if (curNumEntries > 0) {
523 A->insertGlobalValues(myRows[i], curColInd, curValues);
524 }
525 }
526 // We've entered in all our matrix entries, so we can delete
527 // the original data. This will save memory when we call
528 // fillComplete(), so that we never keep more than two copies
529 // of the matrix's data in memory at once.
530 myNumEntriesPerRow = null;
531 myRowPtr = null;
532 myColInd = null;
533 myValues = null;
534
535 if (callFillComplete) {
536 A->fillComplete(pDomainMap, pRangeMap);
537 }
538 return A;
539}
540
541template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
542Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
543MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeMatrix(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
544 Teuchos::ArrayRCP<size_t>& myRowPtr,
547 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& pRowMap,
548 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& pRangeMap,
549 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& pDomainMap,
550 const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
551 const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams) {
552 using std::cerr;
553 using std::endl;
554 using Teuchos::ArrayView;
555 using Teuchos::null;
556 using Teuchos::RCP;
557 using Teuchos::rcp;
558 // Typedef to make certain type declarations shorter.
560 using size_type = typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::size_type;
561
562 // The row pointer array always has at least one entry, even
563 // if the matrix has zero rows. myNumEntriesPerRow, myColInd,
564 // and myValues would all be empty arrays in that degenerate
565 // case, but the row and domain maps would still be nonnull
566 // (though they would be trivial maps).
567 TEUCHOS_TEST_FOR_EXCEPTION(
568 myRowPtr.is_null(), std::logic_error,
569 "makeMatrix: myRowPtr array is null. "
570 "Please report this bug to the Tpetra developers.");
571 TEUCHOS_TEST_FOR_EXCEPTION(
572 pDomainMap.is_null(), std::logic_error,
573 "makeMatrix: domain map is null. "
574 "Please report this bug to the Tpetra developers.");
575 TEUCHOS_TEST_FOR_EXCEPTION(
576 pRangeMap.is_null(), std::logic_error,
577 "makeMatrix: range map is null. "
578 "Please report this bug to the Tpetra developers.");
579 TEUCHOS_TEST_FOR_EXCEPTION(
580 pRowMap.is_null(), std::logic_error,
581 "makeMatrix: row map is null. "
582 "Please report this bug to the Tpetra developers.");
583
584 // Construct the CrsMatrix, using the row map, with the
585 // constructor specifying the number of nonzeros for each row.
586 RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type> A =
587 rcp(new typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type(pRowMap, myNumEntriesPerRow(),
588 constructorParams));
589
590 // List of the global indices of my rows.
591 // They may or may not be contiguous.
592 ArrayView<const GO> myRows = pRowMap->getLocalElementList();
593 const size_type myNumRows = myRows.size();
594
595 // Add this processor's matrix entries to the CrsMatrix.
596 const GO indexBase = pRowMap->getIndexBase();
597 for (size_type i = 0; i < myNumRows; ++i) {
598 const size_type myCurPos = myRowPtr[i];
599 const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_ordinal_type curNumEntries = myNumEntriesPerRow[i];
600 ArrayView<GO> curColInd = myColInd.view(myCurPos, curNumEntries);
601 ArrayView<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::scalar_type> curValues = myValues.view(myCurPos, curNumEntries);
602
603 // Modify the column indices in place to have the right index base.
604 for (size_type k = 0; k < curNumEntries; ++k) {
605 curColInd[k] += indexBase;
606 }
607 if (curNumEntries > 0) {
608 A->insertGlobalValues(myRows[i], curColInd, curValues);
609 }
610 }
611 // We've entered in all our matrix entries, so we can delete
612 // the original data. This will save memory when we call
613 // fillComplete(), so that we never keep more than two copies
614 // of the matrix's data in memory at once.
615 myNumEntriesPerRow = null;
616 myRowPtr = null;
617 myColInd = null;
618 myValues = null;
619
620 A->fillComplete(pDomainMap, pRangeMap, fillCompleteParams);
621 return A;
622}
623
624template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
625Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
626MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeMatrix(Teuchos::ArrayRCP<size_t>& myNumEntriesPerRow,
627 Teuchos::ArrayRCP<size_t>& myRowPtr,
628 Teuchos::ArrayRCP<global_ordinal_type>& myColInd,
629 Teuchos::ArrayRCP<scalar_type>& myValues,
630 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& rowMap,
631 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& colMap,
632 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& domainMap,
633 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& rangeMap,
634 const bool callFillComplete) {
635 using Teuchos::ArrayView;
636 using Teuchos::as;
637 using Teuchos::null;
638 using Teuchos::RCP;
639 using Teuchos::rcp;
640 typedef global_ordinal_type GO;
641
642 // Construct the CrsMatrix.
643
644 RCP<sparse_matrix_type> A; // the matrix to return.
645 if (colMap.is_null()) { // the user didn't provide a column Map
646 A = rcp(new sparse_matrix_type(rowMap, myNumEntriesPerRow()));
647 } else { // the user provided a column Map
648 A = rcp(new sparse_matrix_type(rowMap, colMap, myNumEntriesPerRow()));
649 }
650
651 // List of the global indices of my rows.
652 // They may or may not be contiguous.
653 ArrayView<const GO> myRows = rowMap->getLocalElementList();
654 const size_type myNumRows = myRows.size();
655
656 // Add this process' matrix entries to the CrsMatrix.
657 const GO indexBase = rowMap->getIndexBase();
658 for (size_type i = 0; i < myNumRows; ++i) {
659 const size_type myCurPos = myRowPtr[i];
660 const size_type curNumEntries = as<size_type>(myNumEntriesPerRow[i]);
661 ArrayView<GO> curColInd = myColInd.view(myCurPos, curNumEntries);
662 ArrayView<scalar_type> curValues = myValues.view(myCurPos, curNumEntries);
663
664 // Modify the column indices in place to have the right index base.
665 for (size_type k = 0; k < curNumEntries; ++k) {
666 curColInd[k] += indexBase;
667 }
668 if (curNumEntries > 0) {
669 A->insertGlobalValues(myRows[i], curColInd, curValues);
670 }
671 }
672 // We've entered in all our matrix entries, so we can delete
673 // the original data. This will save memory when we call
674 // fillComplete(), so that we never keep more than two copies
675 // of the matrix's data in memory at once.
676 myNumEntriesPerRow = null;
677 myRowPtr = null;
678 myColInd = null;
679 myValues = null;
680
681 if (callFillComplete) {
682 A->fillComplete(domainMap, rangeMap);
683 if (colMap.is_null()) {
684 colMap = A->getColMap();
685 }
686 }
687 return A;
688}
689
690template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
691Teuchos::RCP<const Teuchos::MatrixMarket::Banner>
692MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readBanner(std::istream& in,
693 size_t& lineNumber,
694 const bool tolerant,
695 const bool /* debug */,
696 const bool isGraph) {
697 using std::cerr;
698 using std::endl;
699 using Teuchos::RCP;
700 using Teuchos::rcp;
701 using Teuchos::MatrixMarket::Banner;
702 typedef Teuchos::ScalarTraits<scalar_type> STS;
703
704 RCP<Banner> pBanner; // On output, if successful: the read-in Banner.
705 std::string line; // If read from stream successful: the Banner line
706
707 // Try to read a line from the input stream.
708 const bool readFailed = !getline(in, line);
709 TEUCHOS_TEST_FOR_EXCEPTION(readFailed, std::invalid_argument,
710 "Failed to get Matrix Market banner line from input.");
711
712 // We read a line from the input stream.
713 lineNumber++;
714
715 // Assume that the line we found is the Banner line.
716 try {
717 pBanner = rcp(new Banner(line, tolerant));
718 } catch (std::exception& e) {
719 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
720 "Matrix Market banner line contains syntax error(s): "
721 << e.what());
722 }
723
724 TEUCHOS_TEST_FOR_EXCEPTION(pBanner->objectType() != "matrix",
725 std::invalid_argument,
726 "The Matrix Market file does not contain "
727 "matrix data. Its Banner (first) line says that its object type is \""
728 << pBanner->matrixType() << "\", rather than the required \"matrix\".");
729
730 // Validate the data type of the matrix, with respect to the
731 // Scalar type of the CrsMatrix entries.
732 TEUCHOS_TEST_FOR_EXCEPTION(
733 !STS::isComplex && pBanner->dataType() == "complex",
734 std::invalid_argument,
735 "The Matrix Market file contains complex-valued data, but you are "
736 "trying to read it into a matrix containing entries of the real-"
737 "valued Scalar type \""
738 << Teuchos::TypeNameTraits<scalar_type>::name() << "\".");
739 TEUCHOS_TEST_FOR_EXCEPTION(
740 !isGraph &&
741 pBanner->dataType() != "real" &&
742 pBanner->dataType() != "complex" &&
743 pBanner->dataType() != "integer",
744 std::invalid_argument,
745 "When reading Matrix Market data into a Tpetra::CrsMatrix, the "
746 "Matrix Market file may not contain a \"pattern\" matrix. A "
747 "pattern matrix is really just a graph with no weights. It "
748 "should be stored in a CrsGraph, not a CrsMatrix.");
749
750 TEUCHOS_TEST_FOR_EXCEPTION(
751 isGraph &&
752 pBanner->dataType() != "pattern",
753 std::invalid_argument,
754 "When reading Matrix Market data into a Tpetra::CrsGraph, the "
755 "Matrix Market file must contain a \"pattern\" matrix.");
756
757 return pBanner;
758}
759
760template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
761Teuchos::Tuple<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::global_ordinal_type, 3>
762MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readCoordDims(std::istream& in,
763 size_t& lineNumber,
764 const Teuchos::RCP<const Teuchos::MatrixMarket::Banner>& pBanner,
766 const bool tolerant,
767 const bool /* debug */) {
768 using Teuchos::Tuple;
769 using Teuchos::MatrixMarket::readCoordinateDimensions;
770
771 // Packed coordinate matrix dimensions (numRows, numCols,
772 // numNonzeros); computed on Rank 0 and broadcasted to all MPI
773 // ranks.
774 Tuple<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::global_ordinal_type, 3> dims;
775
776 // Read in the coordinate matrix dimensions from the input
777 // stream. "success" tells us whether reading in the
778 // coordinate matrix dimensions succeeded ("Guilty unless
779 // proven innocent").
780 bool success = false;
781 if (pComm->getRank() == 0) {
782 TEUCHOS_TEST_FOR_EXCEPTION(pBanner->matrixType() != "coordinate",
783 std::invalid_argument,
784 "The Tpetra::CrsMatrix Matrix Market reader "
785 "only accepts \"coordinate\" (sparse) matrix data.");
786 // Unpacked coordinate matrix dimensions
788 // Only MPI Rank 0 reads from the input stream
789 success = readCoordinateDimensions(in, numRows, numCols,
790 numNonzeros, lineNumber,
791 tolerant);
792 // Pack up the data into a Tuple so we can send them with
793 // one broadcast instead of three.
794 dims[0] = numRows;
795 dims[1] = numCols;
796 dims[2] = numNonzeros;
797 }
798 // Only Rank 0 did the reading, so it decides success.
799 //
800 // FIXME (mfh 02 Feb 2011) Teuchos::broadcast doesn't know how
801 // to send bools. For now, we convert to/from int instead,
802 // using the usual "true is 1, false is 0" encoding.
803 {
804 int the_success = success ? 1 : 0; // only matters on MPI Rank 0
805 Teuchos::broadcast(*pComm, 0, &the_success);
806 success = (the_success == 1);
807 }
808 if (success) {
809 // Broadcast (numRows, numCols, numNonzeros) from Rank 0
810 // to all the other MPI ranks.
811 Teuchos::broadcast(*pComm, 0, dims);
812 } else {
813 // Perhaps in tolerant mode, we could set all the
814 // dimensions to zero for now, and deduce correct
815 // dimensions by reading all of the file's entries and
816 // computing the max(row index) and max(column index).
817 // However, for now we just error out in that case.
818 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
819 "Error reading Matrix Market sparse matrix: failed to read "
820 "coordinate matrix dimensions.");
821 }
822 return dims;
823}
824
825template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
826Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::adder_type>
827MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeAdder(const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
828 Teuchos::RCP<const Teuchos::MatrixMarket::Banner>& pBanner,
829 const Teuchos::Tuple<global_ordinal_type, 3>& dims,
830 const bool tolerant,
831 const bool debug) {
832 if (pComm->getRank() == 0) {
833 typedef Teuchos::MatrixMarket::Raw::Adder<scalar_type,
835 raw_adder_type;
836 Teuchos::RCP<raw_adder_type> pRaw =
837 Teuchos::rcp(new raw_adder_type(dims[0], dims[1], dims[2],
838 tolerant, debug));
839 return Teuchos::rcp(new adder_type(pRaw, pBanner->symmType()));
840 } else {
841 return Teuchos::null;
842 }
843}
844
845template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
846Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::graph_adder_type>
847MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeGraphAdder(const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
848 Teuchos::RCP<const Teuchos::MatrixMarket::Banner>& pBanner,
849 const Teuchos::Tuple<global_ordinal_type, 3>& dims,
850 const bool tolerant,
851 const bool debug) {
852 if (pComm->getRank() == 0) {
853 typedef Teuchos::MatrixMarket::Raw::GraphAdder<global_ordinal_type> raw_adder_type;
854 Teuchos::RCP<raw_adder_type> pRaw =
855 Teuchos::rcp(new raw_adder_type(dims[0], dims[1], dims[2],
856 tolerant, debug));
857 return Teuchos::rcp(new graph_adder_type(pRaw, pBanner->symmType()));
858 } else {
859 return Teuchos::null;
860 }
861}
862
863template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
864Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_graph_type>
865MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readSparseGraphHelper(std::istream& in,
866 const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
867 const Teuchos::RCP<const map_type>& rowMap,
868 Teuchos::RCP<const map_type>& colMap,
869 const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
870 const bool tolerant,
871 const bool debug) {
872 using std::cerr;
873 using std::endl;
874 using Teuchos::ptr;
875 using Teuchos::RCP;
876 using Teuchos::Tuple;
877 using Teuchos::MatrixMarket::Banner;
878
879 const int myRank = pComm->getRank();
880 const int rootRank = 0;
881
882 // Current line number in the input stream. Various calls
883 // will modify this depending on the number of lines that are
884 // read from the input stream. Only Rank 0 modifies this.
885 size_t lineNumber = 1;
886
887 if (debug && myRank == rootRank) {
888 cerr << "Matrix Market reader: readGraph:" << endl
889 << "-- Reading banner line" << endl;
890 }
891
892 // The "Banner" tells you whether the input stream represents
893 // a sparse matrix, the symmetry type of the matrix, and the
894 // type of the data it contains.
895 //
896 // pBanner will only be nonnull on MPI Rank 0. It will be
897 // null on all other MPI processes.
898 RCP<const Banner> pBanner;
899 {
900 // We read and validate the Banner on Proc 0, but broadcast
901 // the validation result to all processes.
902 // Teuchos::broadcast doesn't currently work with bool, so
903 // we use int (true -> 1, false -> 0).
904 int bannerIsCorrect = 1;
905 std::ostringstream errMsg;
906
907 if (myRank == rootRank) {
908 // Read the Banner line from the input stream.
909 try {
910 pBanner = readBanner(in, lineNumber, tolerant, debug, true);
911 } catch (std::exception& e) {
912 errMsg << "Attempt to read the Matrix Market file's Banner line "
913 "threw an exception: "
914 << e.what();
915 bannerIsCorrect = 0;
916 }
917
918 if (bannerIsCorrect) {
919 // Validate the Banner for the case of a sparse graph.
920 // We validate on Proc 0, since it reads the Banner.
921
922 // In intolerant mode, the matrix type must be "coordinate".
923 if (!tolerant && pBanner->matrixType() != "coordinate") {
924 bannerIsCorrect = 0;
925 errMsg << "The Matrix Market input file must contain a "
926 "\"coordinate\"-format sparse graph in order to create a "
927 "Tpetra::CrsGraph object from it, but the file's matrix "
928 "type is \""
929 << pBanner->matrixType() << "\" instead.";
930 }
931 // In tolerant mode, we allow the matrix type to be
932 // anything other than "array" (which would mean that
933 // the file contains a dense matrix).
934 if (tolerant && pBanner->matrixType() == "array") {
935 bannerIsCorrect = 0;
936 errMsg << "Matrix Market file must contain a \"coordinate\"-"
937 "format sparse graph in order to create a Tpetra::CrsGraph "
938 "object from it, but the file's matrix type is \"array\" "
939 "instead. That probably means the file contains dense matrix "
940 "data.";
941 }
942 }
943 } // Proc 0: Done reading the Banner, hopefully successfully.
944
945 // Broadcast from Proc 0 whether the Banner was read correctly.
946 broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
947
948 // If the Banner is invalid, all processes throw an
949 // exception. Only Proc 0 gets the exception message, but
950 // that's OK, since the main point is to "stop the world"
951 // (rather than throw an exception on one process and leave
952 // the others hanging).
953 TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
954 std::invalid_argument, errMsg.str());
955 } // Done reading the Banner line and broadcasting success.
956 if (debug && myRank == rootRank) {
957 cerr << "-- Reading dimensions line" << endl;
958 }
959
960 // Read the graph dimensions from the Matrix Market metadata.
961 // dims = (numRows, numCols, numEntries). Proc 0 does the
962 // reading, but it broadcasts the results to all MPI
963 // processes. Thus, readCoordDims() is a collective
964 // operation. It does a collective check for correctness too.
965 Tuple<global_ordinal_type, 3> dims =
966 readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
967
968 if (debug && myRank == rootRank) {
969 cerr << "-- Making Adder for collecting graph data" << endl;
970 }
971
972 // "Adder" object for collecting all the sparse graph entries
973 // from the input stream. This is only nonnull on Proc 0.
974 // The Adder internally converts the one-based indices (native
975 // Matrix Market format) into zero-based indices.
976 RCP<graph_adder_type> pAdder =
977 makeGraphAdder(pComm, pBanner, dims, tolerant, debug);
978
979 if (debug && myRank == rootRank) {
980 cerr << "-- Reading graph data" << endl;
981 }
982 //
983 // Read the graph entries from the input stream on Proc 0.
984 //
985 {
986 // We use readSuccess to broadcast the results of the read
987 // (succeeded or not) to all MPI processes. Since
988 // Teuchos::broadcast doesn't currently know how to send
989 // bools, we convert to int (true -> 1, false -> 0).
990 int readSuccess = 1;
991 std::ostringstream errMsg; // Exception message (only valid on Proc 0)
992 if (myRank == rootRank) {
993 try {
994 // Reader for "coordinate" format sparse graph data.
995 typedef Teuchos::MatrixMarket::CoordPatternReader<graph_adder_type,
997 reader_type;
998 reader_type reader(pAdder);
999
1000 // Read the sparse graph entries.
1001 std::pair<bool, std::vector<size_t>> results =
1002 reader.read(in, lineNumber, tolerant, debug);
1003 readSuccess = results.first ? 1 : 0;
1004 } catch (std::exception& e) {
1005 readSuccess = 0;
1006 errMsg << e.what();
1007 }
1008 }
1009 broadcast(*pComm, rootRank, ptr(&readSuccess));
1010
1011 // It would be nice to add a "verbose" flag, so that in
1012 // tolerant mode, we could log any bad line number(s) on
1013 // Proc 0. For now, we just throw if the read fails to
1014 // succeed.
1015 //
1016 // Question: If we're in tolerant mode, and if the read did
1017 // not succeed, should we attempt to call fillComplete()?
1018 TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
1019 "Failed to read the Matrix Market sparse graph file: "
1020 << errMsg.str());
1021 } // Done reading the graph entries (stored on Proc 0 for now)
1022
1023 if (debug && myRank == rootRank) {
1024 cerr << "-- Successfully read the Matrix Market data" << endl;
1025 }
1026
1027 // In tolerant mode, we need to rebroadcast the graph
1028 // dimensions, since they may be different after reading the
1029 // actual graph data. We only need to broadcast the number
1030 // of rows and columns. Only Rank 0 needs to know the actual
1031 // global number of entries, since (a) we need to merge
1032 // duplicates on Rank 0 first anyway, and (b) when we
1033 // distribute the entries, each rank other than Rank 0 will
1034 // only need to know how many entries it owns, not the total
1035 // number of entries.
1036 if (tolerant) {
1037 if (debug && myRank == rootRank) {
1038 cerr << "-- Tolerant mode: rebroadcasting graph dimensions"
1039 << endl
1040 << "----- Dimensions before: "
1041 << dims[0] << " x " << dims[1]
1042 << endl;
1043 }
1044 // Packed coordinate graph dimensions (numRows, numCols).
1045 Tuple<global_ordinal_type, 2> updatedDims;
1046 if (myRank == rootRank) {
1047 // If one or more bottom rows of the graph contain no
1048 // entries, then the Adder will report that the number
1049 // of rows is less than that specified in the
1050 // metadata. We allow this case, and favor the
1051 // metadata so that the zero row(s) will be included.
1052 updatedDims[0] =
1053 std::max(dims[0], pAdder->getAdder()->numRows());
1054 updatedDims[1] = pAdder->getAdder()->numCols();
1055 }
1056 broadcast(*pComm, rootRank, updatedDims);
1057 dims[0] = updatedDims[0];
1058 dims[1] = updatedDims[1];
1059 if (debug && myRank == rootRank) {
1060 cerr << "----- Dimensions after: " << dims[0] << " x "
1061 << dims[1] << endl;
1062 }
1063 } else {
1064 // In strict mode, we require that the graph's metadata and
1065 // its actual data agree, at least somewhat. In particular,
1066 // the number of rows must agree, since otherwise we cannot
1067 // distribute the graph correctly.
1068
1069 // Teuchos::broadcast() doesn't know how to broadcast bools,
1070 // so we use an int with the standard 1 == true, 0 == false
1071 // encoding.
1072 int dimsMatch = 1;
1073 if (myRank == rootRank) {
1074 // If one or more bottom rows of the graph contain no
1075 // entries, then the Adder will report that the number of
1076 // rows is less than that specified in the metadata. We
1077 // allow this case, and favor the metadata, but do not
1078 // allow the Adder to think there are more rows in the
1079 // graph than the metadata says.
1080 if (dims[0] < pAdder->getAdder()->numRows()) {
1081 dimsMatch = 0;
1082 }
1083 }
1084 broadcast(*pComm, 0, ptr(&dimsMatch));
1085 if (dimsMatch == 0) {
1086 // We're in an error state anyway, so we might as well
1087 // work a little harder to print an informative error
1088 // message.
1089 //
1090 // Broadcast the Adder's idea of the graph dimensions
1091 // from Proc 0 to all processes.
1092 Tuple<global_ordinal_type, 2> addersDims;
1093 if (myRank == rootRank) {
1094 addersDims[0] = pAdder->getAdder()->numRows();
1095 addersDims[1] = pAdder->getAdder()->numCols();
1096 }
1097 broadcast(*pComm, 0, addersDims);
1098 TEUCHOS_TEST_FOR_EXCEPTION(
1099 dimsMatch == 0, std::runtime_error,
1100 "The graph metadata says that the graph is " << dims[0] << " x "
1101 << dims[1] << ", but the actual data says that the graph is "
1102 << addersDims[0] << " x " << addersDims[1] << ". That means the "
1103 "data includes more rows than reported in the metadata. This "
1104 "is not allowed when parsing in strict mode. Parse the graph in "
1105 "tolerant mode to ignore the metadata when it disagrees with the "
1106 "data.");
1107 }
1108 } // Matrix dimensions (# rows, # cols, # entries) agree.
1109
1110 // Create a map describing a distribution where the root owns EVERYTHING
1111 RCP<map_type> proc0Map;
1112 global_ordinal_type indexBase;
1113 if (Teuchos::is_null(rowMap)) {
1114 indexBase = 0;
1115 } else {
1116 indexBase = rowMap->getIndexBase();
1117 }
1118 if (myRank == rootRank) {
1119 proc0Map = rcp(new map_type(dims[0], dims[0], indexBase, pComm));
1120 } else {
1121 proc0Map = rcp(new map_type(dims[0], 0, indexBase, pComm));
1122 }
1123
1124 // Create the graph where the root owns EVERYTHING
1125 std::map<global_ordinal_type, size_t> numEntriesPerRow_map;
1126 if (myRank == rootRank) {
1127 const auto& entries = pAdder()->getAdder()->getEntries();
1128 // This will count duplicates, but it's better than dense.
1129 // An even better approach would use a classic algorithm,
1130 // likely in Saad's old textbook, for converting COO (entries)
1131 // to CSR (the local part of the sparse matrix data structure).
1132 for (const auto& entry : entries) {
1133 const global_ordinal_type gblRow = entry.rowIndex() + indexBase;
1134 ++numEntriesPerRow_map[gblRow];
1135 }
1136 }
1137
1138 Teuchos::Array<size_t> numEntriesPerRow(proc0Map->getLocalNumElements());
1139 for (const auto& ent : numEntriesPerRow_map) {
1140 const local_ordinal_type lclRow = proc0Map->getLocalElement(ent.first);
1141 TEUCHOS_TEST_FOR_EXCEPTION(
1142 lclRow == Teuchos::OrdinalTraits<local_ordinal_type>::invalid(),
1143 std::runtime_error,
1144 "MatrixMarketReader::readSparseGraphHelper: global row "
1145 << ent.first << " is not owned by proc0Map. This should not happen.");
1146 numEntriesPerRow[lclRow] = ent.second;
1147 }
1148 // Free anything we don't need before allocating the graph.
1149 // Swapping with an empty data structure is the standard idiom
1150 // for freeing memory used by Standard Library containers.
1151 // (Just resizing to 0 doesn't promise to free memory.)
1152 {
1153 std::map<global_ordinal_type, size_t> empty_map;
1154 std::swap(numEntriesPerRow_map, empty_map);
1155 }
1156
1157 RCP<sparse_graph_type> proc0Graph =
1158 rcp(new sparse_graph_type(proc0Map, numEntriesPerRow(),
1159 constructorParams));
1160 if (myRank == rootRank) {
1161 typedef Teuchos::MatrixMarket::Raw::GraphElement<global_ordinal_type> element_type;
1162
1163 // Get the entries
1164 const std::vector<element_type>& entries =
1165 pAdder->getAdder()->getEntries();
1166
1167 // Insert them one at a time
1168 for (size_t curPos = 0; curPos < entries.size(); curPos++) {
1169 const element_type& curEntry = entries[curPos];
1170 const global_ordinal_type curRow = curEntry.rowIndex() + indexBase;
1171 const global_ordinal_type curCol = curEntry.colIndex() + indexBase;
1172 Teuchos::ArrayView<const global_ordinal_type> colView(&curCol, 1);
1173 proc0Graph->insertGlobalIndices(curRow, colView);
1174 }
1175 }
1176 proc0Graph->fillComplete();
1177
1178 RCP<sparse_graph_type> distGraph;
1179 if (Teuchos::is_null(rowMap)) {
1180 // Create a map describing the distribution we actually want
1181 RCP<map_type> distMap =
1182 rcp(new map_type(dims[0], 0, pComm, GloballyDistributed));
1183
1184 // Create the graph with that distribution too
1185 distGraph = rcp(new sparse_graph_type(distMap, colMap, 0, constructorParams));
1186
1187 // Create an importer/exporter/vandelay to redistribute the graph
1189 import_type importer(proc0Map, distMap);
1190
1191 // Import the data
1192 distGraph->doImport(*proc0Graph, importer, INSERT);
1193 } else {
1194 distGraph = rcp(new sparse_graph_type(rowMap, colMap, 0, constructorParams));
1195
1196 // Create an importer/exporter/vandelay to redistribute the graph
1198 import_type importer(proc0Map, rowMap);
1199
1200 // Import the data
1201 distGraph->doImport(*proc0Graph, importer, INSERT);
1202 }
1203
1204 return distGraph;
1205}
1206
1207template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1208Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_graph_type>
1210 const trcp_tcomm_t& comm,
1211 const bool callFillComplete,
1212 const bool tolerant,
1213 const bool debug) {
1214 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true);
1215
1216 return readSparseGraph(in, comm,
1217 callFillComplete,
1218 tolerant, debug);
1219 // We can rely on the destructor of the input stream to close
1220 // the file on scope exit, even if readSparseGraph() throws an
1221 // exception.
1222}
1223
1224template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1225Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1226 sparse_graph_type>
1228 const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1229 const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1230 const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
1231 const bool tolerant,
1232 const bool debug) {
1233 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(pComm, filename, true);
1234
1235 return readSparseGraph(in, pComm,
1236 constructorParams,
1237 fillCompleteParams, tolerant, debug);
1238 // We can rely on the destructor of the input stream to close
1239 // the file on scope exit, even if readSparseGraph() throws an
1240 // exception.
1241}
1242
1243template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1244Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_graph_type>
1246 const Teuchos::RCP<const map_type>& rowMap,
1247 Teuchos::RCP<const map_type>& colMap,
1248 const Teuchos::RCP<const map_type>& domainMap,
1249 const Teuchos::RCP<const map_type>& rangeMap,
1250 const bool callFillComplete,
1251 const bool tolerant,
1252 const bool debug) {
1253 TEUCHOS_TEST_FOR_EXCEPTION(rowMap.is_null(), std::invalid_argument,
1254 "Input rowMap must be nonnull.");
1255 trcp_tcomm_t comm = rowMap->getComm();
1256 if (comm.is_null()) {
1257 // If the input communicator is null on some process, then
1258 // that process does not participate in the collective.
1259 return Teuchos::null;
1260 }
1261
1262 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true);
1263
1264 return readSparseGraph(in, rowMap, colMap, domainMap, rangeMap,
1265 callFillComplete, tolerant, debug);
1266}
1267
1268template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1269Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_graph_type>
1271 const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1272 const bool callFillComplete,
1273 const bool tolerant,
1274 const bool debug) {
1275 Teuchos::RCP<const map_type> fakeRowMap;
1276 Teuchos::RCP<const map_type> fakeColMap;
1277 Teuchos::RCP<Teuchos::ParameterList> fakeCtorParams;
1278
1279 Teuchos::RCP<sparse_graph_type> graph =
1280 readSparseGraphHelper(in, pComm,
1281 fakeRowMap, fakeColMap,
1282 fakeCtorParams, tolerant, debug);
1283 if (callFillComplete) {
1284 graph->fillComplete();
1285 }
1286 return graph;
1287}
1288
1289template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1290Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_graph_type>
1292 const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1293 const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1294 const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
1295 const bool tolerant,
1296 const bool debug) {
1297 Teuchos::RCP<const map_type> fakeRowMap;
1298 Teuchos::RCP<const map_type> fakeColMap;
1299 Teuchos::RCP<sparse_graph_type> graph =
1300 readSparseGraphHelper(in, pComm,
1301 fakeRowMap, fakeColMap,
1302 constructorParams, tolerant, debug);
1303 graph->fillComplete(fillCompleteParams);
1304 return graph;
1305}
1306
1307template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1308Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_graph_type>
1310 const Teuchos::RCP<const map_type>& rowMap,
1311 Teuchos::RCP<const map_type>& colMap,
1312 const Teuchos::RCP<const map_type>& domainMap,
1313 const Teuchos::RCP<const map_type>& rangeMap,
1314 const bool callFillComplete,
1315 const bool tolerant,
1316 const bool debug) {
1317 Teuchos::RCP<sparse_graph_type> graph =
1318 readSparseGraphHelper(in, rowMap->getComm(),
1319 rowMap, colMap, Teuchos::null, tolerant,
1320 debug);
1321 if (callFillComplete) {
1322 graph->fillComplete(domainMap, rangeMap);
1323 }
1324 return graph;
1325}
1326
1327#include "MatrixMarket_TpetraNew.hpp"
1328
1329template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1330Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
1331MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readSparseFile(const std::string& filename,
1332 const trcp_tcomm_t& comm,
1333 const bool callFillComplete,
1334 const bool tolerant,
1335 const bool debug) {
1336 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true);
1337
1338 return readSparse(in, comm, callFillComplete, tolerant, debug);
1339 // We can rely on the destructor of the input stream to close
1340 // the file on scope exit, even if readSparse() throws an
1341 // exception.
1342}
1343
1344template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1345Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
1346MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readSparseFile(const std::string& filename,
1347 const trcp_tcomm_t& comm,
1348 const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1349 const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
1350 const bool tolerant,
1351 const bool debug) {
1352 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true);
1353
1354 return readSparse(in, comm, constructorParams,
1355 fillCompleteParams, tolerant, debug);
1356}
1357
1358template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1359Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
1360MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readSparseFile(const std::string& filename,
1361 const Teuchos::RCP<const map_type>& rowMap,
1362 Teuchos::RCP<const map_type>& colMap,
1363 const Teuchos::RCP<const map_type>& domainMap,
1364 const Teuchos::RCP<const map_type>& rangeMap,
1365 const bool callFillComplete,
1366 const bool tolerant,
1367 const bool debug) {
1368 TEUCHOS_TEST_FOR_EXCEPTION(
1369 rowMap.is_null(), std::invalid_argument,
1370 "Row Map must be nonnull.");
1371
1372 trcp_tcomm_t comm = rowMap->getComm();
1373
1374 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true);
1375
1376 return readSparse(in, rowMap, colMap, domainMap, rangeMap,
1377 callFillComplete, tolerant, debug);
1378}
1379
1380template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1381Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
1383 const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1384 const bool callFillComplete,
1385 const bool tolerant,
1386 const bool debug) {
1387 using std::cerr;
1388 using std::endl;
1389 using Teuchos::arcp;
1390 using Teuchos::ArrayRCP;
1391 using Teuchos::broadcast;
1392 using Teuchos::null;
1393 using Teuchos::ptr;
1394 using Teuchos::RCP;
1395 using Teuchos::REDUCE_MAX;
1396 using Teuchos::reduceAll;
1397 using Teuchos::Tuple;
1398 using Teuchos::MatrixMarket::Banner;
1399 typedef Teuchos::ScalarTraits<scalar_type> STS;
1400
1401 const bool extraDebug = false;
1402 const int myRank = pComm->getRank();
1403 const int rootRank = 0;
1404
1405 // Current line number in the input stream. Various calls
1406 // will modify this depending on the number of lines that are
1407 // read from the input stream. Only Rank 0 modifies this.
1408 size_t lineNumber = 1;
1409
1410 if (debug && myRank == rootRank) {
1411 cerr << "Matrix Market reader: readSparse:" << endl
1412 << "-- Reading banner line" << endl;
1413 }
1414
1415 // The "Banner" tells you whether the input stream represents
1416 // a sparse matrix, the symmetry type of the matrix, and the
1417 // type of the data it contains.
1418 //
1419 // pBanner will only be nonnull on MPI Rank 0. It will be
1420 // null on all other MPI processes.
1421 RCP<const Banner> pBanner;
1422 {
1423 // We read and validate the Banner on Proc 0, but broadcast
1424 // the validation result to all processes.
1425 // Teuchos::broadcast doesn't currently work with bool, so
1426 // we use int (true -> 1, false -> 0).
1427 int bannerIsCorrect = 1;
1428 std::ostringstream errMsg;
1429
1430 if (myRank == rootRank) {
1431 // Read the Banner line from the input stream.
1432 try {
1433 pBanner = readBanner(in, lineNumber, tolerant, debug);
1434 } catch (std::exception& e) {
1435 errMsg << "Attempt to read the Matrix Market file's Banner line "
1436 "threw an exception: "
1437 << e.what();
1438 bannerIsCorrect = 0;
1439 }
1440
1441 if (bannerIsCorrect) {
1442 // Validate the Banner for the case of a sparse matrix.
1443 // We validate on Proc 0, since it reads the Banner.
1444
1445 // In intolerant mode, the matrix type must be "coordinate".
1446 if (!tolerant && pBanner->matrixType() != "coordinate") {
1447 bannerIsCorrect = 0;
1448 errMsg << "The Matrix Market input file must contain a "
1449 "\"coordinate\"-format sparse matrix in order to create a "
1450 "Tpetra::CrsMatrix object from it, but the file's matrix "
1451 "type is \""
1452 << pBanner->matrixType() << "\" instead.";
1453 }
1454 // In tolerant mode, we allow the matrix type to be
1455 // anything other than "array" (which would mean that
1456 // the file contains a dense matrix).
1457 if (tolerant && pBanner->matrixType() == "array") {
1458 bannerIsCorrect = 0;
1459 errMsg << "Matrix Market file must contain a \"coordinate\"-"
1460 "format sparse matrix in order to create a Tpetra::CrsMatrix "
1461 "object from it, but the file's matrix type is \"array\" "
1462 "instead. That probably means the file contains dense matrix "
1463 "data.";
1464 }
1465 }
1466 } // Proc 0: Done reading the Banner, hopefully successfully.
1467
1468 // Broadcast from Proc 0 whether the Banner was read correctly.
1469 broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
1470
1471 // If the Banner is invalid, all processes throw an
1472 // exception. Only Proc 0 gets the exception message, but
1473 // that's OK, since the main point is to "stop the world"
1474 // (rather than throw an exception on one process and leave
1475 // the others hanging).
1476 TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
1477 std::invalid_argument, errMsg.str());
1478 } // Done reading the Banner line and broadcasting success.
1479 if (debug && myRank == rootRank) {
1480 cerr << "-- Reading dimensions line" << endl;
1481 }
1482
1483 // Read the matrix dimensions from the Matrix Market metadata.
1484 // dims = (numRows, numCols, numEntries). Proc 0 does the
1485 // reading, but it broadcasts the results to all MPI
1486 // processes. Thus, readCoordDims() is a collective
1487 // operation. It does a collective check for correctness too.
1488 Tuple<global_ordinal_type, 3> dims =
1489 readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
1490
1491 if (debug && myRank == rootRank) {
1492 cerr << "-- Making Adder for collecting matrix data" << endl;
1493 }
1494
1495 // "Adder" object for collecting all the sparse matrix entries
1496 // from the input stream. This is only nonnull on Proc 0.
1497 RCP<adder_type> pAdder =
1498 makeAdder(pComm, pBanner, dims, tolerant, debug);
1499
1500 if (debug && myRank == rootRank) {
1501 cerr << "-- Reading matrix data" << endl;
1502 }
1503 //
1504 // Read the matrix entries from the input stream on Proc 0.
1505 //
1506 {
1507 // We use readSuccess to broadcast the results of the read
1508 // (succeeded or not) to all MPI processes. Since
1509 // Teuchos::broadcast doesn't currently know how to send
1510 // bools, we convert to int (true -> 1, false -> 0).
1511 int readSuccess = 1;
1512 std::ostringstream errMsg; // Exception message (only valid on Proc 0)
1513 if (myRank == rootRank) {
1514 try {
1515 // Reader for "coordinate" format sparse matrix data.
1516 typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
1517 global_ordinal_type, scalar_type, STS::isComplex>
1518 reader_type;
1519 reader_type reader(pAdder);
1520
1521 // Read the sparse matrix entries.
1522 std::pair<bool, std::vector<size_t>> results =
1523 reader.read(in, lineNumber, tolerant, debug);
1524 readSuccess = results.first ? 1 : 0;
1525 } catch (std::exception& e) {
1526 readSuccess = 0;
1527 errMsg << e.what();
1528 }
1529 }
1530 broadcast(*pComm, rootRank, ptr(&readSuccess));
1531
1532 // It would be nice to add a "verbose" flag, so that in
1533 // tolerant mode, we could log any bad line number(s) on
1534 // Proc 0. For now, we just throw if the read fails to
1535 // succeed.
1536 //
1537 // Question: If we're in tolerant mode, and if the read did
1538 // not succeed, should we attempt to call fillComplete()?
1539 TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
1540 "Failed to read the Matrix Market sparse matrix file: "
1541 << errMsg.str());
1542 } // Done reading the matrix entries (stored on Proc 0 for now)
1543
1544 if (debug && myRank == rootRank) {
1545 cerr << "-- Successfully read the Matrix Market data" << endl;
1546 }
1547
1548 // In tolerant mode, we need to rebroadcast the matrix
1549 // dimensions, since they may be different after reading the
1550 // actual matrix data. We only need to broadcast the number
1551 // of rows and columns. Only Rank 0 needs to know the actual
1552 // global number of entries, since (a) we need to merge
1553 // duplicates on Rank 0 first anyway, and (b) when we
1554 // distribute the entries, each rank other than Rank 0 will
1555 // only need to know how many entries it owns, not the total
1556 // number of entries.
1557 if (tolerant) {
1558 if (debug && myRank == rootRank) {
1559 cerr << "-- Tolerant mode: rebroadcasting matrix dimensions"
1560 << endl
1561 << "----- Dimensions before: "
1562 << dims[0] << " x " << dims[1]
1563 << endl;
1564 }
1565 // Packed coordinate matrix dimensions (numRows, numCols).
1566 Tuple<global_ordinal_type, 2> updatedDims;
1567 if (myRank == rootRank) {
1568 // If one or more bottom rows of the matrix contain no
1569 // entries, then the Adder will report that the number
1570 // of rows is less than that specified in the
1571 // metadata. We allow this case, and favor the
1572 // metadata so that the zero row(s) will be included.
1573 updatedDims[0] =
1574 std::max(dims[0], pAdder->getAdder()->numRows());
1575 updatedDims[1] = pAdder->getAdder()->numCols();
1576 }
1577 broadcast(*pComm, rootRank, updatedDims);
1578 dims[0] = updatedDims[0];
1579 dims[1] = updatedDims[1];
1580 if (debug && myRank == rootRank) {
1581 cerr << "----- Dimensions after: " << dims[0] << " x "
1582 << dims[1] << endl;
1583 }
1584 } else {
1585 // In strict mode, we require that the matrix's metadata and
1586 // its actual data agree, at least somewhat. In particular,
1587 // the number of rows must agree, since otherwise we cannot
1588 // distribute the matrix correctly.
1589
1590 // Teuchos::broadcast() doesn't know how to broadcast bools,
1591 // so we use an int with the standard 1 == true, 0 == false
1592 // encoding.
1593 int dimsMatch = 1;
1594 if (myRank == rootRank) {
1595 // If one or more bottom rows of the matrix contain no
1596 // entries, then the Adder will report that the number of
1597 // rows is less than that specified in the metadata. We
1598 // allow this case, and favor the metadata, but do not
1599 // allow the Adder to think there are more rows in the
1600 // matrix than the metadata says.
1601 if (dims[0] < pAdder->getAdder()->numRows()) {
1602 dimsMatch = 0;
1603 }
1604 }
1605 broadcast(*pComm, 0, ptr(&dimsMatch));
1606 if (dimsMatch == 0) {
1607 // We're in an error state anyway, so we might as well
1608 // work a little harder to print an informative error
1609 // message.
1610 //
1611 // Broadcast the Adder's idea of the matrix dimensions
1612 // from Proc 0 to all processes.
1613 Tuple<global_ordinal_type, 2> addersDims;
1614 if (myRank == rootRank) {
1615 addersDims[0] = pAdder->getAdder()->numRows();
1616 addersDims[1] = pAdder->getAdder()->numCols();
1617 }
1618 broadcast(*pComm, 0, addersDims);
1619 TEUCHOS_TEST_FOR_EXCEPTION(
1620 dimsMatch == 0, std::runtime_error,
1621 "The matrix metadata says that the matrix is " << dims[0] << " x "
1622 << dims[1] << ", but the actual data says that the matrix is "
1623 << addersDims[0] << " x " << addersDims[1] << ". That means the "
1624 "data includes more rows than reported in the metadata. This "
1625 "is not allowed when parsing in strict mode. Parse the matrix in "
1626 "tolerant mode to ignore the metadata when it disagrees with the "
1627 "data.");
1628 }
1629 } // Matrix dimensions (# rows, # cols, # entries) agree.
1630
1631 if (debug && myRank == rootRank) {
1632 cerr << "-- Converting matrix data into CSR format on Proc 0" << endl;
1633 }
1634
1635 // Now that we've read in all the matrix entries from the
1636 // input stream into the adder on Proc 0, post-process them
1637 // into CSR format (still on Proc 0). This will facilitate
1638 // distributing them to all the processors.
1639 //
1640 // These arrays represent the global matrix data as a CSR
1641 // matrix (with numEntriesPerRow as redundant but convenient
1642 // metadata, since it's computable from rowPtr and vice
1643 // versa). They are valid only on Proc 0.
1644 ArrayRCP<size_t> numEntriesPerRow;
1645 ArrayRCP<size_t> rowPtr;
1646 ArrayRCP<global_ordinal_type> colInd;
1647 ArrayRCP<scalar_type> values;
1648
1649 // Proc 0 first merges duplicate entries, and then converts
1650 // the coordinate-format matrix data to CSR.
1651 {
1652 int mergeAndConvertSucceeded = 1;
1653 std::ostringstream errMsg;
1654
1655 if (myRank == rootRank) {
1656 try {
1657 typedef Teuchos::MatrixMarket::Raw::Element<scalar_type,
1659 element_type;
1660
1661 // Number of rows in the matrix. If we are in tolerant
1662 // mode, we've already synchronized dims with the actual
1663 // matrix data. If in strict mode, we should use dims
1664 // (as read from the file's metadata) rather than the
1665 // matrix data to determine the dimensions. (The matrix
1666 // data will claim fewer rows than the metadata, if one
1667 // or more rows have no entries stored in the file.)
1668 const size_type numRows = dims[0];
1669
1670 // Additively merge duplicate matrix entries.
1671 pAdder->getAdder()->merge();
1672
1673 // Get a temporary const view of the merged matrix entries.
1674 const std::vector<element_type>& entries =
1675 pAdder->getAdder()->getEntries();
1676
1677 // Number of matrix entries (after merging).
1678 const size_t numEntries = (size_t)entries.size();
1679
1680 if (debug) {
1681 cerr << "----- Proc 0: Matrix has numRows=" << numRows
1682 << " rows and numEntries=" << numEntries
1683 << " entries." << endl;
1684 }
1685
1686 // Make space for the CSR matrix data. Converting to
1687 // CSR is easier if we fill numEntriesPerRow with zeros
1688 // at first.
1689 numEntriesPerRow = arcp<size_t>(numRows);
1690 std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
1691 rowPtr = arcp<size_t>(numRows + 1);
1692 std::fill(rowPtr.begin(), rowPtr.end(), 0);
1693 colInd = arcp<global_ordinal_type>(numEntries);
1694 values = arcp<scalar_type>(numEntries);
1695
1696 // Convert from array-of-structs coordinate format to CSR
1697 // (compressed sparse row) format.
1698 global_ordinal_type prvRow = 0;
1699 size_t curPos = 0;
1700 rowPtr[0] = 0;
1701 for (curPos = 0; curPos < numEntries; ++curPos) {
1702 const element_type& curEntry = entries[curPos];
1703 const global_ordinal_type curRow = curEntry.rowIndex();
1704 TEUCHOS_TEST_FOR_EXCEPTION(
1705 curRow < prvRow, std::logic_error,
1706 "Row indices are out of order, even though they are supposed "
1707 "to be sorted. curRow = "
1708 << curRow << ", prvRow = "
1709 << prvRow << ", at curPos = " << curPos << ". Please report "
1710 "this bug to the Tpetra developers.");
1711 if (curRow > prvRow) {
1712 for (global_ordinal_type r = prvRow + 1; r <= curRow; ++r) {
1713 rowPtr[r] = curPos;
1714 }
1715 prvRow = curRow;
1716 }
1717 numEntriesPerRow[curRow]++;
1718 colInd[curPos] = curEntry.colIndex();
1719 values[curPos] = curEntry.value();
1720 }
1721 // rowPtr has one more entry than numEntriesPerRow. The
1722 // last entry of rowPtr is the number of entries in
1723 // colInd and values.
1724 rowPtr[numRows] = numEntries;
1725 } // Finished conversion to CSR format
1726 catch (std::exception& e) {
1727 mergeAndConvertSucceeded = 0;
1728 errMsg << "Failed to merge sparse matrix entries and convert to "
1729 "CSR format: "
1730 << e.what();
1731 }
1732
1733 if (debug && mergeAndConvertSucceeded) {
1734 // Number of rows in the matrix.
1735 const size_type numRows = dims[0];
1736 const size_type maxToDisplay = 100;
1737
1738 cerr << "----- Proc 0: numEntriesPerRow[0.."
1739 << (numEntriesPerRow.size() - 1) << "] ";
1740 if (numRows > maxToDisplay) {
1741 cerr << "(only showing first and last few entries) ";
1742 }
1743 cerr << "= [";
1744 if (numRows > 0) {
1745 if (numRows > maxToDisplay) {
1746 for (size_type k = 0; k < 2; ++k) {
1747 cerr << numEntriesPerRow[k] << " ";
1748 }
1749 cerr << "... ";
1750 for (size_type k = numRows - 2; k < numRows - 1; ++k) {
1751 cerr << numEntriesPerRow[k] << " ";
1752 }
1753 } else {
1754 for (size_type k = 0; k < numRows - 1; ++k) {
1755 cerr << numEntriesPerRow[k] << " ";
1756 }
1757 }
1758 cerr << numEntriesPerRow[numRows - 1];
1759 } // numRows > 0
1760 cerr << "]" << endl;
1761
1762 cerr << "----- Proc 0: rowPtr ";
1763 if (numRows > maxToDisplay) {
1764 cerr << "(only showing first and last few entries) ";
1765 }
1766 cerr << "= [";
1767 if (numRows > maxToDisplay) {
1768 for (size_type k = 0; k < 2; ++k) {
1769 cerr << rowPtr[k] << " ";
1770 }
1771 cerr << "... ";
1772 for (size_type k = numRows - 2; k < numRows; ++k) {
1773 cerr << rowPtr[k] << " ";
1774 }
1775 } else {
1776 for (size_type k = 0; k < numRows; ++k) {
1777 cerr << rowPtr[k] << " ";
1778 }
1779 }
1780 cerr << rowPtr[numRows] << "]" << endl;
1781 }
1782
1783 } // if myRank == rootRank
1784
1785 // Broadcast success flag to all ranks
1786 int mergeAndConvertSucceededAll = mergeAndConvertSucceeded;
1787 broadcast(*pComm, rootRank, ptr(&mergeAndConvertSucceededAll));
1788 if (mergeAndConvertSucceededAll == 0) {
1789 TEUCHOS_TEST_FOR_EXCEPTION(
1790 true, std::runtime_error,
1791 "Failed to merge sparse matrix entries and convert to CSR format: "
1792 << errMsg.str());
1793 }
1794 } // Done converting sparse matrix data to CSR format
1795
1796 // Now we're done with the Adder, so we can release the
1797 // reference ("free" it) to save space. This only actually
1798 // does anything on Rank 0, since pAdder is null on all the
1799 // other MPI processes.
1800 pAdder = null;
1801
1802 if (debug && myRank == rootRank) {
1803 cerr << "-- Making range, domain, and row maps" << endl;
1804 }
1805
1806 // Make the maps that describe the matrix's range and domain,
1807 // and the distribution of its rows. Creating a Map is a
1808 // collective operation, so we don't have to do a broadcast of
1809 // a success Boolean.
1810 RCP<const map_type> pRangeMap = makeRangeMap(pComm, dims[0]);
1811 RCP<const map_type> pDomainMap =
1812 makeDomainMap(pRangeMap, dims[0], dims[1]);
1813 RCP<const map_type> pRowMap = makeRowMap(null, pComm, dims[0]);
1814
1815 if (debug && myRank == rootRank) {
1816 cerr << "-- Distributing the matrix data" << endl;
1817 }
1818
1819 // Distribute the matrix data. Each processor has to add the
1820 // rows that it owns. If you try to make Proc 0 call
1821 // insertGlobalValues() for _all_ the rows, not just those it
1822 // owns, then fillComplete() will compute the number of
1823 // columns incorrectly. That's why Proc 0 has to distribute
1824 // the matrix data and why we make all the processors (not
1825 // just Proc 0) call insertGlobalValues() on their own data.
1826 //
1827 // These arrays represent each processor's part of the matrix
1828 // data, in "CSR" format (sort of, since the row indices might
1829 // not be contiguous).
1830 ArrayRCP<size_t> myNumEntriesPerRow;
1831 ArrayRCP<size_t> myRowPtr;
1832 ArrayRCP<global_ordinal_type> myColInd;
1833 ArrayRCP<scalar_type> myValues;
1834 // Distribute the matrix data. This is a collective operation.
1835 distribute(myNumEntriesPerRow, myRowPtr, myColInd, myValues, pRowMap,
1836 numEntriesPerRow, rowPtr, colInd, values, debug);
1837
1838 if (debug && myRank == rootRank) {
1839 cerr << "-- Inserting matrix entries on each processor";
1840 if (callFillComplete) {
1841 cerr << " and calling fillComplete()";
1842 }
1843 cerr << endl;
1844 }
1845 // Each processor inserts its part of the matrix data, and
1846 // then they all call fillComplete(). This method invalidates
1847 // the my* distributed matrix data before calling
1848 // fillComplete(), in order to save space. In general, we
1849 // never store more than two copies of the matrix's entries in
1850 // memory at once, which is no worse than what Tpetra
1851 // promises.
1852 RCP<sparse_matrix_type> pMatrix =
1853 makeMatrix(myNumEntriesPerRow, myRowPtr, myColInd, myValues,
1854 pRowMap, pRangeMap, pDomainMap, callFillComplete);
1855 // Only use a reduce-all in debug mode to check if pMatrix is
1856 // null. Otherwise, just throw an exception. We never expect
1857 // a null pointer here, so we can save a communication.
1858 if (debug) {
1859 int localIsNull = pMatrix.is_null() ? 1 : 0;
1860 int globalIsNull = 0;
1861 reduceAll(*pComm, REDUCE_MAX, localIsNull, ptr(&globalIsNull));
1862 TEUCHOS_TEST_FOR_EXCEPTION(globalIsNull != 0, std::logic_error,
1863 "MatrixMarketReader::makeMatrix() returned a null pointer on at least one "
1864 "process. Please report this bug to the Tpetra developers.");
1865 } else {
1866 TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::logic_error,
1867 "MatrixMarketReader::makeMatrix() returned a null pointer. "
1868 "Please report this bug to the Tpetra developers.");
1869 }
1870
1871 // We can't get the dimensions of the matrix until after
1872 // fillComplete() is called. Thus, we can't do the sanity
1873 // check (dimensions read from the Matrix Market data,
1874 // vs. dimensions reported by the CrsMatrix) unless the user
1875 // asked makeMatrix() to call fillComplete().
1876 //
1877 // Note that pMatrix->getGlobalNum{Rows,Cols}() does _not_ do
1878 // what one might think it does, so you have to ask the range
1879 // resp. domain map for the number of rows resp. columns.
1880 if (callFillComplete) {
1881 const int numProcs = pComm->getSize();
1882
1883 if (extraDebug && debug) {
1884 const global_size_t globalNumRows =
1885 pRangeMap->getGlobalNumElements();
1886 const global_size_t globalNumCols =
1887 pDomainMap->getGlobalNumElements();
1888 if (myRank == rootRank) {
1889 cerr << "-- Matrix is "
1890 << globalNumRows << " x " << globalNumCols
1891 << " with " << pMatrix->getGlobalNumEntries()
1892 << " entries, and index base "
1893 << pMatrix->getIndexBase() << "." << endl;
1894 }
1895 pComm->barrier();
1896 for (int p = 0; p < numProcs; ++p) {
1897 if (myRank == p) {
1898 cerr << "-- Proc " << p << " owns "
1899 << pMatrix->getLocalNumCols() << " columns, and "
1900 << pMatrix->getLocalNumEntries() << " entries." << endl;
1901 }
1902 pComm->barrier();
1903 }
1904 } // if (extraDebug && debug)
1905 } // if (callFillComplete)
1906
1907 if (debug && myRank == rootRank) {
1908 cerr << "-- Done creating the CrsMatrix from the Matrix Market data"
1909 << endl;
1910 }
1911 return pMatrix;
1912}
1913
1914template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1915Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
1917 const Teuchos::RCP<const Teuchos::Comm<int>>& pComm,
1918 const Teuchos::RCP<Teuchos::ParameterList>& constructorParams,
1919 const Teuchos::RCP<Teuchos::ParameterList>& fillCompleteParams,
1920 const bool tolerant,
1921 const bool debug) {
1922 using std::cerr;
1923 using std::endl;
1924 using Teuchos::arcp;
1925 using Teuchos::ArrayRCP;
1926 using Teuchos::broadcast;
1927 using Teuchos::null;
1928 using Teuchos::ptr;
1929 using Teuchos::RCP;
1930 using Teuchos::reduceAll;
1931 using Teuchos::Tuple;
1932 using Teuchos::MatrixMarket::Banner;
1933 typedef Teuchos::ScalarTraits<scalar_type> STS;
1934
1935 const bool extraDebug = false;
1936 const int myRank = pComm->getRank();
1937 const int rootRank = 0;
1938
1939 // Current line number in the input stream. Various calls
1940 // will modify this depending on the number of lines that are
1941 // read from the input stream. Only Rank 0 modifies this.
1942 size_t lineNumber = 1;
1943
1944 if (debug && myRank == rootRank) {
1945 cerr << "Matrix Market reader: readSparse:" << endl
1946 << "-- Reading banner line" << endl;
1947 }
1948
1949 // The "Banner" tells you whether the input stream represents
1950 // a sparse matrix, the symmetry type of the matrix, and the
1951 // type of the data it contains.
1952 //
1953 // pBanner will only be nonnull on MPI Rank 0. It will be
1954 // null on all other MPI processes.
1955 RCP<const Banner> pBanner;
1956 {
1957 // We read and validate the Banner on Proc 0, but broadcast
1958 // the validation result to all processes.
1959 // Teuchos::broadcast doesn't currently work with bool, so
1960 // we use int (true -> 1, false -> 0).
1961 int bannerIsCorrect = 1;
1962 std::ostringstream errMsg;
1963
1964 if (myRank == rootRank) {
1965 // Read the Banner line from the input stream.
1966 try {
1967 pBanner = readBanner(in, lineNumber, tolerant, debug);
1968 } catch (std::exception& e) {
1969 errMsg << "Attempt to read the Matrix Market file's Banner line "
1970 "threw an exception: "
1971 << e.what();
1972 bannerIsCorrect = 0;
1973 }
1974
1975 if (bannerIsCorrect) {
1976 // Validate the Banner for the case of a sparse matrix.
1977 // We validate on Proc 0, since it reads the Banner.
1978
1979 // In intolerant mode, the matrix type must be "coordinate".
1980 if (!tolerant && pBanner->matrixType() != "coordinate") {
1981 bannerIsCorrect = 0;
1982 errMsg << "The Matrix Market input file must contain a "
1983 "\"coordinate\"-format sparse matrix in order to create a "
1984 "Tpetra::CrsMatrix object from it, but the file's matrix "
1985 "type is \""
1986 << pBanner->matrixType() << "\" instead.";
1987 }
1988 // In tolerant mode, we allow the matrix type to be
1989 // anything other than "array" (which would mean that
1990 // the file contains a dense matrix).
1991 if (tolerant && pBanner->matrixType() == "array") {
1992 bannerIsCorrect = 0;
1993 errMsg << "Matrix Market file must contain a \"coordinate\"-"
1994 "format sparse matrix in order to create a Tpetra::CrsMatrix "
1995 "object from it, but the file's matrix type is \"array\" "
1996 "instead. That probably means the file contains dense matrix "
1997 "data.";
1998 }
1999 }
2000 } // Proc 0: Done reading the Banner, hopefully successfully.
2001
2002 // Broadcast from Proc 0 whether the Banner was read correctly.
2003 broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
2004
2005 // If the Banner is invalid, all processes throw an
2006 // exception. Only Proc 0 gets the exception message, but
2007 // that's OK, since the main point is to "stop the world"
2008 // (rather than throw an exception on one process and leave
2009 // the others hanging).
2010 TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
2011 std::invalid_argument, errMsg.str());
2012 } // Done reading the Banner line and broadcasting success.
2013 if (debug && myRank == rootRank) {
2014 cerr << "-- Reading dimensions line" << endl;
2015 }
2016
2017 // Read the matrix dimensions from the Matrix Market metadata.
2018 // dims = (numRows, numCols, numEntries). Proc 0 does the
2019 // reading, but it broadcasts the results to all MPI
2020 // processes. Thus, readCoordDims() is a collective
2021 // operation. It does a collective check for correctness too.
2022 Tuple<global_ordinal_type, 3> dims =
2023 readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
2024
2025 if (debug && myRank == rootRank) {
2026 cerr << "-- Making Adder for collecting matrix data" << endl;
2027 }
2028
2029 // "Adder" object for collecting all the sparse matrix entries
2030 // from the input stream. This is only nonnull on Proc 0.
2031 RCP<adder_type> pAdder =
2032 makeAdder(pComm, pBanner, dims, tolerant, debug);
2033
2034 if (debug && myRank == rootRank) {
2035 cerr << "-- Reading matrix data" << endl;
2036 }
2037 //
2038 // Read the matrix entries from the input stream on Proc 0.
2039 //
2040 {
2041 // We use readSuccess to broadcast the results of the read
2042 // (succeeded or not) to all MPI processes. Since
2043 // Teuchos::broadcast doesn't currently know how to send
2044 // bools, we convert to int (true -> 1, false -> 0).
2045 int readSuccess = 1;
2046 std::ostringstream errMsg; // Exception message (only valid on Proc 0)
2047 if (myRank == rootRank) {
2048 try {
2049 // Reader for "coordinate" format sparse matrix data.
2050 typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
2051 global_ordinal_type, scalar_type, STS::isComplex>
2052 reader_type;
2053 reader_type reader(pAdder);
2054
2055 // Read the sparse matrix entries.
2056 std::pair<bool, std::vector<size_t>> results =
2057 reader.read(in, lineNumber, tolerant, debug);
2058 readSuccess = results.first ? 1 : 0;
2059 } catch (std::exception& e) {
2060 readSuccess = 0;
2061 errMsg << e.what();
2062 }
2063 }
2064 broadcast(*pComm, rootRank, ptr(&readSuccess));
2065
2066 // It would be nice to add a "verbose" flag, so that in
2067 // tolerant mode, we could log any bad line number(s) on
2068 // Proc 0. For now, we just throw if the read fails to
2069 // succeed.
2070 //
2071 // Question: If we're in tolerant mode, and if the read did
2072 // not succeed, should we attempt to call fillComplete()?
2073 TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
2074 "Failed to read the Matrix Market sparse matrix file: "
2075 << errMsg.str());
2076 } // Done reading the matrix entries (stored on Proc 0 for now)
2077
2078 if (debug && myRank == rootRank) {
2079 cerr << "-- Successfully read the Matrix Market data" << endl;
2080 }
2081
2082 // In tolerant mode, we need to rebroadcast the matrix
2083 // dimensions, since they may be different after reading the
2084 // actual matrix data. We only need to broadcast the number
2085 // of rows and columns. Only Rank 0 needs to know the actual
2086 // global number of entries, since (a) we need to merge
2087 // duplicates on Rank 0 first anyway, and (b) when we
2088 // distribute the entries, each rank other than Rank 0 will
2089 // only need to know how many entries it owns, not the total
2090 // number of entries.
2091 if (tolerant) {
2092 if (debug && myRank == rootRank) {
2093 cerr << "-- Tolerant mode: rebroadcasting matrix dimensions"
2094 << endl
2095 << "----- Dimensions before: "
2096 << dims[0] << " x " << dims[1]
2097 << endl;
2098 }
2099 // Packed coordinate matrix dimensions (numRows, numCols).
2100 Tuple<global_ordinal_type, 2> updatedDims;
2101 if (myRank == rootRank) {
2102 // If one or more bottom rows of the matrix contain no
2103 // entries, then the Adder will report that the number
2104 // of rows is less than that specified in the
2105 // metadata. We allow this case, and favor the
2106 // metadata so that the zero row(s) will be included.
2107 updatedDims[0] =
2108 std::max(dims[0], pAdder->getAdder()->numRows());
2109 updatedDims[1] = pAdder->getAdder()->numCols();
2110 }
2111 broadcast(*pComm, rootRank, updatedDims);
2112 dims[0] = updatedDims[0];
2113 dims[1] = updatedDims[1];
2114 if (debug && myRank == rootRank) {
2115 cerr << "----- Dimensions after: " << dims[0] << " x "
2116 << dims[1] << endl;
2117 }
2118 } else {
2119 // In strict mode, we require that the matrix's metadata and
2120 // its actual data agree, at least somewhat. In particular,
2121 // the number of rows must agree, since otherwise we cannot
2122 // distribute the matrix correctly.
2123
2124 // Teuchos::broadcast() doesn't know how to broadcast bools,
2125 // so we use an int with the standard 1 == true, 0 == false
2126 // encoding.
2127 int dimsMatch = 1;
2128 if (myRank == rootRank) {
2129 // If one or more bottom rows of the matrix contain no
2130 // entries, then the Adder will report that the number of
2131 // rows is less than that specified in the metadata. We
2132 // allow this case, and favor the metadata, but do not
2133 // allow the Adder to think there are more rows in the
2134 // matrix than the metadata says.
2135 if (dims[0] < pAdder->getAdder()->numRows()) {
2136 dimsMatch = 0;
2137 }
2138 }
2139 broadcast(*pComm, 0, ptr(&dimsMatch));
2140 if (dimsMatch == 0) {
2141 // We're in an error state anyway, so we might as well
2142 // work a little harder to print an informative error
2143 // message.
2144 //
2145 // Broadcast the Adder's idea of the matrix dimensions
2146 // from Proc 0 to all processes.
2147 Tuple<global_ordinal_type, 2> addersDims;
2148 if (myRank == rootRank) {
2149 addersDims[0] = pAdder->getAdder()->numRows();
2150 addersDims[1] = pAdder->getAdder()->numCols();
2151 }
2152 broadcast(*pComm, 0, addersDims);
2153 TEUCHOS_TEST_FOR_EXCEPTION(
2154 dimsMatch == 0, std::runtime_error,
2155 "The matrix metadata says that the matrix is " << dims[0] << " x "
2156 << dims[1] << ", but the actual data says that the matrix is "
2157 << addersDims[0] << " x " << addersDims[1] << ". That means the "
2158 "data includes more rows than reported in the metadata. This "
2159 "is not allowed when parsing in strict mode. Parse the matrix in "
2160 "tolerant mode to ignore the metadata when it disagrees with the "
2161 "data.");
2162 }
2163 } // Matrix dimensions (# rows, # cols, # entries) agree.
2164
2165 if (debug && myRank == rootRank) {
2166 cerr << "-- Converting matrix data into CSR format on Proc 0" << endl;
2167 }
2168
2169 // Now that we've read in all the matrix entries from the
2170 // input stream into the adder on Proc 0, post-process them
2171 // into CSR format (still on Proc 0). This will facilitate
2172 // distributing them to all the processors.
2173 //
2174 // These arrays represent the global matrix data as a CSR
2175 // matrix (with numEntriesPerRow as redundant but convenient
2176 // metadata, since it's computable from rowPtr and vice
2177 // versa). They are valid only on Proc 0.
2178 ArrayRCP<size_t> numEntriesPerRow;
2179 ArrayRCP<size_t> rowPtr;
2180 ArrayRCP<global_ordinal_type> colInd;
2181 ArrayRCP<scalar_type> values;
2182
2183 // Proc 0 first merges duplicate entries, and then converts
2184 // the coordinate-format matrix data to CSR.
2185 {
2186 int mergeAndConvertSucceeded = 1;
2187 std::ostringstream errMsg;
2188
2189 if (myRank == rootRank) {
2190 try {
2191 typedef Teuchos::MatrixMarket::Raw::Element<scalar_type,
2193 element_type;
2194
2195 // Number of rows in the matrix. If we are in tolerant
2196 // mode, we've already synchronized dims with the actual
2197 // matrix data. If in strict mode, we should use dims
2198 // (as read from the file's metadata) rather than the
2199 // matrix data to determine the dimensions. (The matrix
2200 // data will claim fewer rows than the metadata, if one
2201 // or more rows have no entries stored in the file.)
2202 const size_type numRows = dims[0];
2203
2204 // Additively merge duplicate matrix entries.
2205 pAdder->getAdder()->merge();
2206
2207 // Get a temporary const view of the merged matrix entries.
2208 const std::vector<element_type>& entries =
2209 pAdder->getAdder()->getEntries();
2210
2211 // Number of matrix entries (after merging).
2212 const size_t numEntries = (size_t)entries.size();
2213
2214 if (debug) {
2215 cerr << "----- Proc 0: Matrix has numRows=" << numRows
2216 << " rows and numEntries=" << numEntries
2217 << " entries." << endl;
2218 }
2219
2220 // Make space for the CSR matrix data. Converting to
2221 // CSR is easier if we fill numEntriesPerRow with zeros
2222 // at first.
2223 numEntriesPerRow = arcp<size_t>(numRows);
2224 std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
2225 rowPtr = arcp<size_t>(numRows + 1);
2226 std::fill(rowPtr.begin(), rowPtr.end(), 0);
2227 colInd = arcp<global_ordinal_type>(numEntries);
2228 values = arcp<scalar_type>(numEntries);
2229
2230 // Convert from array-of-structs coordinate format to CSR
2231 // (compressed sparse row) format.
2232 global_ordinal_type prvRow = 0;
2233 size_t curPos = 0;
2234 rowPtr[0] = 0;
2235 for (curPos = 0; curPos < numEntries; ++curPos) {
2236 const element_type& curEntry = entries[curPos];
2237 const global_ordinal_type curRow = curEntry.rowIndex();
2238 TEUCHOS_TEST_FOR_EXCEPTION(
2239 curRow < prvRow, std::logic_error,
2240 "Row indices are out of order, even though they are supposed "
2241 "to be sorted. curRow = "
2242 << curRow << ", prvRow = "
2243 << prvRow << ", at curPos = " << curPos << ". Please report "
2244 "this bug to the Tpetra developers.");
2245 if (curRow > prvRow) {
2246 for (global_ordinal_type r = prvRow + 1; r <= curRow; ++r) {
2247 rowPtr[r] = curPos;
2248 }
2249 prvRow = curRow;
2250 }
2251 numEntriesPerRow[curRow]++;
2252 colInd[curPos] = curEntry.colIndex();
2253 values[curPos] = curEntry.value();
2254 }
2255 // rowPtr has one more entry than numEntriesPerRow. The
2256 // last entry of rowPtr is the number of entries in
2257 // colInd and values.
2258 rowPtr[numRows] = numEntries;
2259 } // Finished conversion to CSR format
2260 catch (std::exception& e) {
2261 mergeAndConvertSucceeded = 0;
2262 errMsg << "Failed to merge sparse matrix entries and convert to "
2263 "CSR format: "
2264 << e.what();
2265 }
2266
2267 if (debug && mergeAndConvertSucceeded) {
2268 // Number of rows in the matrix.
2269 const size_type numRows = dims[0];
2270 const size_type maxToDisplay = 100;
2271
2272 cerr << "----- Proc 0: numEntriesPerRow[0.."
2273 << (numEntriesPerRow.size() - 1) << "] ";
2274 if (numRows > maxToDisplay) {
2275 cerr << "(only showing first and last few entries) ";
2276 }
2277 cerr << "= [";
2278 if (numRows > 0) {
2279 if (numRows > maxToDisplay) {
2280 for (size_type k = 0; k < 2; ++k) {
2281 cerr << numEntriesPerRow[k] << " ";
2282 }
2283 cerr << "... ";
2284 for (size_type k = numRows - 2; k < numRows - 1; ++k) {
2285 cerr << numEntriesPerRow[k] << " ";
2286 }
2287 } else {
2288 for (size_type k = 0; k < numRows - 1; ++k) {
2289 cerr << numEntriesPerRow[k] << " ";
2290 }
2291 }
2292 cerr << numEntriesPerRow[numRows - 1];
2293 } // numRows > 0
2294 cerr << "]" << endl;
2295
2296 cerr << "----- Proc 0: rowPtr ";
2297 if (numRows > maxToDisplay) {
2298 cerr << "(only showing first and last few entries) ";
2299 }
2300 cerr << "= [";
2301 if (numRows > maxToDisplay) {
2302 for (size_type k = 0; k < 2; ++k) {
2303 cerr << rowPtr[k] << " ";
2304 }
2305 cerr << "... ";
2306 for (size_type k = numRows - 2; k < numRows; ++k) {
2307 cerr << rowPtr[k] << " ";
2308 }
2309 } else {
2310 for (size_type k = 0; k < numRows; ++k) {
2311 cerr << rowPtr[k] << " ";
2312 }
2313 }
2314 cerr << rowPtr[numRows] << "]" << endl;
2315 }
2316 } // if myRank == rootRank
2317
2318 // Broadcast success flag to all ranks
2319 int mergeAndConvertSucceededAll = mergeAndConvertSucceeded;
2320 broadcast(*pComm, rootRank, ptr(&mergeAndConvertSucceededAll));
2321 if (mergeAndConvertSucceededAll == 0) {
2322 TEUCHOS_TEST_FOR_EXCEPTION(
2323 true, std::runtime_error,
2324 "Failed to merge sparse matrix entries and convert to CSR format: "
2325 << errMsg.str());
2326 }
2327 } // Done converting sparse matrix data to CSR format
2328
2329 // Now we're done with the Adder, so we can release the
2330 // reference ("free" it) to save space. This only actually
2331 // does anything on Rank 0, since pAdder is null on all the
2332 // other MPI processes.
2333 pAdder = null;
2334
2335 if (debug && myRank == rootRank) {
2336 cerr << "-- Making range, domain, and row maps" << endl;
2337 }
2338
2339 // Make the maps that describe the matrix's range and domain,
2340 // and the distribution of its rows. Creating a Map is a
2341 // collective operation, so we don't have to do a broadcast of
2342 // a success Boolean.
2343 RCP<const map_type> pRangeMap = makeRangeMap(pComm, dims[0]);
2344 RCP<const map_type> pDomainMap =
2345 makeDomainMap(pRangeMap, dims[0], dims[1]);
2346 RCP<const map_type> pRowMap = makeRowMap(null, pComm, dims[0]);
2347
2348 if (debug && myRank == rootRank) {
2349 cerr << "-- Distributing the matrix data" << endl;
2350 }
2351
2352 // Distribute the matrix data. Each processor has to add the
2353 // rows that it owns. If you try to make Proc 0 call
2354 // insertGlobalValues() for _all_ the rows, not just those it
2355 // owns, then fillComplete() will compute the number of
2356 // columns incorrectly. That's why Proc 0 has to distribute
2357 // the matrix data and why we make all the processors (not
2358 // just Proc 0) call insertGlobalValues() on their own data.
2359 //
2360 // These arrays represent each processor's part of the matrix
2361 // data, in "CSR" format (sort of, since the row indices might
2362 // not be contiguous).
2363 ArrayRCP<size_t> myNumEntriesPerRow;
2364 ArrayRCP<size_t> myRowPtr;
2365 ArrayRCP<global_ordinal_type> myColInd;
2366 ArrayRCP<scalar_type> myValues;
2367 // Distribute the matrix data. This is a collective operation.
2368 distribute(myNumEntriesPerRow, myRowPtr, myColInd, myValues, pRowMap,
2369 numEntriesPerRow, rowPtr, colInd, values, debug);
2370
2371 if (debug && myRank == rootRank) {
2372 cerr << "-- Inserting matrix entries on each process "
2373 "and calling fillComplete()"
2374 << endl;
2375 }
2376 // Each processor inserts its part of the matrix data, and
2377 // then they all call fillComplete(). This method invalidates
2378 // the my* distributed matrix data before calling
2379 // fillComplete(), in order to save space. In general, we
2380 // never store more than two copies of the matrix's entries in
2381 // memory at once, which is no worse than what Tpetra
2382 // promises.
2383 Teuchos::RCP<sparse_matrix_type> pMatrix =
2384 makeMatrix(myNumEntriesPerRow, myRowPtr, myColInd, myValues,
2385 pRowMap, pRangeMap, pDomainMap, constructorParams,
2386 fillCompleteParams);
2387 // Only use a reduce-all in debug mode to check if pMatrix is
2388 // null. Otherwise, just throw an exception. We never expect
2389 // a null pointer here, so we can save a communication.
2390 if (debug) {
2391 int localIsNull = pMatrix.is_null() ? 1 : 0;
2392 int globalIsNull = 0;
2393 reduceAll(*pComm, Teuchos::REDUCE_MAX, localIsNull, ptr(&globalIsNull));
2394 TEUCHOS_TEST_FOR_EXCEPTION(globalIsNull != 0, std::logic_error,
2395 "MatrixMarketReader::makeMatrix() returned a null pointer on at least one "
2396 "process. Please report this bug to the Tpetra developers.");
2397 } else {
2398 TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::logic_error,
2399 "MatrixMarketReader::makeMatrix() returned a null pointer. "
2400 "Please report this bug to the Tpetra developers.");
2401 }
2402
2403 // Sanity check for dimensions (read from the Matrix Market
2404 // data, vs. dimensions reported by the CrsMatrix).
2405 //
2406 // Note that pMatrix->getGlobalNum{Rows,Cols}() does _not_ do
2407 // what one might think it does, so you have to ask the range
2408 // resp. domain map for the number of rows resp. columns.
2409 if (extraDebug && debug) {
2410 const int numProcs = pComm->getSize();
2411 const global_size_t globalNumRows =
2412 pRangeMap->getGlobalNumElements();
2413 const global_size_t globalNumCols =
2414 pDomainMap->getGlobalNumElements();
2415 if (myRank == rootRank) {
2416 cerr << "-- Matrix is "
2417 << globalNumRows << " x " << globalNumCols
2418 << " with " << pMatrix->getGlobalNumEntries()
2419 << " entries, and index base "
2420 << pMatrix->getIndexBase() << "." << endl;
2421 }
2422 pComm->barrier();
2423 for (int p = 0; p < numProcs; ++p) {
2424 if (myRank == p) {
2425 cerr << "-- Proc " << p << " owns "
2426 << pMatrix->getLocalNumCols() << " columns, and "
2427 << pMatrix->getLocalNumEntries() << " entries." << endl;
2428 }
2429 pComm->barrier();
2430 }
2431 } // if (extraDebug && debug)
2432
2433 if (debug && myRank == rootRank) {
2434 cerr << "-- Done creating the CrsMatrix from the Matrix Market data"
2435 << endl;
2436 }
2437 return pMatrix;
2438}
2439
2440template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2441Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
2443 const Teuchos::RCP<const map_type>& rowMap,
2444 Teuchos::RCP<const map_type>& colMap,
2445 const Teuchos::RCP<const map_type>& domainMap,
2446 const Teuchos::RCP<const map_type>& rangeMap,
2447 const bool callFillComplete,
2448 const bool tolerant,
2449 const bool debug) {
2450 using std::cerr;
2451 using std::endl;
2452 using Teuchos::arcp;
2453 using Teuchos::ArrayRCP;
2454 using Teuchos::ArrayView;
2455 using Teuchos::as;
2456 using Teuchos::broadcast;
2457 using Teuchos::Comm;
2458 using Teuchos::null;
2459 using Teuchos::ptr;
2460 using Teuchos::RCP;
2461 using Teuchos::reduceAll;
2462 using Teuchos::Tuple;
2463 using Teuchos::MatrixMarket::Banner;
2464 typedef Teuchos::ScalarTraits<scalar_type> STS;
2465
2466 RCP<const Comm<int>> pComm = rowMap->getComm();
2467 const int myRank = pComm->getRank();
2468 const int rootRank = 0;
2469 const bool extraDebug = false;
2470
2471 // Fast checks for invalid input. We can't check other
2472 // attributes of the Maps until we've read in the matrix
2473 // dimensions.
2474 TEUCHOS_TEST_FOR_EXCEPTION(
2475 rowMap.is_null(), std::invalid_argument,
2476 "Row Map must be nonnull.");
2477 TEUCHOS_TEST_FOR_EXCEPTION(
2478 rangeMap.is_null(), std::invalid_argument,
2479 "Range Map must be nonnull.");
2480 TEUCHOS_TEST_FOR_EXCEPTION(
2481 domainMap.is_null(), std::invalid_argument,
2482 "Domain Map must be nonnull.");
2483 TEUCHOS_TEST_FOR_EXCEPTION(
2484 rowMap->getComm().getRawPtr() != pComm.getRawPtr(),
2485 std::invalid_argument,
2486 "The specified row Map's communicator (rowMap->getComm())"
2487 "differs from the given separately supplied communicator pComm.");
2488 TEUCHOS_TEST_FOR_EXCEPTION(
2489 domainMap->getComm().getRawPtr() != pComm.getRawPtr(),
2490 std::invalid_argument,
2491 "The specified domain Map's communicator (domainMap->getComm())"
2492 "differs from the given separately supplied communicator pComm.");
2493 TEUCHOS_TEST_FOR_EXCEPTION(
2494 rangeMap->getComm().getRawPtr() != pComm.getRawPtr(),
2495 std::invalid_argument,
2496 "The specified range Map's communicator (rangeMap->getComm())"
2497 "differs from the given separately supplied communicator pComm.");
2498
2499 // Current line number in the input stream. Various calls
2500 // will modify this depending on the number of lines that are
2501 // read from the input stream. Only Rank 0 modifies this.
2502 size_t lineNumber = 1;
2503
2504 if (debug && myRank == rootRank) {
2505 cerr << "Matrix Market reader: readSparse:" << endl
2506 << "-- Reading banner line" << endl;
2507 }
2508
2509 // The "Banner" tells you whether the input stream represents
2510 // a sparse matrix, the symmetry type of the matrix, and the
2511 // type of the data it contains.
2512 //
2513 // pBanner will only be nonnull on MPI Rank 0. It will be
2514 // null on all other MPI processes.
2515 RCP<const Banner> pBanner;
2516 {
2517 // We read and validate the Banner on Proc 0, but broadcast
2518 // the validation result to all processes.
2519 // Teuchos::broadcast doesn't currently work with bool, so
2520 // we use int (true -> 1, false -> 0).
2521 int bannerIsCorrect = 1;
2522 std::ostringstream errMsg;
2523
2524 if (myRank == rootRank) {
2525 // Read the Banner line from the input stream.
2526 try {
2527 pBanner = readBanner(in, lineNumber, tolerant, debug);
2528 } catch (std::exception& e) {
2529 errMsg << "Attempt to read the Matrix Market file's Banner line "
2530 "threw an exception: "
2531 << e.what();
2532 bannerIsCorrect = 0;
2533 }
2534
2535 if (bannerIsCorrect) {
2536 // Validate the Banner for the case of a sparse matrix.
2537 // We validate on Proc 0, since it reads the Banner.
2538
2539 // In intolerant mode, the matrix type must be "coordinate".
2540 if (!tolerant && pBanner->matrixType() != "coordinate") {
2541 bannerIsCorrect = 0;
2542 errMsg << "The Matrix Market input file must contain a "
2543 "\"coordinate\"-format sparse matrix in order to create a "
2544 "Tpetra::CrsMatrix object from it, but the file's matrix "
2545 "type is \""
2546 << pBanner->matrixType() << "\" instead.";
2547 }
2548 // In tolerant mode, we allow the matrix type to be
2549 // anything other than "array" (which would mean that
2550 // the file contains a dense matrix).
2551 if (tolerant && pBanner->matrixType() == "array") {
2552 bannerIsCorrect = 0;
2553 errMsg << "Matrix Market file must contain a \"coordinate\"-"
2554 "format sparse matrix in order to create a Tpetra::CrsMatrix "
2555 "object from it, but the file's matrix type is \"array\" "
2556 "instead. That probably means the file contains dense matrix "
2557 "data.";
2558 }
2559 }
2560 } // Proc 0: Done reading the Banner, hopefully successfully.
2561
2562 // Broadcast from Proc 0 whether the Banner was read correctly.
2563 broadcast(*pComm, rootRank, ptr(&bannerIsCorrect));
2564
2565 // If the Banner is invalid, all processes throw an
2566 // exception. Only Proc 0 gets the exception message, but
2567 // that's OK, since the main point is to "stop the world"
2568 // (rather than throw an exception on one process and leave
2569 // the others hanging).
2570 TEUCHOS_TEST_FOR_EXCEPTION(bannerIsCorrect == 0,
2571 std::invalid_argument, errMsg.str());
2572 } // Done reading the Banner line and broadcasting success.
2573 if (debug && myRank == rootRank) {
2574 cerr << "-- Reading dimensions line" << endl;
2575 }
2576
2577 // Read the matrix dimensions from the Matrix Market metadata.
2578 // dims = (numRows, numCols, numEntries). Proc 0 does the
2579 // reading, but it broadcasts the results to all MPI
2580 // processes. Thus, readCoordDims() is a collective
2581 // operation. It does a collective check for correctness too.
2582 Tuple<global_ordinal_type, 3> dims =
2583 readCoordDims(in, lineNumber, pBanner, pComm, tolerant, debug);
2584
2585 if (debug && myRank == rootRank) {
2586 cerr << "-- Making Adder for collecting matrix data" << endl;
2587 }
2588
2589 // "Adder" object for collecting all the sparse matrix entries
2590 // from the input stream. This is only nonnull on Proc 0.
2591 // The Adder internally converts the one-based indices (native
2592 // Matrix Market format) into zero-based indices.
2593 RCP<adder_type> pAdder =
2594 makeAdder(pComm, pBanner, dims, tolerant, debug);
2595
2596 if (debug && myRank == rootRank) {
2597 cerr << "-- Reading matrix data" << endl;
2598 }
2599 //
2600 // Read the matrix entries from the input stream on Proc 0.
2601 //
2602 {
2603 // We use readSuccess to broadcast the results of the read
2604 // (succeeded or not) to all MPI processes. Since
2605 // Teuchos::broadcast doesn't currently know how to send
2606 // bools, we convert to int (true -> 1, false -> 0).
2607 int readSuccess = 1;
2608 std::ostringstream errMsg; // Exception message (only valid on Proc 0)
2609 if (myRank == rootRank) {
2610 try {
2611 // Reader for "coordinate" format sparse matrix data.
2612 typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
2613 global_ordinal_type, scalar_type, STS::isComplex>
2614 reader_type;
2615 reader_type reader(pAdder);
2616
2617 // Read the sparse matrix entries.
2618 std::pair<bool, std::vector<size_t>> results =
2619 reader.read(in, lineNumber, tolerant, debug);
2620 readSuccess = results.first ? 1 : 0;
2621 } catch (std::exception& e) {
2622 readSuccess = 0;
2623 errMsg << e.what();
2624 }
2625 }
2626 broadcast(*pComm, rootRank, ptr(&readSuccess));
2627
2628 // It would be nice to add a "verbose" flag, so that in
2629 // tolerant mode, we could log any bad line number(s) on
2630 // Proc 0. For now, we just throw if the read fails to
2631 // succeed.
2632 //
2633 // Question: If we're in tolerant mode, and if the read did
2634 // not succeed, should we attempt to call fillComplete()?
2635 TEUCHOS_TEST_FOR_EXCEPTION(readSuccess == 0, std::runtime_error,
2636 "Failed to read the Matrix Market sparse matrix file: "
2637 << errMsg.str());
2638 } // Done reading the matrix entries (stored on Proc 0 for now)
2639
2640 if (debug && myRank == rootRank) {
2641 cerr << "-- Successfully read the Matrix Market data" << endl;
2642 }
2643
2644 // In tolerant mode, we need to rebroadcast the matrix
2645 // dimensions, since they may be different after reading the
2646 // actual matrix data. We only need to broadcast the number
2647 // of rows and columns. Only Rank 0 needs to know the actual
2648 // global number of entries, since (a) we need to merge
2649 // duplicates on Rank 0 first anyway, and (b) when we
2650 // distribute the entries, each rank other than Rank 0 will
2651 // only need to know how many entries it owns, not the total
2652 // number of entries.
2653 if (tolerant) {
2654 if (debug && myRank == rootRank) {
2655 cerr << "-- Tolerant mode: rebroadcasting matrix dimensions"
2656 << endl
2657 << "----- Dimensions before: "
2658 << dims[0] << " x " << dims[1]
2659 << endl;
2660 }
2661 // Packed coordinate matrix dimensions (numRows, numCols).
2662 Tuple<global_ordinal_type, 2> updatedDims;
2663 if (myRank == rootRank) {
2664 // If one or more bottom rows of the matrix contain no
2665 // entries, then the Adder will report that the number
2666 // of rows is less than that specified in the
2667 // metadata. We allow this case, and favor the
2668 // metadata so that the zero row(s) will be included.
2669 updatedDims[0] =
2670 std::max(dims[0], pAdder->getAdder()->numRows());
2671 updatedDims[1] = pAdder->getAdder()->numCols();
2672 }
2673 broadcast(*pComm, rootRank, updatedDims);
2674 dims[0] = updatedDims[0];
2675 dims[1] = updatedDims[1];
2676 if (debug && myRank == rootRank) {
2677 cerr << "----- Dimensions after: " << dims[0] << " x "
2678 << dims[1] << endl;
2679 }
2680 } else {
2681 // In strict mode, we require that the matrix's metadata and
2682 // its actual data agree, at least somewhat. In particular,
2683 // the number of rows must agree, since otherwise we cannot
2684 // distribute the matrix correctly.
2685
2686 // Teuchos::broadcast() doesn't know how to broadcast bools,
2687 // so we use an int with the standard 1 == true, 0 == false
2688 // encoding.
2689 int dimsMatch = 1;
2690 if (myRank == rootRank) {
2691 // If one or more bottom rows of the matrix contain no
2692 // entries, then the Adder will report that the number of
2693 // rows is less than that specified in the metadata. We
2694 // allow this case, and favor the metadata, but do not
2695 // allow the Adder to think there are more rows in the
2696 // matrix than the metadata says.
2697 if (dims[0] < pAdder->getAdder()->numRows()) {
2698 dimsMatch = 0;
2699 }
2700 }
2701 broadcast(*pComm, 0, ptr(&dimsMatch));
2702 if (dimsMatch == 0) {
2703 // We're in an error state anyway, so we might as well
2704 // work a little harder to print an informative error
2705 // message.
2706 //
2707 // Broadcast the Adder's idea of the matrix dimensions
2708 // from Proc 0 to all processes.
2709 Tuple<global_ordinal_type, 2> addersDims;
2710 if (myRank == rootRank) {
2711 addersDims[0] = pAdder->getAdder()->numRows();
2712 addersDims[1] = pAdder->getAdder()->numCols();
2713 }
2714 broadcast(*pComm, 0, addersDims);
2715 TEUCHOS_TEST_FOR_EXCEPTION(
2716 dimsMatch == 0, std::runtime_error,
2717 "The matrix metadata says that the matrix is " << dims[0] << " x "
2718 << dims[1] << ", but the actual data says that the matrix is "
2719 << addersDims[0] << " x " << addersDims[1] << ". That means the "
2720 "data includes more rows than reported in the metadata. This "
2721 "is not allowed when parsing in strict mode. Parse the matrix in "
2722 "tolerant mode to ignore the metadata when it disagrees with the "
2723 "data.");
2724 }
2725 } // Matrix dimensions (# rows, # cols, # entries) agree.
2726
2727 if (debug && myRank == rootRank) {
2728 cerr << "-- Converting matrix data into CSR format on Proc 0" << endl;
2729 }
2730
2731 // Now that we've read in all the matrix entries from the
2732 // input stream into the adder on Proc 0, post-process them
2733 // into CSR format (still on Proc 0). This will facilitate
2734 // distributing them to all the processors.
2735 //
2736 // These arrays represent the global matrix data as a CSR
2737 // matrix (with numEntriesPerRow as redundant but convenient
2738 // metadata, since it's computable from rowPtr and vice
2739 // versa). They are valid only on Proc 0.
2740 ArrayRCP<size_t> numEntriesPerRow;
2741 ArrayRCP<size_t> rowPtr;
2742 ArrayRCP<global_ordinal_type> colInd;
2743 ArrayRCP<scalar_type> values;
2744
2745 // Proc 0 first merges duplicate entries, and then converts
2746 // the coordinate-format matrix data to CSR.
2747 {
2748 int mergeAndConvertSucceeded = 1;
2749 std::ostringstream errMsg;
2750
2751 if (myRank == rootRank) {
2752 try {
2753 typedef Teuchos::MatrixMarket::Raw::Element<scalar_type,
2755 element_type;
2756
2757 // Number of rows in the matrix. If we are in tolerant
2758 // mode, we've already synchronized dims with the actual
2759 // matrix data. If in strict mode, we should use dims
2760 // (as read from the file's metadata) rather than the
2761 // matrix data to determine the dimensions. (The matrix
2762 // data will claim fewer rows than the metadata, if one
2763 // or more rows have no entries stored in the file.)
2764 const size_type numRows = dims[0];
2765
2766 // Additively merge duplicate matrix entries.
2767 pAdder->getAdder()->merge();
2768
2769 // Get a temporary const view of the merged matrix entries.
2770 const std::vector<element_type>& entries =
2771 pAdder->getAdder()->getEntries();
2772
2773 // Number of matrix entries (after merging).
2774 const size_t numEntries = (size_t)entries.size();
2775
2776 if (debug) {
2777 cerr << "----- Proc 0: Matrix has numRows=" << numRows
2778 << " rows and numEntries=" << numEntries
2779 << " entries." << endl;
2780 }
2781
2782 // Make space for the CSR matrix data. Converting to
2783 // CSR is easier if we fill numEntriesPerRow with zeros
2784 // at first.
2785 numEntriesPerRow = arcp<size_t>(numRows);
2786 std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
2787 rowPtr = arcp<size_t>(numRows + 1);
2788 std::fill(rowPtr.begin(), rowPtr.end(), 0);
2789 colInd = arcp<global_ordinal_type>(numEntries);
2790 values = arcp<scalar_type>(numEntries);
2791
2792 // Convert from array-of-structs coordinate format to CSR
2793 // (compressed sparse row) format.
2794 global_ordinal_type prvRow = 0;
2795 size_t curPos = 0;
2796 rowPtr[0] = 0;
2797 for (curPos = 0; curPos < numEntries; ++curPos) {
2798 const element_type& curEntry = entries[curPos];
2799 const global_ordinal_type curRow = curEntry.rowIndex();
2800 TEUCHOS_TEST_FOR_EXCEPTION(
2801 curRow < prvRow, std::logic_error,
2802 "Row indices are out of order, even though they are supposed "
2803 "to be sorted. curRow = "
2804 << curRow << ", prvRow = "
2805 << prvRow << ", at curPos = " << curPos << ". Please report "
2806 "this bug to the Tpetra developers.");
2807 if (curRow > prvRow) {
2808 prvRow = curRow;
2809 }
2810 numEntriesPerRow[curRow]++;
2811 colInd[curPos] = curEntry.colIndex();
2812 values[curPos] = curEntry.value();
2813 }
2814
2815 rowPtr[0] = 0;
2816 for (global_ordinal_type row = 1; row <= numRows; ++row) {
2817 rowPtr[row] = numEntriesPerRow[row - 1] + rowPtr[row - 1];
2818 }
2819 } // Finished conversion to CSR format
2820 catch (std::exception& e) {
2821 mergeAndConvertSucceeded = 0;
2822 errMsg << "Failed to merge sparse matrix entries and convert to "
2823 "CSR format: "
2824 << e.what();
2825 }
2826
2827 if (debug && mergeAndConvertSucceeded) {
2828 // Number of rows in the matrix.
2829 const size_type numRows = dims[0];
2830 const size_type maxToDisplay = 100;
2831
2832 cerr << "----- Proc 0: numEntriesPerRow[0.."
2833 << (numEntriesPerRow.size() - 1) << "] ";
2834 if (numRows > maxToDisplay) {
2835 cerr << "(only showing first and last few entries) ";
2836 }
2837 cerr << "= [";
2838 if (numRows > 0) {
2839 if (numRows > maxToDisplay) {
2840 for (size_type k = 0; k < 2; ++k) {
2841 cerr << numEntriesPerRow[k] << " ";
2842 }
2843 cerr << "... ";
2844 for (size_type k = numRows - 2; k < numRows - 1; ++k) {
2845 cerr << numEntriesPerRow[k] << " ";
2846 }
2847 } else {
2848 for (size_type k = 0; k < numRows - 1; ++k) {
2849 cerr << numEntriesPerRow[k] << " ";
2850 }
2851 }
2852 cerr << numEntriesPerRow[numRows - 1];
2853 } // numRows > 0
2854 cerr << "]" << endl;
2855
2856 cerr << "----- Proc 0: rowPtr ";
2857 if (numRows > maxToDisplay) {
2858 cerr << "(only showing first and last few entries) ";
2859 }
2860 cerr << "= [";
2861 if (numRows > maxToDisplay) {
2862 for (size_type k = 0; k < 2; ++k) {
2863 cerr << rowPtr[k] << " ";
2864 }
2865 cerr << "... ";
2866 for (size_type k = numRows - 2; k < numRows; ++k) {
2867 cerr << rowPtr[k] << " ";
2868 }
2869 } else {
2870 for (size_type k = 0; k < numRows; ++k) {
2871 cerr << rowPtr[k] << " ";
2872 }
2873 }
2874 cerr << rowPtr[numRows] << "]" << endl;
2875
2876 cerr << "----- Proc 0: colInd = [";
2877 for (size_t k = 0; k < rowPtr[numRows]; ++k) {
2878 cerr << colInd[k] << " ";
2879 }
2880 cerr << "]" << endl;
2881 }
2882 } // if myRank == rootRank
2883 } // Done converting sparse matrix data to CSR format
2884
2885 // Now we're done with the Adder, so we can release the
2886 // reference ("free" it) to save space. This only actually
2887 // does anything on Rank 0, since pAdder is null on all the
2888 // other MPI processes.
2889 pAdder = null;
2890
2891 // Verify details of the Maps. Don't count the global number
2892 // of entries in the row Map, since that number doesn't
2893 // correctly count overlap.
2894 if (debug && myRank == rootRank) {
2895 cerr << "-- Verifying Maps" << endl;
2896 }
2897 TEUCHOS_TEST_FOR_EXCEPTION(
2898 as<global_ordinal_type>(dims[0]) != rangeMap->getMaxAllGlobalIndex() + 1 - rangeMap->getIndexBase(),
2899 std::invalid_argument,
2900 "The range Map has " << rangeMap->getMaxAllGlobalIndex()
2901 << " max entry, but the matrix has a global number of rows " << dims[0]
2902 << ".");
2903 TEUCHOS_TEST_FOR_EXCEPTION(
2904 as<global_ordinal_type>(dims[1]) != domainMap->getMaxAllGlobalIndex() + 1 - domainMap->getIndexBase(),
2905 std::invalid_argument,
2906 "The domain Map has " << domainMap->getMaxAllGlobalIndex()
2907 << " max entry, but the matrix has a global number of columns "
2908 << dims[1] << ".");
2909
2910 // Create a row Map which is entirely owned on Proc 0.
2911 RCP<Teuchos::FancyOStream> err = debug ? Teuchos::getFancyOStream(Teuchos::rcpFromRef(cerr)) : null;
2912
2913 RCP<const map_type> gatherRowMap = Details::computeGatherMap(rowMap, err, debug);
2914 ArrayView<const global_ordinal_type> myRows =
2915 gatherRowMap->getLocalElementList();
2916 const size_type myNumRows = myRows.size();
2917 const global_ordinal_type indexBase = gatherRowMap->getIndexBase();
2918
2919 ArrayRCP<size_t> gatherNumEntriesPerRow = arcp<size_t>(myNumRows);
2920 for (size_type i_ = 0; i_ < myNumRows; i_++) {
2921 gatherNumEntriesPerRow[i_] = numEntriesPerRow[myRows[i_] - indexBase];
2922 }
2923
2924 // Create a matrix using this Map, and fill in on Proc 0. We
2925 // know how many entries there are in each row, so we can use
2926 // static profile.
2927 RCP<sparse_matrix_type> A_proc0 =
2928 rcp(new sparse_matrix_type(gatherRowMap, gatherNumEntriesPerRow()));
2929 if (myRank == rootRank) {
2930 if (debug) {
2931 cerr << "-- Proc 0: Filling gather matrix" << endl;
2932 }
2933 if (debug) {
2934 cerr << "---- Rows: " << Teuchos::toString(myRows) << endl;
2935 }
2936
2937 // Add Proc 0's matrix entries to the CrsMatrix.
2938 for (size_type i_ = 0; i_ < myNumRows; ++i_) {
2939 size_type i = myRows[i_] - indexBase;
2940
2941 const size_type curPos = as<size_type>(rowPtr[i]);
2942 const local_ordinal_type curNumEntries = numEntriesPerRow[i];
2943 ArrayView<global_ordinal_type> curColInd =
2944 colInd.view(curPos, curNumEntries);
2945 ArrayView<scalar_type> curValues =
2946 values.view(curPos, curNumEntries);
2947
2948 // Modify the column indices in place to have the right index base.
2949 for (size_type k = 0; k < curNumEntries; ++k) {
2950 curColInd[k] += indexBase;
2951 }
2952 if (debug) {
2953 cerr << "------ Columns: " << Teuchos::toString(curColInd) << endl;
2954 cerr << "------ Values: " << Teuchos::toString(curValues) << endl;
2955 }
2956 // Avoid constructing empty views of ArrayRCP objects.
2957 if (curNumEntries > 0) {
2958 A_proc0->insertGlobalValues(myRows[i_], curColInd, curValues);
2959 }
2960 }
2961 // Now we can save space by deallocating numEntriesPerRow,
2962 // rowPtr, colInd, and values, since we've already put those
2963 // data in the matrix.
2964 numEntriesPerRow = null;
2965 rowPtr = null;
2966 colInd = null;
2967 values = null;
2968 } // if myRank == rootRank
2969
2970 RCP<sparse_matrix_type> A;
2972 export_type exp(gatherRowMap, rowMap);
2973
2974 // Communicate the precise number of nonzeros per row, which was already
2975 // calculated above.
2976 typedef local_ordinal_type LO;
2977 typedef global_ordinal_type GO;
2979 mv_type_go target_nnzPerRow(rowMap, 1);
2980 mv_type_go source_nnzPerRow(gatherRowMap, 1);
2981 Teuchos::ArrayRCP<GO> srcData = source_nnzPerRow.getDataNonConst(0);
2982 for (int i = 0; i < myNumRows; i++)
2983 srcData[i] = gatherNumEntriesPerRow[i];
2984 srcData = Teuchos::null;
2985 target_nnzPerRow.doExport(source_nnzPerRow, exp, Tpetra::INSERT);
2986 Teuchos::ArrayRCP<GO> targetData = target_nnzPerRow.getDataNonConst(0);
2987 ArrayRCP<size_t> targetData_size_t = arcp<size_t>(targetData.size());
2988 for (int i = 0; i < targetData.size(); i++)
2989 targetData_size_t[i] = targetData[i];
2990
2991 if (colMap.is_null()) {
2992 A = rcp(new sparse_matrix_type(rowMap, targetData_size_t()));
2993 } else {
2994 A = rcp(new sparse_matrix_type(rowMap, colMap, targetData_size_t()));
2995 }
2996 A->doExport(*A_proc0, exp, INSERT);
2997 if (callFillComplete) {
2998 A->fillComplete(domainMap, rangeMap);
2999 }
3000
3001 // We can't get the dimensions of the matrix until after
3002 // fillComplete() is called. Thus, we can't do the sanity
3003 // check (dimensions read from the Matrix Market data,
3004 // vs. dimensions reported by the CrsMatrix) unless the user
3005 // asked us to call fillComplete().
3006 //
3007 // Note that pMatrix->getGlobalNum{Rows,Cols}() does _not_ do
3008 // what one might think it does, so you have to ask the range
3009 // resp. domain map for the number of rows resp. columns.
3010 if (callFillComplete) {
3011 const int numProcs = pComm->getSize();
3012
3013 if (extraDebug && debug) {
3014 const global_size_t globalNumRows = rangeMap->getGlobalNumElements();
3015 const global_size_t globalNumCols = domainMap->getGlobalNumElements();
3016 if (myRank == rootRank) {
3017 cerr << "-- Matrix is "
3018 << globalNumRows << " x " << globalNumCols
3019 << " with " << A->getGlobalNumEntries()
3020 << " entries, and index base "
3021 << A->getIndexBase() << "." << endl;
3022 }
3023 pComm->barrier();
3024 for (int p = 0; p < numProcs; ++p) {
3025 if (myRank == p) {
3026 cerr << "-- Proc " << p << " owns "
3027 << A->getLocalNumCols() << " columns, and "
3028 << A->getLocalNumEntries() << " entries." << endl;
3029 }
3030 pComm->barrier();
3031 }
3032 } // if (extraDebug && debug)
3033 } // if (callFillComplete)
3034
3035 if (debug && myRank == rootRank) {
3036 cerr << "-- Done creating the CrsMatrix from the Matrix Market data"
3037 << endl;
3038 }
3039 return A;
3040}
3041
3042template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3043Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::multivector_type>
3046 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& map,
3047 const bool tolerant,
3048 const bool debug,
3049 const bool binary) {
3050 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true, binary ? std::ios::binary : std::ios::in);
3051
3052 return readDense(in, comm, map, tolerant, debug, binary);
3053}
3054
3055template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3057 const trcp_tcomm_t& comm,
3058 const std::string& filename, const bool safe,
3059 std::ios_base::openmode mode) {
3060 // Input stream.
3061 std::ifstream in;
3062
3063 // Placeholder for broadcasting in-stream state.
3064 int all_should_stop = false;
3065
3066 // Try to open the in-stream on root rank.
3067 if (comm->getRank() == 0) {
3068 in.open(filename, mode);
3069 all_should_stop = !in && safe;
3070 }
3071
3072 // Broadcast state and possibly throw.
3073 if (comm) Teuchos::broadcast(*comm, 0, &all_should_stop);
3074
3075 TEUCHOS_TEST_FOR_EXCEPTION(
3076 all_should_stop,
3077 std::runtime_error,
3078 "Could not open input file '" + filename + "' on root rank 0.");
3079
3080 return in;
3081}
3082
3083template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3084Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::vector_type>
3087 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& map,
3088 const bool tolerant,
3089 const bool debug) {
3090 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true);
3091
3092 return readVector(in, comm, map, tolerant, debug);
3093}
3094
3095template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3096Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::multivector_type>
3099 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& map,
3100 const bool tolerant,
3101 const bool debug,
3102 const bool binary) {
3103 Teuchos::RCP<Teuchos::FancyOStream> err =
3104 Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
3105 return readDenseImpl<scalar_type>(in, comm, map, err, tolerant, debug, binary);
3106}
3107
3108template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3109Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::vector_type>
3112 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& map,
3113 const bool tolerant,
3114 const bool debug) {
3115 Teuchos::RCP<Teuchos::FancyOStream> err =
3116 Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
3117 return readVectorImpl<scalar_type>(in, comm, map, err, tolerant, debug);
3118}
3119
3120template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3121Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>
3124 const bool tolerant,
3125 const bool debug,
3126 const bool binary) {
3127 std::ifstream in = MatrixMarketReader::openInFileOnRankZero(comm, filename, true, binary ? std::ios::binary : std::ios::in);
3128
3129 return readMap(in, comm, tolerant, debug, binary);
3130}
3131
3132template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3133template <class MultiVectorScalarType>
3134Teuchos::RCP<Tpetra::MultiVector<MultiVectorScalarType,
3138MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readDenseImpl(std::istream& in,
3140 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& map,
3141 const Teuchos::RCP<Teuchos::FancyOStream>& err,
3142 const bool tolerant,
3143 const bool debug,
3144 const bool binary) {
3145 using std::endl;
3146 using Teuchos::ArrayRCP;
3147 using Teuchos::as;
3148 using Teuchos::broadcast;
3149 using Teuchos::outArg;
3150 using Teuchos::RCP;
3151 using Teuchos::Tuple;
3152 using Teuchos::MatrixMarket::Banner;
3153 using Teuchos::MatrixMarket::checkCommentLine;
3154 typedef MultiVectorScalarType ST;
3155 typedef local_ordinal_type LO;
3156 typedef global_ordinal_type GO;
3157 typedef node_type NT;
3158 typedef Teuchos::ScalarTraits<ST> STS;
3159 typedef typename STS::magnitudeType MT;
3160 typedef Teuchos::ScalarTraits<MT> STM;
3162
3163 // Rank 0 is the only (MPI) process allowed to read from the
3164 // input stream.
3165 const int myRank = comm->getRank();
3166
3167 if (!err.is_null()) {
3168 err->pushTab();
3169 }
3170 if (debug) {
3171 *err << myRank << ": readDenseImpl" << endl;
3172 }
3173 if (!err.is_null()) {
3174 err->pushTab();
3175 }
3176
3177 // mfh 17 Feb 2013: It's not strictly necessary that the Comm
3178 // instances be identical and that the Node instances be
3179 // identical. The essential condition is more complicated to
3180 // test and isn't the same for all Node types. Thus, we just
3181 // leave it up to the user.
3182
3183 // // If map is nonnull, check the precondition that its
3184 // // communicator resp. node equal comm resp. node. Checking
3185 // // now avoids doing a lot of file reading before we detect the
3186 // // violated precondition.
3187 // TEUCHOS_TEST_FOR_EXCEPTION(
3188 // ! map.is_null() && (map->getComm() != comm || map->getNode () != node,
3189 // std::invalid_argument, "If you supply a nonnull Map, the Map's "
3190 // "communicator and node must equal the supplied communicator resp. "
3191 // "node.");
3192
3193 // Process 0 will read in the matrix dimensions from the file,
3194 // and broadcast them to all ranks in the given communicator.
3195 // There are only 2 dimensions in the matrix, but we use the
3196 // third element of the Tuple to encode the banner's reported
3197 // data type: "real" == 0, "complex" == 1, and "integer" == 0
3198 // (same as "real"). We don't allow pattern matrices (i.e.,
3199 // graphs) since they only make sense for sparse data.
3200 Tuple<GO, 3> dims;
3201 dims[0] = 0;
3202 dims[1] = 0;
3203 dims[2] = 0;
3204
3205 // Current line number in the input stream. Only valid on
3206 // Proc 0. Various calls will modify this depending on the
3207 // number of lines that are read from the input stream.
3208 size_t lineNumber = 1;
3209
3210 // Capture errors and their messages on Proc 0.
3211 std::ostringstream exMsg;
3212 int localBannerReadSuccess = 1;
3213 int localDimsReadSuccess = 1;
3214
3215 // Only Proc 0 gets to read matrix data from the input stream.
3216 if (myRank == 0) {
3217 if (!binary) {
3218 if (debug) {
3219 *err << myRank << ": readDenseImpl: Reading banner line (dense)" << endl;
3220 }
3221
3222 // The "Banner" tells you whether the input stream
3223 // represents a dense matrix, the symmetry type of the
3224 // matrix, and the type of the data it contains.
3225 RCP<const Banner> pBanner;
3226 try {
3227 pBanner = readBanner(in, lineNumber, tolerant, debug);
3228 } catch (std::exception& e) {
3229 exMsg << e.what();
3230 localBannerReadSuccess = 0;
3231 }
3232 // Make sure the input stream is the right kind of data.
3233 if (localBannerReadSuccess) {
3234 if (pBanner->matrixType() != "array") {
3235 exMsg << "The Matrix Market file does not contain dense matrix "
3236 "data. Its banner (first) line says that its matrix type is \""
3237 << pBanner->matrixType() << "\", rather that the required "
3238 "\"array\".";
3239 localBannerReadSuccess = 0;
3240 } else if (pBanner->dataType() == "pattern") {
3241 exMsg << "The Matrix Market file's banner (first) "
3242 "line claims that the matrix's data type is \"pattern\". This does "
3243 "not make sense for a dense matrix, yet the file reports the matrix "
3244 "as dense. The only valid data types for a dense matrix are "
3245 "\"real\", \"complex\", and \"integer\".";
3246 localBannerReadSuccess = 0;
3247 } else {
3248 // Encode the data type reported by the Banner as the
3249 // third element of the dimensions Tuple.
3250 dims[2] = encodeDataType(pBanner->dataType());
3251 }
3252 } // if we successfully read the banner line
3253
3254 // At this point, we've successfully read the banner line.
3255 // Now read the dimensions line.
3256 if (localBannerReadSuccess) {
3257 if (debug) {
3258 *err << myRank << ": readDenseImpl: Reading dimensions line (dense)" << endl;
3259 }
3260 // Keep reading lines from the input stream until we find
3261 // a non-comment line, or until we run out of lines. The
3262 // latter is an error, since every "array" format Matrix
3263 // Market file must have a dimensions line after the
3264 // banner (even if the matrix has zero rows or columns, or
3265 // zero entries).
3266 std::string line;
3267 bool commentLine = true;
3268
3269 while (commentLine) {
3270 // Test whether it is even valid to read from the input
3271 // stream wrapping the line.
3272 if (in.eof() || in.fail()) {
3273 exMsg << "Unable to get array dimensions line (at all) from line "
3274 << lineNumber << " of input stream. The input stream "
3275 << "claims that it is "
3276 << (in.eof() ? "at end-of-file." : "in a failed state.");
3277 localDimsReadSuccess = 0;
3278 } else {
3279 // Try to get the next line from the input stream.
3280 if (getline(in, line)) {
3281 ++lineNumber; // We did actually read a line.
3282 }
3283 // Is the current line a comment line? Ignore start
3284 // and size; they are only useful for reading the
3285 // actual matrix entries. (We could use them here as
3286 // an optimization, but we've chosen not to.)
3287 size_t start = 0, size = 0;
3288 commentLine = checkCommentLine(line, start, size, lineNumber, tolerant);
3289 } // whether we failed to read the line at all
3290 } // while the line we just read is a comment line
3291
3292 //
3293 // Get <numRows> <numCols> from the line we just read.
3294 //
3295 std::istringstream istr(line);
3296
3297 // Test whether it is even valid to read from the input
3298 // stream wrapping the line.
3299 if (istr.eof() || istr.fail()) {
3300 exMsg << "Unable to read any data from line " << lineNumber
3301 << " of input; the line should contain the matrix dimensions "
3302 << "\"<numRows> <numCols>\".";
3303 localDimsReadSuccess = 0;
3304 } else { // It's valid to read from the line.
3305 GO theNumRows = 0;
3306 istr >> theNumRows; // Read in the number of rows.
3307 if (istr.fail()) {
3308 exMsg << "Failed to get number of rows from line "
3309 << lineNumber << " of input; the line should contains the "
3310 << "matrix dimensions \"<numRows> <numCols>\".";
3311 localDimsReadSuccess = 0;
3312 } else { // We successfully read the number of rows
3313 dims[0] = theNumRows; // Save the number of rows
3314 if (istr.eof()) { // Do we still have data to read?
3315 exMsg << "No more data after number of rows on line "
3316 << lineNumber << " of input; the line should contain the "
3317 << "matrix dimensions \"<numRows> <numCols>\".";
3318 localDimsReadSuccess = 0;
3319 } else { // Still data left to read; read in number of columns.
3320 GO theNumCols = 0;
3321 istr >> theNumCols; // Read in the number of columns
3322 if (istr.fail()) {
3323 exMsg << "Failed to get number of columns from line "
3324 << lineNumber << " of input; the line should contain "
3325 << "the matrix dimensions \"<numRows> <numCols>\".";
3326 localDimsReadSuccess = 0;
3327 } else { // We successfully read the number of columns
3328 dims[1] = theNumCols; // Save the number of columns
3329 } // if istr.fail ()
3330 } // if istr.eof ()
3331 } // if we read the number of rows
3332 } // if the input stream wrapping the dims line was (in)valid
3333 } // if we successfully read the banner line
3334 } // not in binary format
3335 else { // in binary format
3336 global_size_t numRows, numCols;
3337 in.read(reinterpret_cast<char*>(&numRows), sizeof(numRows));
3338 in.read(reinterpret_cast<char*>(&numCols), sizeof(numCols));
3339 TEUCHOS_TEST_FOR_EXCEPTION(
3340 numRows == 0 || numCols == 0,
3341 std::runtime_error,
3342 "Matrix Market dense binary header has non-positive dimensions: "
3343 "numRows="
3344 << numRows << ", numCols=" << numCols);
3345 dims[0] = Teuchos::as<GO>(numRows);
3346 dims[1] = Teuchos::as<GO>(numCols);
3347 if ((typeid(ST) == typeid(double)) || Teuchos::ScalarTraits<ST>::isOrdinal) {
3348 dims[2] = 0;
3349 } else if (Teuchos::ScalarTraits<ST>::isComplex) {
3350 dims[2] = 1;
3351 } else {
3352 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
3353 "Unrecognized Matrix Market data type. "
3354 "We should never get here. "
3355 "Please report this bug to the Tpetra developers.");
3356 }
3357 localDimsReadSuccess = true;
3358 localDimsReadSuccess = true;
3359 } // in binary format
3360 } // if (myRank == 0)
3361
3362 // Broadcast the matrix dimensions, the encoded data type, and
3363 // whether or not Proc 0 succeeded in reading the banner and
3364 // dimensions.
3365 Tuple<GO, 5> bannerDimsReadResult;
3366 if (myRank == 0) {
3367 bannerDimsReadResult[0] = dims[0]; // numRows
3368 bannerDimsReadResult[1] = dims[1]; // numCols
3369 bannerDimsReadResult[2] = dims[2]; // encoded data type
3370 bannerDimsReadResult[3] = localBannerReadSuccess;
3371 bannerDimsReadResult[4] = localDimsReadSuccess;
3372 }
3373 // Broadcast matrix dimensions and the encoded data type from
3374 // Proc 0 to all the MPI processes.
3375 broadcast(*comm, 0, bannerDimsReadResult);
3376
3377 TEUCHOS_TEST_FOR_EXCEPTION(
3378 bannerDimsReadResult[3] == 0, std::runtime_error,
3379 "Failed to read banner line: " << exMsg.str());
3380 TEUCHOS_TEST_FOR_EXCEPTION(
3381 bannerDimsReadResult[4] == 0, std::runtime_error,
3382 "Failed to read matrix dimensions line: " << exMsg.str());
3383 if (myRank != 0) {
3384 dims[0] = bannerDimsReadResult[0];
3385 dims[1] = bannerDimsReadResult[1];
3386 dims[2] = bannerDimsReadResult[2];
3387 }
3388
3389 // Tpetra objects want the matrix dimensions in these types.
3390 const global_size_t numRows = static_cast<global_size_t>(dims[0]);
3391 const size_t numCols = static_cast<size_t>(dims[1]);
3392
3393 // Make a "Proc 0 owns everything" Map that we will use to
3394 // read in the multivector entries in the correct order on
3395 // Proc 0. This must be a collective
3396 RCP<const map_type> proc0Map; // "Proc 0 owns everything" Map
3397 if (map.is_null()) {
3398 // The user didn't supply a Map. Make a contiguous
3399 // distributed Map for them, using the read-in multivector
3400 // dimensions.
3401 map = createUniformContigMapWithNode<LO, GO, NT>(numRows, comm);
3402 const size_t localNumRows = (myRank == 0) ? numRows : 0;
3403 // At this point, map exists and has a nonnull node.
3404 proc0Map = createContigMapWithNode<LO, GO, NT>(numRows, localNumRows,
3405 comm);
3406 } else { // The user supplied a Map.
3407 proc0Map = Details::computeGatherMap<map_type>(map, err, debug);
3408 }
3409
3410 // Make a multivector X owned entirely by Proc 0.
3411 RCP<MV> X = createMultiVector<ST, LO, GO, NT>(proc0Map, numCols);
3412
3413 //
3414 // On Proc 0, read the Matrix Market data from the input
3415 // stream into the multivector X.
3416 //
3417 int localReadDataSuccess = 1;
3418 if (myRank == 0) {
3419 if (!binary) {
3420 try {
3421 if (debug) {
3422 *err << myRank << ": readDenseImpl: Reading matrix data (dense)"
3423 << endl;
3424 }
3425
3426 // Make sure that we can get a 1-D view of X.
3427 TEUCHOS_TEST_FOR_EXCEPTION(
3428 !X->isConstantStride(), std::logic_error,
3429 "Can't get a 1-D view of the entries of the MultiVector X on "
3430 "Process 0, because the stride between the columns of X is not "
3431 "constant. This shouldn't happen because we just created X and "
3432 "haven't filled it in yet. Please report this bug to the Tpetra "
3433 "developers.");
3434
3435 // Get a writeable 1-D view of the entries of X. Rank 0
3436 // owns all of them. The view will expire at the end of
3437 // scope, so (if necessary) it will be written back to X
3438 // at this time.
3439 ArrayRCP<ST> X_view = X->get1dViewNonConst();
3440 TEUCHOS_TEST_FOR_EXCEPTION(
3441 as<global_size_t>(X_view.size()) < numRows * numCols,
3442 std::logic_error,
3443 "The view of X has size " << X_view.size() << " which is not enough to "
3444 "accommodate the expected number of entries numRows*numCols = "
3445 << numRows << "*" << numCols << " = " << numRows * numCols << ". "
3446 "Please report this bug to the Tpetra developers.");
3447 const size_t stride = X->getStride();
3448
3449 // The third element of the dimensions Tuple encodes the data
3450 // type reported by the Banner: "real" == 0, "complex" == 1,
3451 // "integer" == 0 (same as "real"), "pattern" == 2. We do not
3452 // allow dense matrices to be pattern matrices, so dims[2] ==
3453 // 0 or 1. We've already checked for this above.
3454 const bool isComplex = (dims[2] == 1);
3455 size_type count = 0, curRow = 0, curCol = 0;
3456
3457 std::string line;
3458 while (getline(in, line)) {
3459 ++lineNumber;
3460 // Is the current line a comment line? If it's not,
3461 // line.substr(start,size) contains the data.
3462 size_t start = 0, size = 0;
3463 const bool commentLine =
3464 checkCommentLine(line, start, size, lineNumber, tolerant);
3465 if (!commentLine) {
3466 // Make sure we have room in which to put the new matrix
3467 // entry. We check this only after checking for a
3468 // comment line, because there may be one or more
3469 // comment lines at the end of the file. In tolerant
3470 // mode, we simply ignore any extra data.
3471 if (count >= X_view.size()) {
3472 if (tolerant) {
3473 break;
3474 } else {
3475 TEUCHOS_TEST_FOR_EXCEPTION(
3476 count >= X_view.size(),
3477 std::runtime_error,
3478 "The Matrix Market input stream has more data in it than "
3479 "its metadata reported. Current line number is "
3480 << lineNumber << ".");
3481 }
3482 }
3483
3484 // mfh 19 Dec 2012: Ignore everything up to the initial
3485 // colon. writeDense() has the option to print out the
3486 // global row index in front of each entry, followed by
3487 // a colon and space.
3488 {
3489 const size_t pos = line.substr(start, size).find(':');
3490 if (pos != std::string::npos) {
3491 start = pos + 1;
3492 }
3493 }
3494 std::istringstream istr(line.substr(start, size));
3495 // Does the line contain anything at all? Can we
3496 // safely read from the input stream wrapping the
3497 // line?
3498 if (istr.eof() || istr.fail()) {
3499 // In tolerant mode, simply ignore the line.
3500 if (tolerant) {
3501 break;
3502 }
3503 // We repeat the full test here so the exception
3504 // message is more informative.
3505 TEUCHOS_TEST_FOR_EXCEPTION(
3506 !tolerant && (istr.eof() || istr.fail()),
3507 std::runtime_error,
3508 "Line " << lineNumber << " of the Matrix Market file is "
3509 "empty, or we cannot read from it for some other reason.");
3510 }
3511 // Current matrix entry to read in.
3512 ST val = STS::zero();
3513 // Real and imaginary parts of the current matrix entry.
3514 // The imaginary part is zero if the matrix is real-valued.
3515 MT real = STM::zero(), imag = STM::zero();
3516
3517 // isComplex refers to the input stream's data, not to
3518 // the scalar type S. It's OK to read real-valued
3519 // data into a matrix storing complex-valued data; in
3520 // that case, all entries' imaginary parts are zero.
3521 if (isComplex) {
3522 // STS::real() and STS::imag() return a copy of
3523 // their respective components, not a writeable
3524 // reference. Otherwise we could just assign to
3525 // them using the istream extraction operator (>>).
3526 // That's why we have separate magnitude type "real"
3527 // and "imag" variables.
3528
3529 // Attempt to read the real part of the current entry.
3530 istr >> real;
3531 if (istr.fail()) {
3532 TEUCHOS_TEST_FOR_EXCEPTION(
3533 !tolerant && istr.eof(), std::runtime_error,
3534 "Failed to get the real part of a complex-valued matrix "
3535 "entry from line "
3536 << lineNumber << " of the Matrix Market "
3537 "file.");
3538 // In tolerant mode, just skip bad lines.
3539 if (tolerant) {
3540 break;
3541 }
3542 } else if (istr.eof()) {
3543 TEUCHOS_TEST_FOR_EXCEPTION(
3544 !tolerant && istr.eof(), std::runtime_error,
3545 "Missing imaginary part of a complex-valued matrix entry "
3546 "on line "
3547 << lineNumber << " of the Matrix Market file.");
3548 // In tolerant mode, let any missing imaginary part be 0.
3549 } else {
3550 // Attempt to read the imaginary part of the current
3551 // matrix entry.
3552 istr >> imag;
3553 TEUCHOS_TEST_FOR_EXCEPTION(
3554 !tolerant && istr.fail(), std::runtime_error,
3555 "Failed to get the imaginary part of a complex-valued "
3556 "matrix entry from line "
3557 << lineNumber << " of the "
3558 "Matrix Market file.");
3559 // In tolerant mode, let any missing or corrupted
3560 // imaginary part be 0.
3561 }
3562 } else { // Matrix Market file contains real-valued data.
3563 // Attempt to read the current matrix entry.
3564 istr >> real;
3565 TEUCHOS_TEST_FOR_EXCEPTION(
3566 !tolerant && istr.fail(), std::runtime_error,
3567 "Failed to get a real-valued matrix entry from line "
3568 << lineNumber << " of the Matrix Market file.");
3569 // In tolerant mode, simply ignore the line if
3570 // we failed to read a matrix entry.
3571 if (istr.fail() && tolerant) {
3572 break;
3573 }
3574 }
3575 // In tolerant mode, we simply let pass through whatever
3576 // data we got.
3577 TEUCHOS_TEST_FOR_EXCEPTION(
3578 !tolerant && istr.fail(), std::runtime_error,
3579 "Failed to read matrix data from line " << lineNumber
3580 << " of the Matrix Market file.");
3581
3582 // Assign val = ST(real, imag).
3583 Teuchos::MatrixMarket::details::assignScalar<ST>(val, real, imag);
3584
3585 curRow = count % numRows;
3586 curCol = count / numRows;
3587 X_view[curRow + curCol * stride] = val;
3588 ++count;
3589 } // if not a comment line
3590 } // while there are still lines in the file, get the next one
3591
3592 TEUCHOS_TEST_FOR_EXCEPTION(
3593 !tolerant && static_cast<global_size_t>(count) < numRows * numCols,
3594 std::runtime_error,
3595 "The Matrix Market metadata reports that the dense matrix is "
3596 << numRows << " x " << numCols << ", and thus has "
3597 << numRows * numCols << " total entries, but we only found " << count
3598 << " entr" << (count == 1 ? "y" : "ies") << " in the file.");
3599 } catch (std::exception& e) {
3600 exMsg << e.what();
3601 localReadDataSuccess = 0;
3602 }
3603 } // not binary file
3604 else {
3605 if (debug) {
3606 *err << myRank << ": readDenseImpl: Reading matrix data (dense)"
3607 << endl;
3608 }
3609
3610 // Make sure that we can get a 1-D view of X.
3611 TEUCHOS_TEST_FOR_EXCEPTION(
3612 !X->isConstantStride(), std::logic_error,
3613 "Can't get a 1-D view of the entries of the MultiVector X on "
3614 "Process 0, because the stride between the columns of X is not "
3615 "constant. This shouldn't happen because we just created X and "
3616 "haven't filled it in yet. Please report this bug to the Tpetra "
3617 "developers.");
3618
3619 // Get a writeable 1-D view of the entries of X. Rank 0
3620 // owns all of them. The view will expire at the end of
3621 // scope, so (if necessary) it will be written back to X
3622 // at this time.
3623 auto X_view = X->getLocalViewHost(Access::OverwriteAll);
3624
3625 TEUCHOS_TEST_FOR_EXCEPTION(
3626 as<global_size_t>(X_view.extent(0)) < numRows,
3627 std::logic_error,
3628 "The view of X has " << X_view.extent(0) << " rows which is not enough to "
3629 "accommodate the expected number of entries numRows = "
3630 << numRows << ". "
3631 "Please report this bug to the Tpetra developers.");
3632 TEUCHOS_TEST_FOR_EXCEPTION(
3633 as<global_size_t>(X_view.extent(1)) < numCols,
3634 std::logic_error,
3635 "The view of X has " << X_view.extent(1) << " colums which is not enough to "
3636 "accommodate the expected number of entries numRows = "
3637 << numCols << ". "
3638 "Please report this bug to the Tpetra developers.");
3639
3640 for (size_t curRow = 0; curRow < numRows; ++curRow) {
3641 for (size_t curCol = 0; curCol < numCols; ++curCol) {
3642 if (Teuchos::ScalarTraits<ST>::isOrdinal) {
3643 global_size_t val;
3644 in.read(reinterpret_cast<char*>(&val), sizeof(val));
3645 X_view(curRow, curCol) = val;
3646 } else {
3647 double val;
3648 in.read(reinterpret_cast<char*>(&val), sizeof(val));
3649 X_view(curRow, curCol) = val;
3650 }
3651 }
3652 }
3653 } // binary
3654 } // if (myRank == 0)
3655
3656 if (debug) {
3657 *err << myRank << ": readDenseImpl: done reading data" << endl;
3658 }
3659
3660 // Synchronize on whether Proc 0 successfully read the data.
3661 int globalReadDataSuccess = localReadDataSuccess;
3662 broadcast(*comm, 0, outArg(globalReadDataSuccess));
3663 TEUCHOS_TEST_FOR_EXCEPTION(
3664 globalReadDataSuccess == 0, std::runtime_error,
3665 "Failed to read the multivector's data: " << exMsg.str());
3666
3667 // If there's only one MPI process and the user didn't supply
3668 // a Map (i.e., pMap is null), we're done. Set pMap to the
3669 // Map used to distribute X, and return X.
3670 if (comm->getSize() == 1 && map.is_null()) {
3671 map = proc0Map;
3672 if (!err.is_null()) {
3673 err->popTab();
3674 }
3675 if (debug) {
3676 *err << myRank << ": readDenseImpl: done" << endl;
3677 }
3678 if (!err.is_null()) {
3679 err->popTab();
3680 }
3681 return X;
3682 }
3683
3684 if (debug) {
3685 *err << myRank << ": readDenseImpl: Creating target MV" << endl;
3686 }
3687
3688 // Make a multivector Y with the distributed map pMap.
3689 RCP<MV> Y = createMultiVector<ST, LO, GO, NT>(map, numCols);
3690
3691 if (debug) {
3692 *err << myRank << ": readDenseImpl: Creating Export" << endl;
3693 }
3694
3695 // Make an Export object that will export X to Y. First
3696 // argument is the source map, second argument is the target
3697 // map.
3698 Export<LO, GO, NT> exporter(proc0Map, map, err);
3699
3700 if (debug) {
3701 *err << myRank << ": readDenseImpl: Exporting" << endl;
3702 }
3703 // Export X into Y.
3704 Y->doExport(*X, exporter, INSERT);
3705
3706 if (!err.is_null()) {
3707 err->popTab();
3708 }
3709 if (debug) {
3710 *err << myRank << ": readDenseImpl: done" << endl;
3711 }
3712 if (!err.is_null()) {
3713 err->popTab();
3714 }
3715
3716 // Y is distributed over all process(es) in the communicator.
3717 return Y;
3718}
3719
3720template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3721template <class VectorScalarType>
3722Teuchos::RCP<Tpetra::Vector<VectorScalarType,
3726MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::readVectorImpl(std::istream& in,
3728 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& map,
3729 const Teuchos::RCP<Teuchos::FancyOStream>& err,
3730 const bool tolerant,
3731 const bool debug) {
3732 using std::endl;
3733 using Teuchos::as;
3734 using Teuchos::broadcast;
3735 using Teuchos::outArg;
3736 using Teuchos::RCP;
3737 using Teuchos::Tuple;
3738 using Teuchos::MatrixMarket::Banner;
3739 using Teuchos::MatrixMarket::checkCommentLine;
3740 typedef VectorScalarType ST;
3741 typedef local_ordinal_type LO;
3742 typedef global_ordinal_type GO;
3743 typedef node_type NT;
3744 typedef Teuchos::ScalarTraits<ST> STS;
3745 typedef typename STS::magnitudeType MT;
3746 typedef Teuchos::ScalarTraits<MT> STM;
3747 typedef Tpetra::Vector<ST, LO, GO, NT> MV;
3748
3749 // Rank 0 is the only (MPI) process allowed to read from the
3750 // input stream.
3751 const int myRank = comm->getRank();
3752
3753 if (!err.is_null()) {
3754 err->pushTab();
3755 }
3756 if (debug) {
3757 *err << myRank << ": readVectorImpl" << endl;
3758 }
3759 if (!err.is_null()) {
3760 err->pushTab();
3761 }
3762
3763 // mfh 17 Feb 2013: It's not strictly necessary that the Comm
3764 // instances be identical and that the Node instances be
3765 // identical. The essential condition is more complicated to
3766 // test and isn't the same for all Node types. Thus, we just
3767 // leave it up to the user.
3768
3769 // // If map is nonnull, check the precondition that its
3770 // // communicator resp. node equal comm resp. node. Checking
3771 // // now avoids doing a lot of file reading before we detect the
3772 // // violated precondition.
3773 // TEUCHOS_TEST_FOR_EXCEPTION(
3774 // ! map.is_null() && (map->getComm() != comm || map->getNode () != node,
3775 // std::invalid_argument, "If you supply a nonnull Map, the Map's "
3776 // "communicator and node must equal the supplied communicator resp. "
3777 // "node.");
3778
3779 // Process 0 will read in the matrix dimensions from the file,
3780 // and broadcast them to all ranks in the given communicator.
3781 // There are only 2 dimensions in the matrix, but we use the
3782 // third element of the Tuple to encode the banner's reported
3783 // data type: "real" == 0, "complex" == 1, and "integer" == 0
3784 // (same as "real"). We don't allow pattern matrices (i.e.,
3785 // graphs) since they only make sense for sparse data.
3786 Tuple<GO, 3> dims;
3787 dims[0] = 0;
3788 dims[1] = 0;
3789
3790 // Current line number in the input stream. Only valid on
3791 // Proc 0. Various calls will modify this depending on the
3792 // number of lines that are read from the input stream.
3793 size_t lineNumber = 1;
3794
3795 // Capture errors and their messages on Proc 0.
3796 std::ostringstream exMsg;
3797 int localBannerReadSuccess = 1;
3798 int localDimsReadSuccess = 1;
3799
3800 // Only Proc 0 gets to read matrix data from the input stream.
3801 if (myRank == 0) {
3802 if (debug) {
3803 *err << myRank << ": readVectorImpl: Reading banner line (dense)" << endl;
3804 }
3805
3806 // The "Banner" tells you whether the input stream
3807 // represents a dense matrix, the symmetry type of the
3808 // matrix, and the type of the data it contains.
3809 RCP<const Banner> pBanner;
3810 try {
3811 pBanner = readBanner(in, lineNumber, tolerant, debug);
3812 } catch (std::exception& e) {
3813 exMsg << e.what();
3814 localBannerReadSuccess = 0;
3815 }
3816 // Make sure the input stream is the right kind of data.
3817 if (localBannerReadSuccess) {
3818 if (pBanner->matrixType() != "array") {
3819 exMsg << "The Matrix Market file does not contain dense matrix "
3820 "data. Its banner (first) line says that its matrix type is \""
3821 << pBanner->matrixType() << "\", rather that the required "
3822 "\"array\".";
3823 localBannerReadSuccess = 0;
3824 } else if (pBanner->dataType() == "pattern") {
3825 exMsg << "The Matrix Market file's banner (first) "
3826 "line claims that the matrix's data type is \"pattern\". This does "
3827 "not make sense for a dense matrix, yet the file reports the matrix "
3828 "as dense. The only valid data types for a dense matrix are "
3829 "\"real\", \"complex\", and \"integer\".";
3830 localBannerReadSuccess = 0;
3831 } else {
3832 // Encode the data type reported by the Banner as the
3833 // third element of the dimensions Tuple.
3834 dims[2] = encodeDataType(pBanner->dataType());
3835 }
3836 } // if we successfully read the banner line
3837
3838 // At this point, we've successfully read the banner line.
3839 // Now read the dimensions line.
3840 if (localBannerReadSuccess) {
3841 if (debug) {
3842 *err << myRank << ": readVectorImpl: Reading dimensions line (dense)" << endl;
3843 }
3844 // Keep reading lines from the input stream until we find
3845 // a non-comment line, or until we run out of lines. The
3846 // latter is an error, since every "array" format Matrix
3847 // Market file must have a dimensions line after the
3848 // banner (even if the matrix has zero rows or columns, or
3849 // zero entries).
3850 std::string line;
3851 bool commentLine = true;
3852
3853 while (commentLine) {
3854 // Test whether it is even valid to read from the input
3855 // stream wrapping the line.
3856 if (in.eof() || in.fail()) {
3857 exMsg << "Unable to get array dimensions line (at all) from line "
3858 << lineNumber << " of input stream. The input stream "
3859 << "claims that it is "
3860 << (in.eof() ? "at end-of-file." : "in a failed state.");
3861 localDimsReadSuccess = 0;
3862 } else {
3863 // Try to get the next line from the input stream.
3864 if (getline(in, line)) {
3865 ++lineNumber; // We did actually read a line.
3866 }
3867 // Is the current line a comment line? Ignore start
3868 // and size; they are only useful for reading the
3869 // actual matrix entries. (We could use them here as
3870 // an optimization, but we've chosen not to.)
3871 size_t start = 0, size = 0;
3872 commentLine = checkCommentLine(line, start, size, lineNumber, tolerant);
3873 } // whether we failed to read the line at all
3874 } // while the line we just read is a comment line
3875
3876 //
3877 // Get <numRows> <numCols> from the line we just read.
3878 //
3879 std::istringstream istr(line);
3880
3881 // Test whether it is even valid to read from the input
3882 // stream wrapping the line.
3883 if (istr.eof() || istr.fail()) {
3884 exMsg << "Unable to read any data from line " << lineNumber
3885 << " of input; the line should contain the matrix dimensions "
3886 << "\"<numRows> <numCols>\".";
3887 localDimsReadSuccess = 0;
3888 } else { // It's valid to read from the line.
3889 GO theNumRows = 0;
3890 istr >> theNumRows; // Read in the number of rows.
3891 if (istr.fail()) {
3892 exMsg << "Failed to get number of rows from line "
3893 << lineNumber << " of input; the line should contains the "
3894 << "matrix dimensions \"<numRows> <numCols>\".";
3895 localDimsReadSuccess = 0;
3896 } else { // We successfully read the number of rows
3897 dims[0] = theNumRows; // Save the number of rows
3898 if (istr.eof()) { // Do we still have data to read?
3899 exMsg << "No more data after number of rows on line "
3900 << lineNumber << " of input; the line should contain the "
3901 << "matrix dimensions \"<numRows> <numCols>\".";
3902 localDimsReadSuccess = 0;
3903 } else { // Still data left to read; read in number of columns.
3904 GO theNumCols = 0;
3905 istr >> theNumCols; // Read in the number of columns
3906 if (istr.fail()) {
3907 exMsg << "Failed to get number of columns from line "
3908 << lineNumber << " of input; the line should contain "
3909 << "the matrix dimensions \"<numRows> <numCols>\".";
3910 localDimsReadSuccess = 0;
3911 } else { // We successfully read the number of columns
3912 dims[1] = theNumCols; // Save the number of columns
3913 } // if istr.fail ()
3914 } // if istr.eof ()
3915 } // if we read the number of rows
3916 } // if the input stream wrapping the dims line was (in)valid
3917 } // if we successfully read the banner line
3918 } // if (myRank == 0)
3919
3920 // Check if file has a Vector
3921 if (dims[1] != 1) {
3922 exMsg << "File does not contain a 1D Vector";
3923 localDimsReadSuccess = 0;
3924 }
3925
3926 // Broadcast the matrix dimensions, the encoded data type, and
3927 // whether or not Proc 0 succeeded in reading the banner and
3928 // dimensions.
3929 Tuple<GO, 5> bannerDimsReadResult;
3930 if (myRank == 0) {
3931 bannerDimsReadResult[0] = dims[0]; // numRows
3932 bannerDimsReadResult[1] = dims[1]; // numCols
3933 bannerDimsReadResult[2] = dims[2]; // encoded data type
3934 bannerDimsReadResult[3] = localBannerReadSuccess;
3935 bannerDimsReadResult[4] = localDimsReadSuccess;
3936 }
3937
3938 // Broadcast matrix dimensions and the encoded data type from
3939 // Proc 0 to all the MPI processes.
3940 broadcast(*comm, 0, bannerDimsReadResult);
3941
3942 TEUCHOS_TEST_FOR_EXCEPTION(
3943 bannerDimsReadResult[3] == 0, std::runtime_error,
3944 "Failed to read banner line: " << exMsg.str());
3945 TEUCHOS_TEST_FOR_EXCEPTION(
3946 bannerDimsReadResult[4] == 0, std::runtime_error,
3947 "Failed to read matrix dimensions line: " << exMsg.str());
3948 if (myRank != 0) {
3949 dims[0] = bannerDimsReadResult[0];
3950 dims[1] = bannerDimsReadResult[1];
3951 dims[2] = bannerDimsReadResult[2];
3952 }
3953
3954 // Tpetra objects want the matrix dimensions in these types.
3955 const global_size_t numRows = static_cast<global_size_t>(dims[0]);
3956 const size_t numCols = static_cast<size_t>(dims[1]);
3957
3958 // Make a "Proc 0 owns everything" Map that we will use to
3959 // read in the multivector entries in the correct order on
3960 // Proc 0. This must be a collective
3961 RCP<const map_type> proc0Map; // "Proc 0 owns everything" Map
3962 if (map.is_null()) {
3963 // The user didn't supply a Map. Make a contiguous
3964 // distributed Map for them, using the read-in multivector
3965 // dimensions.
3966 map = createUniformContigMapWithNode<LO, GO, NT>(numRows, comm);
3967 const size_t localNumRows = (myRank == 0) ? numRows : 0;
3968 // At this point, map exists and has a nonnull node.
3969 proc0Map = createContigMapWithNode<LO, GO, NT>(numRows, localNumRows,
3970 comm);
3971 } else { // The user supplied a Map.
3972 proc0Map = Details::computeGatherMap<map_type>(map, err, debug);
3973 }
3974
3975 // Make a multivector X owned entirely by Proc 0.
3976 RCP<MV> X = createVector<ST, LO, GO, NT>(proc0Map);
3977
3978 //
3979 // On Proc 0, read the Matrix Market data from the input
3980 // stream into the multivector X.
3981 //
3982 int localReadDataSuccess = 1;
3983 if (myRank == 0) {
3984 try {
3985 if (debug) {
3986 *err << myRank << ": readVectorImpl: Reading matrix data (dense)"
3987 << endl;
3988 }
3989
3990 // Make sure that we can get a 1-D view of X.
3991 TEUCHOS_TEST_FOR_EXCEPTION(
3992 !X->isConstantStride(), std::logic_error,
3993 "Can't get a 1-D view of the entries of the MultiVector X on "
3994 "Process 0, because the stride between the columns of X is not "
3995 "constant. This shouldn't happen because we just created X and "
3996 "haven't filled it in yet. Please report this bug to the Tpetra "
3997 "developers.");
3998
3999 // Get a writeable 1-D view of the entries of X. Rank 0
4000 // owns all of them. The view will expire at the end of
4001 // scope, so (if necessary) it will be written back to X
4002 // at this time.
4003 Teuchos::ArrayRCP<ST> X_view = X->get1dViewNonConst();
4004 TEUCHOS_TEST_FOR_EXCEPTION(
4005 as<global_size_t>(X_view.size()) < numRows * numCols,
4006 std::logic_error,
4007 "The view of X has size " << X_view << " which is not enough to "
4008 "accommodate the expected number of entries numRows*numCols = "
4009 << numRows << "*" << numCols << " = " << numRows * numCols << ". "
4010 "Please report this bug to the Tpetra developers.");
4011 const size_t stride = X->getStride();
4012
4013 // The third element of the dimensions Tuple encodes the data
4014 // type reported by the Banner: "real" == 0, "complex" == 1,
4015 // "integer" == 0 (same as "real"), "pattern" == 2. We do not
4016 // allow dense matrices to be pattern matrices, so dims[2] ==
4017 // 0 or 1. We've already checked for this above.
4018 const bool isComplex = (dims[2] == 1);
4019 size_type count = 0, curRow = 0, curCol = 0;
4020
4021 std::string line;
4022 while (getline(in, line)) {
4023 ++lineNumber;
4024 // Is the current line a comment line? If it's not,
4025 // line.substr(start,size) contains the data.
4026 size_t start = 0, size = 0;
4027 const bool commentLine =
4028 checkCommentLine(line, start, size, lineNumber, tolerant);
4029 if (!commentLine) {
4030 // Make sure we have room in which to put the new matrix
4031 // entry. We check this only after checking for a
4032 // comment line, because there may be one or more
4033 // comment lines at the end of the file. In tolerant
4034 // mode, we simply ignore any extra data.
4035 if (count >= X_view.size()) {
4036 if (tolerant) {
4037 break;
4038 } else {
4039 TEUCHOS_TEST_FOR_EXCEPTION(
4040 count >= X_view.size(),
4041 std::runtime_error,
4042 "The Matrix Market input stream has more data in it than "
4043 "its metadata reported. Current line number is "
4044 << lineNumber << ".");
4045 }
4046 }
4047
4048 // mfh 19 Dec 2012: Ignore everything up to the initial
4049 // colon. writeDense() has the option to print out the
4050 // global row index in front of each entry, followed by
4051 // a colon and space.
4052 {
4053 const size_t pos = line.substr(start, size).find(':');
4054 if (pos != std::string::npos) {
4055 start = pos + 1;
4056 }
4057 }
4058 std::istringstream istr(line.substr(start, size));
4059 // Does the line contain anything at all? Can we
4060 // safely read from the input stream wrapping the
4061 // line?
4062 if (istr.eof() || istr.fail()) {
4063 // In tolerant mode, simply ignore the line.
4064 if (tolerant) {
4065 break;
4066 }
4067 // We repeat the full test here so the exception
4068 // message is more informative.
4069 TEUCHOS_TEST_FOR_EXCEPTION(
4070 !tolerant && (istr.eof() || istr.fail()),
4071 std::runtime_error,
4072 "Line " << lineNumber << " of the Matrix Market file is "
4073 "empty, or we cannot read from it for some other reason.");
4074 }
4075 // Current matrix entry to read in.
4076 ST val = STS::zero();
4077 // Real and imaginary parts of the current matrix entry.
4078 // The imaginary part is zero if the matrix is real-valued.
4079 MT real = STM::zero(), imag = STM::zero();
4080
4081 // isComplex refers to the input stream's data, not to
4082 // the scalar type S. It's OK to read real-valued
4083 // data into a matrix storing complex-valued data; in
4084 // that case, all entries' imaginary parts are zero.
4085 if (isComplex) {
4086 // STS::real() and STS::imag() return a copy of
4087 // their respective components, not a writeable
4088 // reference. Otherwise we could just assign to
4089 // them using the istream extraction operator (>>).
4090 // That's why we have separate magnitude type "real"
4091 // and "imag" variables.
4092
4093 // Attempt to read the real part of the current entry.
4094 istr >> real;
4095 if (istr.fail()) {
4096 TEUCHOS_TEST_FOR_EXCEPTION(
4097 !tolerant && istr.eof(), std::runtime_error,
4098 "Failed to get the real part of a complex-valued matrix "
4099 "entry from line "
4100 << lineNumber << " of the Matrix Market "
4101 "file.");
4102 // In tolerant mode, just skip bad lines.
4103 if (tolerant) {
4104 break;
4105 }
4106 } else if (istr.eof()) {
4107 TEUCHOS_TEST_FOR_EXCEPTION(
4108 !tolerant && istr.eof(), std::runtime_error,
4109 "Missing imaginary part of a complex-valued matrix entry "
4110 "on line "
4111 << lineNumber << " of the Matrix Market file.");
4112 // In tolerant mode, let any missing imaginary part be 0.
4113 } else {
4114 // Attempt to read the imaginary part of the current
4115 // matrix entry.
4116 istr >> imag;
4117 TEUCHOS_TEST_FOR_EXCEPTION(
4118 !tolerant && istr.fail(), std::runtime_error,
4119 "Failed to get the imaginary part of a complex-valued "
4120 "matrix entry from line "
4121 << lineNumber << " of the "
4122 "Matrix Market file.");
4123 // In tolerant mode, let any missing or corrupted
4124 // imaginary part be 0.
4125 }
4126 } else { // Matrix Market file contains real-valued data.
4127 // Attempt to read the current matrix entry.
4128 istr >> real;
4129 TEUCHOS_TEST_FOR_EXCEPTION(
4130 !tolerant && istr.fail(), std::runtime_error,
4131 "Failed to get a real-valued matrix entry from line "
4132 << lineNumber << " of the Matrix Market file.");
4133 // In tolerant mode, simply ignore the line if
4134 // we failed to read a matrix entry.
4135 if (istr.fail() && tolerant) {
4136 break;
4137 }
4138 }
4139 // In tolerant mode, we simply let pass through whatever
4140 // data we got.
4141 TEUCHOS_TEST_FOR_EXCEPTION(
4142 !tolerant && istr.fail(), std::runtime_error,
4143 "Failed to read matrix data from line " << lineNumber
4144 << " of the Matrix Market file.");
4145
4146 // Assign val = ST(real, imag).
4147 Teuchos::MatrixMarket::details::assignScalar<ST>(val, real, imag);
4148
4149 curRow = count % numRows;
4150 curCol = count / numRows;
4151 X_view[curRow + curCol * stride] = val;
4152 ++count;
4153 } // if not a comment line
4154 } // while there are still lines in the file, get the next one
4155
4156 TEUCHOS_TEST_FOR_EXCEPTION(
4157 !tolerant && static_cast<global_size_t>(count) < numRows * numCols,
4158 std::runtime_error,
4159 "The Matrix Market metadata reports that the dense matrix is "
4160 << numRows << " x " << numCols << ", and thus has "
4161 << numRows * numCols << " total entries, but we only found " << count
4162 << " entr" << (count == 1 ? "y" : "ies") << " in the file.");
4163 } catch (std::exception& e) {
4164 exMsg << e.what();
4165 localReadDataSuccess = 0;
4166 }
4167 } // if (myRank == 0)
4168
4169 if (debug) {
4170 *err << myRank << ": readVectorImpl: done reading data" << endl;
4171 }
4172
4173 // Synchronize on whether Proc 0 successfully read the data.
4174 int globalReadDataSuccess = localReadDataSuccess;
4175 broadcast(*comm, 0, outArg(globalReadDataSuccess));
4176 TEUCHOS_TEST_FOR_EXCEPTION(
4177 globalReadDataSuccess == 0, std::runtime_error,
4178 "Failed to read the multivector's data: " << exMsg.str());
4179
4180 // If there's only one MPI process and the user didn't supply
4181 // a Map (i.e., pMap is null), we're done. Set pMap to the
4182 // Map used to distribute X, and return X.
4183 if (comm->getSize() == 1 && map.is_null()) {
4184 map = proc0Map;
4185 if (!err.is_null()) {
4186 err->popTab();
4187 }
4188 if (debug) {
4189 *err << myRank << ": readVectorImpl: done" << endl;
4190 }
4191 if (!err.is_null()) {
4192 err->popTab();
4193 }
4194 return X;
4195 }
4196
4197 if (debug) {
4198 *err << myRank << ": readVectorImpl: Creating target MV" << endl;
4199 }
4200
4201 // Make a multivector Y with the distributed map pMap.
4202 RCP<MV> Y = createVector<ST, LO, GO, NT>(map);
4203
4204 if (debug) {
4205 *err << myRank << ": readVectorImpl: Creating Export" << endl;
4206 }
4207
4208 // Make an Export object that will export X to Y. First
4209 // argument is the source map, second argument is the target
4210 // map.
4211 Export<LO, GO, NT> exporter(proc0Map, map, err);
4212
4213 if (debug) {
4214 *err << myRank << ": readVectorImpl: Exporting" << endl;
4215 }
4216 // Export X into Y.
4217 Y->doExport(*X, exporter, INSERT);
4218
4219 if (!err.is_null()) {
4220 err->popTab();
4221 }
4222 if (debug) {
4223 *err << myRank << ": readVectorImpl: done" << endl;
4224 }
4225 if (!err.is_null()) {
4226 err->popTab();
4227 }
4228
4229 // Y is distributed over all process(es) in the communicator.
4230 return Y;
4231}
4232
4233template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4234Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>
4237 const bool tolerant,
4238 const bool debug,
4239 const bool binary) {
4240 Teuchos::RCP<Teuchos::FancyOStream> err =
4241 Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
4242 return readMap(in, comm, err, tolerant, debug, binary);
4243}
4244
4245template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4246Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>
4249 const Teuchos::RCP<Teuchos::FancyOStream>& err,
4250 const bool tolerant,
4251 const bool debug,
4252 const bool binary) {
4253 using std::endl;
4254 using Teuchos::arcp;
4255 using Teuchos::Array;
4256 using Teuchos::ArrayRCP;
4257 using Teuchos::as;
4258 using Teuchos::broadcast;
4259 using Teuchos::Comm;
4260 using Teuchos::CommRequest;
4261 using Teuchos::inOutArg;
4262 using Teuchos::ireceive;
4263 using Teuchos::isend;
4264 using Teuchos::outArg;
4265 using Teuchos::RCP;
4266 using Teuchos::receive;
4267 using Teuchos::REDUCE_MIN;
4268 using Teuchos::reduceAll;
4269 using Teuchos::SerialComm;
4270 using Teuchos::toString;
4271 using Teuchos::wait;
4272 typedef Tpetra::global_size_t GST;
4273 typedef ptrdiff_t int_type; // Can hold int and GO
4274 typedef local_ordinal_type LO;
4275 typedef global_ordinal_type GO;
4276 typedef node_type NT;
4277 typedef Tpetra::MultiVector<GO, LO, GO, NT> MV;
4278
4279 const int numProcs = comm->getSize();
4280 const int myRank = comm->getRank();
4281
4282 if (!err.is_null()) {
4283 err->pushTab();
4284 }
4285 if (debug) {
4286 std::ostringstream os;
4287 os << myRank << ": readMap: " << endl;
4288 *err << os.str();
4289 }
4290 if (!err.is_null()) {
4291 err->pushTab();
4292 }
4293
4294 // Tag for receive-size / send-size messages. writeMap used
4295 // tags 1337 and 1338; we count up from there.
4296 const int sizeTag = 1339;
4297 // Tag for receive-data / send-data messages.
4298 const int dataTag = 1340;
4299
4300 // These are for sends on Process 0, and for receives on all
4301 // other processes. sizeReq is for the {receive,send}-size
4302 // message, and dataReq is for the message containing the
4303 // actual GIDs to belong to the receiving process.
4304 RCP<CommRequest<int>> sizeReq;
4305 RCP<CommRequest<int>> dataReq;
4306
4307 // Each process will have to receive the number of GIDs to
4308 // expect. Thus, we can post the receives now, and cancel
4309 // them if something should go wrong in the meantime.
4310 ArrayRCP<int_type> numGidsToRecv(1);
4311 numGidsToRecv[0] = 0;
4312 if (myRank != 0) {
4313 sizeReq = ireceive<int, int_type>(numGidsToRecv, 0, sizeTag, *comm);
4314 }
4315
4316 int readSuccess = 1;
4317 std::ostringstream exMsg;
4318 RCP<MV> data; // Will only be valid on Proc 0
4319 if (myRank == 0) {
4320 // If we want to reuse readDenseImpl, we have to make a
4321 // communicator that only contains Proc 0. Otherwise,
4322 // readDenseImpl will redistribute the data to all
4323 // processes. While we eventually want that, neither we nor
4324 // readDenseImpl know the correct Map to use at the moment.
4325 // That depends on the second column of the multivector.
4326 RCP<const Comm<int>> proc0Comm(new SerialComm<int>());
4327 try {
4328 RCP<const map_type> dataMap;
4329 // This is currently the only place where we use the
4330 // 'tolerant' argument. Later, if we want to be clever,
4331 // we could have tolerant mode allow PIDs out of order.
4332 data = readDenseImpl<GO>(in, proc0Comm, dataMap, err, tolerant, debug, binary);
4333 (void)dataMap; // Silence "unused" warnings
4334 if (data.is_null()) {
4335 readSuccess = 0;
4336 exMsg << "readDenseImpl() returned null." << endl;
4337 }
4338 } catch (std::exception& e) {
4339 readSuccess = 0;
4340 exMsg << e.what() << endl;
4341 }
4342 }
4343
4344 // Map from PID to all the GIDs for that PID.
4345 // Only populated on Process 0.
4346 std::map<int, Array<GO>> pid2gids;
4347
4348 // The index base must be the global minimum GID.
4349 // We will compute this on Process 0 and broadcast,
4350 // so that all processes can set up the Map.
4351 int_type globalNumGIDs = 0;
4352
4353 // The index base must be the global minimum GID.
4354 // We will compute this on Process 0 and broadcast,
4355 // so that all processes can set up the Map.
4356 GO indexBase = 0;
4357
4358 // Process 0: If the above read of the MultiVector succeeded,
4359 // extract the GIDs and PIDs into pid2gids, and find the
4360 // global min GID.
4361 if (myRank == 0 && readSuccess == 1) {
4362 if (data->getNumVectors() == 2) { // Map format 1.0
4363 ArrayRCP<const GO> GIDs = data->getData(0);
4364 ArrayRCP<const GO> PIDs = data->getData(1); // convert to int
4365 globalNumGIDs = GIDs.size();
4366
4367 // Start computing the global min GID, while collecting
4368 // the GIDs for each PID.
4369 if (globalNumGIDs > 0) {
4370 const int pid = static_cast<int>(PIDs[0]);
4371
4372 if (pid < 0 || pid >= numProcs) {
4373 readSuccess = 0;
4374 exMsg << "Tpetra::MatrixMarket::readMap: "
4375 << "Encountered invalid PID " << pid << "." << endl;
4376 } else {
4377 const GO gid = GIDs[0];
4378 pid2gids[pid].push_back(gid);
4379 indexBase = gid; // the current min GID
4380 }
4381 }
4382 if (readSuccess == 1) {
4383 // Collect the rest of the GIDs for each PID, and compute
4384 // the global min GID.
4385 for (size_type k = 1; k < globalNumGIDs; ++k) {
4386 const int pid = static_cast<int>(PIDs[k]);
4387 if (pid < 0 || pid >= numProcs) {
4388 readSuccess = 0;
4389 exMsg << "Tpetra::MatrixMarket::readMap: "
4390 << "Encountered invalid PID " << pid << "." << endl;
4391 } else {
4392 const int_type gid = GIDs[k];
4393 pid2gids[pid].push_back(gid);
4394 if (gid < indexBase) {
4395 indexBase = gid; // the current min GID
4396 }
4397 }
4398 }
4399 }
4400 } else if (data->getNumVectors() == 1) { // Map format 2.0
4401 if (data->getGlobalLength() % 2 != static_cast<GST>(0)) {
4402 readSuccess = 0;
4403 exMsg << "Tpetra::MatrixMarket::readMap: Input data has the "
4404 "wrong format (for Map format 2.0). The global number of rows "
4405 "in the MultiVector must be even (divisible by 2)."
4406 << endl;
4407 } else {
4408 ArrayRCP<const GO> theData = data->getData(0);
4409 globalNumGIDs = static_cast<GO>(data->getGlobalLength()) /
4410 static_cast<GO>(2);
4411
4412 // Start computing the global min GID, while
4413 // collecting the GIDs for each PID.
4414 if (globalNumGIDs > 0) {
4415 const int pid = static_cast<int>(theData[1]);
4416 if (pid < 0 || pid >= numProcs) {
4417 readSuccess = 0;
4418 exMsg << "Tpetra::MatrixMarket::readMap: "
4419 << "Encountered invalid PID " << pid << "." << endl;
4420 } else {
4421 const GO gid = theData[0];
4422 pid2gids[pid].push_back(gid);
4423 indexBase = gid; // the current min GID
4424 }
4425 }
4426 // Collect the rest of the GIDs for each PID, and
4427 // compute the global min GID.
4428 for (int_type k = 1; k < globalNumGIDs; ++k) {
4429 const int pid = static_cast<int>(theData[2 * k + 1]);
4430 if (pid < 0 || pid >= numProcs) {
4431 readSuccess = 0;
4432 exMsg << "Tpetra::MatrixMarket::readMap: "
4433 << "Encountered invalid PID " << pid << "." << endl;
4434 } else {
4435 const GO gid = theData[2 * k];
4436 pid2gids[pid].push_back(gid);
4437 if (gid < indexBase) {
4438 indexBase = gid; // the current min GID
4439 }
4440 }
4441 } // for each GID
4442 } // if the amount of data is correct
4443 } else {
4444 readSuccess = 0;
4445 exMsg << "Tpetra::MatrixMarket::readMap: Input data must have "
4446 "either 1 column (for the new Map format 2.0) or 2 columns (for "
4447 "the old Map format 1.0).";
4448 }
4449 } // myRank is zero
4450
4451 // Broadcast the indexBase, the global number of GIDs, and the
4452 // current success status. Use int_type for all of these.
4453 {
4454 int_type readResults[3];
4455 readResults[0] = static_cast<int_type>(indexBase);
4456 readResults[1] = static_cast<int_type>(globalNumGIDs);
4457 readResults[2] = static_cast<int_type>(readSuccess);
4458 broadcast<int, int_type>(*comm, 0, 3, readResults);
4459
4460 indexBase = static_cast<GO>(readResults[0]);
4461 globalNumGIDs = static_cast<int_type>(readResults[1]);
4462 readSuccess = static_cast<int>(readResults[2]);
4463 }
4464
4465 // Unwinding the stack will invoke sizeReq's destructor, which
4466 // will cancel the receive-size request on all processes that
4467 // posted it.
4468 TEUCHOS_TEST_FOR_EXCEPTION(
4469 readSuccess != 1, std::runtime_error,
4470 "Tpetra::MatrixMarket::readMap: Reading the Map failed with the "
4471 "following exception message: "
4472 << exMsg.str());
4473
4474 if (myRank == 0) {
4475 // Proc 0: Send each process' number of GIDs to that process.
4476 for (int p = 1; p < numProcs; ++p) {
4477 ArrayRCP<int_type> numGidsToSend(1);
4478
4479 auto it = pid2gids.find(p);
4480 if (it == pid2gids.end()) {
4481 numGidsToSend[0] = 0;
4482 } else {
4483 numGidsToSend[0] = it->second.size();
4484 }
4485 sizeReq = isend<int, int_type>(numGidsToSend, p, sizeTag, *comm);
4486 wait<int>(*comm, outArg(sizeReq));
4487 }
4488 } else {
4489 // Wait on the receive-size message to finish.
4490 wait<int>(*comm, outArg(sizeReq));
4491 }
4492
4493 // Allocate / get the array for my GIDs.
4494 // Only Process 0 will have its actual GIDs at this point.
4495 ArrayRCP<GO> myGids;
4496 int_type myNumGids = 0;
4497 if (myRank == 0) {
4498 GO* myGidsRaw = NULL;
4499
4500 typename std::map<int, Array<GO>>::iterator it = pid2gids.find(0);
4501 if (it != pid2gids.end()) {
4502 myGidsRaw = it->second.getRawPtr();
4503 myNumGids = it->second.size();
4504 // Nonowning ArrayRCP just views the Array.
4505 myGids = arcp<GO>(myGidsRaw, 0, myNumGids, false);
4506 }
4507 } else { // myRank != 0
4508 myNumGids = numGidsToRecv[0];
4509 myGids = arcp<GO>(myNumGids);
4510 }
4511
4512 if (myRank != 0) {
4513 // Post receive for data, now that we know how much data we
4514 // will receive. Only post receive if my process actually
4515 // has nonzero GIDs.
4516 if (myNumGids > 0) {
4517 dataReq = ireceive<int, GO>(myGids, 0, dataTag, *comm);
4518 }
4519 }
4520
4521 for (int p = 1; p < numProcs; ++p) {
4522 if (myRank == 0) {
4523 ArrayRCP<GO> sendGids; // to send to Process p
4524 GO* sendGidsRaw = NULL;
4525 int_type numSendGids = 0;
4526
4527 typename std::map<int, Array<GO>>::iterator it = pid2gids.find(p);
4528 if (it != pid2gids.end()) {
4529 numSendGids = it->second.size();
4530 sendGidsRaw = it->second.getRawPtr();
4531 sendGids = arcp<GO>(sendGidsRaw, 0, numSendGids, false);
4532 }
4533 // Only send if that process actually has nonzero GIDs.
4534 if (numSendGids > 0) {
4535 dataReq = isend<int, GO>(sendGids, p, dataTag, *comm);
4536 }
4537 wait<int>(*comm, outArg(dataReq));
4538 } else if (myRank == p) {
4539 // Wait on my receive of GIDs to finish.
4540 wait<int>(*comm, outArg(dataReq));
4541 }
4542 } // for each process rank p in 1, 2, ..., numProcs-1
4543
4544 if (debug) {
4545 std::ostringstream os;
4546 os << myRank << ": readMap: creating Map" << endl;
4547 *err << os.str();
4548 }
4549 const GST INVALID = Teuchos::OrdinalTraits<GST>::invalid();
4550 RCP<const map_type> newMap;
4551
4552 // Create the Map; test whether the constructor threw. This
4553 // avoids deadlock and makes error reporting more readable.
4554
4555 int lclSuccess = 1;
4556 int gblSuccess = 0; // output argument
4557 std::ostringstream errStrm;
4558 try {
4559 newMap = rcp(new map_type(INVALID, myGids(), indexBase, comm));
4560 } catch (std::exception& e) {
4561 lclSuccess = 0;
4562 errStrm << "Process " << comm->getRank() << " threw an exception: "
4563 << e.what() << std::endl;
4564 } catch (...) {
4565 lclSuccess = 0;
4566 errStrm << "Process " << comm->getRank() << " threw an exception "
4567 "not a subclass of std::exception"
4568 << std::endl;
4569 }
4570 Teuchos::reduceAll<int, int>(*comm, Teuchos::REDUCE_MIN,
4571 lclSuccess, Teuchos::outArg(gblSuccess));
4572 if (gblSuccess != 1) {
4573 Tpetra::Details::gathervPrint(std::cerr, errStrm.str(), *comm);
4574 }
4575 TEUCHOS_TEST_FOR_EXCEPTION(gblSuccess != 1, std::runtime_error, "Map constructor failed!");
4576
4577 if (!err.is_null()) {
4578 err->popTab();
4579 }
4580 if (debug) {
4581 std::ostringstream os;
4582 os << myRank << ": readMap: done" << endl;
4583 *err << os.str();
4584 }
4585 if (!err.is_null()) {
4586 err->popTab();
4587 }
4588 return newMap;
4589}
4590
4591template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4592int MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::encodeDataType(const std::string& dataType) {
4593 if (dataType == "real" || dataType == "integer") {
4594 return 0;
4595 } else if (dataType == "complex") {
4596 return 1;
4597 } else if (dataType == "pattern") {
4598 return 2;
4599 } else {
4600 // We should never get here, since Banner validates the
4601 // reported data type and ensures it is one of the accepted
4602 // values.
4603 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
4604 "Unrecognized Matrix Market data type \"" << dataType
4605 << "\". We should never get here. "
4606 "Please report this bug to the Tpetra developers.");
4607 }
4608}
4609
4610template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4611Teuchos::RCP<typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type>
4613 const std::string& filename_suffix,
4614 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& rowMap,
4615 Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& colMap,
4616 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& domainMap,
4617 const Teuchos::RCP<const typename MatrixMarketReader<Scalar, LocalOrdinal, GlobalOrdinal, Node>::map_type>& rangeMap,
4618 const bool callFillComplete,
4619 const bool tolerant,
4620 const int ranksToReadAtOnce,
4621 const bool debug) {
4622 using ST = scalar_type;
4623 using LO = local_ordinal_type;
4624 using GO = global_ordinal_type;
4625 using STS = typename Teuchos::ScalarTraits<ST>;
4626 using Teuchos::arcp;
4627 using Teuchos::ArrayRCP;
4628 using Teuchos::RCP;
4629 using Teuchos::rcp;
4630
4631 // Sanity Checks
4632 // Fast checks for invalid input. We can't check other
4633 // attributes of the Maps until we've read in the matrix
4634 // dimensions.
4635 TEUCHOS_TEST_FOR_EXCEPTION(
4636 rowMap.is_null(), std::invalid_argument,
4637 "Row Map must be nonnull.");
4638 Teuchos::RCP<const Teuchos::Comm<int>> comm = rowMap->getComm();
4639 TEUCHOS_TEST_FOR_EXCEPTION(comm.is_null(), std::invalid_argument,
4640 "The input row map's communicator (Teuchos::Comm object) is null.");
4641 TEUCHOS_TEST_FOR_EXCEPTION(
4642 rangeMap.is_null(), std::invalid_argument,
4643 "Range Map must be nonnull.");
4644 TEUCHOS_TEST_FOR_EXCEPTION(
4645 domainMap.is_null(), std::invalid_argument,
4646 "Domain Map must be nonnull.");
4647 TEUCHOS_TEST_FOR_EXCEPTION(
4648 domainMap->getComm().getRawPtr() != comm.getRawPtr(),
4649 std::invalid_argument,
4650 "The specified domain Map's communicator (domainMap->getComm())"
4651 "differs from row Map's communicator");
4652 TEUCHOS_TEST_FOR_EXCEPTION(
4653 rangeMap->getComm().getRawPtr() != comm.getRawPtr(),
4654 std::invalid_argument,
4655 "The specified range Map's communicator (rangeMap->getComm())"
4656 "differs from row Map's communicator");
4657
4658 // Setup
4659 const int myRank = comm->getRank();
4660 const int numProc = comm->getSize();
4661 std::string filename = filename_prefix + std::to_string(myRank) + filename_suffix;
4662
4663 // Bounds check the writing limits
4664 int rank_limit = std::min(std::max(ranksToReadAtOnce, 1), numProc);
4665
4666 // Data structures for constructor
4667 ArrayRCP<size_t> numEntriesPerRow;
4668 ArrayRCP<size_t> rowPtr;
4669 ArrayRCP<global_ordinal_type> colInd;
4670 ArrayRCP<scalar_type> values;
4671 std::ostringstream errMsg;
4672
4674 // Start the reading of the banners to get
4675 // local row / nnz counts and then read the
4676 // data. We'll pack everything into big ol'
4677 // rowptr/colind/values ArrayRCPs
4678 bool success = true;
4679 int bannerIsCorrect = 1, readSuccess = 1;
4680 LO numRows, numCols, numNonzeros;
4681 for (int base_rank = 0; base_rank < numProc; base_rank += rank_limit) {
4682 int stop = std::min(base_rank + rank_limit, numProc);
4683
4684 // Is my rank in this batch?
4685 if (base_rank <= myRank && myRank < stop) {
4686 // My turn to read
4687 std::ifstream in(filename);
4688 using Teuchos::MatrixMarket::Banner;
4689 size_t lineNumber = 1;
4690 RCP<const Banner> pBanner;
4691 try {
4692 pBanner = readBanner(in, lineNumber, tolerant, debug);
4693 } catch (std::exception& e) {
4694 errMsg << "Attempt to read the Matrix Market file's Banner line "
4695 "threw an exception: "
4696 << e.what();
4697 bannerIsCorrect = 0;
4698 }
4699 if (bannerIsCorrect) {
4700 // Validate the Banner for the case of a sparse matrix.
4701 // We validate on Proc 0, since it reads the Banner.
4702
4703 // In intolerant mode, the matrix type must be "coordinate".
4704 if (!tolerant && pBanner->matrixType() != "coordinate") {
4705 bannerIsCorrect = 0;
4706 errMsg << "The Matrix Market input file must contain a "
4707 "\"coordinate\"-format sparse matrix in order to create a "
4708 "Tpetra::CrsMatrix object from it, but the file's matrix "
4709 "type is \""
4710 << pBanner->matrixType() << "\" instead.";
4711 }
4712 // In tolerant mode, we allow the matrix type to be
4713 // anything other than "array" (which would mean that
4714 // the file contains a dense matrix).
4715 if (tolerant && pBanner->matrixType() == "array") {
4716 bannerIsCorrect = 0;
4717 errMsg << "Matrix Market file must contain a \"coordinate\"-"
4718 "format sparse matrix in order to create a Tpetra::CrsMatrix "
4719 "object from it, but the file's matrix type is \"array\" "
4720 "instead. That probably means the file contains dense matrix "
4721 "data.";
4722 }
4723 }
4724
4725 // Unpacked coordinate matrix dimensions
4726 using Teuchos::MatrixMarket::readCoordinateDimensions;
4727 success = readCoordinateDimensions(in, numRows, numCols,
4728 numNonzeros, lineNumber,
4729 tolerant);
4730
4731 // Sanity checking of headers
4732 TEUCHOS_TEST_FOR_EXCEPTION(numRows != (LO)rowMap->getLocalNumElements(), std::invalid_argument,
4733 "# rows in file does not match rowmap.");
4734 TEUCHOS_TEST_FOR_EXCEPTION(!colMap.is_null() && numCols != (LO)colMap->getLocalNumElements(), std::invalid_argument,
4735 "# rows in file does not match colmap.");
4736
4737 // Read the data
4738 typedef Teuchos::MatrixMarket::Raw::Adder<scalar_type, global_ordinal_type> raw_adder_type;
4739 bool tolerant_required = true;
4740 Teuchos::RCP<raw_adder_type> pRaw =
4741 Teuchos::rcp(new raw_adder_type(numRows, numCols, numNonzeros, tolerant_required, debug));
4742 RCP<adder_type> pAdder = Teuchos::rcp(new adder_type(pRaw, pBanner->symmType()));
4743
4744 if (debug) {
4745 std::cerr << "-- Reading matrix data" << std::endl;
4746 }
4747
4748 try {
4749 // Reader for "coordinate" format sparse matrix data.
4750 typedef Teuchos::MatrixMarket::CoordDataReader<adder_type,
4751 global_ordinal_type, scalar_type, STS::isComplex>
4752 reader_type;
4753 reader_type reader(pAdder);
4754
4755 // Read the sparse matrix entries.
4756 std::pair<bool, std::vector<size_t>> results = reader.read(in, lineNumber, tolerant_required, debug);
4757
4758 readSuccess = results.first ? 1 : 0;
4759 } catch (std::exception& e) {
4760 readSuccess = 0;
4761 errMsg << e.what();
4762 }
4763
4765 // Create the CSR Arrays
4766 typedef Teuchos::MatrixMarket::Raw::Element<scalar_type, global_ordinal_type> element_type;
4767
4768 // Additively merge duplicate matrix entries.
4769 pAdder->getAdder()->merge();
4770
4771 // Get a temporary const view of the merged matrix entries.
4772 const std::vector<element_type>& entries = pAdder->getAdder()->getEntries();
4773
4774 // Number of matrix entries (after merging).
4775 const size_t numEntries = (size_t)entries.size();
4776
4777 if (debug) {
4778 std::cerr << "----- Proc " << myRank << ": Matrix has numRows=" << numRows
4779 << " rows and numEntries=" << numEntries
4780 << " entries." << std::endl;
4781 }
4782
4783 // Make space for the CSR matrix data. Converting to
4784 // CSR is easier if we fill numEntriesPerRow with zeros
4785 // at first.
4786 numEntriesPerRow = arcp<size_t>(numRows);
4787 std::fill(numEntriesPerRow.begin(), numEntriesPerRow.end(), 0);
4788 rowPtr = arcp<size_t>(numRows + 1);
4789 std::fill(rowPtr.begin(), rowPtr.end(), 0);
4790 colInd = arcp<global_ordinal_type>(numEntries);
4791 values = arcp<scalar_type>(numEntries);
4792
4793 // Convert from array-of-structs coordinate format to CSR
4794 // (compressed sparse row) format.
4795 global_ordinal_type l_prvRow = 0;
4796 size_t curPos = 0;
4797 LO INVALID = Teuchos::OrdinalTraits<LO>::invalid();
4798 rowPtr[0] = 0;
4799 LO indexBase = rowMap->getIndexBase();
4800 for (curPos = 0; curPos < numEntries; ++curPos) {
4801 const element_type& curEntry = entries[curPos];
4802 const global_ordinal_type curRow = curEntry.rowIndex() + indexBase;
4803 LO l_curRow = rowMap->getLocalElement(curRow);
4804
4805 TEUCHOS_TEST_FOR_EXCEPTION(l_curRow == INVALID, std::logic_error,
4806 "Current global row " << curRow << " is invalid.");
4807
4808 TEUCHOS_TEST_FOR_EXCEPTION(l_curRow < l_prvRow, std::logic_error,
4809 "Row indices are out of order, even though they are supposed "
4810 "to be sorted. curRow = "
4811 << l_curRow << ", prvRow = "
4812 << l_prvRow << ", at curPos = " << curPos << ". Please report "
4813 "this bug to the Tpetra developers.");
4814 if (l_curRow > l_prvRow) {
4815 for (LO r = l_prvRow + 1; r <= l_curRow; ++r) {
4816 rowPtr[r] = curPos;
4817 }
4818 l_prvRow = l_curRow;
4819 }
4820 numEntriesPerRow[l_curRow]++;
4821 colInd[curPos] = curEntry.colIndex() + indexBase;
4822 values[curPos] = curEntry.value();
4823 }
4824 // rowPtr has one more entry than numEntriesPerRow. The
4825 // last entry of rowPtr is the number of entries in
4826 // colInd and values.
4827 rowPtr[numRows] = numEntries;
4828
4829 } // end base_rank <= myRank < stop
4830
4831 // Barrier between batches to keep the filesystem happy
4832 comm->barrier();
4833
4834 } // end outer rank loop
4835
4836 // Call the matrix constructor and fill. This isn't particularly efficient
4837 RCP<sparse_matrix_type> A;
4838 if (colMap.is_null()) {
4839 A = rcp(new sparse_matrix_type(rowMap, numEntriesPerRow()));
4840 for (size_t i = 0; i < rowMap->getLocalNumElements(); i++) {
4841 GO g_row = rowMap->getGlobalElement(i);
4842 size_t start = rowPtr[i];
4843 size_t size = rowPtr[i + 1] - rowPtr[i];
4844 if (size > 0) {
4845 A->insertGlobalValues(g_row, size, &values[start], &colInd[start]);
4846 }
4847 }
4848 } else {
4849 throw std::runtime_error("Reading with a column map is not yet implemented");
4850 }
4851 RCP<const map_type> myDomainMap = domainMap.is_null() ? rowMap : domainMap;
4852 RCP<const map_type> myRangeMap = rangeMap.is_null() ? rowMap : rangeMap;
4853
4854 A->fillComplete(myDomainMap, myRangeMap);
4855
4856 if (!readSuccess)
4857 success = false;
4858 TEUCHOS_TEST_FOR_EXCEPTION(success == false, std::runtime_error,
4859 "Read failed.");
4860
4861 return A;
4862} // end readSparsePerRank
4863
4864template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4866 const sparse_matrix_type& matrix,
4867 const std::string& matrixName,
4868 const std::string& matrixDescription,
4869 const bool debug) {
4870 trcp_tcomm_t comm = matrix.getComm();
4871 TEUCHOS_TEST_FOR_EXCEPTION(comm.is_null(), std::invalid_argument,
4872 "The input matrix's communicator (Teuchos::Comm object) is null.");
4873 const int myRank = comm->getRank();
4874
4875 auto out = MatrixMarketWriter::openOutFileOnRankZero(comm, filename, myRank, true);
4876
4877 writeSparse(out, matrix, matrixName, matrixDescription, debug);
4878 // We can rely on the destructor of the output stream to close
4879 // the file on scope exit, even if writeSparse() throws an
4880 // exception.
4881}
4882
4883template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4885 const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
4886 const std::string& matrixName,
4887 const std::string& matrixDescription,
4888 const bool debug) {
4889 TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::invalid_argument,
4890 "The input matrix is null.");
4891 writeSparseFile(filename, *pMatrix, matrixName,
4892 matrixDescription, debug);
4893}
4894
4895template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4897 const sparse_matrix_type& matrix,
4898 const bool debug) {
4899 writeSparseFile(filename, matrix, "", "", debug);
4900}
4901
4902template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4904 const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
4905 const bool debug) {
4906 writeSparseFile(filename, *pMatrix, "", "", debug);
4907}
4908
4909template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4911 const sparse_matrix_type& matrix,
4912 const std::string& matrixName,
4913 const std::string& matrixDescription,
4914 const bool debug) {
4915 using std::cerr;
4916 using std::endl;
4917 using Teuchos::ArrayView;
4918 using Teuchos::Comm;
4919 using Teuchos::FancyOStream;
4920 using Teuchos::getFancyOStream;
4921 using Teuchos::null;
4922 using Teuchos::RCP;
4923 using Teuchos::rcpFromRef;
4924 using ST = scalar_type;
4925 using LO = local_ordinal_type;
4926 using GO = global_ordinal_type;
4927 using STS = typename Teuchos::ScalarTraits<ST>;
4928
4929 // Make the output stream write floating-point numbers in
4930 // scientific notation. It will politely put the output
4931 // stream back to its state on input, when this scope
4932 // terminates.
4933 Teuchos::SetScientific<ST> sci(out);
4934
4935 // Get the matrix's communicator.
4936 trcp_tcomm_t comm = matrix.getComm();
4937 TEUCHOS_TEST_FOR_EXCEPTION(
4938 comm.is_null(), std::invalid_argument,
4939 "The input matrix's communicator (Teuchos::Comm object) is null.");
4940 const int myRank = comm->getRank();
4941
4942 // Optionally, make a stream for debugging output.
4943 RCP<FancyOStream> err =
4944 debug ? getFancyOStream(rcpFromRef(std::cerr)) : null;
4945 if (debug) {
4946 std::ostringstream os;
4947 os << myRank << ": writeSparse" << endl;
4948 *err << os.str();
4949 comm->barrier();
4950 os << "-- " << myRank << ": past barrier" << endl;
4951 *err << os.str();
4952 }
4953
4954 // Whether to print debugging output to stderr.
4955 const bool debugPrint = debug && myRank == 0;
4956
4957 RCP<const map_type> rowMap = matrix.getRowMap();
4958 RCP<const map_type> colMap = matrix.getColMap();
4959 RCP<const map_type> domainMap = matrix.getDomainMap();
4960 RCP<const map_type> rangeMap = matrix.getRangeMap();
4961
4962 if (!rowMap->haveGlobalConstants())
4963 Teuchos::rcp_const_cast<map_type>(rowMap)->computeGlobalConstants();
4964 if (!colMap->haveGlobalConstants())
4965 Teuchos::rcp_const_cast<map_type>(colMap)->computeGlobalConstants();
4966 if (!domainMap->haveGlobalConstants())
4967 Teuchos::rcp_const_cast<map_type>(domainMap)->computeGlobalConstants();
4968 if (!rangeMap->haveGlobalConstants())
4969 Teuchos::rcp_const_cast<map_type>(rangeMap)->computeGlobalConstants();
4970
4971 const global_size_t numRows = rangeMap->getMaxAllGlobalIndex() + 1 - rangeMap->getIndexBase();
4972 const global_size_t numCols = domainMap->getMaxAllGlobalIndex() + 1 - domainMap->getIndexBase();
4973
4974 if (debug && myRank == 0) {
4975 std::ostringstream os;
4976 os << "-- Input sparse matrix is:"
4977 << "---- " << numRows << " x " << numCols << endl
4978 << "---- "
4979 << (matrix.isGloballyIndexed() ? "Globally" : "Locally")
4980 << " indexed." << endl
4981 << "---- Its row map has " << rowMap->getGlobalNumElements()
4982 << " elements." << endl
4983 << "---- Its col map has " << colMap->getGlobalNumElements()
4984 << " elements." << endl;
4985 *err << os.str();
4986 }
4987 // Make the "gather" row map, where Proc 0 owns all rows and
4988 // the other procs own no rows.
4989 const size_t localNumRows = (myRank == 0) ? numRows : 0;
4990 if (debug) {
4991 std::ostringstream os;
4992 os << "-- " << myRank << ": making gatherRowMap" << endl;
4993 *err << os.str();
4994 }
4995 RCP<const map_type> gatherRowMap =
4996 Details::computeGatherMap(rowMap, err, debug);
4997
4998 // Since the matrix may in general be non-square, we need to
4999 // make a column map as well. In this case, the column map
5000 // contains all the columns of the original matrix, because we
5001 // are gathering the whole matrix onto Proc 0. We call
5002 // computeGatherMap to preserve the original order of column
5003 // indices over all the processes.
5004 const size_t localNumCols = (myRank == 0) ? numCols : 0;
5005 RCP<const map_type> gatherColMap =
5006 Details::computeGatherMap(colMap, err, debug);
5007
5008 // Current map is the source map, gather map is the target map.
5010 import_type importer(rowMap, gatherRowMap);
5011
5012 // Create a new CrsMatrix to hold the result of the import.
5013 // The constructor needs a column map as well as a row map,
5014 // for the case that the matrix is not square.
5015 RCP<sparse_matrix_type> newMatrix =
5016 rcp(new sparse_matrix_type(gatherRowMap, gatherColMap,
5017 static_cast<size_t>(0)));
5018 // Import the sparse matrix onto Proc 0.
5019 newMatrix->doImport(matrix, importer, INSERT);
5020
5021 // fillComplete() needs the domain and range maps for the case
5022 // that the matrix is not square.
5023 {
5024 RCP<const map_type> gatherDomainMap =
5025 rcp(new map_type(numCols, localNumCols,
5026 domainMap->getIndexBase(),
5027 comm));
5028 RCP<const map_type> gatherRangeMap =
5029 rcp(new map_type(numRows, localNumRows,
5030 rangeMap->getIndexBase(),
5031 comm));
5032 newMatrix->fillComplete(gatherDomainMap, gatherRangeMap);
5033 }
5034
5035 if (debugPrint) {
5036 cerr << "-- Output sparse matrix is:"
5037 << "---- " << newMatrix->getRangeMap()->getGlobalNumElements()
5038 << " x "
5039 << newMatrix->getDomainMap()->getGlobalNumElements()
5040 << " with "
5041 << newMatrix->getGlobalNumEntries() << " entries;" << endl
5042 << "---- "
5043 << (newMatrix->isGloballyIndexed() ? "Globally" : "Locally")
5044 << " indexed." << endl
5045 << "---- Its row map has "
5046 << newMatrix->getRowMap()->getGlobalNumElements()
5047 << " elements, with index base "
5048 << newMatrix->getRowMap()->getIndexBase() << "." << endl
5049 << "---- Its col map has "
5050 << newMatrix->getColMap()->getGlobalNumElements()
5051 << " elements, with index base "
5052 << newMatrix->getColMap()->getIndexBase() << "." << endl
5053 << "---- Element count of output matrix's column Map may differ "
5054 << "from that of the input matrix's column Map, if some columns "
5055 << "of the matrix contain no entries." << endl;
5056 }
5057
5058 //
5059 // Print the metadata and the matrix entries on Rank 0.
5060 //
5061 if (myRank == 0) {
5062 // Print the Matrix Market banner line. CrsMatrix stores
5063 // data nonsymmetrically ("general"). This implies that
5064 // readSparse() on a symmetrically stored input file,
5065 // followed by writeSparse() on the resulting sparse matrix,
5066 // will result in an output file with a different banner
5067 // line than the original input file.
5068 out << "%%MatrixMarket matrix coordinate "
5069 << (STS::isComplex ? "complex" : "real")
5070 << " general" << endl;
5071
5072 // Print comments (the matrix name and / or description).
5073 if (matrixName != "") {
5074 printAsComment(out, matrixName);
5075 }
5076 if (matrixDescription != "") {
5077 printAsComment(out, matrixDescription);
5078 }
5079
5080 // Print the Matrix Market header (# rows, # columns, #
5081 // nonzeros). Use the range resp. domain map for the number
5082 // of rows resp. columns, since Tpetra::CrsMatrix uses the
5083 // column map for the number of columns. That only
5084 // corresponds to the "linear-algebraic" number of columns
5085 // when the column map is uniquely owned (a.k.a. one-to-one),
5086 // which only happens if the matrix is (block) diagonal.
5087 out << newMatrix->getRangeMap()->getMaxAllGlobalIndex() + 1 - newMatrix->getRangeMap()->getIndexBase() << " "
5088 << newMatrix->getDomainMap()->getMaxAllGlobalIndex() + 1 - newMatrix->getDomainMap()->getIndexBase() << " "
5089 << newMatrix->getGlobalNumEntries() << endl;
5090
5091 // The Matrix Market format expects one-based row and column
5092 // indices. We'll convert the indices on output from
5093 // whatever index base they use to one-based indices.
5094 const GO rowIndexBase = gatherRowMap->getIndexBase();
5095 const GO colIndexBase = newMatrix->getColMap()->getIndexBase();
5096 //
5097 // Print the entries of the matrix.
5098 //
5099 // newMatrix can never be globally indexed, since we called
5100 // fillComplete() on it. We include code for both cases
5101 // (globally or locally indexed) just in case that ever
5102 // changes.
5103 if (newMatrix->isGloballyIndexed()) {
5104 // We know that the "gather" row Map is contiguous, so we
5105 // don't need to get the list of GIDs.
5106 const GO minAllGlobalIndex = gatherRowMap->getMinAllGlobalIndex();
5107 const GO maxAllGlobalIndex = gatherRowMap->getMaxAllGlobalIndex();
5108 for (GO globalRowIndex = minAllGlobalIndex;
5109 globalRowIndex <= maxAllGlobalIndex; // inclusive range
5110 ++globalRowIndex) {
5111 typename sparse_matrix_type::global_inds_host_view_type ind;
5112 typename sparse_matrix_type::values_host_view_type val;
5113 newMatrix->getGlobalRowView(globalRowIndex, ind, val);
5114 for (size_t ii = 0; ii < ind.extent(0); ii++) {
5115 const GO globalColIndex = ind(ii);
5116 // Convert row and column indices to 1-based.
5117 // This works because the global index type is signed.
5118 out << (globalRowIndex + 1 - rowIndexBase) << " "
5119 << (globalColIndex + 1 - colIndexBase) << " ";
5120 if (STS::isComplex) {
5121 out << STS::real(val(ii)) << " " << STS::imag(val(ii));
5122 } else {
5123 out << val(ii);
5124 }
5125 out << endl;
5126 } // For each entry in the current row
5127 } // For each row of the "gather" matrix
5128 } else { // newMatrix is locally indexed
5129 using OTG = Teuchos::OrdinalTraits<GO>;
5130 for (LO localRowIndex = gatherRowMap->getMinLocalIndex();
5131 localRowIndex <= gatherRowMap->getMaxLocalIndex();
5132 ++localRowIndex) {
5133 // Convert from local to global row index.
5134 const GO globalRowIndex =
5135 gatherRowMap->getGlobalElement(localRowIndex);
5136 TEUCHOS_TEST_FOR_EXCEPTION(
5137 globalRowIndex == OTG::invalid(), std::logic_error,
5138 "Failed to convert the supposed local row index "
5139 << localRowIndex << " into a global row index. "
5140 "Please report this bug to the Tpetra developers.");
5141 typename sparse_matrix_type::local_inds_host_view_type ind;
5142 typename sparse_matrix_type::values_host_view_type val;
5143 newMatrix->getLocalRowView(localRowIndex, ind, val);
5144 for (size_t ii = 0; ii < ind.extent(0); ii++) {
5145 // Convert the column index from local to global.
5146 const GO globalColIndex =
5147 newMatrix->getColMap()->getGlobalElement(ind(ii));
5148 TEUCHOS_TEST_FOR_EXCEPTION(
5149 globalColIndex == OTG::invalid(), std::logic_error,
5150 "On local row " << localRowIndex << " of the sparse matrix: "
5151 "Failed to convert the supposed local column index "
5152 << ind(ii) << " into a global column index. Please report "
5153 "this bug to the Tpetra developers.");
5154 // Convert row and column indices to 1-based.
5155 // This works because the global index type is signed.
5156 out << (globalRowIndex + 1 - rowIndexBase) << " "
5157 << (globalColIndex + 1 - colIndexBase) << " ";
5158 if (STS::isComplex) {
5159 out << STS::real(val(ii)) << " " << STS::imag(val(ii));
5160 } else {
5161 out << val(ii);
5162 }
5163 out << endl;
5164 } // For each entry in the current row
5165 } // For each row of the "gather" matrix
5166 } // Whether the "gather" matrix is locally or globally indexed
5167 } // If my process' rank is 0
5168}
5169
5170template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5172 const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
5173 const std::string& matrixName,
5174 const std::string& matrixDescription,
5175 const bool debug) {
5176 TEUCHOS_TEST_FOR_EXCEPTION(pMatrix.is_null(), std::invalid_argument,
5177 "The input matrix is null.");
5178 writeSparse(out, *pMatrix, matrixName, matrixDescription, debug);
5179}
5180
5181template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5183 const crs_graph_type& graph,
5184 const std::string& graphName,
5185 const std::string& graphDescription,
5186 const bool debug) {
5187 using std::cerr;
5188 using std::endl;
5189 using Teuchos::ArrayView;
5190 using Teuchos::Comm;
5191 using Teuchos::FancyOStream;
5192 using Teuchos::getFancyOStream;
5193 using Teuchos::null;
5194 using Teuchos::RCP;
5195 using Teuchos::rcpFromRef;
5196 typedef local_ordinal_type LO;
5197 typedef global_ordinal_type GO;
5198
5199 // Get the graph's communicator. Processes on which the
5200 // graph's Map or communicator is null don't participate in
5201 // this operation. This function shouldn't even be called on
5202 // those processes.
5203 auto rowMap = graph.getRowMap();
5204 if (rowMap.is_null()) {
5205 return;
5206 }
5207 auto comm = rowMap->getComm();
5208 if (comm.is_null()) {
5209 return;
5210 }
5211 const int myRank = comm->getRank();
5212
5213 // Optionally, make a stream for debugging output.
5214 RCP<FancyOStream> err =
5215 debug ? getFancyOStream(rcpFromRef(std::cerr)) : null;
5216 if (debug) {
5217 std::ostringstream os;
5218 os << myRank << ": writeSparseGraph" << endl;
5219 *err << os.str();
5220 comm->barrier();
5221 os << "-- " << myRank << ": past barrier" << endl;
5222 *err << os.str();
5223 }
5224
5225 // Whether to print debugging output to stderr.
5226 const bool debugPrint = debug && myRank == 0;
5227
5228 // We've already gotten the rowMap above.
5229 auto colMap = graph.getColMap();
5230 auto domainMap = graph.getDomainMap();
5231 auto rangeMap = graph.getRangeMap();
5232
5233 if (!rowMap->haveGlobalConstants())
5234 Teuchos::rcp_const_cast<map_type>(rowMap)->computeGlobalConstants();
5235 if (!colMap->haveGlobalConstants())
5236 Teuchos::rcp_const_cast<map_type>(colMap)->computeGlobalConstants();
5237 if (!domainMap->haveGlobalConstants())
5238 Teuchos::rcp_const_cast<map_type>(domainMap)->computeGlobalConstants();
5239 if (!rangeMap->haveGlobalConstants())
5240 Teuchos::rcp_const_cast<map_type>(rangeMap)->computeGlobalConstants();
5241
5242 const global_size_t numRows = rangeMap->getGlobalNumElements();
5243 const global_size_t numCols = domainMap->getGlobalNumElements();
5244
5245 if (debug && myRank == 0) {
5246 std::ostringstream os;
5247 os << "-- Input sparse graph is:"
5248 << "---- " << numRows << " x " << numCols << " with "
5249 << graph.getGlobalNumEntries() << " entries;" << endl
5250 << "---- "
5251 << (graph.isGloballyIndexed() ? "Globally" : "Locally")
5252 << " indexed." << endl
5253 << "---- Its row Map has " << rowMap->getGlobalNumElements()
5254 << " elements." << endl
5255 << "---- Its col Map has " << colMap->getGlobalNumElements()
5256 << " elements." << endl;
5257 *err << os.str();
5258 }
5259 // Make the "gather" row map, where Proc 0 owns all rows and
5260 // the other procs own no rows.
5261 const size_t localNumRows = (myRank == 0) ? numRows : 0;
5262 if (debug) {
5263 std::ostringstream os;
5264 os << "-- " << myRank << ": making gatherRowMap" << endl;
5265 *err << os.str();
5266 }
5267 auto gatherRowMap = Details::computeGatherMap(rowMap, err, debug);
5268
5269 // Since the graph may in general be non-square, we need to
5270 // make a column map as well. In this case, the column map
5271 // contains all the columns of the original graph, because we
5272 // are gathering the whole graph onto Proc 0. We call
5273 // computeGatherMap to preserve the original order of column
5274 // indices over all the processes.
5275 const size_t localNumCols = (myRank == 0) ? numCols : 0;
5276 auto gatherColMap = Details::computeGatherMap(colMap, err, debug);
5277
5278 // Current map is the source map, gather map is the target map.
5279 Import<LO, GO, node_type> importer(rowMap, gatherRowMap);
5280
5281 // Create a new CrsGraph to hold the result of the import.
5282 // The constructor needs a column map as well as a row map,
5283 // for the case that the graph is not square.
5284 crs_graph_type newGraph(gatherRowMap, gatherColMap,
5285 static_cast<size_t>(0));
5286 // Import the sparse graph onto Proc 0.
5287 newGraph.doImport(graph, importer, INSERT);
5288
5289 // fillComplete() needs the domain and range maps for the case
5290 // that the graph is not square.
5291 {
5292 RCP<const map_type> gatherDomainMap =
5293 rcp(new map_type(numCols, localNumCols,
5294 domainMap->getIndexBase(),
5295 comm));
5296 RCP<const map_type> gatherRangeMap =
5297 rcp(new map_type(numRows, localNumRows,
5298 rangeMap->getIndexBase(),
5299 comm));
5300 newGraph.fillComplete(gatherDomainMap, gatherRangeMap);
5301 }
5302
5303 if (debugPrint) {
5304 cerr << "-- Output sparse graph is:"
5305 << "---- " << newGraph.getRangeMap()->getGlobalNumElements()
5306 << " x "
5307 << newGraph.getDomainMap()->getGlobalNumElements()
5308 << " with "
5309 << newGraph.getGlobalNumEntries() << " entries;" << endl
5310 << "---- "
5311 << (newGraph.isGloballyIndexed() ? "Globally" : "Locally")
5312 << " indexed." << endl
5313 << "---- Its row map has "
5314 << newGraph.getRowMap()->getGlobalNumElements()
5315 << " elements, with index base "
5316 << newGraph.getRowMap()->getIndexBase() << "." << endl
5317 << "---- Its col map has "
5318 << newGraph.getColMap()->getGlobalNumElements()
5319 << " elements, with index base "
5320 << newGraph.getColMap()->getIndexBase() << "." << endl
5321 << "---- Element count of output graph's column Map may differ "
5322 << "from that of the input matrix's column Map, if some columns "
5323 << "of the matrix contain no entries." << endl;
5324 }
5325
5326 //
5327 // Print the metadata and the graph entries on Process 0 of
5328 // the graph's communicator.
5329 //
5330 if (myRank == 0) {
5331 // Print the Matrix Market banner line. CrsGraph stores
5332 // data nonsymmetrically ("general"). This implies that
5333 // readSparseGraph() on a symmetrically stored input file,
5334 // followed by writeSparseGraph() on the resulting sparse
5335 // graph, will result in an output file with a different
5336 // banner line than the original input file.
5337 out << "%%MatrixMarket matrix coordinate pattern general" << endl;
5338
5339 // Print comments (the graph name and / or description).
5340 if (graphName != "") {
5341 printAsComment(out, graphName);
5342 }
5343 if (graphDescription != "") {
5344 printAsComment(out, graphDescription);
5345 }
5346
5347 // Print the Matrix Market header (# rows, # columns, #
5348 // stored entries). Use the range resp. domain map for the
5349 // number of rows resp. columns, since Tpetra::CrsGraph uses
5350 // the column map for the number of columns. That only
5351 // corresponds to the "linear-algebraic" number of columns
5352 // when the column map is uniquely owned
5353 // (a.k.a. one-to-one), which only happens if the graph is
5354 // block diagonal (one block per process).
5355 out << newGraph.getRangeMap()->getMaxAllGlobalIndex() + 1 - newGraph.getRangeMap()->getIndexBase() << " "
5356 << newGraph.getDomainMap()->getMaxAllGlobalIndex() + 1 - newGraph.getDomainMap()->getIndexBase() << " "
5357 << newGraph.getGlobalNumEntries() << endl;
5358
5359 // The Matrix Market format expects one-based row and column
5360 // indices. We'll convert the indices on output from
5361 // whatever index base they use to one-based indices.
5362 const GO rowIndexBase = gatherRowMap->getIndexBase();
5363 const GO colIndexBase = newGraph.getColMap()->getIndexBase();
5364 //
5365 // Print the entries of the graph.
5366 //
5367 // newGraph can never be globally indexed, since we called
5368 // fillComplete() on it. We include code for both cases
5369 // (globally or locally indexed) just in case that ever
5370 // changes.
5371 if (newGraph.isGloballyIndexed()) {
5372 // We know that the "gather" row Map is contiguous, so we
5373 // don't need to get the list of GIDs.
5374 const GO minAllGlobalIndex = gatherRowMap->getMinAllGlobalIndex();
5375 const GO maxAllGlobalIndex = gatherRowMap->getMaxAllGlobalIndex();
5376 for (GO globalRowIndex = minAllGlobalIndex;
5377 globalRowIndex <= maxAllGlobalIndex; // inclusive range
5378 ++globalRowIndex) {
5379 typename crs_graph_type::global_inds_host_view_type ind;
5380 newGraph.getGlobalRowView(globalRowIndex, ind);
5381 for (size_t ii = 0; ii < ind.extent(0); ii++) {
5382 const GO globalColIndex = ind(ii);
5383 // Convert row and column indices to 1-based.
5384 // This works because the global index type is signed.
5385 out << (globalRowIndex + 1 - rowIndexBase) << " "
5386 << (globalColIndex + 1 - colIndexBase) << " ";
5387 out << endl;
5388 } // For each entry in the current row
5389 } // For each row of the "gather" graph
5390 } else { // newGraph is locally indexed
5391 typedef Teuchos::OrdinalTraits<GO> OTG;
5392 for (LO localRowIndex = gatherRowMap->getMinLocalIndex();
5393 localRowIndex <= gatherRowMap->getMaxLocalIndex();
5394 ++localRowIndex) {
5395 // Convert from local to global row index.
5396 const GO globalRowIndex =
5397 gatherRowMap->getGlobalElement(localRowIndex);
5398 TEUCHOS_TEST_FOR_EXCEPTION(globalRowIndex == OTG::invalid(), std::logic_error,
5399 "Failed "
5400 "to convert the supposed local row index "
5401 << localRowIndex << " into a global row index. Please report this bug to the "
5402 "Tpetra developers.");
5403 typename crs_graph_type::local_inds_host_view_type ind;
5404 newGraph.getLocalRowView(localRowIndex, ind);
5405 for (size_t ii = 0; ii < ind.extent(0); ii++) {
5406 // Convert the column index from local to global.
5407 const GO globalColIndex =
5408 newGraph.getColMap()->getGlobalElement(ind(ii));
5409 TEUCHOS_TEST_FOR_EXCEPTION(
5410 globalColIndex == OTG::invalid(), std::logic_error,
5411 "On local row " << localRowIndex << " of the sparse graph: "
5412 "Failed to convert the supposed local column index "
5413 << ind(ii) << " into a global column index. Please report "
5414 "this bug to the Tpetra developers.");
5415 // Convert row and column indices to 1-based.
5416 // This works because the global index type is signed.
5417 out << (globalRowIndex + 1 - rowIndexBase) << " "
5418 << (globalColIndex + 1 - colIndexBase) << " ";
5419 out << endl;
5420 } // For each entry in the current row
5421 } // For each row of the "gather" graph
5422 } // Whether the "gather" graph is locally or globally indexed
5423 } // If my process' rank is 0
5424}
5425
5426template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5428 const crs_graph_type& graph,
5429 const bool debug) {
5430 writeSparseGraph(out, graph, "", "", debug);
5431}
5432
5433template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5435 const crs_graph_type& graph,
5436 const std::string& graphName,
5437 const std::string& graphDescription,
5438 const bool debug) {
5439 auto comm = graph.getComm();
5440 if (comm.is_null()) {
5441 // Processes on which the communicator is null shouldn't
5442 // even call this function. The convention is that
5443 // processes on which the object's communicator is null do
5444 // not participate in collective operations involving the
5445 // object.
5446 return;
5447 }
5448 const int myRank = comm->getRank();
5449
5450 auto out = MatrixMarketWriter::openOutFileOnRankZero(comm, filename, myRank, true);
5451
5452 writeSparseGraph(out, graph, graphName, graphDescription, debug);
5453 // We can rely on the destructor of the output stream to close
5454 // the file on scope exit, even if writeSparseGraph() throws
5455 // an exception.
5456}
5457
5458template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5460 const crs_graph_type& graph,
5461 const bool debug) {
5462 writeSparseGraphFile(filename, graph, "", "", debug);
5463}
5464
5465template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5467 const Teuchos::RCP<const crs_graph_type>& pGraph,
5468 const std::string& graphName,
5469 const std::string& graphDescription,
5470 const bool debug) {
5471 writeSparseGraphFile(filename, *pGraph, graphName, graphDescription, debug);
5472}
5473
5474template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5476 const Teuchos::RCP<const crs_graph_type>& pGraph,
5477 const bool debug) {
5478 writeSparseGraphFile(filename, *pGraph, "", "", debug);
5479}
5480
5481template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5483 const sparse_matrix_type& matrix,
5484 const bool debug) {
5485 writeSparse(out, matrix, "", "", debug);
5486}
5487
5488template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5490 const Teuchos::RCP<const sparse_matrix_type>& pMatrix,
5491 const bool debug) {
5492 writeSparse(out, *pMatrix, "", "", debug);
5493}
5494
5495template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5498 const std::string& matrixName,
5499 const std::string& matrixDescription,
5500 const Teuchos::RCP<Teuchos::FancyOStream>& err,
5501 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
5502 trcp_tcomm_t comm = MatrixMarketWriter::getComm(X.getMap());
5503 const int myRank = MatrixMarketWriter::getRank(comm);
5504
5505 auto out = MatrixMarketWriter::openOutFileOnRankZero(comm, filename, myRank, true);
5506
5507 writeDense(out, X, matrixName, matrixDescription, err, dbg);
5508 // We can rely on the destructor of the output stream to close
5509 // the file on scope exit, even if writeDense() throws an
5510 // exception.
5511}
5512
5513template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5516 const std::string& matrixName,
5517 const std::string& matrixDescription,
5518 const Teuchos::RCP<Teuchos::FancyOStream>& err,
5519 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
5520 TEUCHOS_TEST_FOR_EXCEPTION(
5521 X.is_null(), std::invalid_argument,
5522 "Tpetra::MatrixMarket::"
5523 "writeDenseFile: The input MultiVector X is null.");
5524 writeDenseFile(filename, *X, matrixName, matrixDescription, err, dbg);
5525}
5526
5527template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5530 const Teuchos::RCP<Teuchos::FancyOStream>& err,
5531 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
5532 writeDenseFile(filename, X, "", "", err, dbg);
5533}
5534
5535template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5538 const Teuchos::RCP<Teuchos::FancyOStream>& err,
5539 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
5540 TEUCHOS_TEST_FOR_EXCEPTION(
5541 X.is_null(), std::invalid_argument,
5542 "Tpetra::MatrixMarket::"
5543 "writeDenseFile: The input MultiVector X is null.");
5544 writeDenseFile(filename, *X, err, dbg);
5545}
5546
5547template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5549 const multivector_type& X,
5550 const std::string& matrixName,
5551 const std::string& matrixDescription,
5552 const Teuchos::RCP<Teuchos::FancyOStream>& err,
5553 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
5554 using std::endl;
5555 using Teuchos::Comm;
5556 using Teuchos::outArg;
5557 using Teuchos::REDUCE_MAX;
5558 using Teuchos::reduceAll;
5559
5561 const int myRank = MatrixMarketWriter::getRank(comm);
5562
5563 // If the caller provides a nonnull debug output stream, we
5564 // print debugging output to it. This is a local thing; we
5565 // don't have to check across processes.
5566 const bool debug = !dbg.is_null();
5567 if (debug) {
5568 dbg->pushTab();
5569 std::ostringstream os;
5570 os << myRank << ": writeDense" << endl;
5571 *dbg << os.str();
5572 dbg->pushTab();
5573 }
5574 // Print the Matrix Market header.
5575 writeDenseHeader(out, X, matrixName, matrixDescription, err, dbg);
5576
5577 // Print each column one at a time. This is a (perhaps)
5578 // temporary fix for Bug 6288.
5579 const size_t numVecs = X.getNumVectors();
5580 for (size_t j = 0; j < numVecs; ++j) {
5581 writeDenseColumn(out, *(X.getVector(j)), err, dbg);
5582 }
5583
5584 if (debug) {
5585 dbg->popTab();
5586 std::ostringstream os;
5587 os << myRank << ": writeDense: Done" << endl;
5588 *dbg << os.str();
5589 dbg->popTab();
5590 }
5591}
5592
5593template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5594std::ofstream MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::openOutFileOnRankZero(
5595 const trcp_tcomm_t& comm,
5596 const std::string& filename, const int rank, const bool safe,
5597 const std::ios_base::openmode mode) {
5598 // Placeholder for the output stream.
5599 std::ofstream out;
5600
5601 // State that will make all ranks throw if the root rank wasn't able to open the stream (using @c int for broadcasting).
5602 int all_should_stop = 0;
5603
5604 // Try to open the file and update the state.
5605 if (rank == 0) {
5606 out.open(filename, mode);
5607 all_should_stop = !out && safe;
5608 }
5609
5610 // Broadcast the stream state and throw from all ranks if needed.
5611 if (comm) Teuchos::broadcast(*comm, 0, &all_should_stop);
5612
5613 TEUCHOS_TEST_FOR_EXCEPTION(
5614 all_should_stop,
5615 std::runtime_error,
5616 "Could not open output file '" + filename + "' on root rank 0.");
5617
5618 return out;
5619}
5620
5621template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5622void MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeDenseHeader(std::ostream& out,
5624 const std::string& matrixName,
5625 const std::string& matrixDescription,
5626 const Teuchos::RCP<Teuchos::FancyOStream>& err,
5627 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
5628 using std::endl;
5629 using Teuchos::Comm;
5630 using Teuchos::outArg;
5631 using Teuchos::RCP;
5632 using Teuchos::REDUCE_MAX;
5633 using Teuchos::reduceAll;
5634 typedef Teuchos::ScalarTraits<scalar_type> STS;
5635 const char prefix[] = "Tpetra::MatrixMarket::writeDenseHeader: ";
5636
5637 trcp_tcomm_t comm = MatrixMarketWriter::getComm(X.getMap());
5638 const int myRank = MatrixMarketWriter::getRank(comm);
5639 int lclErr = 0; // whether this MPI process has seen an error
5640 int gblErr = 0; // whether we know if some MPI process has seen an error
5641
5642 // If the caller provides a nonnull debug output stream, we
5643 // print debugging output to it. This is a local thing; we
5644 // don't have to check across processes.
5645 const bool debug = !dbg.is_null();
5646
5647 if (debug) {
5648 dbg->pushTab();
5649 std::ostringstream os;
5650 os << myRank << ": writeDenseHeader" << endl;
5651 *dbg << os.str();
5652 dbg->pushTab();
5653 }
5654
5655 //
5656 // Process 0: Write the MatrixMarket header.
5657 //
5658 if (myRank == 0) {
5659 try {
5660 // Print the Matrix Market header. MultiVector stores data
5661 // nonsymmetrically, hence "general" in the banner line.
5662 // Print first to a temporary string output stream, and then
5663 // write it to the main output stream, so that at least the
5664 // header output has transactional semantics. We can't
5665 // guarantee transactional semantics for the whole output,
5666 // since that would not be memory scalable. (This could be
5667 // done in the file system by using a temporary file; we
5668 // don't do this, but users could.)
5669 std::ostringstream hdr;
5670 {
5671 std::string dataType;
5672 if (STS::isComplex) {
5673 dataType = "complex";
5674 } else if (STS::isOrdinal) {
5675 dataType = "integer";
5676 } else {
5677 dataType = "real";
5678 }
5679 hdr << "%%MatrixMarket matrix array " << dataType << " general"
5680 << endl;
5681 }
5682
5683 // Print comments (the matrix name and / or description).
5684 if (matrixName != "") {
5685 printAsComment(hdr, matrixName);
5686 }
5687 if (matrixDescription != "") {
5688 printAsComment(hdr, matrixDescription);
5689 }
5690 // Print the Matrix Market dimensions header for dense matrices.
5691 hdr << X.getGlobalLength() << " " << X.getNumVectors() << endl;
5692
5693 // Write the MatrixMarket header to the output stream.
5694 out << hdr.str();
5695 } catch (std::exception& e) {
5696 if (!err.is_null()) {
5697 *err << prefix << "While writing the Matrix Market header, "
5698 "Process 0 threw an exception: "
5699 << e.what() << endl;
5700 }
5701 lclErr = 1;
5702 }
5703 } // if I am Process 0
5704
5705 // Establish global agreement on the error state. It wouldn't
5706 // be good for other processes to keep going, if Process 0
5707 // finds out that it can't write to the given output stream.
5708 reduceAll<int, int>(*comm, REDUCE_MAX, lclErr, outArg(gblErr));
5709 TEUCHOS_TEST_FOR_EXCEPTION(
5710 gblErr == 1, std::runtime_error, prefix << "Some error occurred "
5711 "which prevented this method from completing.");
5712
5713 if (debug) {
5714 dbg->popTab();
5715 *dbg << myRank << ": writeDenseHeader: Done" << endl;
5716 dbg->popTab();
5717 }
5718}
5719
5720template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5721void MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeDenseColumn(std::ostream& out,
5723 const Teuchos::RCP<Teuchos::FancyOStream>& err,
5724 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
5725 using std::endl;
5726 using Teuchos::arcp;
5727 using Teuchos::Array;
5728 using Teuchos::ArrayRCP;
5729 using Teuchos::ArrayView;
5730 using Teuchos::Comm;
5731 using Teuchos::CommRequest;
5732 using Teuchos::ireceive;
5733 using Teuchos::isend;
5734 using Teuchos::outArg;
5735 using Teuchos::RCP;
5736 using Teuchos::REDUCE_MAX;
5737 using Teuchos::reduceAll;
5738 using Teuchos::TypeNameTraits;
5739 using Teuchos::wait;
5740 typedef Teuchos::ScalarTraits<scalar_type> STS;
5741
5742 const Comm<int>& comm = *(X.getMap()->getComm());
5743 const int myRank = comm.getRank();
5744 const int numProcs = comm.getSize();
5745 int lclErr = 0; // whether this MPI process has seen an error
5746 int gblErr = 0; // whether we know if some MPI process has seen an error
5747
5748 // If the caller provides a nonnull debug output stream, we
5749 // print debugging output to it. This is a local thing; we
5750 // don't have to check across processes.
5751 const bool debug = !dbg.is_null();
5752
5753 if (debug) {
5754 dbg->pushTab();
5755 std::ostringstream os;
5756 os << myRank << ": writeDenseColumn" << endl;
5757 *dbg << os.str();
5758 dbg->pushTab();
5759 }
5760
5761 // Make the output stream write floating-point numbers in
5762 // scientific notation. It will politely put the output
5763 // stream back to its state on input, when this scope
5764 // terminates.
5765 Teuchos::SetScientific<scalar_type> sci(out);
5766
5767 const size_t myNumRows = X.getLocalLength();
5768 const size_t numCols = X.getNumVectors();
5769 // Use a different tag for the "size" messages than for the
5770 // "data" messages, in order to help us debug any mix-ups.
5771 const int sizeTag = 1337;
5772 const int dataTag = 1338;
5773
5774 // Process 0 pipelines nonblocking receives with file output.
5775 //
5776 // Constraints:
5777 // - Process 0 can't post a receive for another process'
5778 // actual data, until it posts and waits on the receive
5779 // from that process with the amount of data to receive.
5780 // (We could just post receives with a max data size, but
5781 // I feel uncomfortable about that.)
5782 // - The C++ standard library doesn't allow nonblocking
5783 // output to an std::ostream. (Thus, we have to start a
5784 // receive or send before starting the write, and hope
5785 // that MPI completes it in the background.)
5786 //
5787 // Process 0: Post receive-size receives from Processes 1 and 2.
5788 // Process 1: Post send-size send to Process 0.
5789 // Process 2: Post send-size send to Process 0.
5790 //
5791 // All processes: Pack my entries.
5792 //
5793 // Process 1:
5794 // - Post send-data send to Process 0.
5795 // - Wait on my send-size send to Process 0.
5796 //
5797 // Process 0:
5798 // - Print MatrixMarket header.
5799 // - Print my entries.
5800 // - Wait on receive-size receive from Process 1.
5801 // - Post receive-data receive from Process 1.
5802 //
5803 // For each process p = 1, 2, ... numProcs-1:
5804 // If I am Process 0:
5805 // - Post receive-size receive from Process p + 2
5806 // - Wait on receive-size receive from Process p + 1
5807 // - Post receive-data receive from Process p + 1
5808 // - Wait on receive-data receive from Process p
5809 // - Write data from Process p.
5810 // Else if I am Process p:
5811 // - Wait on my send-data send.
5812 // Else if I am Process p+1:
5813 // - Post send-data send to Process 0.
5814 // - Wait on my send-size send.
5815 // Else if I am Process p+2:
5816 // - Post send-size send to Process 0.
5817 //
5818 // Pipelining has three goals here:
5819 // 1. Overlap communication (the receives) with file I/O
5820 // 2. Give Process 0 a chance to prepost some receives,
5821 // before sends show up, by packing local data before
5822 // posting sends
5823 // 3. Don't post _all_ receives or _all_ sends, because that
5824 // wouldn't be memory scalable. (Just because we can't
5825 // see how much memory MPI consumes, doesn't mean that it
5826 // doesn't consume any!)
5827
5828 // These are used on every process. sendReqSize[0] holds the
5829 // number of rows on this process, and sendReqBuf holds this
5830 // process' data. Process 0 packs into sendReqBuf, but
5831 // doesn't send; it only uses that for printing. All other
5832 // processes send both of these to Process 0.
5833 RCP<CommRequest<int>> sendReqSize, sendReqData;
5834
5835 // These are used only on Process 0, for received data. Keep
5836 // 3 of each, and treat the arrays as circular buffers. When
5837 // receiving from Process p, the corresponding array index
5838 // here is p % 3.
5839 Array<ArrayRCP<size_t>> recvSizeBufs(3);
5840 Array<ArrayRCP<scalar_type>> recvDataBufs(3);
5841 Array<RCP<CommRequest<int>>> recvSizeReqs(3);
5842 Array<RCP<CommRequest<int>>> recvDataReqs(3);
5843
5844 // Buffer for nonblocking send of the "send size."
5845 ArrayRCP<size_t> sendDataSize(1);
5846 sendDataSize[0] = myNumRows;
5847
5848 if (myRank == 0) {
5849 if (debug) {
5850 std::ostringstream os;
5851 os << myRank << ": Post receive-size receives from "
5852 "Procs 1 and 2: tag = "
5853 << sizeTag << endl;
5854 *dbg << os.str();
5855 }
5856 // Process 0: Post receive-size receives from Processes 1 and 2.
5857 recvSizeBufs[0].resize(1);
5858 // Set these three to an invalid value as a flag. If we
5859 // don't get these messages, then the invalid value will
5860 // remain, so we can test for it.
5861 (recvSizeBufs[0])[0] = Teuchos::OrdinalTraits<size_t>::invalid();
5862 recvSizeBufs[1].resize(1);
5863 (recvSizeBufs[1])[0] = Teuchos::OrdinalTraits<size_t>::invalid();
5864 recvSizeBufs[2].resize(1);
5865 (recvSizeBufs[2])[0] = Teuchos::OrdinalTraits<size_t>::invalid();
5866 if (numProcs > 1) {
5867 recvSizeReqs[1] =
5868 ireceive<int, size_t>(recvSizeBufs[1], 1, sizeTag, comm);
5869 }
5870 if (numProcs > 2) {
5871 recvSizeReqs[2] =
5872 ireceive<int, size_t>(recvSizeBufs[2], 2, sizeTag, comm);
5873 }
5874 } else if (myRank == 1 || myRank == 2) {
5875 if (debug) {
5876 std::ostringstream os;
5877 os << myRank << ": Post send-size send: size = "
5878 << sendDataSize[0] << ", tag = " << sizeTag << endl;
5879 *dbg << os.str();
5880 }
5881 // Prime the pipeline by having Processes 1 and 2 start
5882 // their send-size sends. We don't want _all_ the processes
5883 // to start their send-size sends, because that wouldn't be
5884 // memory scalable.
5885 sendReqSize = isend<int, size_t>(sendDataSize, 0, sizeTag, comm);
5886 } else {
5887 if (debug) {
5888 std::ostringstream os;
5889 os << myRank << ": Not posting my send-size send yet" << endl;
5890 *dbg << os.str();
5891 }
5892 }
5893
5894 //
5895 // Pack my entries, in column-major order.
5896 //
5897 if (debug) {
5898 std::ostringstream os;
5899 os << myRank << ": Pack my entries" << endl;
5900 *dbg << os.str();
5901 }
5902 ArrayRCP<scalar_type> sendDataBuf;
5903 try {
5904 sendDataBuf = arcp<scalar_type>(myNumRows * numCols);
5905 X.get1dCopy(sendDataBuf(), myNumRows);
5906 } catch (std::exception& e) {
5907 lclErr = 1;
5908 if (!err.is_null()) {
5909 std::ostringstream os;
5910 os << "Process " << myRank << ": Attempt to pack my MultiVector "
5911 "entries threw an exception: "
5912 << e.what() << endl;
5913 *err << os.str();
5914 }
5915 }
5916 if (debug) {
5917 std::ostringstream os;
5918 os << myRank << ": Done packing my entries" << endl;
5919 *dbg << os.str();
5920 }
5921
5922 //
5923 // Process 1: post send-data send to Process 0.
5924 //
5925 if (myRank == 1) {
5926 if (debug) {
5927 *dbg << myRank << ": Post send-data send: tag = " << dataTag
5928 << endl;
5929 }
5930 sendReqData = isend<int, scalar_type>(sendDataBuf, 0, dataTag, comm);
5931 }
5932
5933 //
5934 // Process 0: Write my entries.
5935 //
5936 if (myRank == 0) {
5937 if (debug) {
5938 std::ostringstream os;
5939 os << myRank << ": Write my entries" << endl;
5940 *dbg << os.str();
5941 }
5942
5943 // Write Process 0's data to the output stream.
5944 // Matrix Market prints dense matrices in column-major order.
5945 const size_t printNumRows = myNumRows;
5946 ArrayView<const scalar_type> printData = sendDataBuf();
5947 const size_t printStride = printNumRows;
5948 if (static_cast<size_t>(printData.size()) < printStride * numCols) {
5949 lclErr = 1;
5950 if (!err.is_null()) {
5951 std::ostringstream os;
5952 os << "Process " << myRank << ": My MultiVector data's size "
5953 << printData.size() << " does not match my local dimensions "
5954 << printStride << " x " << numCols << "." << endl;
5955 *err << os.str();
5956 }
5957 } else {
5958 // Matrix Market dense format wants one number per line.
5959 // It wants each complex number as two real numbers (real
5960 // resp. imaginary parts) with a space between.
5961 for (size_t col = 0; col < numCols; ++col) {
5962 for (size_t row = 0; row < printNumRows; ++row) {
5963 if (STS::isComplex) {
5964 out << STS::real(printData[row + col * printStride]) << " "
5965 << STS::imag(printData[row + col * printStride]) << endl;
5966 } else {
5967 out << printData[row + col * printStride] << endl;
5968 }
5969 }
5970 }
5971 }
5972 }
5973
5974 if (myRank == 0) {
5975 // Wait on receive-size receive from Process 1.
5976 const int recvRank = 1;
5977 const int circBufInd = recvRank % 3;
5978 if (debug) {
5979 std::ostringstream os;
5980 os << myRank << ": Wait on receive-size receive from Process "
5981 << recvRank << endl;
5982 *dbg << os.str();
5983 }
5984 if (numProcs > 1) {
5985 wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
5986
5987 // We received the number of rows of data. (The data
5988 // come in two columns.)
5989 size_t recvNumRows = (recvSizeBufs[circBufInd])[0];
5990 if (recvNumRows == Teuchos::OrdinalTraits<size_t>::invalid()) {
5991 lclErr = 1;
5992 if (!err.is_null()) {
5993 std::ostringstream os;
5994 os << myRank << ": Result of receive-size receive from Process "
5995 << recvRank << " is Teuchos::OrdinalTraits<size_t>::invalid() "
5996 << "= " << Teuchos::OrdinalTraits<size_t>::invalid() << ". "
5997 "This should never happen, and suggests that the receive never "
5998 "got posted. Please report this bug to the Tpetra developers."
5999 << endl;
6000 *err << os.str();
6001 }
6002
6003 // If we're going to continue after error, set the
6004 // number of rows to receive to a reasonable size. This
6005 // may cause MPI_ERR_TRUNCATE if the sending process is
6006 // sending more than 0 rows, but that's better than MPI
6007 // overflowing due to the huge positive value that is
6008 // Teuchos::OrdinalTraits<size_t>::invalid().
6009 recvNumRows = 0;
6010 }
6011
6012 // Post receive-data receive from Process 1.
6013 recvDataBufs[circBufInd].resize(recvNumRows * numCols);
6014 if (debug) {
6015 std::ostringstream os;
6016 os << myRank << ": Post receive-data receive from Process "
6017 << recvRank << ": tag = " << dataTag << ", buffer size = "
6018 << recvDataBufs[circBufInd].size() << endl;
6019 *dbg << os.str();
6020 }
6021 if (!recvSizeReqs[circBufInd].is_null()) {
6022 lclErr = 1;
6023 if (!err.is_null()) {
6024 std::ostringstream os;
6025 os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
6026 "null, before posting the receive-data receive from Process "
6027 << recvRank << ". This should never happen. Please report "
6028 "this bug to the Tpetra developers."
6029 << endl;
6030 *err << os.str();
6031 }
6032 }
6033 recvDataReqs[circBufInd] =
6034 ireceive<int, scalar_type>(recvDataBufs[circBufInd],
6035 recvRank, dataTag, comm);
6036 } // numProcs > 1
6037 } else if (myRank == 1) {
6038 // Wait on my send-size send.
6039 if (debug) {
6040 std::ostringstream os;
6041 os << myRank << ": Wait on my send-size send" << endl;
6042 *dbg << os.str();
6043 }
6044 wait<int>(comm, outArg(sendReqSize));
6045 }
6046
6047 //
6048 // Pipeline loop
6049 //
6050 for (int p = 1; p < numProcs; ++p) {
6051 if (myRank == 0) {
6052 if (p + 2 < numProcs) {
6053 // Post receive-size receive from Process p + 2.
6054 const int recvRank = p + 2;
6055 const int circBufInd = recvRank % 3;
6056 if (debug) {
6057 std::ostringstream os;
6058 os << myRank << ": Post receive-size receive from Process "
6059 << recvRank << ": tag = " << sizeTag << endl;
6060 *dbg << os.str();
6061 }
6062 if (!recvSizeReqs[circBufInd].is_null()) {
6063 lclErr = 1;
6064 if (!err.is_null()) {
6065 std::ostringstream os;
6066 os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
6067 << "null, for the receive-size receive from Process "
6068 << recvRank << "! This may mean that this process never "
6069 << "finished waiting for the receive from Process "
6070 << (recvRank - 3) << "." << endl;
6071 *err << os.str();
6072 }
6073 }
6074 recvSizeReqs[circBufInd] =
6075 ireceive<int, size_t>(recvSizeBufs[circBufInd],
6076 recvRank, sizeTag, comm);
6077 }
6078
6079 if (p + 1 < numProcs) {
6080 const int recvRank = p + 1;
6081 const int circBufInd = recvRank % 3;
6082
6083 // Wait on receive-size receive from Process p + 1.
6084 if (debug) {
6085 std::ostringstream os;
6086 os << myRank << ": Wait on receive-size receive from Process "
6087 << recvRank << endl;
6088 *dbg << os.str();
6089 }
6090 wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
6091
6092 // We received the number of rows of data. (The data
6093 // come in two columns.)
6094 size_t recvNumRows = (recvSizeBufs[circBufInd])[0];
6095 if (recvNumRows == Teuchos::OrdinalTraits<size_t>::invalid()) {
6096 lclErr = 1;
6097 if (!err.is_null()) {
6098 std::ostringstream os;
6099 os << myRank << ": Result of receive-size receive from Process "
6100 << recvRank << " is Teuchos::OrdinalTraits<size_t>::invalid() "
6101 << "= " << Teuchos::OrdinalTraits<size_t>::invalid() << ". "
6102 "This should never happen, and suggests that the receive never "
6103 "got posted. Please report this bug to the Tpetra developers."
6104 << endl;
6105 *err << os.str();
6106 }
6107 // If we're going to continue after error, set the
6108 // number of rows to receive to a reasonable size.
6109 // This may cause MPI_ERR_TRUNCATE if the sending
6110 // process sends more than 0 rows, but that's better
6111 // than MPI overflowing due to the huge positive value
6112 // Teuchos::OrdinalTraits<size_t>::invalid().
6113 recvNumRows = 0;
6114 }
6115
6116 // Post receive-data receive from Process p + 1.
6117 recvDataBufs[circBufInd].resize(recvNumRows * numCols);
6118 if (debug) {
6119 std::ostringstream os;
6120 os << myRank << ": Post receive-data receive from Process "
6121 << recvRank << ": tag = " << dataTag << ", buffer size = "
6122 << recvDataBufs[circBufInd].size() << endl;
6123 *dbg << os.str();
6124 }
6125 if (!recvDataReqs[circBufInd].is_null()) {
6126 lclErr = 1;
6127 if (!err.is_null()) {
6128 std::ostringstream os;
6129 os << myRank << ": recvDataReqs[" << circBufInd << "] is not "
6130 << "null, for the receive-data receive from Process "
6131 << recvRank << "! This may mean that this process never "
6132 << "finished waiting for the receive from Process "
6133 << (recvRank - 3) << "." << endl;
6134 *err << os.str();
6135 }
6136 }
6137 recvDataReqs[circBufInd] =
6138 ireceive<int, scalar_type>(recvDataBufs[circBufInd],
6139 recvRank, dataTag, comm);
6140 }
6141
6142 // Wait on receive-data receive from Process p.
6143 const int recvRank = p;
6144 const int circBufInd = recvRank % 3;
6145 if (debug) {
6146 std::ostringstream os;
6147 os << myRank << ": Wait on receive-data receive from Process "
6148 << recvRank << endl;
6149 *dbg << os.str();
6150 }
6151 wait<int>(comm, outArg(recvDataReqs[circBufInd]));
6152
6153 // Write Process p's data. Number of rows lives in
6154 // recvSizeBufs[circBufInd], and the actual data live in
6155 // recvDataBufs[circBufInd]. Do this after posting receives,
6156 // in order to expose overlap of comm. with file I/O.
6157 if (debug) {
6158 std::ostringstream os;
6159 os << myRank << ": Write entries from Process " << recvRank
6160 << endl;
6161 *dbg << os.str() << endl;
6162 }
6163 size_t printNumRows = (recvSizeBufs[circBufInd])[0];
6164 if (printNumRows == Teuchos::OrdinalTraits<size_t>::invalid()) {
6165 lclErr = 1;
6166 if (!err.is_null()) {
6167 std::ostringstream os;
6168 os << myRank << ": Result of receive-size receive from Process "
6169 << recvRank << " was Teuchos::OrdinalTraits<size_t>::"
6170 "invalid() = "
6171 << Teuchos::OrdinalTraits<size_t>::invalid()
6172 << ". This should never happen, and suggests that its "
6173 "receive-size receive was never posted. "
6174 "Please report this bug to the Tpetra developers."
6175 << endl;
6176 *err << os.str();
6177 }
6178 // If we're going to continue after error, set the
6179 // number of rows to print to a reasonable size.
6180 printNumRows = 0;
6181 }
6182 if (printNumRows > 0 && recvDataBufs[circBufInd].is_null()) {
6183 lclErr = 1;
6184 if (!err.is_null()) {
6185 std::ostringstream os;
6186 os << myRank << ": Result of receive-size receive from Proc "
6187 << recvRank << " was " << printNumRows << " > 0, but "
6188 "recvDataBufs["
6189 << circBufInd << "] is null. This should "
6190 "never happen. Please report this bug to the Tpetra "
6191 "developers."
6192 << endl;
6193 *err << os.str();
6194 }
6195 // If we're going to continue after error, set the
6196 // number of rows to print to a reasonable size.
6197 printNumRows = 0;
6198 }
6199
6200 // Write the received data to the output stream.
6201 // Matrix Market prints dense matrices in column-major order.
6202 ArrayView<const scalar_type> printData = (recvDataBufs[circBufInd])();
6203 const size_t printStride = printNumRows;
6204 // Matrix Market dense format wants one number per line.
6205 // It wants each complex number as two real numbers (real
6206 // resp. imaginary parts) with a space between.
6207 for (size_t col = 0; col < numCols; ++col) {
6208 for (size_t row = 0; row < printNumRows; ++row) {
6209 if (STS::isComplex) {
6210 out << STS::real(printData[row + col * printStride]) << " "
6211 << STS::imag(printData[row + col * printStride]) << endl;
6212 } else {
6213 out << printData[row + col * printStride] << endl;
6214 }
6215 }
6216 }
6217 } else if (myRank == p) { // Process p
6218 // Wait on my send-data send.
6219 if (debug) {
6220 std::ostringstream os;
6221 os << myRank << ": Wait on my send-data send" << endl;
6222 *dbg << os.str();
6223 }
6224 wait<int>(comm, outArg(sendReqData));
6225 } else if (myRank == p + 1) { // Process p + 1
6226 // Post send-data send to Process 0.
6227 if (debug) {
6228 std::ostringstream os;
6229 os << myRank << ": Post send-data send: tag = " << dataTag
6230 << endl;
6231 *dbg << os.str();
6232 }
6233 sendReqData = isend<int, scalar_type>(sendDataBuf, 0, dataTag, comm);
6234 // Wait on my send-size send.
6235 if (debug) {
6236 std::ostringstream os;
6237 os << myRank << ": Wait on my send-size send" << endl;
6238 *dbg << os.str();
6239 }
6240 wait<int>(comm, outArg(sendReqSize));
6241 } else if (myRank == p + 2) { // Process p + 2
6242 // Post send-size send to Process 0.
6243 if (debug) {
6244 std::ostringstream os;
6245 os << myRank << ": Post send-size send: size = "
6246 << sendDataSize[0] << ", tag = " << sizeTag << endl;
6247 *dbg << os.str();
6248 }
6249 sendReqSize = isend<int, size_t>(sendDataSize, 0, sizeTag, comm);
6250 }
6251 }
6252
6253 // Establish global agreement on the error state.
6254 reduceAll<int, int>(comm, REDUCE_MAX, lclErr, outArg(gblErr));
6255 TEUCHOS_TEST_FOR_EXCEPTION(
6256 gblErr == 1, std::runtime_error,
6257 "Tpetra::MatrixMarket::writeDense "
6258 "experienced some kind of error and was unable to complete.");
6259
6260 if (debug) {
6261 dbg->popTab();
6262 *dbg << myRank << ": writeDenseColumn: Done" << endl;
6263 dbg->popTab();
6264 }
6265}
6266
6267template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6270 const std::string& matrixName,
6271 const std::string& matrixDescription,
6272 const Teuchos::RCP<Teuchos::FancyOStream>& err,
6273 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
6274 TEUCHOS_TEST_FOR_EXCEPTION(
6275 X.is_null(), std::invalid_argument,
6276 "Tpetra::MatrixMarket::"
6277 "writeDense: The input MultiVector X is null.");
6278 writeDense(out, *X, matrixName, matrixDescription, err, dbg);
6279}
6280
6281template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6284 const Teuchos::RCP<Teuchos::FancyOStream>& err,
6285 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
6286 writeDense(out, X, "", "", err, dbg);
6287}
6288
6289template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6292 const Teuchos::RCP<Teuchos::FancyOStream>& err,
6293 const Teuchos::RCP<Teuchos::FancyOStream>& dbg) {
6294 TEUCHOS_TEST_FOR_EXCEPTION(
6295 X.is_null(), std::invalid_argument,
6296 "Tpetra::MatrixMarket::"
6297 "writeDense: The input MultiVector X is null.");
6298 writeDense(out, *X, "", "", err, dbg);
6299}
6300
6301template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6303 Teuchos::RCP<Teuchos::FancyOStream> err =
6304 Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
6305 writeMap(out, map, err, debug);
6306}
6307
6308template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6311 const Teuchos::RCP<Teuchos::FancyOStream>& err,
6312 const bool debug) {
6313 using std::endl;
6314 using Teuchos::Array;
6315 using Teuchos::ArrayRCP;
6316 using Teuchos::ArrayView;
6317 using Teuchos::Comm;
6318 using Teuchos::CommRequest;
6319 using Teuchos::ireceive;
6320 using Teuchos::isend;
6321 using Teuchos::RCP;
6322 using Teuchos::TypeNameTraits;
6323 using Teuchos::wait;
6324 typedef global_ordinal_type GO;
6325 typedef int pid_type;
6326
6327 // Treat the Map as a 1-column "multivector." This differs
6328 // from the previous two-column format, in which column 0 held
6329 // the GIDs, and column 1 held the corresponding PIDs. It
6330 // differs because printing that format requires knowing the
6331 // entire first column -- that is, all the GIDs -- in advance.
6332 // Sending messages from each process one at a time saves
6333 // memory, but it means that Process 0 doesn't ever have all
6334 // the GIDs at once.
6335 //
6336 // We pack the entries as ptrdiff_t, since this should be the
6337 // biggest signed built-in integer type that can hold any GO
6338 // or pid_type (= int) quantity without overflow. Test this
6339 // assumption at run time.
6340 typedef ptrdiff_t int_type;
6341 TEUCHOS_TEST_FOR_EXCEPTION(
6342 sizeof(GO) > sizeof(int_type), std::logic_error,
6343 "The global ordinal type GO=" << TypeNameTraits<GO>::name()
6344 << " is too big for ptrdiff_t. sizeof(GO) = " << sizeof(GO)
6345 << " > sizeof(ptrdiff_t) = " << sizeof(ptrdiff_t) << ".");
6346 TEUCHOS_TEST_FOR_EXCEPTION(
6347 sizeof(pid_type) > sizeof(int_type), std::logic_error,
6348 "The (MPI) process rank type pid_type=" << TypeNameTraits<pid_type>::name() << " is too big for ptrdiff_t. "
6349 "sizeof(pid_type) = "
6350 << sizeof(pid_type) << " > sizeof(ptrdiff_t)"
6351 " = "
6352 << sizeof(ptrdiff_t) << ".");
6353
6354 const Comm<int>& comm = *(map.getComm());
6355 const int myRank = comm.getRank();
6356 const int numProcs = comm.getSize();
6357
6358 if (!err.is_null()) {
6359 err->pushTab();
6360 }
6361 if (debug) {
6362 std::ostringstream os;
6363 os << myRank << ": writeMap" << endl;
6364 *err << os.str();
6365 }
6366 if (!err.is_null()) {
6367 err->pushTab();
6368 }
6369
6370 const size_t myNumRows = map.getLocalNumElements();
6371 // Use a different tag for the "size" messages than for the
6372 // "data" messages, in order to help us debug any mix-ups.
6373 const int sizeTag = 1337;
6374 const int dataTag = 1338;
6375
6376 // Process 0 pipelines nonblocking receives with file output.
6377 //
6378 // Constraints:
6379 // - Process 0 can't post a receive for another process'
6380 // actual data, until it posts and waits on the receive
6381 // from that process with the amount of data to receive.
6382 // (We could just post receives with a max data size, but
6383 // I feel uncomfortable about that.)
6384 // - The C++ standard library doesn't allow nonblocking
6385 // output to an std::ostream.
6386 //
6387 // Process 0: Post receive-size receives from Processes 1 and 2.
6388 // Process 1: Post send-size send to Process 0.
6389 // Process 2: Post send-size send to Process 0.
6390 //
6391 // All processes: Pack my GIDs and PIDs.
6392 //
6393 // Process 1:
6394 // - Post send-data send to Process 0.
6395 // - Wait on my send-size send to Process 0.
6396 //
6397 // Process 0:
6398 // - Print MatrixMarket header.
6399 // - Print my GIDs and PIDs.
6400 // - Wait on receive-size receive from Process 1.
6401 // - Post receive-data receive from Process 1.
6402 //
6403 // For each process p = 1, 2, ... numProcs-1:
6404 // If I am Process 0:
6405 // - Post receive-size receive from Process p + 2
6406 // - Wait on receive-size receive from Process p + 1
6407 // - Post receive-data receive from Process p + 1
6408 // - Wait on receive-data receive from Process p
6409 // - Write data from Process p.
6410 // Else if I am Process p:
6411 // - Wait on my send-data send.
6412 // Else if I am Process p+1:
6413 // - Post send-data send to Process 0.
6414 // - Wait on my send-size send.
6415 // Else if I am Process p+2:
6416 // - Post send-size send to Process 0.
6417 //
6418 // Pipelining has three goals here:
6419 // 1. Overlap communication (the receives) with file I/O
6420 // 2. Give Process 0 a chance to prepost some receives,
6421 // before sends show up, by packing local data before
6422 // posting sends
6423 // 3. Don't post _all_ receives or _all_ sends, because that
6424 // wouldn't be memory scalable. (Just because we can't
6425 // see how much memory MPI consumes, doesn't mean that it
6426 // doesn't consume any!)
6427
6428 // These are used on every process. sendReqSize[0] holds the
6429 // number of rows on this process, and sendReqBuf holds this
6430 // process' data. Process 0 packs into sendReqBuf, but
6431 // doesn't send; it only uses that for printing. All other
6432 // processes send both of these to Process 0.
6433 RCP<CommRequest<int>> sendReqSize, sendReqData;
6434
6435 // These are used only on Process 0, for received data. Keep
6436 // 3 of each, and treat the arrays as circular buffers. When
6437 // receiving from Process p, the corresponding array index
6438 // here is p % 3.
6439 Array<ArrayRCP<int_type>> recvSizeBufs(3);
6440 Array<ArrayRCP<int_type>> recvDataBufs(3);
6441 Array<RCP<CommRequest<int>>> recvSizeReqs(3);
6442 Array<RCP<CommRequest<int>>> recvDataReqs(3);
6443
6444 // Buffer for nonblocking send of the "send size."
6445 ArrayRCP<int_type> sendDataSize(1);
6446 sendDataSize[0] = myNumRows;
6447
6448 if (myRank == 0) {
6449 if (debug) {
6450 std::ostringstream os;
6451 os << myRank << ": Post receive-size receives from "
6452 "Procs 1 and 2: tag = "
6453 << sizeTag << endl;
6454 *err << os.str();
6455 }
6456 // Process 0: Post receive-size receives from Processes 1 and 2.
6457 recvSizeBufs[0].resize(1);
6458 (recvSizeBufs[0])[0] = -1; // error flag
6459 recvSizeBufs[1].resize(1);
6460 (recvSizeBufs[1])[0] = -1; // error flag
6461 recvSizeBufs[2].resize(1);
6462 (recvSizeBufs[2])[0] = -1; // error flag
6463 if (numProcs > 1) {
6464 recvSizeReqs[1] =
6465 ireceive<int, int_type>(recvSizeBufs[1], 1, sizeTag, comm);
6466 }
6467 if (numProcs > 2) {
6468 recvSizeReqs[2] =
6469 ireceive<int, int_type>(recvSizeBufs[2], 2, sizeTag, comm);
6470 }
6471 } else if (myRank == 1 || myRank == 2) {
6472 if (debug) {
6473 std::ostringstream os;
6474 os << myRank << ": Post send-size send: size = "
6475 << sendDataSize[0] << ", tag = " << sizeTag << endl;
6476 *err << os.str();
6477 }
6478 // Prime the pipeline by having Processes 1 and 2 start
6479 // their send-size sends. We don't want _all_ the processes
6480 // to start their send-size sends, because that wouldn't be
6481 // memory scalable.
6482 sendReqSize = isend<int, int_type>(sendDataSize, 0, sizeTag, comm);
6483 } else {
6484 if (debug) {
6485 std::ostringstream os;
6486 os << myRank << ": Not posting my send-size send yet" << endl;
6487 *err << os.str();
6488 }
6489 }
6490
6491 //
6492 // Pack my GIDs and PIDs. Each (GID,PID) pair gets packed
6493 // consecutively, for better locality.
6494 //
6495
6496 if (debug) {
6497 std::ostringstream os;
6498 os << myRank << ": Pack my GIDs and PIDs" << endl;
6499 *err << os.str();
6500 }
6501
6502 ArrayRCP<int_type> sendDataBuf(myNumRows * 2);
6503
6504 if (map.isContiguous()) {
6505 const int_type myMinGblIdx =
6506 static_cast<int_type>(map.getMinGlobalIndex());
6507 for (size_t k = 0; k < myNumRows; ++k) {
6508 const int_type gid = myMinGblIdx + static_cast<int_type>(k);
6509 const int_type pid = static_cast<int_type>(myRank);
6510 sendDataBuf[2 * k] = gid;
6511 sendDataBuf[2 * k + 1] = pid;
6512 }
6513 } else {
6514 ArrayView<const GO> myGblInds = map.getLocalElementList();
6515 for (size_t k = 0; k < myNumRows; ++k) {
6516 const int_type gid = static_cast<int_type>(myGblInds[k]);
6517 const int_type pid = static_cast<int_type>(myRank);
6518 sendDataBuf[2 * k] = gid;
6519 sendDataBuf[2 * k + 1] = pid;
6520 }
6521 }
6522
6523 if (debug) {
6524 std::ostringstream os;
6525 os << myRank << ": Done packing my GIDs and PIDs" << endl;
6526 *err << os.str();
6527 }
6528
6529 if (myRank == 1) {
6530 // Process 1: post send-data send to Process 0.
6531 if (debug) {
6532 *err << myRank << ": Post send-data send: tag = " << dataTag
6533 << endl;
6534 }
6535 sendReqData = isend<int, int_type>(sendDataBuf, 0, dataTag, comm);
6536 }
6537
6538 if (myRank == 0) {
6539 if (debug) {
6540 *err << myRank << ": Write MatrixMarket header" << endl;
6541 }
6542
6543 // Process 0: Write the MatrixMarket header.
6544 // Description section explains each column.
6545 std::ostringstream hdr;
6546
6547 // Print the Matrix Market header. MultiVector stores data
6548 // nonsymmetrically, hence "general" in the banner line.
6549 hdr << "%%MatrixMarket matrix array integer general" << endl
6550 << "% Format: Version 2.0" << endl
6551 << "%" << endl
6552 << "% This file encodes a Tpetra::Map." << endl
6553 << "% It is stored as a dense vector, with twice as many " << endl
6554 << "% entries as the global number of GIDs (global indices)." << endl
6555 << "% (GID, PID) pairs are stored contiguously, where the PID " << endl
6556 << "% is the rank of the process owning that GID." << endl
6557 << (2 * map.getGlobalNumElements()) << " " << 1 << endl;
6558 out << hdr.str();
6559
6560 if (debug) {
6561 std::ostringstream os;
6562 os << myRank << ": Write my GIDs and PIDs" << endl;
6563 *err << os.str();
6564 }
6565
6566 // Write Process 0's data to the output stream.
6567 // Matrix Market prints dense matrices in column-major order.
6568 const int_type printNumRows = myNumRows;
6569 ArrayView<const int_type> printData = sendDataBuf();
6570 for (int_type k = 0; k < printNumRows; ++k) {
6571 const int_type gid = printData[2 * k];
6572 const int_type pid = printData[2 * k + 1];
6573 out << gid << endl
6574 << pid << endl;
6575 }
6576 }
6577
6578 if (myRank == 0) {
6579 // Wait on receive-size receive from Process 1.
6580 const int recvRank = 1;
6581 const int circBufInd = recvRank % 3;
6582 if (debug) {
6583 std::ostringstream os;
6584 os << myRank << ": Wait on receive-size receive from Process "
6585 << recvRank << endl;
6586 *err << os.str();
6587 }
6588 if (numProcs > 1) {
6589 wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
6590
6591 // We received the number of rows of data. (The data
6592 // come in two columns.)
6593 const int_type recvNumRows = (recvSizeBufs[circBufInd])[0];
6594 if (debug && recvNumRows == -1) {
6595 std::ostringstream os;
6596 os << myRank << ": Result of receive-size receive from Process "
6597 << recvRank << " is -1. This should never happen, and "
6598 "suggests that the receive never got posted. Please report "
6599 "this bug to the Tpetra developers."
6600 << endl;
6601 *err << os.str();
6602 }
6603
6604 // Post receive-data receive from Process 1.
6605 recvDataBufs[circBufInd].resize(recvNumRows * 2);
6606 if (debug) {
6607 std::ostringstream os;
6608 os << myRank << ": Post receive-data receive from Process "
6609 << recvRank << ": tag = " << dataTag << ", buffer size = "
6610 << recvDataBufs[circBufInd].size() << endl;
6611 *err << os.str();
6612 }
6613 if (!recvSizeReqs[circBufInd].is_null()) {
6614 std::ostringstream os;
6615 os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
6616 "null, before posting the receive-data receive from Process "
6617 << recvRank << ". This should never happen. Please report "
6618 "this bug to the Tpetra developers."
6619 << endl;
6620 *err << os.str();
6621 }
6622 recvDataReqs[circBufInd] =
6623 ireceive<int, int_type>(recvDataBufs[circBufInd],
6624 recvRank, dataTag, comm);
6625 } // numProcs > 1
6626 } else if (myRank == 1) {
6627 // Wait on my send-size send.
6628 if (debug) {
6629 std::ostringstream os;
6630 os << myRank << ": Wait on my send-size send" << endl;
6631 *err << os.str();
6632 }
6633 wait<int>(comm, outArg(sendReqSize));
6634 }
6635
6636 //
6637 // Pipeline loop
6638 //
6639 for (int p = 1; p < numProcs; ++p) {
6640 if (myRank == 0) {
6641 if (p + 2 < numProcs) {
6642 // Post receive-size receive from Process p + 2.
6643 const int recvRank = p + 2;
6644 const int circBufInd = recvRank % 3;
6645 if (debug) {
6646 std::ostringstream os;
6647 os << myRank << ": Post receive-size receive from Process "
6648 << recvRank << ": tag = " << sizeTag << endl;
6649 *err << os.str();
6650 }
6651 if (!recvSizeReqs[circBufInd].is_null()) {
6652 std::ostringstream os;
6653 os << myRank << ": recvSizeReqs[" << circBufInd << "] is not "
6654 << "null, for the receive-size receive from Process "
6655 << recvRank << "! This may mean that this process never "
6656 << "finished waiting for the receive from Process "
6657 << (recvRank - 3) << "." << endl;
6658 *err << os.str();
6659 }
6660 recvSizeReqs[circBufInd] =
6661 ireceive<int, int_type>(recvSizeBufs[circBufInd],
6662 recvRank, sizeTag, comm);
6663 }
6664
6665 if (p + 1 < numProcs) {
6666 const int recvRank = p + 1;
6667 const int circBufInd = recvRank % 3;
6668
6669 // Wait on receive-size receive from Process p + 1.
6670 if (debug) {
6671 std::ostringstream os;
6672 os << myRank << ": Wait on receive-size receive from Process "
6673 << recvRank << endl;
6674 *err << os.str();
6675 }
6676 wait<int>(comm, outArg(recvSizeReqs[circBufInd]));
6677
6678 // We received the number of rows of data. (The data
6679 // come in two columns.)
6680 const int_type recvNumRows = (recvSizeBufs[circBufInd])[0];
6681 if (debug && recvNumRows == -1) {
6682 std::ostringstream os;
6683 os << myRank << ": Result of receive-size receive from Process "
6684 << recvRank << " is -1. This should never happen, and "
6685 "suggests that the receive never got posted. Please report "
6686 "this bug to the Tpetra developers."
6687 << endl;
6688 *err << os.str();
6689 }
6690
6691 // Post receive-data receive from Process p + 1.
6692 recvDataBufs[circBufInd].resize(recvNumRows * 2);
6693 if (debug) {
6694 std::ostringstream os;
6695 os << myRank << ": Post receive-data receive from Process "
6696 << recvRank << ": tag = " << dataTag << ", buffer size = "
6697 << recvDataBufs[circBufInd].size() << endl;
6698 *err << os.str();
6699 }
6700 if (!recvDataReqs[circBufInd].is_null()) {
6701 std::ostringstream os;
6702 os << myRank << ": recvDataReqs[" << circBufInd << "] is not "
6703 << "null, for the receive-data receive from Process "
6704 << recvRank << "! This may mean that this process never "
6705 << "finished waiting for the receive from Process "
6706 << (recvRank - 3) << "." << endl;
6707 *err << os.str();
6708 }
6709 recvDataReqs[circBufInd] =
6710 ireceive<int, int_type>(recvDataBufs[circBufInd],
6711 recvRank, dataTag, comm);
6712 }
6713
6714 // Wait on receive-data receive from Process p.
6715 const int recvRank = p;
6716 const int circBufInd = recvRank % 3;
6717 if (debug) {
6718 std::ostringstream os;
6719 os << myRank << ": Wait on receive-data receive from Process "
6720 << recvRank << endl;
6721 *err << os.str();
6722 }
6723 wait<int>(comm, outArg(recvDataReqs[circBufInd]));
6724
6725 // Write Process p's data. Number of rows lives in
6726 // recvSizeBufs[circBufInd], and the actual data live in
6727 // recvDataBufs[circBufInd]. Do this after posting receives,
6728 // in order to expose overlap of comm. with file I/O.
6729 if (debug) {
6730 std::ostringstream os;
6731 os << myRank << ": Write GIDs and PIDs from Process "
6732 << recvRank << endl;
6733 *err << os.str() << endl;
6734 }
6735 const int_type printNumRows = (recvSizeBufs[circBufInd])[0];
6736 if (debug && printNumRows == -1) {
6737 std::ostringstream os;
6738 os << myRank << ": Result of receive-size receive from Process "
6739 << recvRank << " was -1. This should never happen, and "
6740 "suggests that its receive-size receive was never posted. "
6741 "Please report this bug to the Tpetra developers."
6742 << endl;
6743 *err << os.str();
6744 }
6745 if (debug && printNumRows > 0 && recvDataBufs[circBufInd].is_null()) {
6746 std::ostringstream os;
6747 os << myRank << ": Result of receive-size receive from Proc "
6748 << recvRank << " was " << printNumRows << " > 0, but "
6749 "recvDataBufs["
6750 << circBufInd << "] is null. This should "
6751 "never happen. Please report this bug to the Tpetra "
6752 "developers."
6753 << endl;
6754 *err << os.str();
6755 }
6756 ArrayView<const int_type> printData = (recvDataBufs[circBufInd])();
6757 for (int_type k = 0; k < printNumRows; ++k) {
6758 const int_type gid = printData[2 * k];
6759 const int_type pid = printData[2 * k + 1];
6760 out << gid << endl
6761 << pid << endl;
6762 }
6763 } else if (myRank == p) { // Process p
6764 // Wait on my send-data send.
6765 if (debug) {
6766 std::ostringstream os;
6767 os << myRank << ": Wait on my send-data send" << endl;
6768 *err << os.str();
6769 }
6770 wait<int>(comm, outArg(sendReqData));
6771 } else if (myRank == p + 1) { // Process p + 1
6772 // Post send-data send to Process 0.
6773 if (debug) {
6774 std::ostringstream os;
6775 os << myRank << ": Post send-data send: tag = " << dataTag
6776 << endl;
6777 *err << os.str();
6778 }
6779 sendReqData = isend<int, int_type>(sendDataBuf, 0, dataTag, comm);
6780 // Wait on my send-size send.
6781 if (debug) {
6782 std::ostringstream os;
6783 os << myRank << ": Wait on my send-size send" << endl;
6784 *err << os.str();
6785 }
6786 wait<int>(comm, outArg(sendReqSize));
6787 } else if (myRank == p + 2) { // Process p + 2
6788 // Post send-size send to Process 0.
6789 if (debug) {
6790 std::ostringstream os;
6791 os << myRank << ": Post send-size send: size = "
6792 << sendDataSize[0] << ", tag = " << sizeTag << endl;
6793 *err << os.str();
6794 }
6795 sendReqSize = isend<int, int_type>(sendDataSize, 0, sizeTag, comm);
6796 }
6797 }
6798
6799 if (!err.is_null()) {
6800 err->popTab();
6801 }
6802 if (debug) {
6803 *err << myRank << ": writeMap: Done" << endl;
6804 }
6805 if (!err.is_null()) {
6806 err->popTab();
6807 }
6808}
6809
6810template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6813 const int myRank = map.getComm()->getRank();
6814
6815 auto out = MatrixMarketWriter::openOutFileOnRankZero(map.getComm(), filename, myRank, true);
6816
6817 writeMap(out, map);
6818 // We can rely on the destructor of the output stream to close
6819 // the file on scope exit, even if writeDense() throws an
6820 // exception.
6821}
6822
6823template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6824void MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::printAsComment(std::ostream& out, const std::string& str) {
6825 using std::endl;
6826 std::istringstream inpstream(str);
6827 std::string line;
6828
6829 while (getline(inpstream, line)) {
6830 if (!line.empty()) {
6831 // Note that getline() doesn't store '\n', so we have to
6832 // append the endline ourselves.
6833 if (line[0] == '%') { // Line starts with a comment character.
6834 out << line << endl;
6835 } else { // Line doesn't start with a comment character.
6836 out << "%% " << line << endl;
6837 }
6838 }
6839 }
6840}
6841
6842template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6843void MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeOperator(const std::string& fileName, typename MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::operator_type const& A) {
6844 Teuchos::ParameterList pl;
6845 writeOperator(fileName, A, pl);
6846}
6847
6848template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6849void MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeOperator(std::ostream& out, const typename MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::operator_type& A) {
6850 Teuchos::ParameterList pl;
6851 writeOperator(out, A, pl);
6852}
6853
6854template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6856 const typename MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::operator_type& A,
6857 const Teuchos::ParameterList& params) {
6858 std::ofstream out;
6859 std::string tmpFile = "__TMP__" + fileName;
6860 const int myRank = A.getDomainMap()->getComm()->getRank();
6861 bool precisionChanged = false;
6862 int oldPrecision = 0;
6863 // The number of nonzero entries in a Tpetra::Operator is
6864 // unknown until probing is completed. In order to write a
6865 // MatrixMarket header, we write the matrix to a temporary
6866 // file.
6867 //
6868 // FIXME (mfh 23 May 2015) IT WASN'T MY IDEA TO WRITE TO A
6869 // TEMPORARY FILE.
6870 if (myRank == 0) {
6871 if (std::ifstream(tmpFile))
6872 TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error,
6873 "writeOperator: temporary file " << tmpFile << " already exists");
6874 out.open(tmpFile.c_str());
6875 if (params.isParameter("precision")) {
6876 oldPrecision = out.precision(params.get<int>("precision"));
6877 precisionChanged = true;
6878 }
6879 }
6880
6881 const std::string header = writeOperatorImpl(out, A, params);
6882
6883 if (myRank == 0) {
6884 if (precisionChanged)
6885 out.precision(oldPrecision);
6886 out.close();
6887 out.open(fileName.c_str(), std::ios::binary);
6888 bool printMatrixMarketHeader = true;
6889 if (params.isParameter("print MatrixMarket header"))
6890 printMatrixMarketHeader = params.get<bool>("print MatrixMarket header");
6891 if (printMatrixMarketHeader && myRank == 0) {
6892 // Write header to final file.
6893 out << header;
6894 }
6895 // Append matrix from temporary to final file.
6896 std::ifstream src(tmpFile, std::ios_base::binary);
6897 out << src.rdbuf();
6898 src.close();
6899 // Delete the temporary file.
6900 remove(tmpFile.c_str());
6901 }
6902}
6903
6904template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6906 const typename MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::operator_type& A,
6907 const Teuchos::ParameterList& params) {
6908 const int myRank = A.getDomainMap()->getComm()->getRank();
6909
6910 // The number of nonzero entries in a Tpetra::Operator is
6911 // unknown until probing is completed. In order to write a
6912 // MatrixMarket header, we write the matrix to a temporary
6913 // output stream.
6914 //
6915 // NOTE (mfh 23 May 2015): Writing to a temporary output
6916 // stream may double the memory usage, depending on whether
6917 // 'out' is a file stream or an in-memory output stream (e.g.,
6918 // std::ostringstream). It might be wise to use a temporary
6919 // file instead. However, please look carefully at POSIX
6920 // functions for safe creation of temporary files. Don't just
6921 // prepend "__TMP__" to the filename and hope for the best.
6922 // Furthermore, it should be valid to call the std::ostream
6923 // overload of this method even when Process 0 does not have
6924 // access to a file system.
6925 std::ostringstream tmpOut;
6926 if (myRank == 0) {
6927 if (params.isParameter("precision") && params.isType<int>("precision")) {
6928 (void)tmpOut.precision(params.get<int>("precision"));
6929 }
6930 }
6931
6932 const std::string header = writeOperatorImpl(tmpOut, A, params);
6933
6934 if (myRank == 0) {
6935 bool printMatrixMarketHeader = true;
6936 if (params.isParameter("print MatrixMarket header") &&
6937 params.isType<bool>("print MatrixMarket header")) {
6938 printMatrixMarketHeader = params.get<bool>("print MatrixMarket header");
6939 }
6940 if (printMatrixMarketHeader && myRank == 0) {
6941 out << header; // write header to final output stream
6942 }
6943 // Append matrix from temporary output stream to final output stream.
6944 //
6945 // NOTE (mfh 23 May 2015) This might use a lot of memory.
6946 // However, we should not use temporary files in this
6947 // method. Since it does not access the file system (unlike
6948 // the overload that takes a file name), it should not
6949 // require the file system at all.
6950 //
6951 // If memory usage becomes a problem, one thing we could do
6952 // is write the entries of the Operator one column (or a few
6953 // columns) at a time. The Matrix Market sparse format does
6954 // not impose an order on its entries, so it would be OK to
6955 // write them in that order.
6956 out << tmpOut.str();
6957 }
6958}
6959
6960template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6961std::string
6962MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeOperatorImpl(std::ostream& os,
6963 const typename MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::operator_type& A,
6964 const Teuchos::ParameterList& params) {
6965 using Teuchos::Array;
6966 using Teuchos::ArrayRCP;
6967 using Teuchos::RCP;
6968 using Teuchos::rcp;
6969
6970 typedef local_ordinal_type LO;
6971 typedef global_ordinal_type GO;
6972 typedef Teuchos::OrdinalTraits<LO> TLOT;
6973 typedef Teuchos::OrdinalTraits<GO> TGOT;
6974 typedef Tpetra::Import<LO, GO, node_type> import_type;
6975 typedef Tpetra::MultiVector<GO, LO, GO, node_type> mv_type_go;
6976
6977 const map_type& domainMap = *(A.getDomainMap());
6978 RCP<const map_type> rangeMap = A.getRangeMap();
6979 trcp_tcomm_t comm = rangeMap->getComm();
6980 const int myRank = comm->getRank();
6981 const size_t numProcs = comm->getSize();
6982
6983 size_t numMVs = 10;
6984 if (params.isParameter("probing size"))
6985 numMVs = params.get<int>("probing size");
6986
6987 GO globalNnz = 0;
6988 GO minColGid = domainMap.getMinAllGlobalIndex();
6989 GO maxColGid = domainMap.getMaxAllGlobalIndex();
6990 // Rather than replicating the domainMap on all processors, we instead
6991 // iterate from the min GID to the max GID. If the map is gappy,
6992 // there will be invalid GIDs, i.e., GIDs no one has. This will require
6993 // unnecessary matvecs against potentially zero vectors.
6994 GO numGlobElts = maxColGid - minColGid + TGOT::one();
6995 GO numChunks = numGlobElts / numMVs;
6996 GO rem = numGlobElts % numMVs;
6997 GO indexBase = rangeMap->getIndexBase();
6998
6999 int offsetToUseInPrinting = 1 - indexBase; // default is 1-based indexing
7000 if (params.isParameter("zero-based indexing")) {
7001 if (params.get<bool>("zero-based indexing") == true)
7002 offsetToUseInPrinting = -indexBase; // If 0-based, use as-is. If 1-based, subtract 1.
7003 }
7004
7005 // Create map that replicates the range map on pid 0 and is empty for all other pids
7006 size_t numLocalRangeEntries = rangeMap->getLocalNumElements();
7007
7008 // Create contiguous source map
7009 RCP<const map_type> allGidsMap = rcp(new map_type(TGOT::invalid(), numLocalRangeEntries,
7010 indexBase, comm));
7011 // Create vector based on above map. Populate it with GIDs corresponding to this pid's GIDs in rangeMap.
7012 mv_type_go allGids(allGidsMap, 1);
7013 Teuchos::ArrayRCP<GO> allGidsData = allGids.getDataNonConst(0);
7014
7015 for (size_t i = 0; i < numLocalRangeEntries; i++)
7016 allGidsData[i] = rangeMap->getGlobalElement(i);
7017 allGidsData = Teuchos::null;
7018
7019 // Create target map that is nontrivial only on pid 0
7020 GO numTargetMapEntries = TGOT::zero();
7021 Teuchos::Array<GO> importGidList;
7022 if (myRank == 0) {
7023 numTargetMapEntries = rangeMap->getGlobalNumElements();
7024 importGidList.reserve(numTargetMapEntries);
7025 for (GO j = 0; j < numTargetMapEntries; ++j) importGidList.push_back(j + indexBase);
7026 } else {
7027 importGidList.reserve(numTargetMapEntries);
7028 }
7029 RCP<map_type> importGidMap = rcp(new map_type(TGOT::invalid(), importGidList(), indexBase, comm));
7030
7031 // Import all rangeMap GIDs to pid 0
7032 import_type gidImporter(allGidsMap, importGidMap);
7033 mv_type_go importedGids(importGidMap, 1);
7034 importedGids.doImport(allGids, gidImporter, INSERT);
7035
7036 // The following import map will be non-trivial only on pid 0.
7037 ArrayRCP<const GO> importedGidsData = importedGids.getData(0);
7038 RCP<const map_type> importMap = rcp(new map_type(TGOT::invalid(), importedGidsData(), indexBase, comm));
7039
7040 // Importer from original range map to pid 0
7041 import_type importer(rangeMap, importMap);
7042 // Target vector on pid 0
7043 RCP<mv_type> colsOnPid0 = rcp(new mv_type(importMap, numMVs));
7044
7045 RCP<mv_type> ei = rcp(new mv_type(A.getDomainMap(), numMVs)); // probing vector
7046 RCP<mv_type> colsA = rcp(new mv_type(A.getRangeMap(), numMVs)); // columns of A revealed by probing
7047
7048 Array<GO> globalColsArray, localColsArray;
7049 globalColsArray.reserve(numMVs);
7050 localColsArray.reserve(numMVs);
7051
7052 ArrayRCP<ArrayRCP<Scalar>> eiData(numMVs);
7053 for (size_t i = 0; i < numMVs; ++i)
7054 eiData[i] = ei->getDataNonConst(i);
7055
7056 // //////////////////////////////////////
7057 // Discover A by chunks
7058 // //////////////////////////////////////
7059 for (GO k = 0; k < numChunks; ++k) {
7060 for (size_t j = 0; j < numMVs; ++j) {
7061 // GO curGlobalCol = maxColGid - numMVs + j + TGOT::one();
7062 GO curGlobalCol = minColGid + k * numMVs + j;
7063 globalColsArray.push_back(curGlobalCol);
7064 // TODO extract the g2l map outside of this loop loop
7065 LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
7066 if (curLocalCol != TLOT::invalid()) {
7067 eiData[j][curLocalCol] = TGOT::one();
7068 localColsArray.push_back(curLocalCol);
7069 }
7070 }
7071
7072 // drop host views before apply
7073 for (size_t i = 0; i < numMVs; ++i)
7074 eiData[i] = Teuchos::null;
7075 // probe
7076 A.apply(*ei, *colsA);
7077
7078 colsOnPid0->doImport(*colsA, importer, INSERT);
7079
7080 if (myRank == 0)
7081 globalNnz += writeColumns(os, *colsOnPid0, numMVs, importedGidsData(),
7082 globalColsArray, offsetToUseInPrinting);
7083
7084 // reconstruct dropped eiData
7085 for (size_t i = 0; i < numMVs; ++i)
7086 eiData[i] = ei->getDataNonConst(i);
7087 for (size_t j = 0; j < numMVs; ++j) {
7088 GO curGlobalCol = minColGid + k * numMVs + j;
7089 // TODO extract the g2l map outside of this loop loop
7090 LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
7091 if (curLocalCol != TLOT::invalid()) {
7092 eiData[j][curLocalCol] = TGOT::one();
7093 }
7094 }
7095
7096 // zero out the ei's
7097 for (size_t j = 0; j < numMVs; ++j) {
7098 for (int i = 0; i < localColsArray.size(); ++i)
7099 eiData[j][localColsArray[i]] = TGOT::zero();
7100 }
7101 globalColsArray.clear();
7102 localColsArray.clear();
7103 }
7104
7105 // //////////////////////////////////////
7106 // Handle leftover part of A
7107 // //////////////////////////////////////
7108 if (rem > 0) {
7109 for (int j = 0; j < rem; ++j) {
7110 GO curGlobalCol = maxColGid - rem + j + TGOT::one();
7111 globalColsArray.push_back(curGlobalCol);
7112 // TODO extract the g2l map outside of this loop loop
7113 LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
7114 if (curLocalCol != TLOT::invalid()) {
7115 eiData[j][curLocalCol] = TGOT::one();
7116 localColsArray.push_back(curLocalCol);
7117 }
7118 }
7119
7120 // drop host views before apply
7121 for (size_t i = 0; i < numMVs; ++i)
7122 eiData[i] = Teuchos::null;
7123 // probe
7124 A.apply(*ei, *colsA);
7125
7126 colsOnPid0->doImport(*colsA, importer, INSERT);
7127 if (myRank == 0)
7128 globalNnz += writeColumns(os, *colsOnPid0, rem, importedGidsData(),
7129 globalColsArray, offsetToUseInPrinting);
7130
7131 // reconstruct dropped eiData
7132 for (size_t i = 0; i < numMVs; ++i)
7133 eiData[i] = ei->getDataNonConst(i);
7134 for (int j = 0; j < rem; ++j) {
7135 GO curGlobalCol = maxColGid - rem + j + TGOT::one();
7136 // TODO extract the g2l map outside of this loop loop
7137 LO curLocalCol = domainMap.getLocalElement(curGlobalCol);
7138 if (curLocalCol != TLOT::invalid()) {
7139 eiData[j][curLocalCol] = TGOT::one();
7140 }
7141 }
7142
7143 // zero out the ei's
7144 for (int j = 0; j < rem; ++j) {
7145 for (int i = 0; i < localColsArray.size(); ++i)
7146 eiData[j][localColsArray[i]] = TGOT::zero();
7147 }
7148 globalColsArray.clear();
7149 localColsArray.clear();
7150 }
7151
7152 // Return the Matrix Market header. It includes the header
7153 // line (that starts with "%%"), some comments, and the triple
7154 // of matrix dimensions and number of nonzero entries. We
7155 // don't actually print this here, because we don't know the
7156 // number of nonzero entries until after probing.
7157 std::ostringstream oss;
7158 if (myRank == 0) {
7159 oss << "%%MatrixMarket matrix coordinate ";
7160 if (Teuchos::ScalarTraits<typename operator_type::scalar_type>::isComplex) {
7161 oss << "complex";
7162 } else {
7163 oss << "real";
7164 }
7165 oss << " general" << std::endl;
7166 oss << "% Tpetra::Operator" << std::endl;
7167 std::time_t now = std::time(NULL);
7168 oss << "% time stamp: " << ctime(&now);
7169 oss << "% written from " << numProcs << " processes" << std::endl;
7170 size_t numRows = rangeMap->getGlobalNumElements();
7171 size_t numCols = domainMap.getGlobalNumElements();
7172 oss << numRows << " " << numCols << " " << globalNnz << std::endl;
7173 }
7174
7175 return oss.str();
7176}
7177
7178template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7180MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeColumns(std::ostream& os, mv_type const& colsA, size_t const& numCols,
7181 Teuchos::ArrayView<const typename MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::global_ordinal_type> const& rowGids,
7182 Teuchos::Array<global_ordinal_type> const& colsArray,
7184 typedef global_ordinal_type GO;
7185 typedef Teuchos::ScalarTraits<Scalar> STS;
7186
7187 GO nnz = 0;
7188 const Scalar zero = STS::zero();
7189 const size_t numRows = colsA.getGlobalLength();
7190 for (size_t j = 0; j < numCols; ++j) {
7191 Teuchos::ArrayRCP<const Scalar> const curCol = colsA.getData(j);
7192 const GO J = colsArray[j];
7193 for (size_t i = 0; i < numRows; ++i) {
7194 const Scalar val = curCol[i];
7195 if (val != zero) {
7196 os << rowGids[i] + indexBase << " " << J + indexBase << " " << val << std::endl;
7197 ++nnz;
7198 }
7199 }
7200 }
7201
7202 return nnz;
7203}
7204
7205template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7207 const std::string& filename_suffix,
7208 const typename MatrixMarketWriter<Scalar, LocalOrdinal, GlobalOrdinal, Node>::sparse_matrix_type& matrix,
7209 const std::string& matrixName,
7210 const std::string& matrixDescription,
7211 const int ranksToWriteAtOnce,
7212 const bool debug) {
7213 using ST = scalar_type;
7214 // using LO = local_ordinal_type;
7215 using GO = global_ordinal_type;
7216 using STS = typename Teuchos::ScalarTraits<ST>;
7217 using Teuchos::RCP;
7218
7219 // Sanity Checks
7220 trcp_tcomm_t comm = matrix.getComm();
7221 TEUCHOS_TEST_FOR_EXCEPTION(comm.is_null(), std::invalid_argument,
7222 "The input matrix's communicator (Teuchos::Comm object) is null.");
7223 TEUCHOS_TEST_FOR_EXCEPTION(matrix.isGloballyIndexed() || !matrix.isFillComplete(), std::invalid_argument,
7224 "The input matrix must not be GloballyIndexed and must be fillComplete.");
7225
7226 // Setup
7227 const int myRank = comm->getRank();
7228 const int numProc = comm->getSize();
7229 std::string filename = filename_prefix + std::to_string(myRank) + filename_suffix;
7230 RCP<const map_type> rowMap = matrix.getRowMap();
7231 RCP<const map_type> colMap = matrix.getColMap();
7232 size_t local_nnz = matrix.getLocalNumEntries();
7233 size_t local_num_rows = rowMap->getLocalNumElements();
7234 size_t local_num_cols = colMap->getLocalNumElements();
7235 const GO rowIndexBase = rowMap->getIndexBase();
7236 const GO colIndexBase = colMap->getIndexBase();
7237
7238 // Bounds check the writing limits
7239 int rank_limit = std::min(std::max(ranksToWriteAtOnce, 1), numProc);
7240
7241 // Start the writing
7242 for (int base_rank = 0; base_rank < numProc; base_rank += rank_limit) {
7243 int stop = std::min(base_rank + rank_limit, numProc);
7244
7245 if (base_rank <= myRank && myRank < stop) {
7246 // My turn to write
7247 std::ofstream out(filename);
7248
7249 // MatrixMarket Header
7250 out << "%%MatrixMarket matrix coordinate "
7251 << (STS::isComplex ? "complex" : "real")
7252 << " general" << std::endl;
7253
7254 // Print comments (the matrix name and / or description).
7255 if (matrixName != "") {
7256 printAsComment(out, matrixName);
7257 }
7258 if (matrixDescription != "") {
7259 printAsComment(out, matrixDescription);
7260 }
7261
7262 // Print the Matrix Market header (# local rows, # local columns, #
7263 // local enonzeros). This will *not* be read correctly by a generic matrix
7264 // market reader since we'll be writing out GIDs here and local row/col counts
7265 out << local_num_rows << " " << local_num_cols << " " << local_nnz << std::endl;
7266
7267 {
7268 // Make the output stream write floating-point numbers in
7269 // scientific notation. It will politely put the output
7270 // stream back to its state on input, when this scope
7271 // terminates.
7272 Teuchos::SetScientific<ST> sci(out);
7273
7274 for (size_t l_row = 0; l_row < local_num_rows; l_row++) {
7275 GO g_row = rowMap->getGlobalElement(l_row);
7276
7277 typename sparse_matrix_type::local_inds_host_view_type indices;
7278 typename sparse_matrix_type::values_host_view_type values;
7279 matrix.getLocalRowView(l_row, indices, values);
7280 for (size_t ii = 0; ii < indices.extent(0); ii++) {
7281 const GO g_col = colMap->getGlobalElement(indices(ii));
7282 // Convert row and column indices to 1-based.
7283 // This works because the global index type is signed.
7284 out << (g_row + 1 - rowIndexBase) << " "
7285 << (g_col + 1 - colIndexBase) << " ";
7286 if (STS::isComplex) {
7287 out << STS::real(values(ii)) << " " << STS::imag(values(ii));
7288 } else {
7289 out << values(ii);
7290 }
7291 out << std::endl;
7292 } // For each entry in the current row
7293 } // For each row of the matrix
7294 } // end Teuchos::SetScientfic scoping
7295
7296 out.close();
7297 } // end if base_rank <= myRank < stop
7298
7299 // Barrier after each writing "batch" to make sure we're not hammering the file system
7300 // too aggressively
7301 comm->barrier();
7302
7303 } // end outer loop
7304
7305} // end writeSparsePerRank
7306
7307template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7308template <typename T>
7310 return obj.is_null() ? Teuchos::null : obj->getComm();
7311}
7312
7313template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7315 return comm.is_null() ? 0 : comm->getRank();
7316}
7317
7318} // namespace Tpetra
7319
7320namespace Tpetra {
7321#define TPETRA_MATRIXMARKET_INSTANT(SCALAR, LO, GO, NODE) \
7322 template class MatrixMarketReader<SCALAR, LO, GO, NODE>; \
7323 template class MatrixMarketWriter<SCALAR, LO, GO, NODE>;
7324} // namespace Tpetra
7325
7326#endif
From a distributed map build a map with all GIDs on the root node.
Declaration of a function that prints strings from each process.
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.
bool isGloballyIndexed() const override
Whether the matrix is globally indexed on the calling process.
Teuchos::RCP< const map_type > getDomainMap() const override
The domain Map of this matrix.
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const override
The communicator over which the matrix is distributed.
Teuchos::RCP< const map_type > getRangeMap() const override
The range Map of this matrix.
size_t getLocalNumEntries() const override
The local number of entries in this matrix.
GlobalOrdinal getIndexBase() const override
The index base for global indices for this matrix.
void getLocalRowView(LocalOrdinal LocalRow, local_inds_host_view_type &indices, values_host_view_type &values) const override
Get a constant view of a row of this matrix, using local row and column indices.
Teuchos::RCP< const map_type > getColMap() const override
The Map that describes the column distribution in this matrix.
bool isFillComplete() const override
Whether the matrix is fill complete.
Teuchos::RCP< const map_type > getRowMap() const override
The Map that describes the row distribution in 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...
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const
Accessors for the Teuchos::Comm and Kokkos Node objects.
SparseMatrixType::local_ordinal_type local_ordinal_type
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.
SparseMatrixType::scalar_type scalar_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< 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 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.
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< 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.
static Teuchos::RCP< const map_type > readMapFile(const std::string &filename, const trcp_tcomm_t &comm, const bool tolerant=false, const bool debug=false, const bool binary=false)
Read Map (as a MultiVector) from the given Matrix Market file.
static 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.
Teuchos::RCP< const Teuchos::Comm< int > > trcp_tcomm_t
Type of the MPI communicator.
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< 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.
SparseMatrixType::node_type node_type
The fourth template parameter of CrsMatrix and MultiVector.
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::global_ordinal_type global_ordinal_type
static trcp_tcomm_t getComm(const Teuchos::RCP< T > &obj)
Return obj MPI communicator or Teuchos::null.
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 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 writeOperator(const std::string &fileName, operator_type const &A)
Write a Tpetra::Operator to a file.
Map< local_ordinal_type, global_ordinal_type, node_type > map_type
Specialization of Tpetra::Map that matches SparseMatrixType.
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).
MultiVector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > multivector_type
Specialization of Tpetra::MultiVector that matches SparseMatrixType.
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 int getRank(const trcp_tcomm_t &comm)
Return MPI rank or 0.
static void writeMap(std::ostream &out, const map_type &map, const bool debug=false)
Print the Map to the given output stream.
SparseMatrixType::global_ordinal_type global_ordinal_type
Type of indices as read from the Matrix Market file.
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.
Teuchos::RCP< const Teuchos::Comm< int > > trcp_tcomm_t
Type of the MPI communicator.
static void writeMapFile(const std::string &filename, const map_type &map)
Write the Map to the given file.
SparseMatrixType::local_ordinal_type local_ordinal_type
Type of the local indices of the sparse matrix.
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.
CrsGraph< local_ordinal_type, global_ordinal_type, node_type > crs_graph_type
Specialization of Tpetra::CrsGraph that matches SparseMatrixType.
SparseMatrixType::scalar_type scalar_type
Type of the entries of the sparse matrix.
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.
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.
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...