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