Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
Tpetra_CrsMatrix_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Tpetra: Templated Linear Algebra Services Package
4//
5// Copyright 2008 NTESS and the Tpetra contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef TPETRA_CRSMATRIX_DEF_HPP
11#define TPETRA_CRSMATRIX_DEF_HPP
12
20
23#include "Tpetra_RowMatrix.hpp"
24#include "Tpetra_LocalCrsMatrixOperator.hpp"
25#include "Tpetra_computeRowAndColumnOneNorms.hpp"
27
34#include "Tpetra_Details_getDiagCopyWithoutOffsets.hpp"
42#include "Tpetra_Details_packCrsMatrix.hpp"
43#include "Tpetra_Details_unpackCrsMatrixAndCombine.hpp"
45#include "Teuchos_FancyOStream.hpp"
46#include "Teuchos_RCP.hpp"
47#include "Teuchos_DataAccess.hpp"
48#include "Teuchos_SerialDenseMatrix.hpp" // unused here, could delete
49#include "KokkosBlas1_scal.hpp"
50#include "KokkosSparse_getDiagCopy.hpp"
51#include "KokkosSparse_spmv.hpp"
53
54#include <memory>
55#include <cstring>
56#include <sstream>
57#include <typeinfo>
58#include <utility>
59#include <vector>
60
61namespace Tpetra {
62
63namespace { // (anonymous)
64
65template <class T, class BinaryFunction>
66T atomic_binary_function_update(T* const dest,
67 const T& inputVal,
68 BinaryFunction f) {
69 T oldVal = *dest;
70 T assume;
71
72 // NOTE (mfh 30 Nov 2015) I do NOT need a fence here for IBM
73 // POWER architectures, because 'newval' depends on 'assume',
74 // which depends on 'oldVal', which depends on '*dest'. This
75 // sets up a chain of read dependencies that should ensure
76 // correct behavior given a sane memory model.
77 do {
78 assume = oldVal;
79 T newVal = f(assume, inputVal);
80 oldVal = Kokkos::atomic_compare_exchange(dest, assume, newVal);
81 } while (assume != oldVal);
82
83 return oldVal;
84}
85} // namespace
86
87//
88// Users must never rely on anything in the Details namespace.
89//
90namespace Details {
91
101template <class Scalar>
102struct AbsMax {
104 Scalar operator()(const Scalar& x, const Scalar& y) {
105 typedef Teuchos::ScalarTraits<Scalar> STS;
106 return std::max(STS::magnitude(x), STS::magnitude(y));
107 }
108};
109
110} // namespace Details
111} // namespace Tpetra
112
113namespace Tpetra {
114
115template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
116CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
117 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
118 size_t maxNumEntriesPerRow,
119 const Teuchos::RCP<Teuchos::ParameterList>& params)
120 : dist_object_type(rowMap) {
121 const char tfecfFuncName[] =
122 "CrsMatrix(RCP<const Map>, size_t "
123 "[, RCP<ParameterList>]): ";
124 Teuchos::RCP<crs_graph_type> graph;
125 try {
126 graph = Teuchos::rcp(new crs_graph_type(rowMap, maxNumEntriesPerRow,
127 params));
128 } catch (std::exception& e) {
129 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
130 "CrsGraph constructor (RCP<const Map>, "
131 "size_t [, RCP<ParameterList>]) threw an exception: "
132 << e.what());
133 }
134 // myGraph_ not null means that the matrix owns the graph. That's
135 // different than the const CrsGraph constructor, where the matrix
136 // does _not_ own the graph.
137 myGraph_ = graph;
138 staticGraph_ = myGraph_;
139 resumeFill(params);
141}
142
143template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
144CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
145 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
146 const Teuchos::ArrayView<const size_t>& numEntPerRowToAlloc,
147 const Teuchos::RCP<Teuchos::ParameterList>& params)
148 : dist_object_type(rowMap) {
149 const char tfecfFuncName[] =
150 "CrsMatrix(RCP<const Map>, "
151 "ArrayView<const size_t>[, RCP<ParameterList>]): ";
152 Teuchos::RCP<crs_graph_type> graph;
153 try {
154 using Teuchos::rcp;
155 graph = rcp(new crs_graph_type(rowMap, numEntPerRowToAlloc,
156 params));
157 } catch (std::exception& e) {
158 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
159 "CrsGraph constructor "
160 "(RCP<const Map>, ArrayView<const size_t>"
161 "[, RCP<ParameterList>]) threw an exception: "
162 << e.what());
163 }
164 // myGraph_ not null means that the matrix owns the graph. That's
165 // different than the const CrsGraph constructor, where the matrix
166 // does _not_ own the graph.
167 myGraph_ = graph;
168 staticGraph_ = graph;
169 resumeFill(params);
171}
172
173template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
174CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
175 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
176 const Teuchos::RCP<const map_type>& colMap,
177 const size_t maxNumEntPerRow,
178 const Teuchos::RCP<Teuchos::ParameterList>& params)
179 : dist_object_type(rowMap) {
180 const char tfecfFuncName[] =
181 "CrsMatrix(RCP<const Map>, "
182 "RCP<const Map>, size_t[, RCP<ParameterList>]): ";
183 const char suffix[] =
184 " Please report this bug to the Tpetra developers.";
185
186 // An artifact of debugging something a while back.
187 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!staticGraph_.is_null(), std::logic_error,
188 "staticGraph_ is not null at the beginning of the constructor."
189 << suffix);
190 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!myGraph_.is_null(), std::logic_error,
191 "myGraph_ is not null at the beginning of the constructor."
192 << suffix);
193 Teuchos::RCP<crs_graph_type> graph;
194 try {
195 graph = Teuchos::rcp(new crs_graph_type(rowMap, colMap,
196 maxNumEntPerRow,
197 params));
198 } catch (std::exception& e) {
199 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
200 "CrsGraph constructor (RCP<const Map>, "
201 "RCP<const Map>, size_t[, RCP<ParameterList>]) threw an "
202 "exception: "
203 << e.what());
204 }
205 // myGraph_ not null means that the matrix owns the graph. That's
206 // different than the const CrsGraph constructor, where the matrix
207 // does _not_ own the graph.
208 myGraph_ = graph;
209 staticGraph_ = myGraph_;
210 resumeFill(params);
212}
213
214template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
215CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
216 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
217 const Teuchos::RCP<const map_type>& colMap,
218 const Teuchos::ArrayView<const size_t>& numEntPerRowToAlloc,
219 const Teuchos::RCP<Teuchos::ParameterList>& params)
220 : dist_object_type(rowMap) {
221 const char tfecfFuncName[] =
222 "CrsMatrix(RCP<const Map>, RCP<const Map>, "
223 "ArrayView<const size_t>[, RCP<ParameterList>]): ";
224 Teuchos::RCP<crs_graph_type> graph;
225 try {
226 graph = Teuchos::rcp(new crs_graph_type(rowMap, colMap,
227 numEntPerRowToAlloc,
228 params));
229 } catch (std::exception& e) {
230 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
231 "CrsGraph constructor (RCP<const Map>, "
232 "RCP<const Map>, ArrayView<const size_t>[, "
233 "RCP<ParameterList>]) threw an exception: "
234 << e.what());
235 }
236 // myGraph_ not null means that the matrix owns the graph. That's
237 // different than the const CrsGraph constructor, where the matrix
238 // does _not_ own the graph.
239 myGraph_ = graph;
240 staticGraph_ = graph;
241 resumeFill(params);
243}
244
245template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
246CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
247 CrsMatrix(const Teuchos::RCP<const crs_graph_type>& graph,
248 const Teuchos::RCP<Teuchos::ParameterList>& /* params */)
249 : dist_object_type(graph->getRowMap())
250 , staticGraph_(graph)
251 , storageStatus_(Details::STORAGE_1D_PACKED) {
252 using std::endl;
253 typedef typename local_matrix_device_type::values_type values_type;
254 const char tfecfFuncName[] =
255 "CrsMatrix(RCP<const CrsGraph>[, "
256 "RCP<ParameterList>]): ";
257 const bool verbose = Details::Behavior::verbose("CrsMatrix");
258
259 std::unique_ptr<std::string> prefix;
260 if (verbose) {
261 prefix = this->createPrefix("CrsMatrix", "CrsMatrix(graph,params)");
262 std::ostringstream os;
263 os << *prefix << "Start" << endl;
264 std::cerr << os.str();
265 }
266
267 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(graph.is_null(), std::runtime_error, "Input graph is null.");
268 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!graph->isFillComplete(), std::runtime_error,
269 "Input graph "
270 "is not fill complete. You must call fillComplete on the "
271 "graph before using it to construct a CrsMatrix. Note that "
272 "calling resumeFill on the graph makes it not fill complete, "
273 "even if you had previously called fillComplete. In that "
274 "case, you must call fillComplete on the graph again.");
275
276 // The graph is fill complete, so it is locally indexed and has a
277 // fixed structure. This means we can allocate the (1-D) array of
278 // values and build the local matrix right now. Note that the
279 // local matrix's number of columns comes from the column Map, not
280 // the domain Map.
281
282 const size_t numEnt = graph->lclIndsPacked_wdv.extent(0);
283 if (verbose) {
284 std::ostringstream os;
285 os << *prefix << "Allocate values: " << numEnt << endl;
286 std::cerr << os.str();
287 }
288
289 values_type val("Tpetra::CrsMatrix::values", numEnt);
290 valuesPacked_wdv = values_wdv_type(val);
291 valuesUnpacked_wdv = valuesPacked_wdv;
292
294
295 if (verbose) {
296 std::ostringstream os;
297 os << *prefix << "Done" << endl;
298 std::cerr << os.str();
299 }
300}
301
302template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
303CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
304 CrsMatrix(CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& matrix,
305 const Teuchos::RCP<const crs_graph_type>& graph,
306 const Teuchos::RCP<Teuchos::ParameterList>& params)
307 : dist_object_type(graph->getRowMap())
308 , staticGraph_(graph)
310 const char tfecfFuncName[] =
311 "CrsMatrix(RCP<const CrsGraph>, "
312 "local_matrix_device_type::values_type, "
313 "[,RCP<ParameterList>]): ";
314 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(graph.is_null(), std::runtime_error, "Input graph is null.");
315 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!graph->isFillComplete(), std::runtime_error,
316 "Input graph "
317 "is not fill complete. You must call fillComplete on the "
318 "graph before using it to construct a CrsMatrix. Note that "
319 "calling resumeFill on the graph makes it not fill complete, "
320 "even if you had previously called fillComplete. In that "
321 "case, you must call fillComplete on the graph again.");
322
323 size_t numValuesPacked = graph->lclIndsPacked_wdv.extent(0);
324 valuesPacked_wdv = values_wdv_type(matrix.valuesPacked_wdv, 0, numValuesPacked);
325
326 size_t numValuesUnpacked = graph->lclIndsUnpacked_wdv.extent(0);
327 valuesUnpacked_wdv = values_wdv_type(matrix.valuesUnpacked_wdv, 0, numValuesUnpacked);
328
330}
331
332template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
333CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
334 CrsMatrix(const Teuchos::RCP<const crs_graph_type>& graph,
335 const typename local_matrix_device_type::values_type& values,
336 const Teuchos::RCP<Teuchos::ParameterList>& /* params */)
337 : dist_object_type(graph->getRowMap())
338 , staticGraph_(graph)
339 , storageStatus_(Details::STORAGE_1D_PACKED) {
340 const char tfecfFuncName[] =
341 "CrsMatrix(RCP<const CrsGraph>, "
342 "local_matrix_device_type::values_type, "
343 "[,RCP<ParameterList>]): ";
344 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(graph.is_null(), std::runtime_error, "Input graph is null.");
345 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!graph->isFillComplete(), std::runtime_error,
346 "Input graph "
347 "is not fill complete. You must call fillComplete on the "
348 "graph before using it to construct a CrsMatrix. Note that "
349 "calling resumeFill on the graph makes it not fill complete, "
350 "even if you had previously called fillComplete. In that "
351 "case, you must call fillComplete on the graph again.");
352
353 // The graph is fill complete, so it is locally indexed and has a
354 // fixed structure. This means we can allocate the (1-D) array of
355 // values and build the local matrix right now. Note that the
356 // local matrix's number of columns comes from the column Map, not
357 // the domain Map.
358
359 valuesPacked_wdv = values_wdv_type(values);
360 valuesUnpacked_wdv = valuesPacked_wdv;
361
362 // FIXME (22 Jun 2016) I would very much like to get rid of
363 // k_values1D_ at some point. I find it confusing to have all
364 // these extra references lying around.
365 // KDDKDD ALMOST THERE, MARK!
366 // k_values1D_ = valuesUnpacked_wdv.getDeviceView(Access::ReadWrite);
367
369}
370
371template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
372CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
373 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
374 const Teuchos::RCP<const map_type>& colMap,
375 const typename local_graph_device_type::row_map_type& rowPointers,
376 const typename local_graph_device_type::entries_type::non_const_type& columnIndices,
377 const typename local_matrix_device_type::values_type& values,
378 const Teuchos::RCP<Teuchos::ParameterList>& params)
379 : dist_object_type(rowMap)
380 , storageStatus_(Details::STORAGE_1D_PACKED) {
381 using Details::getEntryOnHost;
382 using std::endl;
383 using Teuchos::RCP;
384 const char tfecfFuncName[] =
385 "Tpetra::CrsMatrix(RCP<const Map>, "
386 "RCP<const Map>, ptr, ind, val[, params]): ";
387 const char suffix[] =
388 ". Please report this bug to the Tpetra developers.";
389 const bool debug = Details::Behavior::debug("CrsMatrix");
390 const bool verbose = Details::Behavior::verbose("CrsMatrix");
391
392 std::unique_ptr<std::string> prefix;
393 if (verbose) {
394 prefix = this->createPrefix(
395 "CrsMatrix", "CrsMatrix(rowMap,colMap,ptr,ind,val[,params])");
396 std::ostringstream os;
397 os << *prefix << "Start" << endl;
398 std::cerr << os.str();
399 }
400
401 // Check the user's input. Note that this might throw only on
402 // some processes but not others, causing deadlock. We prefer
403 // deadlock due to exceptions to segfaults, because users can
404 // catch exceptions.
405 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(values.extent(0) != columnIndices.extent(0),
406 std::invalid_argument, "values.extent(0)=" << values.extent(0) << " != columnIndices.extent(0) = " << columnIndices.extent(0) << ".");
407 if (debug && rowPointers.extent(0) != 0) {
408 const size_t numEnt =
409 getEntryOnHost(rowPointers, rowPointers.extent(0) - 1);
410 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(numEnt != size_t(columnIndices.extent(0)) ||
411 numEnt != size_t(values.extent(0)),
412 std::invalid_argument,
413 "Last entry of rowPointers says that "
414 "the matrix has "
415 << numEnt << " entr"
416 << (numEnt != 1 ? "ies" : "y") << ", but the dimensions of "
417 "columnIndices and values don't match this. "
418 "columnIndices.extent(0)="
419 << columnIndices.extent(0)
420 << " and values.extent(0)=" << values.extent(0) << ".");
421 }
422
423 RCP<crs_graph_type> graph;
424 try {
425 graph = Teuchos::rcp(new crs_graph_type(rowMap, colMap, rowPointers,
426 columnIndices, params));
427 } catch (std::exception& e) {
428 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
429 "CrsGraph constructor (RCP<const Map>, "
430 "RCP<const Map>, ptr, ind[, params]) threw an exception: "
431 << e.what());
432 }
433 // The newly created CrsGraph _must_ have a local graph at this
434 // point. We don't really care whether CrsGraph's constructor
435 // deep-copies or shallow-copies the input, but the dimensions
436 // have to be right. That's how we tell whether the CrsGraph has
437 // a local graph.
438 auto lclGraph = graph->getLocalGraphDevice();
439 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(lclGraph.row_map.extent(0) != rowPointers.extent(0) ||
440 lclGraph.entries.extent(0) != columnIndices.extent(0),
441 std::logic_error,
442 "CrsGraph's constructor (rowMap, colMap, ptr, "
443 "ind[, params]) did not set the local graph correctly."
444 << suffix);
445 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(lclGraph.entries.extent(0) != values.extent(0),
446 std::logic_error,
447 "CrsGraph's constructor (rowMap, colMap, ptr, ind[, "
448 "params]) did not set the local graph correctly. "
449 "lclGraph.entries.extent(0) = "
450 << lclGraph.entries.extent(0)
451 << " != values.extent(0) = " << values.extent(0) << suffix);
452
453 // myGraph_ not null means that the matrix owns the graph. This
454 // is true because the column indices come in as nonconst,
455 // implying shared ownership.
456 myGraph_ = graph;
457 staticGraph_ = graph;
458
459 // The graph may not be fill complete yet. However, it is locally
460 // indexed (since we have a column Map) and has a fixed structure
461 // (due to the input arrays). This means we can allocate the
462 // (1-D) array of values and build the local matrix right now.
463 // Note that the local matrix's number of columns comes from the
464 // column Map, not the domain Map.
465
466 valuesPacked_wdv = values_wdv_type(values);
467 valuesUnpacked_wdv = valuesPacked_wdv;
468
469 // FIXME (22 Jun 2016) I would very much like to get rid of
470 // k_values1D_ at some point. I find it confusing to have all
471 // these extra references lying around.
472 // this->k_values1D_ = valuesPacked_wdv.getDeviceView(Access::ReadWrite);
473
475 if (verbose) {
476 std::ostringstream os;
477 os << *prefix << "Done" << endl;
478 std::cerr << os.str();
479 }
480}
481
482template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
483CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
484 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
485 const Teuchos::RCP<const map_type>& colMap,
486 const Teuchos::ArrayRCP<size_t>& ptr,
487 const Teuchos::ArrayRCP<LocalOrdinal>& ind,
488 const Teuchos::ArrayRCP<Scalar>& val,
489 const Teuchos::RCP<Teuchos::ParameterList>& params)
490 : dist_object_type(rowMap)
491 , storageStatus_(Details::STORAGE_1D_PACKED) {
492 using Kokkos::Compat::getKokkosViewDeepCopy;
493 using Teuchos::av_reinterpret_cast;
494 using Teuchos::RCP;
495 using values_type = typename local_matrix_device_type::values_type;
496 using IST = impl_scalar_type;
497 const char tfecfFuncName[] =
498 "Tpetra::CrsMatrix(RCP<const Map>, "
499 "RCP<const Map>, ptr, ind, val[, params]): ";
500
501 RCP<crs_graph_type> graph;
502 try {
503 graph = Teuchos::rcp(new crs_graph_type(rowMap, colMap, ptr,
504 ind, params));
505 } catch (std::exception& e) {
506 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
507 "CrsGraph constructor (RCP<const Map>, "
508 "RCP<const Map>, ArrayRCP<size_t>, ArrayRCP<LocalOrdinal>[, "
509 "RCP<ParameterList>]) threw an exception: "
510 << e.what());
511 }
512 // myGraph_ not null means that the matrix owns the graph. This
513 // is true because the column indices come in as nonconst,
514 // implying shared ownership.
515 myGraph_ = graph;
516 staticGraph_ = graph;
517
518 // The graph may not be fill complete yet. However, it is locally
519 // indexed (since we have a column Map) and has a fixed structure
520 // (due to the input arrays). This means we can allocate the
521 // (1-D) array of values and build the local matrix right now.
522 // Note that the local matrix's number of columns comes from the
523 // column Map, not the domain Map.
524
525 // The graph _must_ have a local graph at this point. We don't
526 // really care whether CrsGraph's constructor deep-copies or
527 // shallow-copies the input, but the dimensions have to be right.
528 // That's how we tell whether the CrsGraph has a local graph.
529 auto lclGraph = staticGraph_->getLocalGraphDevice();
530 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(size_t(lclGraph.row_map.extent(0)) != size_t(ptr.size()) ||
531 size_t(lclGraph.entries.extent(0)) != size_t(ind.size()),
532 std::logic_error,
533 "CrsGraph's constructor (rowMap, colMap, "
534 "ptr, ind[, params]) did not set the local graph correctly. "
535 "Please report this bug to the Tpetra developers.");
536
537 values_type valIn =
538 getKokkosViewDeepCopy<device_type>(av_reinterpret_cast<IST>(val()));
539 valuesPacked_wdv = values_wdv_type(valIn);
540 valuesUnpacked_wdv = valuesPacked_wdv;
541
542 // FIXME (22 Jun 2016) I would very much like to get rid of
543 // k_values1D_ at some point. I find it confusing to have all
544 // these extra references lying around.
545 // this->k_values1D_ = valuesPacked_wdv.getDeviceView(Access::ReadWrite);
546
548}
549
550template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
551CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
552 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
553 const Teuchos::RCP<const map_type>& colMap,
554 const local_matrix_device_type& lclMatrix,
555 const Teuchos::RCP<Teuchos::ParameterList>& params)
556 : dist_object_type(rowMap)
557 , storageStatus_(Details::STORAGE_1D_PACKED)
558 , fillComplete_(true) {
559 const char tfecfFuncName[] =
560 "Tpetra::CrsMatrix(RCP<const Map>, "
561 "RCP<const Map>, local_matrix_device_type[, RCP<ParameterList>]): ";
562 const char suffix[] =
563 " Please report this bug to the Tpetra developers.";
564
565 Teuchos::RCP<crs_graph_type> graph;
566 try {
567 graph = Teuchos::rcp(new crs_graph_type(rowMap, colMap,
568 lclMatrix.graph, params));
569 } catch (std::exception& e) {
570 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
571 "CrsGraph constructor (RCP<const Map>, "
572 "RCP<const Map>, local_graph_device_type[, RCP<ParameterList>]) threw an "
573 "exception: "
574 << e.what());
575 }
576 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!graph->isFillComplete(), std::logic_error,
577 "CrsGraph constructor (RCP"
578 "<const Map>, RCP<const Map>, local_graph_device_type[, RCP<ParameterList>]) "
579 "did not produce a fill-complete graph. Please report this bug to the "
580 "Tpetra developers.");
581 // myGraph_ not null means that the matrix owns the graph. This
582 // is true because the column indices come in as nonconst through
583 // the matrix, implying shared ownership.
584 myGraph_ = graph;
585 staticGraph_ = graph;
586
587 valuesPacked_wdv = values_wdv_type(lclMatrix.values);
588 valuesUnpacked_wdv = valuesPacked_wdv;
589
590 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(isFillActive(), std::logic_error,
591 "At the end of a CrsMatrix constructor that should produce "
592 "a fillComplete matrix, isFillActive() is true."
593 << suffix);
594 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillComplete(), std::logic_error,
595 "At the end of a "
596 "CrsMatrix constructor that should produce a fillComplete "
597 "matrix, isFillComplete() is false."
598 << suffix);
600}
601
602template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
603CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
604 CrsMatrix(const local_matrix_device_type& lclMatrix,
605 const Teuchos::RCP<const map_type>& rowMap,
606 const Teuchos::RCP<const map_type>& colMap,
607 const Teuchos::RCP<const map_type>& domainMap,
608 const Teuchos::RCP<const map_type>& rangeMap,
609 const Teuchos::RCP<Teuchos::ParameterList>& params)
610 : dist_object_type(rowMap)
611 , storageStatus_(Details::STORAGE_1D_PACKED)
612 , fillComplete_(true) {
613 const char tfecfFuncName[] =
614 "Tpetra::CrsMatrix(RCP<const Map>, "
615 "RCP<const Map>, RCP<const Map>, RCP<const Map>, "
616 "local_matrix_device_type[, RCP<ParameterList>]): ";
617 const char suffix[] =
618 " Please report this bug to the Tpetra developers.";
619
620 Teuchos::RCP<crs_graph_type> graph;
621 try {
622 graph = Teuchos::rcp(new crs_graph_type(lclMatrix.graph, rowMap, colMap,
623 domainMap, rangeMap, params));
624 } catch (std::exception& e) {
625 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
626 "CrsGraph constructor (RCP<const Map>, "
627 "RCP<const Map>, RCP<const Map>, RCP<const Map>, local_graph_device_type[, "
628 "RCP<ParameterList>]) threw an exception: "
629 << e.what());
630 }
631 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!graph->isFillComplete(), std::logic_error,
632 "CrsGraph "
633 "constructor (RCP<const Map>, RCP<const Map>, RCP<const Map>, "
634 "RCP<const Map>, local_graph_device_type[, RCP<ParameterList>]) did "
635 "not produce a fillComplete graph."
636 << suffix);
637 // myGraph_ not null means that the matrix owns the graph. This
638 // is true because the column indices come in as nonconst through
639 // the matrix, implying shared ownership.
640 myGraph_ = graph;
641 staticGraph_ = graph;
642
643 valuesPacked_wdv = values_wdv_type(lclMatrix.values);
644 valuesUnpacked_wdv = valuesPacked_wdv;
645
646 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(isFillActive(), std::logic_error,
647 "At the end of a CrsMatrix constructor that should produce "
648 "a fillComplete matrix, isFillActive() is true."
649 << suffix);
650 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillComplete(), std::logic_error,
651 "At the end of a "
652 "CrsMatrix constructor that should produce a fillComplete "
653 "matrix, isFillComplete() is false."
654 << suffix);
656}
657
658template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
659CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
660 CrsMatrix(const local_matrix_device_type& lclMatrix,
661 const Teuchos::RCP<const map_type>& rowMap,
662 const Teuchos::RCP<const map_type>& colMap,
663 const Teuchos::RCP<const map_type>& domainMap,
664 const Teuchos::RCP<const map_type>& rangeMap,
665 const Teuchos::RCP<const import_type>& importer,
666 const Teuchos::RCP<const export_type>& exporter,
667 const Teuchos::RCP<Teuchos::ParameterList>& params)
668 : dist_object_type(rowMap)
669 , storageStatus_(Details::STORAGE_1D_PACKED)
670 , fillComplete_(true) {
671 using Teuchos::rcp;
672 const char tfecfFuncName[] =
673 "Tpetra::CrsMatrix"
674 "(lclMat,Map,Map,Map,Map,Import,Export,params): ";
675 const char suffix[] =
676 " Please report this bug to the Tpetra developers.";
677
678 Teuchos::RCP<crs_graph_type> graph;
679 try {
680 graph = rcp(new crs_graph_type(lclMatrix.graph, rowMap, colMap,
681 domainMap, rangeMap, importer,
682 exporter, params));
683 } catch (std::exception& e) {
684 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
685 "CrsGraph constructor "
686 "(local_graph_device_type, Map, Map, Map, Map, Import, Export, "
687 "params) threw: "
688 << e.what());
689 }
690 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!graph->isFillComplete(), std::logic_error,
691 "CrsGraph "
692 "constructor (local_graph_device_type, Map, Map, Map, Map, Import, "
693 "Export, params) did not produce a fill-complete graph. "
694 "Please report this bug to the Tpetra developers.");
695 // myGraph_ not null means that the matrix owns the graph. This
696 // is true because the column indices come in as nonconst through
697 // the matrix, implying shared ownership.
698 myGraph_ = graph;
699 staticGraph_ = graph;
700
701 valuesPacked_wdv = values_wdv_type(lclMatrix.values);
702 valuesUnpacked_wdv = valuesPacked_wdv;
703
704 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(isFillActive(), std::logic_error,
705 "At the end of a CrsMatrix constructor that should produce "
706 "a fillComplete matrix, isFillActive() is true."
707 << suffix);
708 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillComplete(), std::logic_error,
709 "At the end of a "
710 "CrsMatrix constructor that should produce a fillComplete "
711 "matrix, isFillComplete() is false."
712 << suffix);
714}
715
716template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
717CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
718 CrsMatrix(const Teuchos::RCP<const map_type>& rowMap,
719 const Teuchos::RCP<const map_type>& colMap,
720 const CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& source,
721 const Teuchos::RCP<Teuchos::ParameterList>& params)
722 : dist_object_type(rowMap)
723 , storageStatus_(Details::STORAGE_1D_PACKED)
724 , fillComplete_(true) {
725 const char tfecfFuncName[] =
726 "Tpetra::CrsMatrix(RCP<const Map>, "
727 "RCP<const Map>, CrsMatrix[, RCP<ParameterList>]): ";
728 const char suffix[] =
729 " Please report this bug to the Tpetra developers.";
730
731 Teuchos::RCP<crs_graph_type> graph = Teuchos::rcp(new crs_graph_type(rowMap, colMap, *source.getCrsGraph(), params));
732 if (!graph->isFillComplete()) {
733 graph->fillComplete();
734 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!graph->isFillComplete(), std::logic_error,
735 "CrsGraph constructor (RCP"
736 "<const Map>, RCP<const Map>, local_graph_device_type[, RCP<ParameterList>]) "
737 "did not produce a fill-complete graph. Please report this bug to the "
738 "Tpetra developers.");
739 }
740 // myGraph_ not null means that the matrix owns the graph. This
741 // is true because the column indices come in as nonconst through
742 // the matrix, implying shared ownership.
743 myGraph_ = graph;
744 staticGraph_ = graph;
745
746 valuesPacked_wdv = values_wdv_type(source.valuesPacked_wdv);
747 valuesUnpacked_wdv = values_wdv_type(source.valuesUnpacked_wdv);
748
749 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(isFillActive(), std::logic_error,
750 "At the end of a CrsMatrix constructor that should produce "
751 "a fillComplete matrix, isFillActive() is true."
752 << suffix);
753 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillComplete(), std::logic_error,
754 "At the end of a "
755 "CrsMatrix constructor that should produce a fillComplete "
756 "matrix, isFillComplete() is false."
757 << suffix);
759}
760
761template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
762CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
763 CrsMatrix(const CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& source,
764 const Teuchos::DataAccess copyOrView)
765 : dist_object_type(source.getCrsGraph()->getRowMap())
766 , staticGraph_(source.getCrsGraph())
768 const char tfecfFuncName[] =
769 "Tpetra::CrsMatrix("
770 "const CrsMatrix&, const Teuchos::DataAccess): ";
771 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!source.isFillComplete(), std::invalid_argument,
772 "Source graph must be fillComplete().");
773
774 if (copyOrView == Teuchos::Copy) {
775 using values_type = typename local_matrix_device_type::values_type;
776 auto vals = source.getLocalValuesDevice(Access::ReadOnly);
777 using Kokkos::view_alloc;
778 using Kokkos::WithoutInitializing;
779 values_type newvals(view_alloc("val", WithoutInitializing),
780 vals.extent(0));
781 // DEEP_COPY REVIEW - DEVICE-TO_DEVICE
782 Kokkos::deep_copy(newvals, vals);
783 valuesPacked_wdv = values_wdv_type(newvals);
784 valuesUnpacked_wdv = valuesPacked_wdv;
785 fillComplete(source.getDomainMap(), source.getRangeMap());
786 } else if (copyOrView == Teuchos::View) {
787 valuesPacked_wdv = values_wdv_type(source.valuesPacked_wdv);
788 valuesUnpacked_wdv = values_wdv_type(source.valuesUnpacked_wdv);
789 fillComplete(source.getDomainMap(), source.getRangeMap());
790 } else {
791 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::invalid_argument,
792 "Second argument 'copyOrView' "
793 "has an invalid value "
794 << copyOrView << ". Valid values "
795 "include Teuchos::Copy = "
796 << Teuchos::Copy << " and "
797 "Teuchos::View = "
798 << Teuchos::View << ".");
799 }
801}
802
803template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
805 swap(CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& crs_matrix) {
806 std::swap(crs_matrix.importMV_, this->importMV_);
807 std::swap(crs_matrix.exportMV_, this->exportMV_);
808 std::swap(crs_matrix.staticGraph_, this->staticGraph_);
809 std::swap(crs_matrix.myGraph_, this->myGraph_);
810 std::swap(crs_matrix.valuesPacked_wdv, this->valuesPacked_wdv);
811 std::swap(crs_matrix.valuesUnpacked_wdv, this->valuesUnpacked_wdv);
812 std::swap(crs_matrix.storageStatus_, this->storageStatus_);
813 std::swap(crs_matrix.fillComplete_, this->fillComplete_);
814 std::swap(crs_matrix.nonlocals_, this->nonlocals_);
815}
816
817template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
818Teuchos::RCP<const Teuchos::Comm<int>>
820 getComm() const {
821 return getCrsGraphRef().getComm();
822}
823
824template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
829
830template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
835
836template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
838 isStorageOptimized() const {
839 return this->getCrsGraphRef().isStorageOptimized();
840}
841
842template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
844 isLocallyIndexed() const {
845 return getCrsGraphRef().isLocallyIndexed();
846}
847
848template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
850 isGloballyIndexed() const {
851 return getCrsGraphRef().isGloballyIndexed();
852}
853
854template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
856 hasColMap() const {
857 return getCrsGraphRef().hasColMap();
858}
859
860template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
863 getGlobalNumEntries() const {
864 return getCrsGraphRef().getGlobalNumEntries();
865}
866
867template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
868size_t
870 getLocalNumEntries() const {
871 return getCrsGraphRef().getLocalNumEntries();
872}
873
874template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
877 getGlobalNumRows() const {
878 return getCrsGraphRef().getGlobalNumRows();
879}
880
881template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
884 getGlobalNumCols() const {
885 return getCrsGraphRef().getGlobalNumCols();
886}
887
888template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
889size_t
891 getLocalNumRows() const {
892 return getCrsGraphRef().getLocalNumRows();
893}
894
895template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
896size_t
898 getLocalNumCols() const {
899 return getCrsGraphRef().getLocalNumCols();
900}
901
902template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
903size_t
905 getNumEntriesInGlobalRow(GlobalOrdinal globalRow) const {
906 return getCrsGraphRef().getNumEntriesInGlobalRow(globalRow);
907}
908
909template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
910size_t
912 getNumEntriesInLocalRow(LocalOrdinal localRow) const {
913 return getCrsGraphRef().getNumEntriesInLocalRow(localRow);
914}
915
916template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
917size_t
920 return getCrsGraphRef().getGlobalMaxNumRowEntries();
921}
922
923template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
924size_t
927 return getCrsGraphRef().getLocalMaxNumRowEntries();
928}
929
930template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
931GlobalOrdinal
936
937template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
938Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>
940 getRowMap() const {
941 return getCrsGraphRef().getRowMap();
942}
943
944template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
945Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>
947 getColMap() const {
948 return getCrsGraphRef().getColMap();
949}
950
951template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
952Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>
954 getDomainMap() const {
955 return getCrsGraphRef().getDomainMap();
956}
957
958template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
959Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>
961 getRangeMap() const {
962 return getCrsGraphRef().getRangeMap();
963}
964
965template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
966Teuchos::RCP<const RowGraph<LocalOrdinal, GlobalOrdinal, Node>>
968 getGraph() const {
969 if (staticGraph_ != Teuchos::null) {
970 return staticGraph_;
971 }
972 return myGraph_;
973}
975template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
976Teuchos::RCP<const CrsGraph<LocalOrdinal, GlobalOrdinal, Node>>
978 getCrsGraph() const {
979 if (staticGraph_ != Teuchos::null) {
980 return staticGraph_;
981 }
982 return myGraph_;
983}
984
985template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
988 getCrsGraphRef() const {
989#ifdef HAVE_TPETRA_DEBUG
990 constexpr bool debug = true;
991#else
992 constexpr bool debug = false;
993#endif // HAVE_TPETRA_DEBUG
994
995 if (!this->staticGraph_.is_null()) {
996 return *(this->staticGraph_);
997 } else {
998 if (debug) {
999 const char tfecfFuncName[] = "getCrsGraphRef: ";
1000 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->myGraph_.is_null(), std::logic_error,
1001 "Both staticGraph_ and myGraph_ are null. "
1002 "Please report this bug to the Tpetra developers.");
1003 }
1004 return *(this->myGraph_);
1005 }
1006}
1007
1008template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1011 getLocalMatrixDevice() const {
1012 auto numCols = staticGraph_->getColMap()->getLocalNumElements();
1013 return local_matrix_device_type("Tpetra::CrsMatrix::lclMatrixDevice",
1014 numCols,
1015 valuesPacked_wdv.getDeviceView(Access::ReadWrite),
1016 staticGraph_->getLocalGraphDevice());
1017}
1018
1019template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1020typename CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_host_type
1022 getLocalMatrixHost() const {
1023 auto numCols = staticGraph_->getColMap()->getLocalNumElements();
1024 return local_matrix_host_type("Tpetra::CrsMatrix::lclMatrixHost", numCols,
1025 valuesPacked_wdv.getHostView(Access::ReadWrite),
1026 staticGraph_->getLocalGraphHost());
1027}
1028
1029template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1031 isStaticGraph() const {
1032 return myGraph_.is_null();
1033}
1034
1035template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1040
1041template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1046
1047template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1049 allocateValues(ELocalGlobal lg, GraphAllocationStatus gas,
1050 const bool verbose) {
1051 using Details::Behavior;
1053 using std::endl;
1054 const char tfecfFuncName[] = "allocateValues: ";
1055 const char suffix[] =
1056 " Please report this bug to the Tpetra developers.";
1057 ProfilingRegion region("Tpetra::CrsMatrix::allocateValues");
1058
1059 std::unique_ptr<std::string> prefix;
1060 if (verbose) {
1061 prefix = this->createPrefix("CrsMatrix", "allocateValues");
1062 std::ostringstream os;
1063 os << *prefix << "lg: "
1064 << (lg == LocalIndices ? "Local" : "Global") << "Indices"
1065 << ", gas: Graph"
1066 << (gas == GraphAlreadyAllocated ? "Already" : "NotYet")
1067 << "Allocated" << endl;
1068 std::cerr << os.str();
1069 }
1070
1071 const bool debug = Behavior::debug("CrsMatrix");
1072 if (debug) {
1073 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->staticGraph_.is_null(), std::logic_error,
1074 "staticGraph_ is null." << suffix);
1075
1076 // If the graph indices are already allocated, then gas should be
1077 // GraphAlreadyAllocated. Otherwise, gas should be
1078 // GraphNotYetAllocated.
1079 if ((gas == GraphAlreadyAllocated) !=
1080 staticGraph_->indicesAreAllocated()) {
1081 const char err1[] =
1082 "The caller has asserted that the graph "
1083 "is ";
1084 const char err2[] =
1085 "already allocated, but the static graph "
1086 "says that its indices are ";
1087 const char err3[] = "already allocated. ";
1088 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(gas == GraphAlreadyAllocated &&
1089 !staticGraph_->indicesAreAllocated(),
1090 std::logic_error,
1091 err1 << err2 << "not " << err3 << suffix);
1092 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(gas != GraphAlreadyAllocated &&
1093 staticGraph_->indicesAreAllocated(),
1094 std::logic_error,
1095 err1 << "not " << err2 << err3 << suffix);
1096 }
1097
1098 // If the graph is unallocated, then it had better be a
1099 // matrix-owned graph. ("Matrix-owned graph" means that the
1100 // matrix gets to define the graph structure. If the CrsMatrix
1101 // constructor that takes an RCP<const CrsGraph> was used, then
1102 // the matrix does _not_ own the graph.)
1103 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!this->staticGraph_->indicesAreAllocated() &&
1104 this->myGraph_.is_null(),
1105 std::logic_error,
1106 "The static graph says that its indices are not allocated, "
1107 "but the graph is not owned by the matrix."
1108 << suffix);
1109 }
1110
1111 if (gas == GraphNotYetAllocated) {
1112 if (debug) {
1113 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->myGraph_.is_null(), std::logic_error,
1114 "gas = GraphNotYetAllocated, but myGraph_ is null." << suffix);
1115 }
1116 try {
1117 this->myGraph_->allocateIndices(lg, verbose);
1118 } catch (std::exception& e) {
1119 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
1120 "CrsGraph::allocateIndices "
1121 "threw an exception: "
1122 << e.what());
1123 } catch (...) {
1124 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
1125 "CrsGraph::allocateIndices "
1126 "threw an exception not a subclass of std::exception.");
1127 }
1128 }
1129
1130 // Allocate matrix values.
1131 const size_t lclTotalNumEntries = this->staticGraph_->getLocalAllocationSize();
1132 if (debug) {
1133 const size_t lclNumRows = this->staticGraph_->getLocalNumRows();
1134 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->staticGraph_->getRowPtrsUnpackedHost()(lclNumRows) != lclTotalNumEntries, std::logic_error,
1135 "length of staticGraph's lclIndsUnpacked does not match final entry of rowPtrsUnapcked_host." << suffix);
1136 }
1137
1138 // Allocate array of (packed???) matrix values.
1139 using values_type = typename local_matrix_device_type::values_type;
1140 if (verbose) {
1141 std::ostringstream os;
1142 os << *prefix << "Allocate values_wdv: Pre "
1143 << valuesUnpacked_wdv.extent(0) << ", post "
1144 << lclTotalNumEntries << endl;
1145 std::cerr << os.str();
1146 }
1147 // this->k_values1D_ =
1148 valuesUnpacked_wdv = values_wdv_type(
1149 values_type("Tpetra::CrsMatrix::values",
1150 lclTotalNumEntries));
1151}
1152
1153template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1155 fillLocalGraphAndMatrix(const Teuchos::RCP<Teuchos::ParameterList>& params) {
1156 using std::endl;
1157 using Teuchos::arcp_const_cast;
1158 using Teuchos::Array;
1159 using Teuchos::ArrayRCP;
1160 using Teuchos::null;
1161 using Teuchos::RCP;
1162 using Teuchos::rcp;
1164 using ::Tpetra::Details::getEntryOnHost;
1165 using row_map_type = typename local_graph_device_type::row_map_type;
1166 using lclinds_1d_type = typename Graph::local_graph_device_type::entries_type::non_const_type;
1167 using values_type = typename local_matrix_device_type::values_type;
1168 Details::ProfilingRegion regionFLGAM("Tpetra::CrsMatrix::fillLocalGraphAndMatrix");
1169
1170 const char tfecfFuncName[] =
1171 "fillLocalGraphAndMatrix (called from "
1172 "fillComplete or expertStaticFillComplete): ";
1173 const char suffix[] =
1174 " Please report this bug to the Tpetra developers.";
1175 const bool debug = Details::Behavior::debug("CrsMatrix");
1176 const bool verbose = Details::Behavior::verbose("CrsMatrix");
1177
1178 std::unique_ptr<std::string> prefix;
1179 if (verbose) {
1180 prefix = this->createPrefix("CrsMatrix", "fillLocalGraphAndMatrix");
1181 std::ostringstream os;
1182 os << *prefix << endl;
1183 std::cerr << os.str();
1184 }
1185
1186 if (debug) {
1187 // fillComplete() only calls fillLocalGraphAndMatrix() if the
1188 // matrix owns the graph, which means myGraph_ is not null.
1189 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(myGraph_.is_null(), std::logic_error,
1190 "The nonconst graph "
1191 "(myGraph_) is null. This means that the matrix has a "
1192 "const (a.k.a. \"static\") graph. fillComplete or "
1193 "expertStaticFillComplete should never call "
1194 "fillLocalGraphAndMatrix in that case."
1195 << suffix);
1196 }
1197
1198 const size_t lclNumRows = this->getLocalNumRows();
1199
1200 // This method's goal is to fill in the three arrays (compressed
1201 // sparse row format) that define the sparse graph's and matrix's
1202 // structure, and the sparse matrix's values.
1203 //
1204 // Get references to the data in myGraph_, so we can modify them
1205 // as well. Note that we only call fillLocalGraphAndMatrix() if
1206 // the matrix owns the graph, which means myGraph_ is not null.
1207
1208 // NOTE: This does not work correctly w/ GCC 12.3 + CUDA due to a compiler bug.
1209 // See: https://github.com/trilinos/Trilinos/issues/12237
1210 // using row_entries_type = decltype (myGraph_->k_numRowEntries_);
1211 using row_entries_type = typename crs_graph_type::num_row_entries_type;
1212
1213 typename Graph::local_graph_device_type::row_map_type curRowOffsets =
1214 myGraph_->rowPtrsUnpacked_dev_;
1215
1216 if (debug) {
1217 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(curRowOffsets.extent(0) == 0, std::logic_error,
1218 "curRowOffsets.extent(0) == 0.");
1219 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(curRowOffsets.extent(0) != lclNumRows + 1, std::logic_error,
1220 "curRowOffsets.extent(0) = "
1221 << curRowOffsets.extent(0) << " != lclNumRows + 1 = "
1222 << (lclNumRows + 1) << ".");
1223 const size_t numOffsets = curRowOffsets.extent(0);
1224 const auto valToCheck = myGraph_->getRowPtrsUnpackedHost()(numOffsets - 1);
1225 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(numOffsets != 0 &&
1226 myGraph_->lclIndsUnpacked_wdv.extent(0) != valToCheck,
1227 std::logic_error, "numOffsets = " << numOffsets << " != 0 and myGraph_->lclIndsUnpacked_wdv.extent(0) = " << myGraph_->lclIndsUnpacked_wdv.extent(0) << " != curRowOffsets(" << numOffsets << ") = " << valToCheck << ".");
1228 }
1229
1230 if (myGraph_->getLocalNumEntries() !=
1231 myGraph_->getLocalAllocationSize()) {
1232 // Use the nonconst version of row_map_type for k_ptrs,
1233 // because row_map_type is const and we need to modify k_ptrs here.
1234 typename row_map_type::non_const_type k_ptrs;
1235 row_map_type k_ptrs_const;
1236 lclinds_1d_type k_inds;
1237 values_type k_vals;
1238
1239 if (verbose) {
1240 std::ostringstream os;
1241 const auto numEnt = myGraph_->getLocalNumEntries();
1242 const auto allocSize = myGraph_->getLocalAllocationSize();
1243 os << *prefix << "Unpacked 1-D storage: numEnt=" << numEnt
1244 << ", allocSize=" << allocSize << endl;
1245 std::cerr << os.str();
1246 }
1247 // The matrix's current 1-D storage is "unpacked." This means
1248 // the row offsets may differ from what the final row offsets
1249 // should be. This could happen, for example, if the user
1250 // set an upper
1251 // bound on the number of entries per row, but didn't fill all
1252 // those entries.
1253 if (debug && curRowOffsets.extent(0) != 0) {
1254 const size_t numOffsets =
1255 static_cast<size_t>(curRowOffsets.extent(0));
1256 const auto valToCheck = myGraph_->getRowPtrsUnpackedHost()(numOffsets - 1);
1257 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(valToCheck) !=
1258 static_cast<size_t>(valuesUnpacked_wdv.extent(0)),
1259 std::logic_error,
1260 "(unpacked branch) Before "
1261 "allocating or packing, curRowOffsets("
1262 << (numOffsets - 1)
1263 << ") = " << valToCheck << " != valuesUnpacked_wdv.extent(0)"
1264 " = "
1265 << valuesUnpacked_wdv.extent(0) << ".");
1266 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(valToCheck) !=
1267 static_cast<size_t>(myGraph_->lclIndsUnpacked_wdv.extent(0)),
1268 std::logic_error,
1269 "(unpacked branch) Before "
1270 "allocating or packing, curRowOffsets("
1271 << (numOffsets - 1)
1272 << ") = " << valToCheck
1273 << " != myGraph_->lclIndsUnpacked_wdv.extent(0) = "
1274 << myGraph_->lclIndsUnpacked_wdv.extent(0) << ".");
1275 }
1276 // Pack the row offsets into k_ptrs, by doing a sum-scan of
1277 // the array of valid entry counts per row.
1278
1279 // Total number of entries in the matrix on the calling
1280 // process. We will compute this in the loop below. It's
1281 // cheap to compute and useful as a sanity check.
1282 size_t lclTotalNumEntries = 0;
1283 {
1284 // Allocate the packed row offsets array. We use a nonconst
1285 // temporary (packedRowOffsets) here, because k_ptrs is
1286 // const. We will assign packedRowOffsets to k_ptrs below.
1287 if (verbose) {
1288 std::ostringstream os;
1289 os << *prefix << "Allocate packed row offsets: "
1290 << (lclNumRows + 1) << endl;
1291 std::cerr << os.str();
1292 }
1293 typename row_map_type::non_const_type
1294 packedRowOffsets("Tpetra::CrsGraph::ptr", lclNumRows + 1);
1295 typename row_entries_type::const_type numRowEnt_h =
1296 myGraph_->k_numRowEntries_;
1297 // We're computing offsets on device. This function can
1298 // handle numRowEnt_h being a host View.
1299 lclTotalNumEntries =
1300 computeOffsetsFromCounts(packedRowOffsets, numRowEnt_h);
1301 // packedRowOffsets is modifiable; k_ptrs isn't, so we have
1302 // to use packedRowOffsets in the loop above and assign here.
1303 k_ptrs = packedRowOffsets;
1304 k_ptrs_const = k_ptrs;
1305 }
1306
1307 if (debug) {
1308 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(k_ptrs.extent(0)) != lclNumRows + 1,
1309 std::logic_error,
1310 "(unpacked branch) After packing k_ptrs, "
1311 "k_ptrs.extent(0) = "
1312 << k_ptrs.extent(0) << " != "
1313 "lclNumRows+1 = "
1314 << (lclNumRows + 1) << ".");
1315 const auto valToCheck = getEntryOnHost(k_ptrs, lclNumRows);
1316 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(valToCheck != lclTotalNumEntries, std::logic_error,
1317 "(unpacked branch) After filling k_ptrs, "
1318 "k_ptrs(lclNumRows="
1319 << lclNumRows << ") = " << valToCheck
1320 << " != total number of entries on the calling process = "
1321 << lclTotalNumEntries << ".");
1322 }
1323
1324 // Allocate the arrays of packed column indices and values.
1325 if (verbose) {
1326 std::ostringstream os;
1327 os << *prefix << "Allocate packed local column indices: "
1328 << lclTotalNumEntries << endl;
1329 std::cerr << os.str();
1330 }
1331 k_inds = lclinds_1d_type("Tpetra::CrsGraph::lclInds", lclTotalNumEntries);
1332 if (verbose) {
1333 std::ostringstream os;
1334 os << *prefix << "Allocate packed values: "
1335 << lclTotalNumEntries << endl;
1336 std::cerr << os.str();
1337 }
1338 k_vals = values_type("Tpetra::CrsMatrix::values", lclTotalNumEntries);
1339
1340 // curRowOffsets (myGraph_->rowPtrsUnpacked_) (???), lclIndsUnpacked_wdv,
1341 // and valuesUnpacked_wdv are currently unpacked. Pack them, using
1342 // the packed row offsets array k_ptrs that we created above.
1343 //
1344 // FIXME (mfh 06 Aug 2014) If "Optimize Storage" is false, we
1345 // need to keep around the unpacked row offsets, column
1346 // indices, and values arrays.
1347
1348 // Pack the column indices from unpacked lclIndsUnpacked_wdv into
1349 // packed k_inds. We will replace lclIndsUnpacked_wdv below.
1350 using inds_packer_type = pack_functor<
1351 typename Graph::local_graph_device_type::entries_type::non_const_type,
1352 typename Graph::local_inds_dualv_type::t_dev::const_type,
1353 typename Graph::local_graph_device_type::row_map_type::non_const_type,
1354 typename Graph::local_graph_device_type::row_map_type>;
1355 inds_packer_type indsPacker(
1356 k_inds,
1357 myGraph_->lclIndsUnpacked_wdv.getDeviceView(Access::ReadOnly),
1358 k_ptrs, curRowOffsets);
1359 using exec_space = typename decltype(k_inds)::execution_space;
1360 using range_type = Kokkos::RangePolicy<exec_space, LocalOrdinal>;
1361 Kokkos::parallel_for("Tpetra::CrsMatrix pack column indices",
1362 range_type(0, lclNumRows), indsPacker);
1363
1364 // Pack the values from unpacked valuesUnpacked_wdv into packed
1365 // k_vals. We will replace valuesPacked_wdv below.
1366 using vals_packer_type = pack_functor<
1367 typename values_type::non_const_type,
1368 typename values_type::const_type,
1369 typename row_map_type::non_const_type,
1370 typename row_map_type::const_type>;
1371 vals_packer_type valsPacker(
1372 k_vals,
1373 this->valuesUnpacked_wdv.getDeviceView(Access::ReadOnly),
1374 k_ptrs, curRowOffsets);
1375 Kokkos::parallel_for("Tpetra::CrsMatrix pack values",
1376 range_type(0, lclNumRows), valsPacker);
1377
1378 if (debug) {
1379 const char myPrefix[] =
1380 "(\"Optimize Storage\""
1381 "=true branch) After packing, ";
1382 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(k_ptrs.extent(0) == 0, std::logic_error, myPrefix << "k_ptrs.extent(0) = 0. This probably means that "
1383 "rowPtrsUnpacked_ was never allocated.");
1384 if (k_ptrs.extent(0) != 0) {
1385 const size_t numOffsets(k_ptrs.extent(0));
1386 const auto valToCheck =
1387 getEntryOnHost(k_ptrs, numOffsets - 1);
1388 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(size_t(valToCheck) != k_vals.extent(0),
1389 std::logic_error, myPrefix << "k_ptrs(" << (numOffsets - 1) << ") = " << valToCheck << " != k_vals.extent(0) = " << k_vals.extent(0) << ".");
1390 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(size_t(valToCheck) != k_inds.extent(0),
1391 std::logic_error, myPrefix << "k_ptrs(" << (numOffsets - 1) << ") = " << valToCheck << " != k_inds.extent(0) = " << k_inds.extent(0) << ".");
1392 }
1393 }
1394 // Build the local graph.
1395 myGraph_->setRowPtrsPacked(k_ptrs_const);
1396 myGraph_->lclIndsPacked_wdv =
1397 typename crs_graph_type::local_inds_wdv_type(k_inds);
1398 valuesPacked_wdv = values_wdv_type(k_vals);
1399 } else { // We don't have to pack, so just set the pointers.
1400 // FIXME KDDKDD https://github.com/trilinos/Trilinos/issues/9657
1401 // FIXME? This is already done in the graph fill call - need to avoid the memcpy to host
1402 myGraph_->rowPtrsPacked_dev_ = myGraph_->rowPtrsUnpacked_dev_;
1403 myGraph_->rowPtrsPacked_host_ = myGraph_->rowPtrsUnpacked_host_;
1404 myGraph_->packedUnpackedRowPtrsMatch_ = true;
1405 myGraph_->lclIndsPacked_wdv = myGraph_->lclIndsUnpacked_wdv;
1406 valuesPacked_wdv = valuesUnpacked_wdv;
1407
1408 if (verbose) {
1409 std::ostringstream os;
1410 os << *prefix << "Storage already packed: rowPtrsUnpacked_: "
1411 << myGraph_->getRowPtrsUnpackedHost().extent(0) << ", lclIndsUnpacked_wdv: "
1412 << myGraph_->lclIndsUnpacked_wdv.extent(0) << ", valuesUnpacked_wdv: "
1413 << valuesUnpacked_wdv.extent(0) << endl;
1414 std::cerr << os.str();
1415 }
1416
1417 if (debug) {
1418 const char myPrefix[] =
1419 "(\"Optimize Storage\"=false branch) ";
1420 auto rowPtrsUnpackedHost = myGraph_->getRowPtrsUnpackedHost();
1421 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(myGraph_->rowPtrsUnpacked_dev_.extent(0) == 0, std::logic_error, myPrefix << "myGraph->rowPtrsUnpacked_dev_.extent(0) = 0. This probably means "
1422 "that rowPtrsUnpacked_ was never allocated.");
1423 if (myGraph_->rowPtrsUnpacked_dev_.extent(0) != 0) {
1424 const size_t numOffsets = rowPtrsUnpackedHost.extent(0);
1425 const auto valToCheck = rowPtrsUnpackedHost(numOffsets - 1);
1426 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(size_t(valToCheck) != valuesPacked_wdv.extent(0),
1427 std::logic_error, myPrefix << "k_ptrs_const(" << (numOffsets - 1) << ") = " << valToCheck << " != valuesPacked_wdv.extent(0) = " << valuesPacked_wdv.extent(0) << ".");
1428 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(size_t(valToCheck) != myGraph_->lclIndsPacked_wdv.extent(0),
1429 std::logic_error, myPrefix << "k_ptrs_const(" << (numOffsets - 1) << ") = " << valToCheck << " != myGraph_->lclIndsPacked.extent(0) = " << myGraph_->lclIndsPacked_wdv.extent(0) << ".");
1430 }
1431 }
1432 }
1433
1434 if (debug) {
1435 const char myPrefix[] = "After packing, ";
1436 auto rowPtrsPackedHost = myGraph_->getRowPtrsPackedHost();
1437 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(size_t(rowPtrsPackedHost.extent(0)) != size_t(lclNumRows + 1),
1438 std::logic_error, myPrefix << "myGraph_->rowPtrsPacked_host_.extent(0) = " << rowPtrsPackedHost.extent(0) << " != lclNumRows+1 = " << (lclNumRows + 1) << ".");
1439 if (rowPtrsPackedHost.extent(0) != 0) {
1440 const size_t numOffsets(rowPtrsPackedHost.extent(0));
1441 const size_t valToCheck = rowPtrsPackedHost(numOffsets - 1);
1442 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(valToCheck != size_t(valuesPacked_wdv.extent(0)),
1443 std::logic_error, myPrefix << "k_ptrs_const(" << (numOffsets - 1) << ") = " << valToCheck << " != valuesPacked_wdv.extent(0) = " << valuesPacked_wdv.extent(0) << ".");
1444 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(valToCheck != size_t(myGraph_->lclIndsPacked_wdv.extent(0)),
1445 std::logic_error, myPrefix << "k_ptrs_const(" << (numOffsets - 1) << ") = " << valToCheck << " != myGraph_->lclIndsPacked_wdvk_inds.extent(0) = " << myGraph_->lclIndsPacked_wdv.extent(0) << ".");
1446 }
1447 }
1448
1449 // May we ditch the old allocations for the packed (and otherwise
1450 // "optimized") allocations, later in this routine? Optimize
1451 // storage if the graph is not static, or if the graph already has
1452 // optimized storage.
1453 const bool defaultOptStorage =
1454 !isStaticGraph() || staticGraph_->isStorageOptimized();
1455 const bool requestOptimizedStorage =
1456 (!params.is_null() &&
1457 params->get("Optimize Storage", defaultOptStorage)) ||
1458 (params.is_null() && defaultOptStorage);
1459
1460 // The graph has optimized storage when indices are allocated,
1461 // myGraph_->k_numRowEntries_ is empty, and there are more than
1462 // zero rows on this process.
1463 if (requestOptimizedStorage) {
1464 // Free the old, unpacked, unoptimized allocations.
1465 // Free graph data structures that are only needed for
1466 // unpacked 1-D storage.
1467 if (verbose) {
1468 std::ostringstream os;
1469 os << *prefix << "Optimizing storage: free k_numRowEntries_: "
1470 << myGraph_->k_numRowEntries_.extent(0) << endl;
1471 std::cerr << os.str();
1472 }
1473
1474 myGraph_->k_numRowEntries_ = row_entries_type();
1475
1476 // Keep the new 1-D packed allocations.
1477 // FIXME KDDKDD https://github.com/trilinos/Trilinos/issues/9657
1478 // We directly set the memory spaces to avoid a memcpy from device to host
1479 myGraph_->rowPtrsUnpacked_dev_ = myGraph_->rowPtrsPacked_dev_;
1480 myGraph_->rowPtrsUnpacked_host_ = myGraph_->rowPtrsPacked_host_;
1481 myGraph_->packedUnpackedRowPtrsMatch_ = true;
1482 myGraph_->lclIndsUnpacked_wdv = myGraph_->lclIndsPacked_wdv;
1483 valuesUnpacked_wdv = valuesPacked_wdv;
1484
1485 myGraph_->storageStatus_ = Details::STORAGE_1D_PACKED;
1486 this->storageStatus_ = Details::STORAGE_1D_PACKED;
1487 } else {
1488 if (verbose) {
1489 std::ostringstream os;
1490 os << *prefix << "User requested NOT to optimize storage"
1491 << endl;
1492 std::cerr << os.str();
1493 }
1494 }
1495}
1496
1497template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1499 fillLocalMatrix(const Teuchos::RCP<Teuchos::ParameterList>& params) {
1500 using std::endl;
1501 using Teuchos::Array;
1502 using Teuchos::ArrayRCP;
1503 using Teuchos::null;
1504 using Teuchos::RCP;
1505 using Teuchos::rcp;
1507 using row_map_type = typename Graph::local_graph_device_type::row_map_type;
1508 using non_const_row_map_type = typename row_map_type::non_const_type;
1509 using values_type = typename local_matrix_device_type::values_type;
1510 ProfilingRegion regionFLM("Tpetra::CrsMatrix::fillLocalMatrix");
1511 const size_t lclNumRows = getLocalNumRows();
1512
1513 const bool verbose = Details::Behavior::verbose("CrsMatrix");
1514 std::unique_ptr<std::string> prefix;
1515 if (verbose) {
1516 prefix = this->createPrefix("CrsMatrix", "fillLocalMatrix");
1517 std::ostringstream os;
1518 os << *prefix << "lclNumRows: " << lclNumRows << endl;
1519 std::cerr << os.str();
1520 }
1521
1522 // The goals of this routine are first, to allocate and fill
1523 // packed 1-D storage (see below for an explanation) in the vals
1524 // array, and second, to give vals to the local matrix and
1525 // finalize the local matrix. We only need k_ptrs, the packed 1-D
1526 // row offsets, within the scope of this routine, since we're only
1527 // filling the local matrix here (use fillLocalGraphAndMatrix() to
1528 // fill both the graph and the matrix at the same time).
1529
1530 // get data from staticGraph_
1531 size_t nodeNumEntries = staticGraph_->getLocalNumEntries();
1532 size_t nodeNumAllocated = staticGraph_->getLocalAllocationSize();
1533 row_map_type k_rowPtrs = staticGraph_->rowPtrsPacked_dev_;
1534
1535 row_map_type k_ptrs; // "packed" row offsets array
1536 values_type k_vals; // "packed" values array
1537
1538 // May we ditch the old allocations for the packed (and otherwise
1539 // "optimized") allocations, later in this routine? Request
1540 // optimized storage by default.
1541 bool requestOptimizedStorage = true;
1542 const bool default_OptimizeStorage =
1543 !isStaticGraph() || staticGraph_->isStorageOptimized();
1544 if (!params.is_null() &&
1545 !params->get("Optimize Storage", default_OptimizeStorage)) {
1546 requestOptimizedStorage = false;
1547 }
1548 // If we're not allowed to change a static graph, then we can't
1549 // change the storage of the matrix, either. This means that if
1550 // the graph's storage isn't already optimized, we can't optimize
1551 // the matrix's storage either. Check and give warning, as
1552 // appropriate.
1553 if (!staticGraph_->isStorageOptimized() &&
1554 requestOptimizedStorage) {
1555 TPETRA_ABUSE_WARNING(true, std::runtime_error,
1556 "You requested optimized storage "
1557 "by setting the \"Optimize Storage\" flag to \"true\" in "
1558 "the ParameterList, or by virtue of default behavior. "
1559 "However, the associated CrsGraph was filled separately and "
1560 "requested not to optimize storage. Therefore, the "
1561 "CrsMatrix cannot optimize storage.");
1562 requestOptimizedStorage = false;
1563 }
1564
1565 // NOTE: This does not work correctly w/ GCC 12.3 + CUDA due to a compiler bug.
1566 // See: https://github.com/trilinos/Trilinos/issues/12237
1567 // using row_entries_type = decltype (staticGraph_->k_numRowEntries_);
1568 using row_entries_type = typename crs_graph_type::num_row_entries_type;
1569
1570 // The matrix's values are currently
1571 // stored in a 1-D format. However, this format is "unpacked";
1572 // it doesn't necessarily have the same row offsets as indicated
1573 // by the ptrs array returned by allocRowPtrs. This could
1574 // happen, for example, if the user
1575 // fixed the number of matrix entries in
1576 // each row, but didn't fill all those entries.
1577 //
1578 // As above, we don't need to keep the "packed" row offsets
1579 // array ptrs here, but we do need it here temporarily, so we
1580 // have to allocate it. We'll free ptrs later in this method.
1581 //
1582 // Note that this routine checks whether storage has already
1583 // been packed. This is a common case for solution of nonlinear
1584 // PDEs using the finite element method, as long as the
1585 // structure of the sparse matrix does not change between linear
1586 // solves.
1587 if (nodeNumEntries != nodeNumAllocated) {
1588 if (verbose) {
1589 std::ostringstream os;
1590 os << *prefix << "Unpacked 1-D storage: numEnt="
1591 << nodeNumEntries << ", allocSize=" << nodeNumAllocated
1592 << endl;
1593 std::cerr << os.str();
1594 }
1595 // We have to pack the 1-D storage, since the user didn't fill
1596 // up all requested storage.
1597 if (verbose) {
1598 std::ostringstream os;
1599 os << *prefix << "Allocate packed row offsets: "
1600 << (lclNumRows + 1) << endl;
1601 std::cerr << os.str();
1602 }
1603 non_const_row_map_type tmpk_ptrs("Tpetra::CrsGraph::ptr",
1604 lclNumRows + 1);
1605 // Total number of entries in the matrix on the calling
1606 // process. We will compute this in the loop below. It's
1607 // cheap to compute and useful as a sanity check.
1608 size_t lclTotalNumEntries = 0;
1609 k_ptrs = tmpk_ptrs;
1610 {
1611 typename row_entries_type::const_type numRowEnt_h =
1612 staticGraph_->k_numRowEntries_;
1613 // This function can handle the counts being a host View.
1614 lclTotalNumEntries =
1615 Details::computeOffsetsFromCounts(tmpk_ptrs, numRowEnt_h);
1616 }
1617
1618 // Allocate the "packed" values array.
1619 // It has exactly the right number of entries.
1620 if (verbose) {
1621 std::ostringstream os;
1622 os << *prefix << "Allocate packed values: "
1623 << lclTotalNumEntries << endl;
1624 std::cerr << os.str();
1625 }
1626 k_vals = values_type("Tpetra::CrsMatrix::val", lclTotalNumEntries);
1627
1628 // Pack values_wdv into k_vals. We will replace values_wdv below.
1629 pack_functor<
1630 typename values_type::non_const_type,
1631 typename values_type::const_type,
1632 typename row_map_type::non_const_type,
1633 typename row_map_type::const_type>
1634 valsPacker(k_vals, valuesUnpacked_wdv.getDeviceView(Access::ReadOnly),
1635 tmpk_ptrs, k_rowPtrs);
1636
1637 using exec_space = typename decltype(k_vals)::execution_space;
1638 using range_type = Kokkos::RangePolicy<exec_space, LocalOrdinal>;
1639 Kokkos::parallel_for("Tpetra::CrsMatrix pack values",
1640 range_type(0, lclNumRows), valsPacker);
1641 valuesPacked_wdv = values_wdv_type(k_vals);
1642 } else { // We don't have to pack, so just set the pointer.
1643 valuesPacked_wdv = valuesUnpacked_wdv;
1644 if (verbose) {
1645 std::ostringstream os;
1646 os << *prefix << "Storage already packed: "
1647 << "valuesUnpacked_wdv: " << valuesUnpacked_wdv.extent(0) << endl;
1648 std::cerr << os.str();
1649 }
1650 }
1651
1652 // May we ditch the old allocations for the packed one?
1653 if (requestOptimizedStorage) {
1654 // The user requested optimized storage, so we can dump the
1655 // unpacked 1-D storage, and keep the packed storage.
1656 valuesUnpacked_wdv = valuesPacked_wdv;
1657 // k_values1D_ = valuesPacked_wdv.getDeviceView(Access::ReadWrite);
1658 this->storageStatus_ = Details::STORAGE_1D_PACKED;
1659 }
1660}
1661
1662template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1665 RowInfo& rowInfo,
1666 const typename crs_graph_type::SLocalGlobalViews& newInds,
1667 const Teuchos::ArrayView<impl_scalar_type>& oldRowVals,
1668 const Teuchos::ArrayView<const impl_scalar_type>& newRowVals,
1669 const ELocalGlobal lg,
1670 const ELocalGlobal I) {
1671 const size_t oldNumEnt = rowInfo.numEntries;
1672 const size_t numInserted = graph.insertIndices(rowInfo, newInds, lg, I);
1673
1674 // Use of memcpy here works around an issue with GCC >= 4.9.0,
1675 // that probably relates to scalar_type vs. impl_scalar_type
1676 // aliasing. See history of Tpetra_CrsGraph_def.hpp for
1677 // details; look for GCC_WORKAROUND macro definition.
1678 if (numInserted > 0) {
1679 const size_t startOffset = oldNumEnt;
1680 memcpy((void*)&oldRowVals[startOffset], &newRowVals[0],
1681 numInserted * sizeof(impl_scalar_type));
1682 }
1683}
1684
1685template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1687 insertLocalValues(const LocalOrdinal lclRow,
1688 const Teuchos::ArrayView<const LocalOrdinal>& indices,
1689 const Teuchos::ArrayView<const Scalar>& values,
1690 const CombineMode CM) {
1691 using std::endl;
1692 const char tfecfFuncName[] = "insertLocalValues: ";
1693
1694 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!this->isFillActive(), std::runtime_error,
1695 "Fill is not active. After calling fillComplete, you must call "
1696 "resumeFill before you may insert entries into the matrix again.");
1697 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->isStaticGraph(), std::runtime_error,
1698 "Cannot insert indices with static graph; use replaceLocalValues() "
1699 "instead.");
1700 // At this point, we know that myGraph_ is nonnull.
1701 crs_graph_type& graph = *(this->myGraph_);
1702 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(graph.colMap_.is_null(), std::runtime_error,
1703 "Cannot insert local indices without a column map.");
1704 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(graph.isGloballyIndexed(),
1705 std::runtime_error,
1706 "Graph indices are global; use "
1707 "insertGlobalValues().");
1708 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(values.size() != indices.size(), std::runtime_error,
1709 "values.size() = " << values.size()
1710 << " != indices.size() = " << indices.size() << ".");
1711 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
1712 !graph.rowMap_->isNodeLocalElement(lclRow), std::runtime_error,
1713 "Local row index " << lclRow << " does not belong to this process.");
1714
1715 if (!graph.indicesAreAllocated()) {
1716 // We only allocate values at most once per process, so it's OK
1717 // to check TPETRA_VERBOSE here.
1718 const bool verbose = Details::Behavior::verbose("CrsMatrix");
1719 this->allocateValues(LocalIndices, GraphNotYetAllocated, verbose);
1720 }
1721
1722#ifdef HAVE_TPETRA_DEBUG
1723 const size_t numEntriesToAdd = static_cast<size_t>(indices.size());
1724 // In a debug build, test whether any of the given column indices
1725 // are not in the column Map. Keep track of the invalid column
1726 // indices so we can tell the user about them.
1727 {
1728 using Teuchos::toString;
1729
1730 const map_type& colMap = *(graph.colMap_);
1731 Teuchos::Array<LocalOrdinal> badColInds;
1732 bool allInColMap = true;
1733 for (size_t k = 0; k < numEntriesToAdd; ++k) {
1734 if (!colMap.isNodeLocalElement(indices[k])) {
1735 allInColMap = false;
1736 badColInds.push_back(indices[k]);
1737 }
1738 }
1739 if (!allInColMap) {
1740 std::ostringstream os;
1741 os << "You attempted to insert entries in owned row " << lclRow
1742 << ", at the following column indices: " << toString(indices)
1743 << "." << endl;
1744 os << "Of those, the following indices are not in the column Map on "
1745 "this process: "
1746 << toString(badColInds) << "." << endl
1747 << "Since "
1748 "the matrix has a column Map already, it is invalid to insert "
1749 "entries at those locations.";
1750 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::invalid_argument, os.str());
1751 }
1752 }
1753#endif // HAVE_TPETRA_DEBUG
1754
1755 RowInfo rowInfo = graph.getRowInfo(lclRow);
1756
1757 auto valsView = this->getValuesViewHostNonConst(rowInfo);
1758 if (CM == ADD) {
1759 auto fun = [&](size_t const k, size_t const /*start*/, size_t const offset) { valsView[offset] += values[k]; };
1760 std::function<void(size_t const, size_t const, size_t const)> cb(std::ref(fun));
1761 graph.insertLocalIndicesImpl(lclRow, indices, cb);
1762 } else if (CM == INSERT) {
1763 auto fun = [&](size_t const k, size_t const /*start*/, size_t const offset) { valsView[offset] = values[k]; };
1764 std::function<void(size_t const, size_t const, size_t const)> cb(std::ref(fun));
1765 graph.insertLocalIndicesImpl(lclRow, indices, cb);
1766 } else {
1767 std::ostringstream os;
1768 os << "You attempted to use insertLocalValues with CombineMode " << combineModeToString(CM)
1769 << "but this has not been implemented." << endl;
1770 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::invalid_argument, os.str());
1771 }
1772}
1773
1774template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1776 insertLocalValues(const LocalOrdinal localRow,
1777 const LocalOrdinal numEnt,
1778 const Scalar vals[],
1779 const LocalOrdinal cols[],
1780 const CombineMode CM) {
1781 Teuchos::ArrayView<const LocalOrdinal> colsT(cols, numEnt);
1782 Teuchos::ArrayView<const Scalar> valsT(vals, numEnt);
1783 this->insertLocalValues(localRow, colsT, valsT, CM);
1784}
1785
1786template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1789 RowInfo& rowInfo,
1790 const GlobalOrdinal gblColInds[],
1791 const impl_scalar_type vals[],
1792 const size_t numInputEnt) {
1793#ifdef HAVE_TPETRA_DEBUG
1794 const char tfecfFuncName[] = "insertGlobalValuesImpl: ";
1795 const size_t origNumEnt = graph.getNumEntriesInLocalRow(rowInfo.localRow);
1796 const size_t curNumEnt = rowInfo.numEntries;
1797#endif // HAVE_TPETRA_DEBUG
1798
1799 if (!graph.indicesAreAllocated()) {
1800 // We only allocate values at most once per process, so it's OK
1801 // to check TPETRA_VERBOSE here.
1803 const bool verbose = Behavior::verbose("CrsMatrix");
1804 this->allocateValues(GlobalIndices, GraphNotYetAllocated, verbose);
1805 // mfh 23 Jul 2017: allocateValues invalidates existing
1806 // getRowInfo results. Once we get rid of lazy graph
1807 // allocation, we'll be able to move the getRowInfo call outside
1808 // of this method.
1809 rowInfo = graph.getRowInfo(rowInfo.localRow);
1810 }
1811
1812 auto valsView = this->getValuesViewHostNonConst(rowInfo);
1813 auto fun = [&](size_t const k, size_t const /*start*/, size_t const offset) {
1814 valsView[offset] += vals[k];
1815 };
1816 std::function<void(size_t const, size_t const, size_t const)> cb(std::ref(fun));
1817#ifdef HAVE_TPETRA_DEBUG
1818 // numInserted is only used inside the debug code below.
1819 auto numInserted =
1820#endif
1821 graph.insertGlobalIndicesImpl(rowInfo, gblColInds, numInputEnt, cb);
1822
1823#ifdef HAVE_TPETRA_DEBUG
1824 size_t newNumEnt = curNumEnt + numInserted;
1825 const size_t chkNewNumEnt =
1826 graph.getNumEntriesInLocalRow(rowInfo.localRow);
1827 if (chkNewNumEnt != newNumEnt) {
1828 std::ostringstream os;
1829 os << std::endl
1830 << "newNumEnt = " << newNumEnt
1831 << " != graph.getNumEntriesInLocalRow(" << rowInfo.localRow
1832 << ") = " << chkNewNumEnt << "." << std::endl
1833 << "\torigNumEnt: " << origNumEnt << std::endl
1834 << "\tnumInputEnt: " << numInputEnt << std::endl
1835 << "\tgblColInds: [";
1836 for (size_t k = 0; k < numInputEnt; ++k) {
1837 os << gblColInds[k];
1838 if (k + size_t(1) < numInputEnt) {
1839 os << ",";
1840 }
1841 }
1842 os << "]" << std::endl
1843 << "\tvals: [";
1844 for (size_t k = 0; k < numInputEnt; ++k) {
1845 os << vals[k];
1846 if (k + size_t(1) < numInputEnt) {
1847 os << ",";
1848 }
1849 }
1850 os << "]" << std::endl;
1851
1852 if (this->supportsRowViews()) {
1853 values_host_view_type vals2;
1854 if (this->isGloballyIndexed()) {
1855 global_inds_host_view_type gblColInds2;
1856 const GlobalOrdinal gblRow =
1857 graph.rowMap_->getGlobalElement(rowInfo.localRow);
1858 if (gblRow ==
1859 Tpetra::Details::OrdinalTraits<GlobalOrdinal>::invalid()) {
1860 os << "Local row index " << rowInfo.localRow << " is invalid!"
1861 << std::endl;
1862 } else {
1863 bool getViewThrew = false;
1864 try {
1865 this->getGlobalRowView(gblRow, gblColInds2, vals2);
1866 } catch (std::exception& e) {
1867 getViewThrew = true;
1868 os << "getGlobalRowView threw exception:" << std::endl
1869 << e.what() << std::endl;
1870 }
1871 if (!getViewThrew) {
1872 os << "\tNew global column indices: ";
1873 for (size_t jjj = 0; jjj < gblColInds2.extent(0); jjj++)
1874 os << gblColInds2[jjj] << " ";
1875 os << std::endl;
1876 os << "\tNew values: ";
1877 for (size_t jjj = 0; jjj < vals2.extent(0); jjj++)
1878 os << vals2[jjj] << " ";
1879 os << std::endl;
1880 }
1881 }
1882 } else if (this->isLocallyIndexed()) {
1883 local_inds_host_view_type lclColInds2;
1884 this->getLocalRowView(rowInfo.localRow, lclColInds2, vals2);
1885 os << "\tNew local column indices: ";
1886 for (size_t jjj = 0; jjj < lclColInds2.extent(0); jjj++)
1887 os << lclColInds2[jjj] << " ";
1888 os << std::endl;
1889 os << "\tNew values: ";
1890 for (size_t jjj = 0; jjj < vals2.extent(0); jjj++)
1891 os << vals2[jjj] << " ";
1892 os << std::endl;
1893 }
1894 }
1895
1896 os << "Please report this bug to the Tpetra developers.";
1897 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::logic_error, os.str());
1898 }
1899#endif // HAVE_TPETRA_DEBUG
1900}
1901
1902template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1904 insertGlobalValues(const GlobalOrdinal gblRow,
1905 const Teuchos::ArrayView<const GlobalOrdinal>& indices,
1906 const Teuchos::ArrayView<const Scalar>& values) {
1907 using std::endl;
1908 using Teuchos::toString;
1909 typedef impl_scalar_type IST;
1910 typedef LocalOrdinal LO;
1911 typedef GlobalOrdinal GO;
1912 typedef Tpetra::Details::OrdinalTraits<LO> OTLO;
1913 typedef typename Teuchos::ArrayView<const GO>::size_type size_type;
1914 const char tfecfFuncName[] = "insertGlobalValues: ";
1915
1916#ifdef HAVE_TPETRA_DEBUG
1917 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(values.size() != indices.size(), std::runtime_error,
1918 "values.size() = " << values.size() << " != indices.size() = "
1919 << indices.size() << ".");
1920#endif // HAVE_TPETRA_DEBUG
1921
1922 // getRowMap() is not thread safe, because it increments RCP's
1923 // reference count. getCrsGraphRef() is thread safe.
1924 const map_type& rowMap = *(this->getCrsGraphRef().rowMap_);
1925 const LO lclRow = rowMap.getLocalElement(gblRow);
1926
1927 if (lclRow == OTLO::invalid()) {
1928 // Input row is _not_ owned by the calling process.
1929 //
1930 // See a note (now deleted) from mfh 14 Dec 2012: If input row
1931 // is not in the row Map, it doesn't matter whether or not the
1932 // graph is static; the data just get stashed for later use by
1933 // globalAssemble().
1934 this->insertNonownedGlobalValues(gblRow, indices, values);
1935 } else { // Input row _is_ owned by the calling process
1936 if (this->isStaticGraph()) {
1937 // Uh oh! Not allowed to insert into owned rows in that case.
1938 const int myRank = rowMap.getComm()->getRank();
1939 const int numProcs = rowMap.getComm()->getSize();
1940 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
1941 "The matrix was constructed with a constant (\"static\") graph, "
1942 "yet the given global row index "
1943 << gblRow << " is in the row "
1944 "Map on the calling process (with rank "
1945 << myRank << ", of " << numProcs << " process(es)). In this case, you may not insert "
1946 "new entries into rows owned by the calling process.");
1947 }
1948
1949 crs_graph_type& graph = *(this->myGraph_);
1950 const IST* const inputVals =
1951 reinterpret_cast<const IST*>(values.getRawPtr());
1952 const GO* const inputGblColInds = indices.getRawPtr();
1953 const size_t numInputEnt = indices.size();
1954 RowInfo rowInfo = graph.getRowInfo(lclRow);
1955
1956 // If the matrix has a column Map, check at this point whether
1957 // the column indices belong to the column Map.
1959 // FIXME (mfh 16 May 2013) We may want to consider deferring the
1960 // test to the CrsGraph method, since it may have to do this
1961 // anyway.
1962 if (!graph.colMap_.is_null()) {
1963 const map_type& colMap = *(graph.colMap_);
1964 // In a debug build, keep track of the nonowned ("bad") column
1965 // indices, so that we can display them in the exception
1966 // message. In a release build, just ditch the loop early if
1967 // we encounter a nonowned column index.
1968#ifdef HAVE_TPETRA_DEBUG
1969 Teuchos::Array<GO> badColInds;
1970#endif // HAVE_TPETRA_DEBUG
1971 const size_type numEntriesToInsert = indices.size();
1972 bool allInColMap = true;
1973 for (size_type k = 0; k < numEntriesToInsert; ++k) {
1974 if (!colMap.isNodeGlobalElement(indices[k])) {
1975 allInColMap = false;
1976#ifdef HAVE_TPETRA_DEBUG
1977 badColInds.push_back(indices[k]);
1978#else
1979 break;
1980#endif // HAVE_TPETRA_DEBUG
1981 }
1982 }
1983 if (!allInColMap) {
1984 std::ostringstream os;
1985 os << "You attempted to insert entries in owned row " << gblRow
1986 << ", at the following column indices: " << toString(indices)
1987 << "." << endl;
1988#ifdef HAVE_TPETRA_DEBUG
1989 os << "Of those, the following indices are not in the column Map "
1990 "on this process: "
1991 << toString(badColInds) << "." << endl
1992 << "Since the matrix has a column Map already, it is invalid "
1993 "to insert entries at those locations.";
1994#else
1995 os << "At least one of those indices is not in the column Map "
1996 "on this process."
1997 << endl
1998 << "It is invalid to insert into "
1999 "columns not in the column Map on the process that owns the "
2000 "row.";
2001#endif // HAVE_TPETRA_DEBUG
2002 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::invalid_argument, os.str());
2003 }
2004 }
2005
2006 this->insertGlobalValuesImpl(graph, rowInfo, inputGblColInds,
2007 inputVals, numInputEnt);
2008 }
2009}
2010
2011template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2013 insertGlobalValues(const GlobalOrdinal globalRow,
2014 const LocalOrdinal numEnt,
2015 const Scalar vals[],
2016 const GlobalOrdinal inds[]) {
2017 Teuchos::ArrayView<const GlobalOrdinal> indsT(inds, numEnt);
2018 Teuchos::ArrayView<const Scalar> valsT(vals, numEnt);
2019 this->insertGlobalValues(globalRow, indsT, valsT);
2020}
2021
2022template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2025 const GlobalOrdinal gblRow,
2026 const Teuchos::ArrayView<const GlobalOrdinal>& indices,
2027 const Teuchos::ArrayView<const Scalar>& values,
2028 const bool debug) {
2029 typedef impl_scalar_type IST;
2030 typedef LocalOrdinal LO;
2031 typedef GlobalOrdinal GO;
2032 typedef Tpetra::Details::OrdinalTraits<LO> OTLO;
2033 const char tfecfFuncName[] = "insertGlobalValuesFiltered: ";
2034
2035 if (debug) {
2036 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(values.size() != indices.size(), std::runtime_error,
2037 "values.size() = " << values.size() << " != indices.size() = "
2038 << indices.size() << ".");
2039 }
2040
2041 // getRowMap() is not thread safe, because it increments RCP's
2042 // reference count. getCrsGraphRef() is thread safe.
2043 const map_type& rowMap = *(this->getCrsGraphRef().rowMap_);
2044 const LO lclRow = rowMap.getLocalElement(gblRow);
2045 if (lclRow == OTLO::invalid()) {
2046 // Input row is _not_ owned by the calling process.
2047 //
2048 // See a note (now deleted) from mfh 14 Dec 2012: If input row
2049 // is not in the row Map, it doesn't matter whether or not the
2050 // graph is static; the data just get stashed for later use by
2051 // globalAssemble().
2052 this->insertNonownedGlobalValues(gblRow, indices, values);
2053 } else { // Input row _is_ owned by the calling process
2054 if (this->isStaticGraph()) {
2055 // Uh oh! Not allowed to insert into owned rows in that case.
2056 const int myRank = rowMap.getComm()->getRank();
2057 const int numProcs = rowMap.getComm()->getSize();
2058 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
2059 "The matrix was constructed with a constant (\"static\") graph, "
2060 "yet the given global row index "
2061 << gblRow << " is in the row "
2062 "Map on the calling process (with rank "
2063 << myRank << ", of " << numProcs << " process(es)). In this case, you may not insert "
2064 "new entries into rows owned by the calling process.");
2065 }
2066
2067 crs_graph_type& graph = *(this->myGraph_);
2068 const IST* const inputVals =
2069 reinterpret_cast<const IST*>(values.getRawPtr());
2070 const GO* const inputGblColInds = indices.getRawPtr();
2071 const size_t numInputEnt = indices.size();
2072 RowInfo rowInfo = graph.getRowInfo(lclRow);
2073
2074 if (!graph.colMap_.is_null() && graph.isLocallyIndexed()) {
2075 // This branch is similar in function to the following branch, but for
2076 // the special case that the target graph is locally indexed.
2077 // In this case, we cannot simply filter
2078 // out global indices that don't exist on the receiving process and
2079 // insert the remaining (global) indices, but we must convert them (the
2080 // remaining global indices) to local and call `insertLocalValues`.
2081 const map_type& colMap = *(graph.colMap_);
2082 size_t curOffset = 0;
2083 while (curOffset < numInputEnt) {
2084 // Find a sequence of input indices that are in the column Map on the
2085 // calling process. Doing a sequence at a time, instead of one at a
2086 // time, amortizes some overhead.
2087 Teuchos::Array<LO> lclIndices;
2088 size_t endOffset = curOffset;
2089 for (; endOffset < numInputEnt; ++endOffset) {
2090 auto lclIndex = colMap.getLocalElement(inputGblColInds[endOffset]);
2091 if (lclIndex != OTLO::invalid())
2092 lclIndices.push_back(lclIndex);
2093 else
2094 break;
2095 }
2096 // curOffset, endOffset: half-exclusive range of indices in the column
2097 // Map on the calling process. If endOffset == curOffset, the range is
2098 // empty.
2099 const LO numIndInSeq = (endOffset - curOffset);
2100 if (numIndInSeq != 0) {
2101 this->insertLocalValues(lclRow, lclIndices(), values(curOffset, numIndInSeq));
2103 // Invariant before the increment line: Either endOffset ==
2104 // numInputEnt, or inputGblColInds[endOffset] is not in the column Map
2105 // on the calling process.
2106 if (debug) {
2107 const bool invariant = endOffset == numInputEnt ||
2108 colMap.getLocalElement(inputGblColInds[endOffset]) == OTLO::invalid();
2109 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!invariant, std::logic_error, std::endl
2110 << "Invariant failed!");
2111 }
2112 curOffset = endOffset + 1;
2113 }
2114 } else if (!graph.colMap_.is_null()) { // We have a column Map.
2115 const map_type& colMap = *(graph.colMap_);
2116 size_t curOffset = 0;
2117 while (curOffset < numInputEnt) {
2118 // Find a sequence of input indices that are in the column
2119 // Map on the calling process. Doing a sequence at a time,
2120 // instead of one at a time, amortizes some overhead.
2121 size_t endOffset = curOffset;
2122 for (; endOffset < numInputEnt &&
2123 colMap.getLocalElement(inputGblColInds[endOffset]) != OTLO::invalid();
2124 ++endOffset) {
2125 }
2126 // curOffset, endOffset: half-exclusive range of indices in
2127 // the column Map on the calling process. If endOffset ==
2128 // curOffset, the range is empty.
2129 const LO numIndInSeq = (endOffset - curOffset);
2130 if (numIndInSeq != 0) {
2131 rowInfo = graph.getRowInfo(lclRow); // KDD 5/19 Need fresh RowInfo in each loop iteration
2132 this->insertGlobalValuesImpl(graph, rowInfo,
2133 inputGblColInds + curOffset,
2134 inputVals + curOffset,
2135 numIndInSeq);
2136 }
2137 // Invariant before the increment line: Either endOffset ==
2138 // numInputEnt, or inputGblColInds[endOffset] is not in the
2139 // column Map on the calling process.
2140 if (debug) {
2141 const bool invariant = endOffset == numInputEnt ||
2142 colMap.getLocalElement(inputGblColInds[endOffset]) == OTLO::invalid();
2143 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!invariant, std::logic_error, std::endl
2144 << "Invariant failed!");
2145 }
2146 curOffset = endOffset + 1;
2147 }
2148 } else { // we don't have a column Map.
2149 this->insertGlobalValuesImpl(graph, rowInfo, inputGblColInds,
2150 inputVals, numInputEnt);
2151 }
2152 }
2153}
2154
2155template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2158 const GlobalOrdinal gblRow,
2159 const Teuchos::ArrayView<const GlobalOrdinal>& indices,
2160 const Teuchos::ArrayView<const Scalar>& values,
2161 const char* const prefix,
2162 const bool debug,
2163 const bool verbose) {
2165 using std::endl;
2166
2167 try {
2168 insertGlobalValuesFiltered(gblRow, indices, values, debug);
2169 } catch (std::exception& e) {
2170 std::ostringstream os;
2171 if (verbose) {
2172 const size_t maxNumToPrint =
2174 os << *prefix << ": insertGlobalValuesFiltered threw an "
2175 "exception: "
2176 << e.what() << endl
2177 << "Global row index: " << gblRow << endl;
2178 verbosePrintArray(os, indices, "Global column indices",
2179 maxNumToPrint);
2180 os << endl;
2181 verbosePrintArray(os, values, "Values", maxNumToPrint);
2182 os << endl;
2183 } else {
2184 os << ": insertGlobalValuesFiltered threw an exception: "
2185 << e.what();
2187 TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, os.str());
2188 }
2189}
2190
2191template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2192LocalOrdinal
2195 const crs_graph_type& graph,
2196 const RowInfo& rowInfo,
2197 const LocalOrdinal inds[],
2198 const impl_scalar_type newVals[],
2199 const LocalOrdinal numElts) {
2200 typedef LocalOrdinal LO;
2201 typedef GlobalOrdinal GO;
2202 const bool sorted = graph.isSorted();
2203
2204 size_t hint = 0; // Guess for the current index k into rowVals
2205 LO numValid = 0; // number of valid local column indices
2206
2207 if (graph.isLocallyIndexed()) {
2208 // Get a view of the column indices in the row. This amortizes
2209 // the cost of getting the view over all the entries of inds.
2210 auto colInds = graph.getLocalIndsViewHost(rowInfo);
2211
2212 for (LO j = 0; j < numElts; ++j) {
2213 const LO lclColInd = inds[j];
2214 const size_t offset =
2215 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2216 lclColInd, hint, sorted);
2217 if (offset != rowInfo.numEntries) {
2218 rowVals[offset] = newVals[j];
2219 hint = offset + 1;
2220 ++numValid;
2221 }
2222 }
2223 } else if (graph.isGloballyIndexed()) {
2224 if (graph.colMap_.is_null()) {
2225 return Teuchos::OrdinalTraits<LO>::invalid();
2226 }
2227 const map_type colMap = *(graph.colMap_);
2228
2229 // Get a view of the column indices in the row. This amortizes
2230 // the cost of getting the view over all the entries of inds.
2231 auto colInds = graph.getGlobalIndsViewHost(rowInfo);
2232
2233 for (LO j = 0; j < numElts; ++j) {
2234 const GO gblColInd = colMap.getGlobalElement(inds[j]);
2235 if (gblColInd != Teuchos::OrdinalTraits<GO>::invalid()) {
2236 const size_t offset =
2237 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2238 gblColInd, hint, sorted);
2239 if (offset != rowInfo.numEntries) {
2240 rowVals[offset] = newVals[j];
2241 hint = offset + 1;
2242 ++numValid;
2243 }
2244 }
2245 }
2247 // NOTE (mfh 26 Jun 2014, 26 Nov 2015) In the current version of
2248 // CrsGraph and CrsMatrix, it's possible for a matrix (or graph)
2249 // to be neither locally nor globally indexed on a process.
2250 // This means that the graph or matrix has no entries on that
2251 // process. Epetra also works like this. It's related to lazy
2252 // allocation (on first insertion, not at graph / matrix
2253 // construction). Lazy allocation will go away because it is
2254 // not thread scalable.
2255
2256 return numValid;
2257}
2258
2259template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2260LocalOrdinal
2262 replaceLocalValues(const LocalOrdinal localRow,
2263 const Teuchos::ArrayView<const LocalOrdinal>& lclCols,
2264 const Teuchos::ArrayView<const Scalar>& vals) {
2265 typedef LocalOrdinal LO;
2266
2267 const LO numInputEnt = static_cast<LO>(lclCols.size());
2268 if (static_cast<LO>(vals.size()) != numInputEnt) {
2269 return Teuchos::OrdinalTraits<LO>::invalid();
2271 const LO* const inputInds = lclCols.getRawPtr();
2272 const Scalar* const inputVals = vals.getRawPtr();
2273 return this->replaceLocalValues(localRow, numInputEnt,
2274 inputVals, inputInds);
2275}
2277template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2278typename CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
2279 local_ordinal_type
2282 const local_ordinal_type localRow,
2283 const Kokkos::View<const local_ordinal_type*, Kokkos::AnonymousSpace>& inputInds,
2284 const Kokkos::View<const impl_scalar_type*, Kokkos::AnonymousSpace>& inputVals) {
2285 using LO = local_ordinal_type;
2286 const LO numInputEnt = inputInds.extent(0);
2287 if (numInputEnt != static_cast<LO>(inputVals.extent(0))) {
2288 return Teuchos::OrdinalTraits<LO>::invalid();
2289 }
2290 const Scalar* const inVals =
2291 reinterpret_cast<const Scalar*>(inputVals.data());
2292 return this->replaceLocalValues(localRow, numInputEnt,
2293 inVals, inputInds.data());
2294}
2295
2296template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2297LocalOrdinal
2299 replaceLocalValues(const LocalOrdinal localRow,
2300 const LocalOrdinal numEnt,
2301 const Scalar inputVals[],
2302 const LocalOrdinal inputCols[]) {
2303 typedef impl_scalar_type IST;
2304 typedef LocalOrdinal LO;
2305
2306 if (!this->isFillActive() || this->staticGraph_.is_null()) {
2307 // Fill must be active and the "nonconst" graph must exist.
2308 return Teuchos::OrdinalTraits<LO>::invalid();
2309 }
2310 const crs_graph_type& graph = *(this->staticGraph_);
2311 const RowInfo rowInfo = graph.getRowInfo(localRow);
2312
2313 if (rowInfo.localRow == Teuchos::OrdinalTraits<size_t>::invalid()) {
2314 // The calling process does not own this row, so it is not
2315 // allowed to modify its values.
2316 return static_cast<LO>(0);
2317 }
2318 auto curRowVals = this->getValuesViewHostNonConst(rowInfo);
2319 const IST* const inVals = reinterpret_cast<const IST*>(inputVals);
2320 return this->replaceLocalValuesImpl(curRowVals.data(), graph, rowInfo,
2321 inputCols, inVals, numEnt);
2322}
2323
2324template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2325LocalOrdinal
2328 const crs_graph_type& graph,
2329 const RowInfo& rowInfo,
2330 const GlobalOrdinal inds[],
2331 const impl_scalar_type newVals[],
2332 const LocalOrdinal numElts) {
2334 Teuchos::ArrayView<const GlobalOrdinal> indsT(inds, numElts);
2335 auto fun =
2336 [&](size_t const k, size_t const /*start*/, size_t const offset) {
2337 rowVals[offset] = newVals[k];
2338 };
2339 std::function<void(size_t const, size_t const, size_t const)> cb(std::ref(fun));
2340 return graph.findGlobalIndices(rowInfo, indsT, cb);
2341 } else {
2342 const LocalOrdinal LINV = Teuchos::OrdinalTraits<LocalOrdinal>::invalid();
2343
2344 typedef LocalOrdinal LO;
2345 typedef GlobalOrdinal GO;
2346
2347 const bool sorted = graph.isSorted();
2348 const bool atomic = useAtomicUpdatesByDefault; // FIXME
2349 size_t hint = 0; // guess at the index's relative offset in the row
2350 LO numValid = 0; // number of valid input column indices
2352 if (graph.isLocallyIndexed()) {
2353 // NOTE (mfh 04 Nov 2015) Dereferencing an RCP or reading its
2354 // pointer does NOT change its reference count. Thus, this
2355 // code is still thread safe.
2356 if (graph.colMap_.is_null()) {
2357 // NO input column indices are valid in this case, since if
2358 // the column Map is null on the calling process, then the
2359 // calling process owns no graph entries.
2360 return numValid;
2361 }
2362 const map_type& colMap = *(graph.colMap_);
2363
2364 // Get a view of the column indices in the row. This amortizes
2365 // the cost of getting the view over all the entries of inds.
2366 auto colInds = graph.getLocalIndsViewHost(rowInfo);
2367 if (atomic) {
2368 for (LO j = 0; j < numElts; ++j) {
2369 const LO lclColInd = colMap.getLocalElement(inds[j]);
2370 if (lclColInd != LINV) {
2371 const size_t offset =
2372 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2373 lclColInd, hint, sorted);
2374 if (offset != rowInfo.numEntries) {
2375 Kokkos::atomic_store(&rowVals[offset], newVals[j]);
2376 hint = offset + 1;
2377 numValid++;
2378 }
2379 }
2380 }
2381 } else {
2382 for (LO j = 0; j < numElts; ++j) {
2383 const LO lclColInd = colMap.getLocalElement(inds[j]);
2384 if (lclColInd != LINV) {
2385 const size_t offset =
2386 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2387 lclColInd, hint, sorted);
2388 if (offset != rowInfo.numEntries) {
2389 rowVals[offset] = newVals[j];
2390 hint = offset + 1;
2391 numValid++;
2392 }
2393 }
2394 }
2395 }
2396 return numValid;
2397 } else if (graph.isGloballyIndexed()) {
2398 // Get a view of the column indices in the row. This amortizes
2399 // the cost of getting the view over all the entries of inds.
2400 auto colInds = graph.getGlobalIndsViewHost(rowInfo);
2401
2402 if (atomic) {
2403 for (LO j = 0; j < numElts; ++j) {
2404 const GO gblColInd = inds[j];
2405 const size_t offset =
2406 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2407 gblColInd, hint, sorted);
2408 if (offset != rowInfo.numEntries) {
2409 Kokkos::atomic_store(&rowVals[offset], newVals[j]);
2410 hint = offset + 1;
2411 numValid++;
2412 }
2414 } else {
2415 for (LO j = 0; j < numElts; ++j) {
2416 const GO gblColInd = inds[j];
2417 const size_t offset =
2418 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2419 gblColInd, hint, sorted);
2420 if (offset != rowInfo.numEntries) {
2421 rowVals[offset] = newVals[j];
2422 hint = offset + 1;
2423 numValid++;
2424 }
2425 }
2426 }
2427 return numValid;
2428 } else {
2429 // If the graph is neither locally nor globally indexed on the
2430 // calling process, that means the calling process has no graph
2431 // entries. Thus, none of the input column indices are valid.
2432 return LINV;
2433 }
2434 }
2436
2437template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2438LocalOrdinal
2440 replaceGlobalValues(const GlobalOrdinal globalRow,
2441 const Teuchos::ArrayView<const GlobalOrdinal>& inputGblColInds,
2442 const Teuchos::ArrayView<const Scalar>& inputVals) {
2443 typedef LocalOrdinal LO;
2444
2445 const LO numInputEnt = static_cast<LO>(inputGblColInds.size());
2446 if (static_cast<LO>(inputVals.size()) != numInputEnt) {
2447 return Teuchos::OrdinalTraits<LO>::invalid();
2448 }
2449 return this->replaceGlobalValues(globalRow, numInputEnt,
2450 inputVals.getRawPtr(),
2451 inputGblColInds.getRawPtr());
2452}
2453
2454template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2455LocalOrdinal
2457 replaceGlobalValues(const GlobalOrdinal globalRow,
2458 const LocalOrdinal numEnt,
2459 const Scalar inputVals[],
2460 const GlobalOrdinal inputGblColInds[]) {
2461 typedef impl_scalar_type IST;
2462 typedef LocalOrdinal LO;
2463
2464 if (!this->isFillActive() || this->staticGraph_.is_null()) {
2465 // Fill must be active and the "nonconst" graph must exist.
2466 return Teuchos::OrdinalTraits<LO>::invalid();
2467 }
2468 const crs_graph_type& graph = *(this->staticGraph_);
2469
2470 const RowInfo rowInfo = graph.getRowInfoFromGlobalRowIndex(globalRow);
2471 if (rowInfo.localRow == Teuchos::OrdinalTraits<size_t>::invalid()) {
2472 // The input local row is invalid on the calling process,
2473 // which means that the calling process summed 0 entries.
2474 return static_cast<LO>(0);
2475 }
2476
2477 auto curRowVals = this->getValuesViewHostNonConst(rowInfo);
2478 const IST* const inVals = reinterpret_cast<const IST*>(inputVals);
2479 return this->replaceGlobalValuesImpl(curRowVals.data(), graph, rowInfo,
2480 inputGblColInds, inVals, numEnt);
2482
2483template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2485 local_ordinal_type
2488 const global_ordinal_type globalRow,
2489 const Kokkos::View<const global_ordinal_type*, Kokkos::AnonymousSpace>& inputInds,
2490 const Kokkos::View<const impl_scalar_type*, Kokkos::AnonymousSpace>& inputVals) {
2491 // We use static_assert here to check the template parameters,
2492 // rather than std::enable_if (e.g., on the return value, to
2493 // enable compilation only if the template parameters match the
2494 // desired attributes). This turns obscure link errors into
2495 // clear compilation errors. It also makes the return value a
2496 // lot easier to see.
2497 using LO = local_ordinal_type;
2498 const LO numInputEnt = static_cast<LO>(inputInds.extent(0));
2499 if (static_cast<LO>(inputVals.extent(0)) != numInputEnt) {
2500 return Teuchos::OrdinalTraits<LO>::invalid();
2501 }
2502 const Scalar* const inVals =
2503 reinterpret_cast<const Scalar*>(inputVals.data());
2504 return this->replaceGlobalValues(globalRow, numInputEnt, inVals,
2505 inputInds.data());
2506}
2507
2508template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2509LocalOrdinal
2512 const crs_graph_type& graph,
2513 const RowInfo& rowInfo,
2514 const GlobalOrdinal inds[],
2515 const impl_scalar_type newVals[],
2516 const LocalOrdinal numElts,
2517 const bool atomic) {
2518 typedef LocalOrdinal LO;
2519 typedef GlobalOrdinal GO;
2520
2521 const bool sorted = graph.isSorted();
2522
2523 size_t hint = 0; // guess at the index's relative offset in the row
2524 LO numValid = 0; // number of valid input column indices
2525
2526 if (graph.isLocallyIndexed()) {
2527 // NOTE (mfh 04 Nov 2015) Dereferencing an RCP or reading its
2528 // pointer does NOT change its reference count. Thus, this
2529 // code is still thread safe.
2530 if (graph.colMap_.is_null()) {
2531 // NO input column indices are valid in this case, since if
2532 // the column Map is null on the calling process, then the
2533 // calling process owns no graph entries.
2534 return numValid;
2535 }
2536 const map_type& colMap = *(graph.colMap_);
2538 // Get a view of the column indices in the row. This amortizes
2539 // the cost of getting the view over all the entries of inds.
2540 auto colInds = graph.getLocalIndsViewHost(rowInfo);
2541 const LO LINV = Teuchos::OrdinalTraits<LO>::invalid();
2542
2543 for (LO j = 0; j < numElts; ++j) {
2544 const LO lclColInd = colMap.getLocalElement(inds[j]);
2545 if (lclColInd != LINV) {
2546 const size_t offset =
2547 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2548 lclColInd, hint, sorted);
2549 if (offset != rowInfo.numEntries) {
2550 if (atomic) {
2551 Kokkos::atomic_add(&rowVals[offset], newVals[j]);
2552 } else {
2553 rowVals[offset] += newVals[j];
2554 }
2555 hint = offset + 1;
2556 numValid++;
2557 }
2558 }
2559 }
2560 } else if (graph.isGloballyIndexed()) {
2561 // Get a view of the column indices in the row. This amortizes
2562 // the cost of getting the view over all the entries of inds.
2563 auto colInds = graph.getGlobalIndsViewHost(rowInfo);
2564
2565 for (LO j = 0; j < numElts; ++j) {
2566 const GO gblColInd = inds[j];
2567 const size_t offset =
2568 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2569 gblColInd, hint, sorted);
2570 if (offset != rowInfo.numEntries) {
2571 if (atomic) {
2572 Kokkos::atomic_add(&rowVals[offset], newVals[j]);
2573 } else {
2574 rowVals[offset] += newVals[j];
2575 }
2576 hint = offset + 1;
2577 numValid++;
2578 }
2579 }
2580 }
2581 // If the graph is neither locally nor globally indexed on the
2582 // calling process, that means the calling process has no graph
2583 // entries. Thus, none of the input column indices are valid.
2584
2585 return numValid;
2586}
2587
2588template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2589LocalOrdinal
2591 sumIntoGlobalValues(const GlobalOrdinal gblRow,
2592 const Teuchos::ArrayView<const GlobalOrdinal>& inputGblColInds,
2593 const Teuchos::ArrayView<const Scalar>& inputVals,
2594 const bool atomic) {
2595 typedef LocalOrdinal LO;
2596
2597 const LO numInputEnt = static_cast<LO>(inputGblColInds.size());
2598 if (static_cast<LO>(inputVals.size()) != numInputEnt) {
2599 return Teuchos::OrdinalTraits<LO>::invalid();
2600 }
2601 return this->sumIntoGlobalValues(gblRow, numInputEnt,
2602 inputVals.getRawPtr(),
2603 inputGblColInds.getRawPtr(),
2604 atomic);
2605}
2606
2607template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2608LocalOrdinal
2610 sumIntoGlobalValues(const GlobalOrdinal gblRow,
2611 const LocalOrdinal numInputEnt,
2612 const Scalar inputVals[],
2613 const GlobalOrdinal inputGblColInds[],
2614 const bool atomic) {
2615 typedef impl_scalar_type IST;
2616 typedef LocalOrdinal LO;
2617 typedef GlobalOrdinal GO;
2618
2619 if (!this->isFillActive() || this->staticGraph_.is_null()) {
2620 // Fill must be active and the "nonconst" graph must exist.
2621 return Teuchos::OrdinalTraits<LO>::invalid();
2622 }
2623 const crs_graph_type& graph = *(this->staticGraph_);
2624
2625 const RowInfo rowInfo = graph.getRowInfoFromGlobalRowIndex(gblRow);
2626 if (rowInfo.localRow == Teuchos::OrdinalTraits<size_t>::invalid()) {
2627 // mfh 23 Mar 2017, 26 Jul 2017: This branch may not be not
2628 // thread safe in a debug build, in part because it uses
2629 // Teuchos::ArrayView, and in part because of the data structure
2630 // used to stash outgoing entries.
2631 using Teuchos::ArrayView;
2632 ArrayView<const GO> inputGblColInds_av(
2633 numInputEnt == 0 ? nullptr : inputGblColInds,
2634 numInputEnt);
2635 ArrayView<const Scalar> inputVals_av(
2636 numInputEnt == 0 ? nullptr : inputVals, numInputEnt);
2637 // gblRow is not in the row Map on the calling process, so stash
2638 // the given entries away in a separate data structure.
2639 // globalAssemble() (called during fillComplete()) will exchange
2640 // that data and sum it in using sumIntoGlobalValues().
2641 this->insertNonownedGlobalValues(gblRow, inputGblColInds_av,
2642 inputVals_av);
2643 // FIXME (mfh 08 Jul 2014) It's not clear what to return here,
2644 // since we won't know whether the given indices were valid
2645 // until globalAssemble (called in fillComplete) is called.
2646 // That's why insertNonownedGlobalValues doesn't return
2647 // anything. Just for consistency, I'll return the number of
2648 // entries that the user gave us.
2649 return numInputEnt;
2650 } else { // input row is in the row Map on the calling process
2651 auto curRowVals = this->getValuesViewHostNonConst(rowInfo);
2652 const IST* const inVals = reinterpret_cast<const IST*>(inputVals);
2653 return this->sumIntoGlobalValuesImpl(curRowVals.data(), graph, rowInfo,
2654 inputGblColInds, inVals,
2655 numInputEnt, atomic);
2656 }
2657}
2658
2659template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2660LocalOrdinal
2662 transformLocalValues(const LocalOrdinal lclRow,
2663 const LocalOrdinal numInputEnt,
2664 const impl_scalar_type inputVals[],
2665 const LocalOrdinal inputCols[],
2666 std::function<impl_scalar_type(const impl_scalar_type&, const impl_scalar_type&)> f,
2667 const bool atomic) {
2668 using Tpetra::Details::OrdinalTraits;
2669 typedef LocalOrdinal LO;
2670
2671 if (!this->isFillActive() || this->staticGraph_.is_null()) {
2672 // Fill must be active and the "nonconst" graph must exist.
2673 return Teuchos::OrdinalTraits<LO>::invalid();
2674 }
2675 const crs_graph_type& graph = *(this->staticGraph_);
2676 const RowInfo rowInfo = graph.getRowInfo(lclRow);
2677
2678 if (rowInfo.localRow == OrdinalTraits<size_t>::invalid()) {
2679 // The calling process does not own this row, so it is not
2680 // allowed to modify its values.
2681 return static_cast<LO>(0);
2682 }
2683 auto curRowVals = this->getValuesViewHostNonConst(rowInfo);
2684 return this->transformLocalValues(curRowVals.data(), graph,
2685 rowInfo, inputCols, inputVals,
2686 numInputEnt, f, atomic);
2687}
2688
2689template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2690LocalOrdinal
2692 transformGlobalValues(const GlobalOrdinal gblRow,
2693 const LocalOrdinal numInputEnt,
2694 const impl_scalar_type inputVals[],
2695 const GlobalOrdinal inputCols[],
2696 std::function<impl_scalar_type(const impl_scalar_type&, const impl_scalar_type&)> f,
2697 const bool atomic) {
2698 using Tpetra::Details::OrdinalTraits;
2699 typedef LocalOrdinal LO;
2700
2701 if (!this->isFillActive() || this->staticGraph_.is_null()) {
2702 // Fill must be active and the "nonconst" graph must exist.
2703 return OrdinalTraits<LO>::invalid();
2704 }
2705 const crs_graph_type& graph = *(this->staticGraph_);
2706 const RowInfo rowInfo = graph.getRowInfoFromGlobalRowIndex(gblRow);
2707
2708 if (rowInfo.localRow == OrdinalTraits<size_t>::invalid()) {
2709 // The calling process does not own this row, so it is not
2710 // allowed to modify its values.
2711 return static_cast<LO>(0);
2712 }
2713 auto curRowVals = this->getValuesViewHostNonConst(rowInfo);
2714 return this->transformGlobalValues(curRowVals.data(), graph,
2715 rowInfo, inputCols, inputVals,
2716 numInputEnt, f, atomic);
2717}
2718
2719template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2720LocalOrdinal
2723 const crs_graph_type& graph,
2724 const RowInfo& rowInfo,
2725 const LocalOrdinal inds[],
2726 const impl_scalar_type newVals[],
2727 const LocalOrdinal numElts,
2728 std::function<impl_scalar_type(const impl_scalar_type&, const impl_scalar_type&)> f,
2729 const bool atomic) {
2731 typedef LocalOrdinal LO;
2732 typedef GlobalOrdinal GO;
2733
2734 // if (newVals.extent (0) != inds.extent (0)) {
2735 // The sizes of the input arrays must match.
2736 // return Tpetra::Details::OrdinalTraits<LO>::invalid ();
2737 // }
2738 // const LO numElts = static_cast<LO> (inds.extent (0));
2739 const bool sorted = graph.isSorted();
2740
2741 LO numValid = 0; // number of valid input column indices
2742 size_t hint = 0; // Guess for the current index k into rowVals
2743
2744 if (graph.isLocallyIndexed()) {
2745 // Get a view of the column indices in the row. This amortizes
2746 // the cost of getting the view over all the entries of inds.
2747 auto colInds = graph.getLocalIndsViewHost(rowInfo);
2748
2749 for (LO j = 0; j < numElts; ++j) {
2750 const LO lclColInd = inds[j];
2751 const size_t offset =
2752 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2753 lclColInd, hint, sorted);
2754 if (offset != rowInfo.numEntries) {
2755 if (atomic) {
2756 // NOTE (mfh 30 Nov 2015) The commented-out code is
2757 // wrong because another thread may have changed
2758 // rowVals[offset] between those two lines of code.
2759 //
2760 // const ST newVal = f (rowVals[offset], newVals[j]);
2761 // Kokkos::atomic_assign (&rowVals[offset], newVal);
2762
2763 ST* const dest = &rowVals[offset];
2764 (void)atomic_binary_function_update(dest, newVals[j], f);
2765 } else {
2766 // use binary function f
2767 rowVals[offset] = f(rowVals[offset], newVals[j]);
2768 }
2769 hint = offset + 1;
2770 ++numValid;
2771 }
2772 }
2773 } else if (graph.isGloballyIndexed()) {
2774 // NOTE (mfh 26 Nov 2015) Dereferencing an RCP or reading its
2775 // pointer does NOT change its reference count. Thus, this
2776 // code is still thread safe.
2777 if (graph.colMap_.is_null()) {
2778 // NO input column indices are valid in this case. Either
2779 // the column Map hasn't been set yet (so local indices
2780 // don't exist yet), or the calling process owns no graph
2781 // entries.
2782 return numValid;
2783 }
2784 const map_type& colMap = *(graph.colMap_);
2785 // Get a view of the column indices in the row. This amortizes
2786 // the cost of getting the view over all the entries of inds.
2787 auto colInds = graph.getGlobalIndsViewHost(rowInfo);
2788
2789 const GO GINV = Teuchos::OrdinalTraits<GO>::invalid();
2790 for (LO j = 0; j < numElts; ++j) {
2791 const GO gblColInd = colMap.getGlobalElement(inds[j]);
2792 if (gblColInd != GINV) {
2793 const size_t offset =
2794 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2795 gblColInd, hint, sorted);
2796 if (offset != rowInfo.numEntries) {
2797 if (atomic) {
2798 // NOTE (mfh 30 Nov 2015) The commented-out code is
2799 // wrong because another thread may have changed
2800 // rowVals[offset] between those two lines of code.
2801 //
2802 // const ST newVal = f (rowVals[offset], newVals[j]);
2803 // Kokkos::atomic_assign (&rowVals[offset], newVal);
2804
2805 ST* const dest = &rowVals[offset];
2806 (void)atomic_binary_function_update(dest, newVals[j], f);
2807 } else {
2808 // use binary function f
2809 rowVals[offset] = f(rowVals[offset], newVals[j]);
2810 }
2811 hint = offset + 1;
2812 numValid++;
2813 }
2814 }
2815 }
2816 }
2817 // If the graph is neither locally nor globally indexed on the
2818 // calling process, that means the calling process has no graph
2819 // entries. Thus, none of the input column indices are valid.
2820
2821 return numValid;
2822}
2823
2824template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2825LocalOrdinal
2828 const crs_graph_type& graph,
2829 const RowInfo& rowInfo,
2830 const GlobalOrdinal inds[],
2831 const impl_scalar_type newVals[],
2832 const LocalOrdinal numElts,
2833 std::function<impl_scalar_type(const impl_scalar_type&, const impl_scalar_type&)> f,
2834 const bool atomic) {
2835 typedef impl_scalar_type ST;
2836 typedef LocalOrdinal LO;
2837 typedef GlobalOrdinal GO;
2838
2839 // if (newVals.extent (0) != inds.extent (0)) {
2840 // The sizes of the input arrays must match.
2841 // return Tpetra::Details::OrdinalTraits<LO>::invalid ();
2842 // }
2843 // const LO numElts = static_cast<LO> (inds.extent (0));
2844 const bool sorted = graph.isSorted();
2845
2846 LO numValid = 0; // number of valid input column indices
2847 size_t hint = 0; // Guess for the current index k into rowVals
2848
2849 if (graph.isGloballyIndexed()) {
2850 // Get a view of the column indices in the row. This amortizes
2851 // the cost of getting the view over all the entries of inds.
2852 auto colInds = graph.getGlobalIndsViewHost(rowInfo);
2853
2854 for (LO j = 0; j < numElts; ++j) {
2855 const GO gblColInd = inds[j];
2856 const size_t offset =
2857 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2858 gblColInd, hint, sorted);
2859 if (offset != rowInfo.numEntries) {
2860 if (atomic) {
2861 // NOTE (mfh 30 Nov 2015) The commented-out code is
2862 // wrong because another thread may have changed
2863 // rowVals[offset] between those two lines of code.
2865 // const ST newVal = f (rowVals[offset], newVals[j]);
2866 // Kokkos::atomic_assign (&rowVals[offset], newVal);
2867
2868 ST* const dest = &rowVals[offset];
2869 (void)atomic_binary_function_update(dest, newVals[j], f);
2870 } else {
2871 // use binary function f
2872 rowVals[offset] = f(rowVals[offset], newVals[j]);
2873 }
2874 hint = offset + 1;
2875 ++numValid;
2876 }
2877 }
2878 } else if (graph.isLocallyIndexed()) {
2879 // NOTE (mfh 26 Nov 2015) Dereferencing an RCP or reading its
2880 // pointer does NOT change its reference count. Thus, this
2881 // code is still thread safe.
2882 if (graph.colMap_.is_null()) {
2883 // NO input column indices are valid in this case. Either the
2884 // column Map hasn't been set yet (so local indices don't
2885 // exist yet), or the calling process owns no graph entries.
2886 return numValid;
2887 }
2888 const map_type& colMap = *(graph.colMap_);
2889 // Get a view of the column indices in the row. This amortizes
2890 // the cost of getting the view over all the entries of inds.
2891 auto colInds = graph.getLocalIndsViewHost(rowInfo);
2892
2893 const LO LINV = Teuchos::OrdinalTraits<LO>::invalid();
2894 for (LO j = 0; j < numElts; ++j) {
2895 const LO lclColInd = colMap.getLocalElement(inds[j]);
2896 if (lclColInd != LINV) {
2897 const size_t offset =
2898 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2899 lclColInd, hint, sorted);
2900 if (offset != rowInfo.numEntries) {
2901 if (atomic) {
2902 // NOTE (mfh 30 Nov 2015) The commented-out code is
2903 // wrong because another thread may have changed
2904 // rowVals[offset] between those two lines of code.
2905 //
2906 // const ST newVal = f (rowVals[offset], newVals[j]);
2907 // Kokkos::atomic_assign (&rowVals[offset], newVal);
2908
2909 ST* const dest = &rowVals[offset];
2910 (void)atomic_binary_function_update(dest, newVals[j], f);
2911 } else {
2912 // use binary function f
2913 rowVals[offset] = f(rowVals[offset], newVals[j]);
2915 hint = offset + 1;
2916 numValid++;
2917 }
2918 }
2919 }
2920 }
2921 // If the graph is neither locally nor globally indexed on the
2922 // calling process, that means the calling process has no graph
2923 // entries. Thus, none of the input column indices are valid.
2924
2925 return numValid;
2926}
2927
2928template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
2929LocalOrdinal
2932 const crs_graph_type& graph,
2933 const RowInfo& rowInfo,
2934 const LocalOrdinal inds[],
2935 const impl_scalar_type newVals[],
2936 const LocalOrdinal numElts,
2937 const bool atomic) {
2938 typedef LocalOrdinal LO;
2939 typedef GlobalOrdinal GO;
2940
2941 const bool sorted = graph.isSorted();
2942
2943 size_t hint = 0; // Guess for the current index k into rowVals
2944 LO numValid = 0; // number of valid local column indices
2945
2946 if (graph.isLocallyIndexed()) {
2947 // Get a view of the column indices in the row. This amortizes
2948 // the cost of getting the view over all the entries of inds.
2949 auto colInds = graph.getLocalIndsViewHost(rowInfo);
2950
2951 for (LO j = 0; j < numElts; ++j) {
2952 const LO lclColInd = inds[j];
2953 const size_t offset =
2954 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2955 lclColInd, hint, sorted);
2956 if (offset != rowInfo.numEntries) {
2957 if (atomic) {
2958 Kokkos::atomic_add(&rowVals[offset], newVals[j]);
2959 } else {
2960 rowVals[offset] += newVals[j];
2961 }
2962 hint = offset + 1;
2963 ++numValid;
2964 }
2965 }
2966 } else if (graph.isGloballyIndexed()) {
2967 if (graph.colMap_.is_null()) {
2968 return Teuchos::OrdinalTraits<LO>::invalid();
2969 }
2970 const map_type colMap = *(graph.colMap_);
2971
2972 // Get a view of the column indices in the row. This amortizes
2973 // the cost of getting the view over all the entries of inds.
2974 auto colInds = graph.getGlobalIndsViewHost(rowInfo);
2975
2976 for (LO j = 0; j < numElts; ++j) {
2977 const GO gblColInd = colMap.getGlobalElement(inds[j]);
2978 if (gblColInd != Teuchos::OrdinalTraits<GO>::invalid()) {
2979 const size_t offset =
2980 KokkosSparse::findRelOffset(colInds, rowInfo.numEntries,
2981 gblColInd, hint, sorted);
2982 if (offset != rowInfo.numEntries) {
2983 if (atomic) {
2984 Kokkos::atomic_add(&rowVals[offset], newVals[j]);
2985 } else {
2986 rowVals[offset] += newVals[j];
2987 }
2988 hint = offset + 1;
2989 ++numValid;
2990 }
2991 }
2992 }
2993 }
2994 // NOTE (mfh 26 Jun 2014, 26 Nov 2015) In the current version of
2995 // CrsGraph and CrsMatrix, it's possible for a matrix (or graph)
2996 // to be neither locally nor globally indexed on a process.
2997 // This means that the graph or matrix has no entries on that
2998 // process. Epetra also works like this. It's related to lazy
2999 // allocation (on first insertion, not at graph / matrix
3000 // construction). Lazy allocation will go away because it is
3001 // not thread scalable.
3003 return numValid;
3004}
3005
3006template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3007LocalOrdinal
3009 sumIntoLocalValues(const LocalOrdinal localRow,
3010 const Teuchos::ArrayView<const LocalOrdinal>& indices,
3011 const Teuchos::ArrayView<const Scalar>& values,
3012 const bool atomic) {
3013 using LO = local_ordinal_type;
3014 const LO numInputEnt = static_cast<LO>(indices.size());
3015 if (static_cast<LO>(values.size()) != numInputEnt) {
3016 return Teuchos::OrdinalTraits<LO>::invalid();
3017 }
3018 const LO* const inputInds = indices.getRawPtr();
3019 const scalar_type* const inputVals = values.getRawPtr();
3020 return this->sumIntoLocalValues(localRow, numInputEnt,
3021 inputVals, inputInds, atomic);
3022}
3023
3024template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3026 local_ordinal_type
3029 const local_ordinal_type localRow,
3030 const Kokkos::View<const local_ordinal_type*, Kokkos::AnonymousSpace>& inputInds,
3031 const Kokkos::View<const impl_scalar_type*, Kokkos::AnonymousSpace>& inputVals,
3032 const bool atomic) {
3033 using LO = local_ordinal_type;
3034 const LO numInputEnt = static_cast<LO>(inputInds.extent(0));
3035 if (static_cast<LO>(inputVals.extent(0)) != numInputEnt) {
3036 return Teuchos::OrdinalTraits<LO>::invalid();
3037 }
3038 const scalar_type* inVals =
3039 reinterpret_cast<const scalar_type*>(inputVals.data());
3040 return this->sumIntoLocalValues(localRow, numInputEnt, inVals,
3041 inputInds.data(), atomic);
3042}
3043
3044template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3045LocalOrdinal
3047 sumIntoLocalValues(const LocalOrdinal localRow,
3048 const LocalOrdinal numEnt,
3049 const Scalar vals[],
3050 const LocalOrdinal cols[],
3051 const bool atomic) {
3052 typedef impl_scalar_type IST;
3053 typedef LocalOrdinal LO;
3054
3055 if (!this->isFillActive() || this->staticGraph_.is_null()) {
3056 // Fill must be active and the "nonconst" graph must exist.
3057 return Teuchos::OrdinalTraits<LO>::invalid();
3058 }
3059 const crs_graph_type& graph = *(this->staticGraph_);
3060 const RowInfo rowInfo = graph.getRowInfo(localRow);
3061
3062 if (rowInfo.localRow == Teuchos::OrdinalTraits<size_t>::invalid()) {
3063 // The calling process does not own this row, so it is not
3064 // allowed to modify its values.
3065 return static_cast<LO>(0);
3066 }
3067 auto curRowVals = this->getValuesViewHostNonConst(rowInfo);
3068 const IST* const inputVals = reinterpret_cast<const IST*>(vals);
3069 return this->sumIntoLocalValuesImpl(curRowVals.data(), graph, rowInfo,
3070 cols, inputVals, numEnt, atomic);
3071}
3072
3073template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3075 values_dualv_type::t_host::const_type
3077 getValuesViewHost(const RowInfo& rowinfo) const {
3078 if (rowinfo.allocSize == 0 || valuesUnpacked_wdv.extent(0) == 0)
3079 return typename values_dualv_type::t_host::const_type();
3080 else
3081 return valuesUnpacked_wdv.getHostSubview(rowinfo.offset1D,
3082 rowinfo.allocSize,
3083 Access::ReadOnly);
3084}
3085
3086template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3088 values_dualv_type::t_host
3090 getValuesViewHostNonConst(const RowInfo& rowinfo) {
3091 if (rowinfo.allocSize == 0 || valuesUnpacked_wdv.extent(0) == 0)
3092 return typename values_dualv_type::t_host();
3093 else
3094 return valuesUnpacked_wdv.getHostSubview(rowinfo.offset1D,
3095 rowinfo.allocSize,
3096 Access::ReadWrite);
3097}
3098
3099template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3101 values_dualv_type::t_dev::const_type
3103 getValuesViewDevice(const RowInfo& rowinfo) const {
3104 if (rowinfo.allocSize == 0 || valuesUnpacked_wdv.extent(0) == 0)
3105 return typename values_dualv_type::t_dev::const_type();
3106 else
3107 return valuesUnpacked_wdv.getDeviceSubview(rowinfo.offset1D,
3108 rowinfo.allocSize,
3109 Access::ReadOnly);
3110}
3111
3112template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3114 values_dualv_type::t_dev
3116 getValuesViewDeviceNonConst(const RowInfo& rowinfo) {
3117 if (rowinfo.allocSize == 0 || valuesUnpacked_wdv.extent(0) == 0)
3118 return typename values_dualv_type::t_dev();
3119 else
3120 return valuesUnpacked_wdv.getDeviceSubview(rowinfo.offset1D,
3121 rowinfo.allocSize,
3122 Access::ReadWrite);
3123}
3124
3125template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3128 nonconst_local_inds_host_view_type& indices,
3129 nonconst_values_host_view_type& values,
3130 size_t& numEntries) const {
3131 using Teuchos::ArrayView;
3132 using Teuchos::av_reinterpret_cast;
3133 const char tfecfFuncName[] = "getLocalRowCopy: ";
3134
3135 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!this->hasColMap(), std::runtime_error,
3136 "The matrix does not have a column Map yet. This means we don't have "
3137 "local indices for columns yet, so it doesn't make sense to call this "
3138 "method. If the matrix doesn't have a column Map yet, you should call "
3139 "fillComplete on it first.");
3140
3141 const RowInfo rowinfo = staticGraph_->getRowInfo(localRow);
3142 const size_t theNumEntries = rowinfo.numEntries;
3143 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(indices.size()) < theNumEntries ||
3144 static_cast<size_t>(values.size()) < theNumEntries,
3145 std::runtime_error, "Row with local index " << localRow << " has " << theNumEntries << " entry/ies, but indices.size() = " << indices.size() << " and values.size() = " << values.size() << ".");
3146 numEntries = theNumEntries; // first side effect
3147
3148 if (rowinfo.localRow != Teuchos::OrdinalTraits<size_t>::invalid()) {
3149 if (staticGraph_->isLocallyIndexed()) {
3150 auto curLclInds = staticGraph_->getLocalIndsViewHost(rowinfo);
3151 auto curVals = getValuesViewHost(rowinfo);
3152 for (size_t j = 0; j < theNumEntries; ++j) {
3153 values[j] = curVals[j];
3154 indices[j] = curLclInds(j);
3155 }
3156 } else if (staticGraph_->isGloballyIndexed()) {
3157 // Don't call getColMap(), because it touches RCP's reference count.
3158 const map_type& colMap = *(staticGraph_->colMap_);
3159 auto curGblInds = staticGraph_->getGlobalIndsViewHost(rowinfo);
3160 auto curVals = getValuesViewHost(rowinfo);
3161
3162 for (size_t j = 0; j < theNumEntries; ++j) {
3163 values[j] = curVals[j];
3164 indices[j] = colMap.getLocalElement(curGblInds(j));
3165 }
3166 }
3167 }
3168}
3169
3170template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3173 nonconst_global_inds_host_view_type& indices,
3174 nonconst_values_host_view_type& values,
3175 size_t& numEntries) const {
3176 using Teuchos::ArrayView;
3177 using Teuchos::av_reinterpret_cast;
3178 const char tfecfFuncName[] = "getGlobalRowCopy: ";
3179
3180 const RowInfo rowinfo =
3181 staticGraph_->getRowInfoFromGlobalRowIndex(globalRow);
3182 const size_t theNumEntries = rowinfo.numEntries;
3183 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3184 static_cast<size_t>(indices.size()) < theNumEntries ||
3185 static_cast<size_t>(values.size()) < theNumEntries,
3186 std::runtime_error, "Row with global index " << globalRow << " has " << theNumEntries << " entry/ies, but indices.size() = " << indices.size() << " and values.size() = " << values.size() << ".");
3187 numEntries = theNumEntries; // first side effect
3188
3189 if (rowinfo.localRow != Teuchos::OrdinalTraits<size_t>::invalid()) {
3190 if (staticGraph_->isLocallyIndexed()) {
3191 const map_type& colMap = *(staticGraph_->colMap_);
3192 auto curLclInds = staticGraph_->getLocalIndsViewHost(rowinfo);
3193 auto curVals = getValuesViewHost(rowinfo);
3194 bool err = colMap.getGlobalElements(curLclInds.data(), numEntries, indices.data());
3195 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(err, std::runtime_error, "getGlobalElements error");
3196 // FIXME - this should/could be a kokkos deep copy?
3197 // Kokkos zero-extent views return null from .data(); glibc declares memcpy's
3198 // dst/src params __attribute__((nonnull(1,2))), so UBSan nonnull-arg fires
3199 // on a null pointer even when the byte count is zero.
3200 if (numEntries > 0) {
3201 std::memcpy((void*)values.data(), (const void*)curVals.data(), numEntries * sizeof(*values.data()));
3202 }
3203 } else if (staticGraph_->isGloballyIndexed()) {
3204 auto curGblInds = staticGraph_->getGlobalIndsViewHost(rowinfo);
3205 auto curVals = getValuesViewHost(rowinfo);
3206
3207 for (size_t j = 0; j < theNumEntries; ++j) {
3208 values[j] = curVals[j];
3209 indices[j] = curGblInds(j);
3210 }
3211 }
3212 }
3213}
3214
3215template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3217 getLocalRowView(LocalOrdinal localRow,
3218 local_inds_host_view_type& indices,
3219 values_host_view_type& values) const {
3220 const char tfecfFuncName[] = "getLocalRowView: ";
3221
3222 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3223 isGloballyIndexed(), std::runtime_error,
3224 "The matrix currently stores "
3225 "its indices as global indices, so you cannot get a view with local "
3226 "column indices. If the matrix has a column Map, you may call "
3227 "getLocalRowCopy() to get local column indices; otherwise, you may get "
3228 "a view with global column indices by calling getGlobalRowCopy().");
3229
3230 const RowInfo rowInfo = staticGraph_->getRowInfo(localRow);
3231 if (rowInfo.localRow != Teuchos::OrdinalTraits<size_t>::invalid() &&
3232 rowInfo.numEntries > 0) {
3233 indices = staticGraph_->lclIndsUnpacked_wdv.getHostSubview(
3234 rowInfo.offset1D,
3235 rowInfo.numEntries,
3236 Access::ReadOnly);
3237 values = valuesUnpacked_wdv.getHostSubview(rowInfo.offset1D,
3238 rowInfo.numEntries,
3239 Access::ReadOnly);
3240 } else {
3241 // This does the right thing (reports an empty row) if the input
3242 // row is invalid.
3243 indices = local_inds_host_view_type();
3244 values = values_host_view_type();
3245 }
3246
3247#ifdef HAVE_TPETRA_DEBUG
3248 const char suffix[] =
3249 ". This should never happen. Please report this "
3250 "bug to the Tpetra developers.";
3251 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(indices.size()) !=
3252 static_cast<size_t>(values.size()),
3253 std::logic_error,
3254 "At the end of this method, for local row " << localRow << ", "
3255 "indices.size() = "
3256 << indices.size() << " != values.size () = "
3257 << values.size() << suffix);
3258 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(indices.size()) !=
3259 static_cast<size_t>(rowInfo.numEntries),
3260 std::logic_error,
3261 "At the end of this method, for local row " << localRow << ", "
3262 "indices.size() = "
3263 << indices.size() << " != rowInfo.numEntries = "
3264 << rowInfo.numEntries << suffix);
3265 const size_t expectedNumEntries = getNumEntriesInLocalRow(localRow);
3266 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(rowInfo.numEntries != expectedNumEntries, std::logic_error,
3267 "At the end "
3268 "of this method, for local row "
3269 << localRow << ", rowInfo.numEntries = "
3270 << rowInfo.numEntries << " != getNumEntriesInLocalRow(localRow) = " << expectedNumEntries << suffix);
3271#endif // HAVE_TPETRA_DEBUG
3272}
3273
3274template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3276 getGlobalRowView(GlobalOrdinal globalRow,
3277 global_inds_host_view_type& indices,
3278 values_host_view_type& values) const {
3279 const char tfecfFuncName[] = "getGlobalRowView: ";
3280
3281 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3282 isLocallyIndexed(), std::runtime_error,
3283 "The matrix is locally indexed, so we cannot return a view of the row "
3284 "with global column indices. Use getGlobalRowCopy() instead.");
3285
3286 // This does the right thing (reports an empty row) if the input
3287 // row is invalid.
3288 const RowInfo rowInfo =
3289 staticGraph_->getRowInfoFromGlobalRowIndex(globalRow);
3290 if (rowInfo.localRow != Teuchos::OrdinalTraits<size_t>::invalid() &&
3291 rowInfo.numEntries > 0) {
3292 indices = staticGraph_->gblInds_wdv.getHostSubview(rowInfo.offset1D,
3293 rowInfo.numEntries,
3294 Access::ReadOnly);
3295 values = valuesUnpacked_wdv.getHostSubview(rowInfo.offset1D,
3296 rowInfo.numEntries,
3297 Access::ReadOnly);
3298 } else {
3299 indices = global_inds_host_view_type();
3300 values = values_host_view_type();
3301 }
3302
3303#ifdef HAVE_TPETRA_DEBUG
3304 const char suffix[] =
3305 ". This should never happen. Please report this "
3306 "bug to the Tpetra developers.";
3307 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(indices.size()) !=
3308 static_cast<size_t>(values.size()),
3309 std::logic_error,
3310 "At the end of this method, for global row " << globalRow << ", "
3311 "indices.size() = "
3312 << indices.size() << " != values.size () = "
3313 << values.size() << suffix);
3314 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(static_cast<size_t>(indices.size()) !=
3315 static_cast<size_t>(rowInfo.numEntries),
3316 std::logic_error,
3317 "At the end of this method, for global row " << globalRow << ", "
3318 "indices.size() = "
3319 << indices.size() << " != rowInfo.numEntries = "
3320 << rowInfo.numEntries << suffix);
3321 const size_t expectedNumEntries = getNumEntriesInGlobalRow(globalRow);
3322 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(rowInfo.numEntries != expectedNumEntries, std::logic_error,
3323 "At the end "
3324 "of this method, for global row "
3325 << globalRow << ", rowInfo.numEntries "
3326 "= "
3327 << rowInfo.numEntries << " != getNumEntriesInGlobalRow(globalRow) ="
3328 " "
3329 << expectedNumEntries << suffix);
3330#endif // HAVE_TPETRA_DEBUG
3331}
3332
3333template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3335 scale(const Scalar& alpha) {
3336 const impl_scalar_type theAlpha = static_cast<impl_scalar_type>(alpha);
3337
3338 const size_t nlrs = staticGraph_->getLocalNumRows();
3339 const size_t numEntries = staticGraph_->getLocalNumEntries();
3340 if (!staticGraph_->indicesAreAllocated() ||
3341 nlrs == 0 || numEntries == 0) {
3342 // do nothing
3343 } else {
3344 auto vals = valuesPacked_wdv.getDeviceView(Access::ReadWrite);
3345 KokkosBlas::scal(vals, theAlpha, vals);
3346 }
3347}
3348
3349template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3351 setAllToScalar(const Scalar& alpha) {
3352 const impl_scalar_type theAlpha = static_cast<impl_scalar_type>(alpha);
3353
3354 // replace all values in the matrix
3355 // it is easiest to replace all allocated values, instead of replacing only the ones with valid entries
3356 // however, if there are no valid entries, we can short-circuit
3357 // furthermore, if the values aren't allocated, we can short-circuit (no entry have been inserted so far)
3358 const size_t numEntries = staticGraph_->getLocalNumEntries();
3359 if (!staticGraph_->indicesAreAllocated() || numEntries == 0) {
3360 // do nothing
3361 } else {
3362 // DEEP_COPY REVIEW - VALUE-TO-DEVICE
3363 Kokkos::deep_copy(execution_space(), valuesUnpacked_wdv.getDeviceView(Access::OverwriteAll),
3364 theAlpha);
3365 // CAG: This fence was found to be required on Cuda with UVM=on.
3366 Kokkos::fence("CrsMatrix::setAllToScalar");
3367 }
3368}
3369
3370template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3372 setAllValues(const typename local_graph_device_type::row_map_type& rowPointers,
3373 const typename local_graph_device_type::entries_type::non_const_type& columnIndices,
3374 const typename local_matrix_device_type::values_type& values) {
3375 using ProfilingRegion = Details::ProfilingRegion;
3376 ProfilingRegion region("Tpetra::CrsMatrix::setAllValues");
3377 const char tfecfFuncName[] = "setAllValues: ";
3378 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(columnIndices.size() != values.size(), std::invalid_argument,
3379 "columnIndices.size() = " << columnIndices.size() << " != values.size()"
3380 " = "
3381 << values.size() << ".");
3382 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(myGraph_.is_null(), std::runtime_error, "myGraph_ must not be null.");
3383
3384 try {
3385 myGraph_->setAllIndices(rowPointers, columnIndices);
3386 } catch (std::exception& e) {
3387 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
3388 "myGraph_->setAllIndices() threw an "
3389 "exception: "
3390 << e.what());
3391 }
3392
3393 // Make sure that myGraph_ now has a local graph. It may not be
3394 // fillComplete yet, so it's important to check. We don't care
3395 // whether setAllIndices() did a shallow copy or a deep copy, so a
3396 // good way to check is to compare dimensions.
3397 auto lclGraph = myGraph_->getLocalGraphDevice();
3398 const size_t numEnt = lclGraph.entries.extent(0);
3399 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(lclGraph.row_map.extent(0) != rowPointers.extent(0) ||
3400 numEnt != static_cast<size_t>(columnIndices.extent(0)),
3401 std::logic_error,
3402 "myGraph_->setAllIndices() did not correctly create "
3403 "local graph. Please report this bug to the Tpetra developers.");
3404
3405 valuesPacked_wdv = values_wdv_type(values);
3406 valuesUnpacked_wdv = valuesPacked_wdv;
3407
3408 // Storage MUST be packed, since the interface doesn't give any
3409 // way to indicate any extra space at the end of each row.
3410 this->storageStatus_ = Details::STORAGE_1D_PACKED;
3411
3413}
3414
3415template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3417 setAllValues(const local_matrix_device_type& localDeviceMatrix) {
3418 using ProfilingRegion = Details::ProfilingRegion;
3419 ProfilingRegion region("Tpetra::CrsMatrix::setAllValues from KokkosSparse::CrsMatrix");
3420
3421 auto graph = localDeviceMatrix.graph;
3422 // FIXME how to check whether graph is allocated
3423
3424 auto rows = graph.row_map;
3425 auto columns = graph.entries;
3426 auto values = localDeviceMatrix.values;
3427
3428 setAllValues(rows, columns, values);
3429}
3430
3431template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3433 setAllValues(const Teuchos::ArrayRCP<size_t>& ptr,
3434 const Teuchos::ArrayRCP<LocalOrdinal>& ind,
3435 const Teuchos::ArrayRCP<Scalar>& val) {
3436 using Kokkos::Compat::getKokkosViewDeepCopy;
3437 using Teuchos::ArrayRCP;
3438 using Teuchos::av_reinterpret_cast;
3439 typedef device_type DT;
3440 typedef impl_scalar_type IST;
3441 typedef typename local_graph_device_type::row_map_type row_map_type;
3442 // typedef typename row_map_type::non_const_value_type row_offset_type;
3443 const char tfecfFuncName[] = "setAllValues(ArrayRCP<size_t>, ArrayRCP<LO>, ArrayRCP<Scalar>): ";
3444
3445 // The row offset type may depend on the execution space. It may
3446 // not necessarily be size_t. If it's not, we need to make a deep
3447 // copy. We need to make a deep copy anyway so that Kokkos can
3448 // own the memory. Regardless, ptrIn gets the copy.
3449 typename row_map_type::non_const_type ptrNative("ptr", ptr.size());
3450 Kokkos::View<const size_t*,
3451 typename row_map_type::array_layout,
3452 Kokkos::HostSpace,
3453 Kokkos::MemoryUnmanaged>
3454 ptrSizeT(ptr.getRawPtr(), ptr.size());
3455 ::Tpetra::Details::copyOffsets(ptrNative, ptrSizeT);
3456
3457 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(ptrNative.extent(0) != ptrSizeT.extent(0),
3458 std::logic_error, "ptrNative.extent(0) = " << ptrNative.extent(0) << " != ptrSizeT.extent(0) = " << ptrSizeT.extent(0) << ". Please report this bug to the "
3459 "Tpetra developers.");
3460
3461 auto indIn = getKokkosViewDeepCopy<DT>(ind());
3462 auto valIn = getKokkosViewDeepCopy<DT>(av_reinterpret_cast<IST>(val()));
3463 this->setAllValues(ptrNative, indIn, valIn);
3464}
3465
3466template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3468 getLocalDiagOffsets(Teuchos::ArrayRCP<size_t>& offsets) const {
3469 const char tfecfFuncName[] = "getLocalDiagOffsets: ";
3470 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(staticGraph_.is_null(), std::runtime_error, "The matrix has no graph.");
3471
3472 // mfh 11 May 2016: We plan to deprecate the ArrayRCP version of
3473 // this method in CrsGraph too, so don't call it (otherwise build
3474 // warnings will show up and annoy users). Instead, copy results
3475 // in and out, if the memory space requires it.
3476
3477 const size_t lclNumRows = staticGraph_->getLocalNumRows();
3478 if (static_cast<size_t>(offsets.size()) < lclNumRows) {
3479 offsets.resize(lclNumRows);
3480 }
3481
3482 // The input ArrayRCP must always be a host pointer. Thus, if
3483 // device_type::memory_space is Kokkos::HostSpace, it's OK for us
3484 // to write to that allocation directly as a Kokkos::View.
3485 if (std::is_same<memory_space, Kokkos::HostSpace>::value) {
3486 // It is always syntactically correct to assign a raw host
3487 // pointer to a device View, so this code will compile correctly
3488 // even if this branch never runs.
3489 typedef Kokkos::View<size_t*, device_type,
3490 Kokkos::MemoryUnmanaged>
3491 output_type;
3492 output_type offsetsOut(offsets.getRawPtr(), lclNumRows);
3493 staticGraph_->getLocalDiagOffsets(offsetsOut);
3494 } else {
3495 Kokkos::View<size_t*, device_type> offsetsTmp("diagOffsets", lclNumRows);
3496 staticGraph_->getLocalDiagOffsets(offsetsTmp);
3497 typedef Kokkos::View<size_t*, Kokkos::HostSpace,
3498 Kokkos::MemoryUnmanaged>
3499 output_type;
3500 output_type offsetsOut(offsets.getRawPtr(), lclNumRows);
3501 // DEEP_COPY REVIEW - DEVICE-TO-HOST
3502 Kokkos::deep_copy(execution_space(), offsetsOut, offsetsTmp);
3503 // Fence so offsets is safe to access by caller immediately
3504 execution_space().fence();
3505 }
3506}
3507
3508template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3511 using Teuchos::ArrayRCP;
3512 using Teuchos::ArrayView;
3513 using Teuchos::av_reinterpret_cast;
3514 const char tfecfFuncName[] = "getLocalDiagCopy (1-arg): ";
3515 typedef local_ordinal_type LO;
3516
3517 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3518 staticGraph_.is_null(), std::runtime_error,
3519 "This method requires that the matrix have a graph.");
3520 auto rowMapPtr = this->getRowMap();
3521 if (rowMapPtr.is_null() || rowMapPtr->getComm().is_null()) {
3522 // Processes on which the row Map or its communicator is null
3523 // don't participate. Users shouldn't even call this method on
3524 // those processes.
3525 return;
3526 }
3527 auto colMapPtr = this->getColMap();
3528 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!this->hasColMap() || colMapPtr.is_null(), std::runtime_error,
3529 "This method requires that the matrix have a column Map.");
3530 const map_type& rowMap = *rowMapPtr;
3531 const map_type& colMap = *colMapPtr;
3532 const LO myNumRows = static_cast<LO>(this->getLocalNumRows());
3533
3534#ifdef HAVE_TPETRA_DEBUG
3535 // isCompatible() requires an all-reduce, and thus this check
3536 // should only be done in debug mode.
3537 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3538 !diag.getMap()->isCompatible(rowMap), std::runtime_error,
3539 "The input Vector's Map must be compatible with the CrsMatrix's row "
3540 "Map. You may check this by using Map's isCompatible method: "
3541 "diag.getMap ()->isCompatible (A.getRowMap ());");
3542#endif // HAVE_TPETRA_DEBUG
3543
3544 const auto D_lcl = diag.getLocalViewDevice(Access::OverwriteAll);
3545 // 1-D subview of the first (and only) column of D_lcl.
3546 const auto D_lcl_1d =
3547 Kokkos::subview(D_lcl, Kokkos::make_pair(LO(0), myNumRows), 0);
3548
3549 const auto lclRowMap = rowMap.getLocalMap();
3550 const auto lclColMap = colMap.getLocalMap();
3552 (void)getDiagCopyWithoutOffsets(D_lcl_1d, lclRowMap,
3553 lclColMap,
3555}
3556
3557template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3560 const Kokkos::View<const size_t*, device_type,
3561 Kokkos::MemoryUnmanaged>& offsets) const {
3562 typedef LocalOrdinal LO;
3563
3564#ifdef HAVE_TPETRA_DEBUG
3565 const char tfecfFuncName[] = "getLocalDiagCopy: ";
3566 const map_type& rowMap = *(this->getRowMap());
3567 // isCompatible() requires an all-reduce, and thus this check
3568 // should only be done in debug mode.
3569 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3570 !diag.getMap()->isCompatible(rowMap), std::runtime_error,
3571 "The input Vector's Map must be compatible with (in the sense of Map::"
3572 "isCompatible) the CrsMatrix's row Map.");
3573#endif // HAVE_TPETRA_DEBUG
3574
3575 // For now, we fill the Vector on the host and sync to device.
3576 // Later, we may write a parallel kernel that works entirely on
3577 // device.
3578 //
3579 // NOTE (mfh 21 Jan 2016): The host kernel here assumes UVM. Once
3580 // we write a device kernel, it will not need to assume UVM.
3581
3582 auto D_lcl = diag.getLocalViewDevice(Access::OverwriteAll);
3583 const LO myNumRows = static_cast<LO>(this->getLocalNumRows());
3584 // Get 1-D subview of the first (and only) column of D_lcl.
3585 auto D_lcl_1d =
3586 Kokkos::subview(D_lcl, Kokkos::make_pair(LO(0), myNumRows), 0);
3587
3588 KokkosSparse::getDiagCopy(D_lcl_1d, offsets,
3590}
3591
3592template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3595 const Teuchos::ArrayView<const size_t>& offsets) const {
3596 using LO = LocalOrdinal;
3597 using host_execution_space = Kokkos::DefaultHostExecutionSpace;
3598 using IST = impl_scalar_type;
3599
3600#ifdef HAVE_TPETRA_DEBUG
3601 const char tfecfFuncName[] = "getLocalDiagCopy: ";
3602 const map_type& rowMap = *(this->getRowMap());
3603 // isCompatible() requires an all-reduce, and thus this check
3604 // should only be done in debug mode.
3605 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3606 !diag.getMap()->isCompatible(rowMap), std::runtime_error,
3607 "The input Vector's Map must be compatible with (in the sense of Map::"
3608 "isCompatible) the CrsMatrix's row Map.");
3609#endif // HAVE_TPETRA_DEBUG
3610
3611 // See #1510. In case diag has already been marked modified on
3612 // device, we need to clear that flag, since the code below works
3613 // on host.
3614 // diag.clear_sync_state ();
3615
3616 // For now, we fill the Vector on the host and sync to device.
3617 // Later, we may write a parallel kernel that works entirely on
3618 // device.
3619 auto lclVecHost = diag.getLocalViewHost(Access::OverwriteAll);
3620 // 1-D subview of the first (and only) column of lclVecHost.
3621 auto lclVecHost1d = Kokkos::subview(lclVecHost, Kokkos::ALL(), 0);
3622
3623 using host_offsets_view_type =
3624 Kokkos::View<const size_t*, Kokkos::HostSpace,
3625 Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
3626 host_offsets_view_type h_offsets(offsets.getRawPtr(), offsets.size());
3627 // Find the diagonal entries and put them in lclVecHost1d.
3628 using range_type = Kokkos::RangePolicy<host_execution_space, LO>;
3629 const LO myNumRows = static_cast<LO>(this->getLocalNumRows());
3630 const size_t INV = Tpetra::Details::OrdinalTraits<size_t>::invalid();
3631
3632 auto rowPtrsPackedHost = staticGraph_->getRowPtrsPackedHost();
3633 auto valuesPackedHost = valuesPacked_wdv.getHostView(Access::ReadOnly);
3634 Kokkos::parallel_for("Tpetra::CrsMatrix::getLocalDiagCopy",
3635 range_type(0, myNumRows),
3636 [&, INV, h_offsets](const LO lclRow) { // Value capture is a workaround for cuda + gcc-7.2 compiler bug w/c++14
3637 lclVecHost1d(lclRow) = STS::zero(); // default value if no diag entry
3638 if (h_offsets[lclRow] != INV) {
3639 auto curRowOffset = rowPtrsPackedHost(lclRow);
3640 lclVecHost1d(lclRow) =
3641 static_cast<IST>(valuesPackedHost(curRowOffset + h_offsets[lclRow]));
3642 }
3643 });
3644 // diag.sync_device ();
3645}
3646
3647template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3650 using Teuchos::ArrayRCP;
3651 using Teuchos::ArrayView;
3652 using Teuchos::null;
3653 using Teuchos::RCP;
3654 using Teuchos::rcp;
3655 using Teuchos::rcpFromRef;
3658 const char tfecfFuncName[] = "leftScale: ";
3659
3660 ProfilingRegion region("Tpetra::CrsMatrix::leftScale");
3661
3662 RCP<const vec_type> xp;
3663 if (this->getRangeMap()->isSameAs(*(x.getMap()))) {
3664 // Take from Epetra: If we have a non-trivial exporter, we must
3665 // import elements that are permuted or are on other processors.
3666 auto exporter = this->getCrsGraphRef().getExporter();
3667 if (exporter.get() != nullptr) {
3668 RCP<vec_type> tempVec(new vec_type(this->getRowMap()));
3669 tempVec->doImport(x, *exporter, REPLACE); // reverse mode
3670 xp = tempVec;
3671 } else {
3672 xp = rcpFromRef(x);
3673 }
3674 } else if (this->getRowMap()->isSameAs(*(x.getMap()))) {
3675 xp = rcpFromRef(x);
3676 } else {
3677 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::invalid_argument,
3678 "x's Map must be the same as "
3679 "either the row Map or the range Map of the CrsMatrix.");
3680 }
3681
3682 if (this->isFillComplete()) {
3683 auto x_lcl = xp->getLocalViewDevice(Access::ReadOnly);
3684 auto x_lcl_1d = Kokkos::subview(x_lcl, Kokkos::ALL(), 0);
3686 leftScaleLocalCrsMatrix(getLocalMatrixDevice(),
3687 x_lcl_1d, false, false);
3688 } else {
3689 // 6/2020 Disallow leftScale of non-fillComplete matrices #7446
3690 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
3691 "CrsMatrix::leftScale requires matrix to be"
3692 " fillComplete");
3693 }
3694}
3695
3696template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3699 using Teuchos::ArrayRCP;
3700 using Teuchos::ArrayView;
3701 using Teuchos::null;
3702 using Teuchos::RCP;
3703 using Teuchos::rcp;
3704 using Teuchos::rcpFromRef;
3707 const char tfecfFuncName[] = "rightScale: ";
3708
3709 ProfilingRegion region("Tpetra::CrsMatrix::rightScale");
3710
3711 RCP<const vec_type> xp;
3712 if (this->getDomainMap()->isSameAs(*(x.getMap()))) {
3713 // Take from Epetra: If we have a non-trivial exporter, we must
3714 // import elements that are permuted or are on other processors.
3715 auto importer = this->getCrsGraphRef().getImporter();
3716 if (importer.get() != nullptr) {
3717 RCP<vec_type> tempVec(new vec_type(this->getColMap()));
3718 tempVec->doImport(x, *importer, REPLACE);
3719 xp = tempVec;
3720 } else {
3721 xp = rcpFromRef(x);
3722 }
3723 } else if (this->getColMap()->isSameAs(*(x.getMap()))) {
3724 xp = rcpFromRef(x);
3725 } else {
3726 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
3727 "x's Map must be the same as "
3728 "either the domain Map or the column Map of the CrsMatrix.");
3729 }
3730
3731 if (this->isFillComplete()) {
3732 auto x_lcl = xp->getLocalViewDevice(Access::ReadOnly);
3733 auto x_lcl_1d = Kokkos::subview(x_lcl, Kokkos::ALL(), 0);
3735 rightScaleLocalCrsMatrix(getLocalMatrixDevice(),
3736 x_lcl_1d, false, false);
3737 } else {
3738 // 6/2020 Disallow rightScale of non-fillComplete matrices #7446
3739 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::runtime_error,
3740 "CrsMatrix::rightScale requires matrix to be"
3741 " fillComplete");
3742 }
3743}
3744
3745template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3748 auto equilInfo = computeRowOneNorms(*this);
3749 mag_type myMax;
3750 using range_type = Kokkos::RangePolicy<execution_space, local_ordinal_type>;
3751 Kokkos::parallel_reduce(
3752 "getNormInf", range_type(0, equilInfo.rowNorms.extent(0)),
3753 KOKKOS_LAMBDA(local_ordinal_type i, mag_type & max) {
3754 max = Kokkos::max(max, equilInfo.rowNorms(i));
3755 },
3756 Kokkos::Max<mag_type>(myMax));
3757 mag_type totalMax = STM::zero();
3758 Teuchos::reduceAll<int, mag_type>(*(getComm()), Teuchos::REDUCE_MAX, myMax,
3759 Teuchos::outArg(totalMax));
3760 return totalMax;
3761}
3762
3763template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3766 getNorm1(const bool assumeSymmetric) const {
3767 if (assumeSymmetric)
3768 return getNormInf();
3769 auto equilInfo = computeRowAndColumnOneNorms(*this, false);
3770 mag_type myMax;
3771 using range_type = Kokkos::RangePolicy<execution_space, local_ordinal_type>;
3772 Kokkos::parallel_reduce(
3773 "getNorm1", range_type(0, equilInfo.colNorms.extent(0)),
3774 KOKKOS_LAMBDA(local_ordinal_type i, mag_type & max) {
3775 max = Kokkos::max(max, equilInfo.colNorms(i));
3776 },
3777 Kokkos::Max<mag_type>(myMax));
3778 mag_type totalMax = STM::zero();
3779 Teuchos::reduceAll<int, mag_type>(*(getComm()), Teuchos::REDUCE_MAX, myMax,
3780 Teuchos::outArg(totalMax));
3781 return totalMax;
3782}
3784template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3787 getFrobeniusNorm() const {
3788 using Teuchos::ArrayView;
3789 using Teuchos::outArg;
3790 using Teuchos::REDUCE_SUM;
3791 using Teuchos::reduceAll;
3792
3793 // FIXME (mfh 05 Aug 2014) Write a thread-parallel kernel for the
3794 // local part of this computation. It could make sense to put
3795 // this operation in the Kokkos::CrsMatrix.
3796
3797 // check the cache first
3798 mag_type mySum = STM::zero();
3799 if (getLocalNumEntries() > 0) {
3800 if (isStorageOptimized()) {
3801 // "Optimized" storage is packed storage. That means we can
3802 // iterate in one pass through the 1-D values array.
3803 const size_t numEntries = getLocalNumEntries();
3804 auto values = valuesPacked_wdv.getHostView(Access::ReadOnly);
3805 for (size_t k = 0; k < numEntries; ++k) {
3806 auto val = values[k];
3807 // Note (etp 06 Jan 2015) We need abs() here for composite types
3808 // (in general, if mag_type is on the left-hand-side, we need
3809 // abs() on the right-hand-side)
3810 const mag_type val_abs = STS::abs(val);
3811 mySum += val_abs * val_abs;
3812 }
3813 } else {
3814 const LocalOrdinal numRows =
3815 static_cast<LocalOrdinal>(this->getLocalNumRows());
3816 for (LocalOrdinal r = 0; r < numRows; ++r) {
3817 const RowInfo rowInfo = myGraph_->getRowInfo(r);
3818 const size_t numEntries = rowInfo.numEntries;
3819 auto A_r = this->getValuesViewHost(rowInfo);
3820 for (size_t k = 0; k < numEntries; ++k) {
3821 const impl_scalar_type val = A_r[k];
3822 const mag_type val_abs = STS::abs(val);
3823 mySum += val_abs * val_abs;
3824 }
3825 }
3826 }
3827 }
3828 mag_type totalSum = STM::zero();
3829 reduceAll<int, mag_type>(*(getComm()), REDUCE_SUM,
3830 mySum, outArg(totalSum));
3831 return STM::sqrt(totalSum);
3832}
3833
3834template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3836 replaceColMap(const Teuchos::RCP<const map_type>& newColMap) {
3837 const char tfecfFuncName[] = "replaceColMap: ";
3838 // FIXME (mfh 06 Aug 2014) What if the graph is locally indexed?
3839 // Then replacing the column Map might mean that we need to
3840 // reindex the column indices.
3841 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3842 myGraph_.is_null(), std::runtime_error,
3843 "This method does not work if the matrix has a const graph. The whole "
3844 "idea of a const graph is that you are not allowed to change it, but "
3845 "this method necessarily must modify the graph, since the graph owns "
3846 "the matrix's column Map.");
3847 myGraph_->replaceColMap(newColMap);
3848}
3849
3850template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3852 reindexColumns(crs_graph_type* const graph,
3853 const Teuchos::RCP<const map_type>& newColMap,
3854 const Teuchos::RCP<const import_type>& newImport,
3855 const bool sortEachRow) {
3856 const char tfecfFuncName[] = "reindexColumns: ";
3857 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3858 graph == nullptr && myGraph_.is_null(), std::invalid_argument,
3859 "The input graph is null, but the matrix does not own its graph.");
3860
3861 crs_graph_type& theGraph = (graph == nullptr) ? *myGraph_ : *graph;
3862 const bool sortGraph = false; // we'll sort graph & matrix together below
3863
3864 theGraph.reindexColumns(newColMap, newImport, sortGraph);
3865
3866 if (sortEachRow && theGraph.isLocallyIndexed() && !theGraph.isSorted()) {
3867 const LocalOrdinal lclNumRows =
3868 static_cast<LocalOrdinal>(theGraph.getLocalNumRows());
3869
3870 for (LocalOrdinal row = 0; row < lclNumRows; ++row) {
3871 const RowInfo rowInfo = theGraph.getRowInfo(row);
3872 auto lclColInds = theGraph.getLocalIndsViewHostNonConst(rowInfo);
3873 auto vals = this->getValuesViewHostNonConst(rowInfo);
3875 sort2(lclColInds.data(),
3876 lclColInds.data() + rowInfo.numEntries,
3877 vals.data());
3878 }
3879 theGraph.indicesAreSorted_ = true;
3880 }
3881}
3882
3883template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3885 replaceDomainMap(const Teuchos::RCP<const map_type>& newDomainMap) {
3886 const char tfecfFuncName[] = "replaceDomainMap: ";
3887 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3888 myGraph_.is_null(), std::runtime_error,
3889 "This method does not work if the matrix has a const graph. The whole "
3890 "idea of a const graph is that you are not allowed to change it, but this"
3891 " method necessarily must modify the graph, since the graph owns the "
3892 "matrix's domain Map and Import objects.");
3893 myGraph_->replaceDomainMap(newDomainMap);
3894}
3895
3896template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3898 replaceDomainMapAndImporter(const Teuchos::RCP<const map_type>& newDomainMap,
3899 Teuchos::RCP<const import_type>& newImporter) {
3900 const char tfecfFuncName[] = "replaceDomainMapAndImporter: ";
3901 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3902 myGraph_.is_null(), std::runtime_error,
3903 "This method does not work if the matrix has a const graph. The whole "
3904 "idea of a const graph is that you are not allowed to change it, but this"
3905 " method necessarily must modify the graph, since the graph owns the "
3906 "matrix's domain Map and Import objects.");
3907 myGraph_->replaceDomainMapAndImporter(newDomainMap, newImporter);
3908}
3910template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3912 replaceRangeMap(const Teuchos::RCP<const map_type>& newRangeMap) {
3913 const char tfecfFuncName[] = "replaceRangeMap: ";
3914 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3915 myGraph_.is_null(), std::runtime_error,
3916 "This method does not work if the matrix has a const graph. The whole "
3917 "idea of a const graph is that you are not allowed to change it, but this"
3918 " method necessarily must modify the graph, since the graph owns the "
3919 "matrix's domain Map and Import objects.");
3920 myGraph_->replaceRangeMap(newRangeMap);
3921}
3922
3923template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3925 replaceRangeMapAndExporter(const Teuchos::RCP<const map_type>& newRangeMap,
3926 Teuchos::RCP<const export_type>& newExporter) {
3927 const char tfecfFuncName[] = "replaceRangeMapAndExporter: ";
3928 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
3929 myGraph_.is_null(), std::runtime_error,
3930 "This method does not work if the matrix has a const graph. The whole "
3931 "idea of a const graph is that you are not allowed to change it, but this"
3932 " method necessarily must modify the graph, since the graph owns the "
3933 "matrix's domain Map and Import objects.");
3934 myGraph_->replaceRangeMapAndExporter(newRangeMap, newExporter);
3935}
3936
3937template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3939 insertNonownedGlobalValues(const GlobalOrdinal globalRow,
3940 const Teuchos::ArrayView<const GlobalOrdinal>& indices,
3941 const Teuchos::ArrayView<const Scalar>& values) {
3942 using Teuchos::Array;
3943 typedef GlobalOrdinal GO;
3944 typedef typename Array<GO>::size_type size_type;
3945
3946 const size_type numToInsert = indices.size();
3947 // Add the new data to the list of nonlocals.
3948 // This creates the arrays if they don't exist yet.
3949 std::pair<Array<GO>, Array<Scalar>>& curRow = nonlocals_[globalRow];
3950 Array<GO>& curRowInds = curRow.first;
3951 Array<Scalar>& curRowVals = curRow.second;
3952 const size_type newCapacity = curRowInds.size() + numToInsert;
3953 curRowInds.reserve(newCapacity);
3954 curRowVals.reserve(newCapacity);
3955 for (size_type k = 0; k < numToInsert; ++k) {
3956 curRowInds.push_back(indices[k]);
3957 curRowVals.push_back(values[k]);
3958 }
3959}
3960
3961template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
3964 using Details::Behavior;
3966 using std::endl;
3967 using Teuchos::Comm;
3968 using Teuchos::outArg;
3969 using Teuchos::RCP;
3970 using Teuchos::rcp;
3971 using Teuchos::REDUCE_MAX;
3972 using Teuchos::REDUCE_MIN;
3973 using Teuchos::reduceAll;
3974 typedef CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node> crs_matrix_type;
3975 // typedef LocalOrdinal LO;
3976 typedef GlobalOrdinal GO;
3977 typedef typename Teuchos::Array<GO>::size_type size_type;
3978 const char tfecfFuncName[] = "globalAssemble: "; // for exception macro
3979 ProfilingRegion regionGlobalAssemble("Tpetra::CrsMatrix::globalAssemble");
3980
3981 const bool verbose = Behavior::verbose("CrsMatrix");
3982 std::unique_ptr<std::string> prefix;
3983 if (verbose) {
3984 prefix = this->createPrefix("CrsMatrix", "globalAssemble");
3985 std::ostringstream os;
3986 os << *prefix << "nonlocals_.size()=" << nonlocals_.size()
3987 << endl;
3988 std::cerr << os.str();
3989 }
3990 RCP<const Comm<int>> comm = getComm();
3991
3992 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillActive(), std::runtime_error,
3993 "Fill must be active before "
3994 "you may call this method.");
3995
3996 const size_t myNumNonlocalRows = nonlocals_.size();
3997
3998 // If no processes have nonlocal rows, then we don't have to do
3999 // anything. Checking this is probably cheaper than constructing
4000 // the Map of nonlocal rows (see below) and noticing that it has
4001 // zero global entries.
4002 {
4003 const int iHaveNonlocalRows = (myNumNonlocalRows == 0) ? 0 : 1;
4004 int someoneHasNonlocalRows = 0;
4005 reduceAll<int, int>(*comm, REDUCE_MAX, iHaveNonlocalRows,
4006 outArg(someoneHasNonlocalRows));
4007 if (someoneHasNonlocalRows == 0) {
4008 return; // no process has nonlocal rows, so nothing to do
4009 }
4010 }
4011
4012 // 1. Create a list of the "nonlocal" rows on each process. this
4013 // requires iterating over nonlocals_, so while we do this,
4014 // deduplicate the entries and get a count for each nonlocal
4015 // row on this process.
4016 // 2. Construct a new row Map corresponding to those rows. This
4017 // Map is likely overlapping. We know that the Map is not
4018 // empty on all processes, because the above all-reduce and
4019 // return exclude that case.
4020
4021 RCP<const map_type> nonlocalRowMap;
4022 Teuchos::Array<size_t> numEntPerNonlocalRow(myNumNonlocalRows);
4023 {
4024 Teuchos::Array<GO> myNonlocalGblRows(myNumNonlocalRows);
4025 size_type curPos = 0;
4026 for (auto mapIter = nonlocals_.begin(); mapIter != nonlocals_.end();
4027 ++mapIter, ++curPos) {
4028 myNonlocalGblRows[curPos] = mapIter->first;
4029 // Get the values and column indices by reference, since we
4030 // intend to change them in place (that's what "erase" does).
4031 Teuchos::Array<GO>& gblCols = (mapIter->second).first;
4032 Teuchos::Array<Scalar>& vals = (mapIter->second).second;
4033
4034 // Sort both arrays jointly, using the column indices as keys,
4035 // then merge them jointly. "Merge" here adds values
4036 // corresponding to the same column indices. The first 2 args
4037 // of merge2 are output arguments that work just like the
4038 // return value of std::unique.
4039 sort2(gblCols.begin(), gblCols.end(), vals.begin());
4040 typename Teuchos::Array<GO>::iterator gblCols_newEnd;
4041 typename Teuchos::Array<Scalar>::iterator vals_newEnd;
4042 merge2(gblCols_newEnd, vals_newEnd,
4043 gblCols.begin(), gblCols.end(),
4044 vals.begin(), vals.end());
4045 gblCols.erase(gblCols_newEnd, gblCols.end());
4046 vals.erase(vals_newEnd, vals.end());
4047 numEntPerNonlocalRow[curPos] = gblCols.size();
4048 }
4049
4050 // Currently, Map requires that its indexBase be the global min
4051 // of all its global indices. Map won't compute this for us, so
4052 // we must do it. If our process has no nonlocal rows, set the
4053 // "min" to the max possible GO value. This ensures that if
4054 // some process has at least one nonlocal row, then it will pick
4055 // that up as the min. We know that at least one process has a
4056 // nonlocal row, since the all-reduce and return at the top of
4057 // this method excluded that case.
4058 GO myMinNonlocalGblRow = std::numeric_limits<GO>::max();
4059 {
4060 auto iter = std::min_element(myNonlocalGblRows.begin(),
4061 myNonlocalGblRows.end());
4062 if (iter != myNonlocalGblRows.end()) {
4063 myMinNonlocalGblRow = *iter;
4064 }
4065 }
4066 GO gblMinNonlocalGblRow = 0;
4067 reduceAll<int, GO>(*comm, REDUCE_MIN, myMinNonlocalGblRow,
4068 outArg(gblMinNonlocalGblRow));
4069 const GO indexBase = gblMinNonlocalGblRow;
4070 const global_size_t INV = Teuchos::OrdinalTraits<global_size_t>::invalid();
4071 nonlocalRowMap = rcp(new map_type(INV, myNonlocalGblRows(), indexBase, comm));
4072 }
4073
4074 // 3. Use the values and column indices for each nonlocal row, as
4075 // stored in nonlocals_, to construct a CrsMatrix corresponding
4076 // to nonlocal rows. We have
4077 // exact counts of the number of entries in each nonlocal row.
4078
4079 if (verbose) {
4080 std::ostringstream os;
4081 os << *prefix << "Create nonlocal matrix" << endl;
4082 std::cerr << os.str();
4083 }
4084 RCP<crs_matrix_type> nonlocalMatrix =
4085 rcp(new crs_matrix_type(nonlocalRowMap, numEntPerNonlocalRow()));
4086 {
4087 size_type curPos = 0;
4088 for (auto mapIter = nonlocals_.begin(); mapIter != nonlocals_.end();
4089 ++mapIter, ++curPos) {
4090 const GO gblRow = mapIter->first;
4091 // Get values & column indices by ref, just to avoid copy.
4092 Teuchos::Array<GO>& gblCols = (mapIter->second).first;
4093 Teuchos::Array<Scalar>& vals = (mapIter->second).second;
4094 // const LO numEnt = static_cast<LO> (numEntPerNonlocalRow[curPos]);
4095 nonlocalMatrix->insertGlobalValues(gblRow, gblCols(), vals());
4096 }
4097 }
4098 // There's no need to fill-complete the nonlocals matrix.
4099 // We just use it as a temporary container for the Export.
4100
4101 // 4. If the original row Map is one to one, then we can Export
4102 // directly from nonlocalMatrix into this. Otherwise, we have
4103 // to create a temporary matrix with a one-to-one row Map,
4104 // Export into that, then Import from the temporary matrix into
4105 // *this.
4106
4107 auto origRowMap = this->getRowMap();
4108 const bool origRowMapIsOneToOne = origRowMap->isOneToOne();
4109
4110 int isLocallyComplete = 1; // true by default
4111
4112 if (origRowMapIsOneToOne) {
4113 if (verbose) {
4114 std::ostringstream os;
4115 os << *prefix << "Original row Map is 1-to-1" << endl;
4116 std::cerr << os.str();
4117 }
4118 export_type exportToOrig(nonlocalRowMap, origRowMap);
4119 if (!exportToOrig.isLocallyComplete()) {
4120 isLocallyComplete = 0;
4121 }
4122 if (verbose) {
4123 std::ostringstream os;
4124 os << *prefix << "doExport from nonlocalMatrix" << endl;
4125 std::cerr << os.str();
4126 }
4127 this->doExport(*nonlocalMatrix, exportToOrig, Tpetra::ADD);
4128 // We're done at this point!
4129 } else {
4130 if (verbose) {
4131 std::ostringstream os;
4132 os << *prefix << "Original row Map is NOT 1-to-1" << endl;
4133 std::cerr << os.str();
4134 }
4135 // If you ask a Map whether it is one to one, it does some
4136 // communication and stashes intermediate results for later use
4137 // by createOneToOne. Thus, calling createOneToOne doesn't cost
4138 // much more then the original cost of calling isOneToOne.
4139 auto oneToOneRowMap = Tpetra::createOneToOne(origRowMap);
4140 export_type exportToOneToOne(nonlocalRowMap, oneToOneRowMap);
4141 if (!exportToOneToOne.isLocallyComplete()) {
4142 isLocallyComplete = 0;
4143 }
4144
4145 // Create a temporary matrix with the one-to-one row Map.
4146 //
4147 // TODO (mfh 09 Sep 2016, 12 Sep 2016) Estimate # entries in
4148 // each row, to avoid reallocation during the Export operation.
4149 if (verbose) {
4150 std::ostringstream os;
4151 os << *prefix << "Create & doExport into 1-to-1 matrix"
4152 << endl;
4153 std::cerr << os.str();
4154 }
4155 crs_matrix_type oneToOneMatrix(oneToOneRowMap, 0);
4156 // Export from matrix of nonlocals into the temp one-to-one matrix.
4157 oneToOneMatrix.doExport(*nonlocalMatrix, exportToOneToOne,
4158 Tpetra::ADD);
4159
4160 // We don't need the matrix of nonlocals anymore, so get rid of
4161 // it, to keep the memory high-water mark down.
4162 if (verbose) {
4163 std::ostringstream os;
4164 os << *prefix << "Free nonlocalMatrix" << endl;
4165 std::cerr << os.str();
4166 }
4167 nonlocalMatrix = Teuchos::null;
4168
4169 // Import from the one-to-one matrix to the original matrix.
4170 if (verbose) {
4171 std::ostringstream os;
4172 os << *prefix << "doImport from 1-to-1 matrix" << endl;
4173 std::cerr << os.str();
4174 }
4175 import_type importToOrig(oneToOneRowMap, origRowMap);
4176 this->doImport(oneToOneMatrix, importToOrig, Tpetra::ADD);
4177 }
4178
4179 // It's safe now to clear out nonlocals_, since we've already
4180 // committed side effects to *this. The standard idiom for
4181 // clearing a Container like std::map, is to swap it with an empty
4182 // Container and let the swapped Container fall out of scope.
4183 if (verbose) {
4184 std::ostringstream os;
4185 os << *prefix << "Free nonlocals_ (std::map)" << endl;
4186 std::cerr << os.str();
4187 }
4188 decltype(nonlocals_) newNonlocals;
4189 std::swap(nonlocals_, newNonlocals);
4190
4191 // FIXME (mfh 12 Sep 2016) I don't like this all-reduce, and I
4192 // don't like throwing an exception here. A local return value
4193 // would likely be more useful to users. However, if users find
4194 // themselves exercising nonlocal inserts often, then they are
4195 // probably novice users who need the help. See Gibhub Issues
4196 // #603 and #601 (esp. the latter) for discussion.
4197
4198 int isGloballyComplete = 0; // output argument of reduceAll
4199 reduceAll<int, int>(*comm, REDUCE_MIN, isLocallyComplete,
4200 outArg(isGloballyComplete));
4201 TEUCHOS_TEST_FOR_EXCEPTION(isGloballyComplete != 1, std::runtime_error,
4202 "On at least one process, "
4203 "you called insertGlobalValues with a global row index which is not in "
4204 "the matrix's row Map on any process in its communicator.");
4205}
4206
4207template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4209 resumeFill(const Teuchos::RCP<Teuchos::ParameterList>& params) {
4210 if (!isStaticGraph()) { // Don't resume fill of a nonowned graph.
4211 myGraph_->resumeFill(params);
4212 }
4213 // Delete the apply helper (if it exists)
4214 applyHelper.reset();
4215 fillComplete_ = false;
4216}
4217
4218template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4220 haveGlobalConstants() const {
4221 return getCrsGraphRef().haveGlobalConstants();
4222}
4223
4224template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4226 fillComplete(const Teuchos::RCP<Teuchos::ParameterList>& params) {
4227 const char tfecfFuncName[] = "fillComplete(params): ";
4228
4229 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->getCrsGraph().is_null(), std::logic_error,
4230 "getCrsGraph() returns null. This should not happen at this point. "
4231 "Please report this bug to the Tpetra developers.");
4232
4233 const crs_graph_type& graph = this->getCrsGraphRef();
4234 if (this->isStaticGraph() && graph.isFillComplete()) {
4235 // If this matrix's graph is fill complete and the user did not
4236 // supply a domain or range Map, use the graph's domain and
4237 // range Maps.
4238 this->fillComplete(graph.getDomainMap(), graph.getRangeMap(), params);
4239 } else { // assume that user's row Map is the domain and range Map
4240 Teuchos::RCP<const map_type> rangeMap = graph.getRowMap();
4241 Teuchos::RCP<const map_type> domainMap = rangeMap;
4242 this->fillComplete(domainMap, rangeMap, params);
4243 }
4244}
4245
4246template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4248 fillComplete(const Teuchos::RCP<const map_type>& domainMap,
4249 const Teuchos::RCP<const map_type>& rangeMap,
4250 const Teuchos::RCP<Teuchos::ParameterList>& params) {
4251 using Details::Behavior;
4253 using std::endl;
4254 using Teuchos::ArrayRCP;
4255 using Teuchos::RCP;
4256 using Teuchos::rcp;
4257 const char tfecfFuncName[] = "fillComplete: ";
4258 ProfilingRegion regionFillComplete("Tpetra::CrsMatrix::fillComplete");
4259 const bool verbose = Behavior::verbose("CrsMatrix");
4260 std::unique_ptr<std::string> prefix;
4261 if (verbose) {
4262 prefix = this->createPrefix("CrsMatrix", "fillComplete(dom,ran,p)");
4263 std::ostringstream os;
4264 os << *prefix << endl;
4265 std::cerr << os.str();
4266 }
4268 "Tpetra::CrsMatrix::fillComplete",
4269 "fillComplete");
4270
4271 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!this->isFillActive() || this->isFillComplete(), std::runtime_error,
4272 "Matrix fill state must be active (isFillActive() "
4273 "must be true) before you may call fillComplete().");
4274 const int numProcs = this->getComm()->getSize();
4275
4276 //
4277 // Read parameters from the input ParameterList.
4278 //
4279 {
4280 Details::ProfilingRegion region_fc("Tpetra::CrsMatrix::fillComplete", "ParameterList");
4281
4282 // If true, the caller promises that no process did nonlocal
4283 // changes since the last call to fillComplete.
4284 bool assertNoNonlocalInserts = false;
4285 // If true, makeColMap sorts remote GIDs (within each remote
4286 // process' group).
4287 bool sortGhosts = true;
4288
4289 if (!params.is_null()) {
4290 assertNoNonlocalInserts = params->get("No Nonlocal Changes",
4291 assertNoNonlocalInserts);
4292 if (params->isParameter("sort column map ghost gids")) {
4293 sortGhosts = params->get("sort column map ghost gids", sortGhosts);
4294 } else if (params->isParameter("Sort column Map ghost GIDs")) {
4295 sortGhosts = params->get("Sort column Map ghost GIDs", sortGhosts);
4296 }
4297 }
4298 // We also don't need to do global assembly if there is only one
4299 // process in the communicator.
4300 const bool needGlobalAssemble = !assertNoNonlocalInserts && numProcs > 1;
4301 // This parameter only matters if this matrix owns its graph.
4302 if (!this->myGraph_.is_null()) {
4303 this->myGraph_->sortGhostsAssociatedWithEachProcessor_ = sortGhosts;
4304 }
4305
4306 if (!this->getCrsGraphRef().indicesAreAllocated()) {
4307 if (this->hasColMap()) { // use local indices
4308 allocateValues(LocalIndices, GraphNotYetAllocated, verbose);
4309 } else { // no column Map, so use global indices
4310 allocateValues(GlobalIndices, GraphNotYetAllocated, verbose);
4311 }
4312 }
4313 // Global assemble, if we need to. This call only costs a single
4314 // all-reduce if we didn't need global assembly after all.
4315 if (needGlobalAssemble) {
4316 this->globalAssemble();
4317 } else {
4318 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(numProcs == 1 && nonlocals_.size() > 0,
4319 std::runtime_error,
4320 "Cannot have nonlocal entries on a serial run. "
4321 "An invalid entry (i.e., with row index not in the row Map) must have "
4322 "been submitted to the CrsMatrix.");
4323 }
4324 }
4325 if (this->isStaticGraph()) {
4326 Details::ProfilingRegion region_isg("Tpetra::CrsMatrix::fillComplete", "isStaticGraph");
4327 // FIXME (mfh 14 Nov 2016) In order to fix #843, I enable the
4328 // checks below only in debug mode. It would be nicer to do a
4329 // local check, then propagate the error state in a deferred
4330 // way, whenever communication happens. That would reduce the
4331 // cost of checking, to the point where it may make sense to
4332 // enable it even in release mode.
4333#ifdef HAVE_TPETRA_DEBUG
4334 // FIXME (mfh 18 Jun 2014) This check for correctness of the
4335 // input Maps incurs a penalty of two all-reduces for the
4336 // otherwise optimal const graph case.
4337 //
4338 // We could turn these (max) 2 all-reduces into (max) 1, by
4339 // fusing them. We could do this by adding a "locallySameAs"
4340 // method to Map, which would return one of four states:
4341 //
4342 // a. Certainly globally the same
4343 // b. Certainly globally not the same
4344 // c. Locally the same
4345 // d. Locally not the same
4346 //
4347 // The first two states don't require further communication.
4348 // The latter two states require an all-reduce to communicate
4349 // globally, but we only need one all-reduce, since we only need
4350 // to check whether at least one of the Maps is wrong.
4351 const bool domainMapsMatch =
4352 this->staticGraph_->getDomainMap()->isSameAs(*domainMap);
4353 const bool rangeMapsMatch =
4354 this->staticGraph_->getRangeMap()->isSameAs(*rangeMap);
4355
4356 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!domainMapsMatch, std::runtime_error,
4357 "The CrsMatrix's domain Map does not match the graph's domain Map. "
4358 "The graph cannot be changed because it was given to the CrsMatrix "
4359 "constructor as const. You can fix this by passing in the graph's "
4360 "domain Map and range Map to the matrix's fillComplete call.");
4361
4362 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!rangeMapsMatch, std::runtime_error,
4363 "The CrsMatrix's range Map does not match the graph's range Map. "
4364 "The graph cannot be changed because it was given to the CrsMatrix "
4365 "constructor as const. You can fix this by passing in the graph's "
4366 "domain Map and range Map to the matrix's fillComplete call.");
4367#endif // HAVE_TPETRA_DEBUG
4368
4369 // The matrix does _not_ own the graph, and the graph's
4370 // structure is already fixed, so just fill the local matrix.
4371 this->fillLocalMatrix(params);
4372 } else {
4373 Details::ProfilingRegion region_insg("Tpetra::CrsMatrix::fillComplete", "isNotStaticGraph");
4374 // Set the graph's domain and range Maps. This will clear the
4375 // Import if the domain Map has changed (is a different
4376 // pointer), and the Export if the range Map has changed (is a
4377 // different pointer).
4378 this->myGraph_->setDomainRangeMaps(domainMap, rangeMap);
4379
4380 // Make the graph's column Map, if necessary.
4381 Teuchos::Array<int> remotePIDs(0);
4382 const bool mustBuildColMap = !this->hasColMap();
4383 if (mustBuildColMap) {
4384 this->myGraph_->makeColMap(remotePIDs);
4385 }
4386
4387 // Make indices local, if necessary. The method won't do
4388 // anything if the graph is already locally indexed.
4389 const std::pair<size_t, std::string> makeIndicesLocalResult =
4390 this->myGraph_->makeIndicesLocal(verbose);
4391 // TODO (mfh 20 Jul 2017) Instead of throwing here, pass along
4392 // the error state to makeImportExport
4393 // which may do all-reduces and thus may
4394 // have the opportunity to communicate that error state.
4395 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(makeIndicesLocalResult.first != 0, std::runtime_error,
4396 makeIndicesLocalResult.second);
4397
4398 if (!isStorageOptimized()) {
4399 const bool sorted = this->myGraph_->isSorted();
4400 const bool merged = this->myGraph_->isMerged();
4401 this->sortAndMergeIndicesAndValues(sorted, merged);
4402 }
4403
4404 // Make Import and Export objects, if they haven't been made
4405 // already. If we made a column Map above, reuse information
4406 // from that process to avoid communiation in the Import setup.
4407 this->myGraph_->makeImportExport(remotePIDs, mustBuildColMap);
4408
4409 // The matrix _does_ own the graph, so fill the local graph at
4410 // the same time as the local matrix.
4411 this->fillLocalGraphAndMatrix(params);
4412
4413 const bool callGraphComputeGlobalConstants = params.get() == nullptr ||
4414 params->get("compute global constants", true);
4415 if (callGraphComputeGlobalConstants) {
4416 this->myGraph_->computeGlobalConstants();
4417 } else {
4418 this->myGraph_->computeLocalConstants();
4419 }
4420 this->myGraph_->fillComplete_ = true;
4421 this->myGraph_->checkInternalState();
4422 }
4423
4424 // FIXME (mfh 28 Aug 2014) "Preserve Local Graph" bool parameter no longer used.
4425
4426 this->fillComplete_ = true; // Now we're fill complete!
4427
4428 this->checkInternalState();
4429} // fillComplete(domainMap, rangeMap, params)
4430
4431template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4433 expertStaticFillComplete(const Teuchos::RCP<const map_type>& domainMap,
4434 const Teuchos::RCP<const map_type>& rangeMap,
4435 const Teuchos::RCP<const import_type>& importer,
4436 const Teuchos::RCP<const export_type>& exporter,
4437 const Teuchos::RCP<Teuchos::ParameterList>& params) {
4438 Tpetra::Details::ProfilingRegion all("Tpetra ESFC-all");
4439
4440 const char tfecfFuncName[] = "expertStaticFillComplete: ";
4441 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillActive() || isFillComplete(),
4442 std::runtime_error,
4443 "Matrix fill state must be active (isFillActive() "
4444 "must be true) before calling fillComplete().");
4445 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
4446 myGraph_.is_null(), std::logic_error, "myGraph_ is null. This is not allowed.");
4447
4448 {
4449 Tpetra::Details::ProfilingRegion graph("Tpetra eSFC-M-Graph");
4450 // We will presume globalAssemble is not needed, so we do the ESFC on the graph
4451 myGraph_->expertStaticFillComplete(domainMap, rangeMap, importer, exporter, params);
4452 }
4453
4454 {
4455 Tpetra::Details::ProfilingRegion fLGAM("Tpetra eSFC-M-fLGAM");
4456 // Fill the local graph and matrix
4458 }
4459 // FIXME (mfh 28 Aug 2014) "Preserve Local Graph" bool parameter no longer used.
4460
4461 // Now we're fill complete!
4462 fillComplete_ = true;
4463
4464 // Sanity checks at the end.
4465#ifdef HAVE_TPETRA_DEBUG
4466 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(isFillActive(), std::logic_error,
4467 ": We're at the end of fillComplete(), but isFillActive() is true. "
4468 "Please report this bug to the Tpetra developers.");
4469 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillComplete(), std::logic_error,
4470 ": We're at the end of fillComplete(), but isFillActive() is true. "
4471 "Please report this bug to the Tpetra developers.");
4472#endif // HAVE_TPETRA_DEBUG
4473
4475}
4476
4477template <class execution_space, class LO, class rowptr_type, class colinds_type, class numRowEntries_type, class values_type>
4478void prepareSortMergeUnpackedMatrix(rowptr_type rowptr, colinds_type colinds, numRowEntries_type numRowEntries, values_type values) {
4479 using ATS = KokkosKernels::ArithTraits<LO>;
4480 using scalarATS = KokkosKernels::ArithTraits<typename values_type::value_type>;
4481 const auto unused = ATS::max();
4482 const auto zero = scalarATS::zero();
4483
4484 auto numRows = rowptr.extent(0) - 1;
4485
4486 // make sure that unused entries will get ordered last
4487 Kokkos::parallel_for(
4488 "flag_unused_entries", Kokkos::RangePolicy<execution_space, LO>(0, numRows), KOKKOS_LAMBDA(const LO rlid) {
4489 for (size_t jj = rowptr(rlid) + numRowEntries(rlid); jj < rowptr(rlid + 1); ++jj) {
4490 colinds(jj) = unused;
4491 values(jj) = zero;
4492 }
4493 });
4494}
4495
4496template <class execution_space, class LO, class rowptr_type, class colinds_type, class numRowEntries_type, class values_type>
4497void mergeUnpackedMatrix(rowptr_type rowptr, colinds_type colinds, numRowEntries_type numRowEntries, values_type values) {
4498 auto numRows = rowptr.extent(0) - 1;
4499
4500 // merge
4501 // We cannot use KokkosSparse::sort_and_merge_matrix since we
4502 // do not actually want to change the allocations.
4503
4504 Kokkos::parallel_for(
4505 "merge_entries", Kokkos::RangePolicy<execution_space>(0, numRows), KOKKOS_LAMBDA(const LO rlid) {
4506 auto rowNNZ = numRowEntries(rlid);
4507 if (rowNNZ == 0) {
4508 return;
4509 }
4510 auto rowBegin = rowptr(rlid);
4511 auto pos = rowBegin;
4512 for (size_t offset = rowBegin + 1; offset < rowBegin + rowNNZ; ++offset) {
4513 if ((colinds(offset) != colinds(pos))) {
4514 ++pos;
4515 colinds(pos) = colinds(offset);
4516 values(pos) = values(offset);
4517 } else {
4518 values(pos) += values(offset);
4519 }
4520 }
4521 numRowEntries(rlid) = pos + 1 - rowBegin;
4522 });
4523}
4524
4525template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4527 sortAndMergeIndicesAndValues(const bool sorted, const bool merged) {
4529
4530 const char tfecfFuncName[] = "sortAndMergeIndicesAndValues: ";
4531
4532 if (!sorted || !merged) {
4533 ProfilingRegion regionSAM("Tpetra::CrsMatrix::sortAndMergeIndicesAndValues");
4534
4535 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->isStaticGraph(), std::runtime_error,
4536 "Cannot sort or merge with "
4537 "\"static\" (const) graph, since the matrix does not own the graph.");
4538 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->myGraph_.is_null(), std::logic_error,
4539 "myGraph_ is null, but "
4540 "this matrix claims ! isStaticGraph(). "
4541 "Please report this bug to the Tpetra developers.");
4542 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->isStorageOptimized(), std::logic_error,
4543 "It is invalid to call "
4544 "this method if the graph's storage has already been optimized. "
4545 "Please report this bug to the Tpetra developers.");
4546
4547 crs_graph_type& graph = *(this->myGraph_);
4548 auto rowptr = graph.rowPtrsUnpacked_dev_;
4549 auto colinds = graph.lclIndsUnpacked_wdv.getDeviceView(Access::ReadWrite);
4550 auto values = valuesUnpacked_wdv.getDeviceView(Access::ReadWrite);
4551
4552 // Create a device copy of k_numRowEntries_.
4553 auto k_numRowEntries_d = Kokkos::create_mirror_view_and_copy(execution_space(), graph.k_numRowEntries_);
4554 // set set unused column entries so they get sorted last
4555 prepareSortMergeUnpackedMatrix<execution_space, LocalOrdinal>(rowptr, colinds, k_numRowEntries_d, values);
4556
4557 if (!sorted) {
4558 // For this to work correctly, we require that the unused column entries have been filled
4559 // with indices that get ordered last.
4560 Import_Util::sortCrsEntries(rowptr, colinds, values);
4561 graph.indicesAreSorted_ = true; // we just sorted every row
4562 }
4563 if (!merged) {
4564 mergeUnpackedMatrix<execution_space, LocalOrdinal>(rowptr, colinds, k_numRowEntries_d, values);
4565 Kokkos::deep_copy(graph.k_numRowEntries_, k_numRowEntries_d);
4566 graph.noRedundancies_ = true; // we just merged every row
4567 }
4568 }
4569}
4570
4571template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4575 Scalar alpha,
4576 Scalar beta) const {
4577 using Teuchos::RCP;
4578 using Teuchos::rcp;
4579 using Teuchos::rcp_const_cast;
4580 using Teuchos::rcpFromRef;
4582 const Scalar ZERO = Teuchos::ScalarTraits<Scalar>::zero();
4583 const Scalar ONE = Teuchos::ScalarTraits<Scalar>::one();
4584
4585 // mfh 05 Jun 2014: Special case for alpha == 0. I added this to
4586 // fix an Ifpack2 test (RILUKSingleProcessUnitTests), which was
4587 // failing only for the Kokkos refactor version of Tpetra. It's a
4588 // good idea regardless to have the bypass.
4589 if (alpha == ZERO) {
4590 if (beta == ZERO) {
4591 Y_in.putScalar(ZERO);
4592 } else if (beta != ONE) {
4593 Y_in.scale(beta);
4594 }
4595 return;
4596 }
4597
4598 // It's possible that X is a view of Y or vice versa. We don't
4599 // allow this (apply() requires that X and Y not alias one
4600 // another), but it's helpful to detect and work around this case.
4601 // We don't try to to detect the more subtle cases (e.g., one is a
4602 // subview of the other, but their initial pointers differ). We
4603 // only need to do this if this matrix's Import is trivial;
4604 // otherwise, we don't actually apply the operator from X into Y.
4605
4606 RCP<const import_type> importer = this->getGraph()->getImporter();
4607 RCP<const export_type> exporter = this->getGraph()->getExporter();
4608
4609 // If beta == 0, then the output MV will be overwritten; none of
4610 // its entries should be read. (Sparse BLAS semantics say that we
4611 // must ignore any Inf or NaN entries in Y_in, if beta is zero.)
4612 // This matters if we need to do an Export operation; see below.
4613 const bool Y_is_overwritten = (beta == ZERO);
4614
4615 // We treat the case of a replicated MV output specially.
4616 const bool Y_is_replicated =
4617 (!Y_in.isDistributed() && this->getComm()->getSize() != 1);
4618
4619 // This is part of the special case for replicated MV output.
4620 // We'll let each process do its thing, but do an all-reduce at
4621 // the end to sum up the results. Setting beta=0 on all processes
4622 // but Proc 0 makes the math work out for the all-reduce. (This
4623 // assumes that the replicated data is correctly replicated, so
4624 // that the data are the same on all processes.)
4625 if (Y_is_replicated && this->getComm()->getRank() > 0) {
4626 beta = ZERO;
4627 }
4628
4629 // Temporary MV for Import operation. After the block of code
4630 // below, this will be an (Imported if necessary) column Map MV
4631 // ready to give to localApply(...).
4632 RCP<const MV> X_colMap;
4633 if (importer.is_null()) {
4634 if (!X_in.isConstantStride()) {
4635 // Not all sparse mat-vec kernels can handle an input MV with
4636 // nonconstant stride correctly, so we have to copy it in that
4637 // case into a constant stride MV. To make a constant stride
4638 // copy of X_in, we force creation of the column (== domain)
4639 // Map MV (if it hasn't already been created, else fetch the
4640 // cached copy). This avoids creating a new MV each time.
4641 RCP<MV> X_colMapNonConst = getColumnMapMultiVector(X_in, true);
4642 Tpetra::deep_copy(*X_colMapNonConst, X_in);
4643 X_colMap = rcp_const_cast<const MV>(X_colMapNonConst);
4644 } else {
4645 // The domain and column Maps are the same, so do the local
4646 // multiply using the domain Map input MV X_in.
4647 X_colMap = rcpFromRef(X_in);
4648 }
4649 } else { // need to Import source (multi)vector
4650 ProfilingRegion regionImport("Tpetra::CrsMatrix::apply: Import");
4651
4652 // We're doing an Import anyway, which will copy the relevant
4653 // elements of the domain Map MV X_in into a separate column Map
4654 // MV. Thus, we don't have to worry whether X_in is constant
4655 // stride.
4656 RCP<MV> X_colMapNonConst = getColumnMapMultiVector(X_in);
4657
4658 // Import from the domain Map MV to the column Map MV.
4659 X_colMapNonConst->doImport(X_in, *importer, INSERT);
4660 X_colMap = rcp_const_cast<const MV>(X_colMapNonConst);
4661 }
4662
4663 // Temporary MV for doExport (if needed), or for copying a
4664 // nonconstant stride output MV into a constant stride MV. This
4665 // is null if we don't need the temporary MV, that is, if the
4666 // Export is trivial (null).
4667 RCP<MV> Y_rowMap = getRowMapMultiVector(Y_in);
4668
4669 // If we have a nontrivial Export object, we must perform an
4670 // Export. In that case, the local multiply result will go into
4671 // the row Map multivector. We don't have to make a
4672 // constant-stride version of Y_in in this case, because we had to
4673 // make a constant stride Y_rowMap MV and do an Export anyway.
4674 if (!exporter.is_null()) {
4675 this->localApply(*X_colMap, *Y_rowMap, Teuchos::NO_TRANS, alpha, ZERO);
4676 {
4677 ProfilingRegion regionExport("Tpetra::CrsMatrix::apply: Export");
4678
4679 // If we're overwriting the output MV Y_in completely (beta ==
4680 // 0), then make sure that it is filled with zeros before we
4681 // do the Export. Otherwise, the ADD combine mode will use
4682 // data in Y_in, which is supposed to be zero.
4683 if (Y_is_overwritten) {
4684 Y_in.putScalar(ZERO);
4685 } else {
4686 // Scale output MV by beta, so that doExport sums in the
4687 // mat-vec contribution: Y_in = beta*Y_in + alpha*A*X_in.
4688 Y_in.scale(beta);
4689 }
4690 // Do the Export operation.
4691 Y_in.doExport(*Y_rowMap, *exporter, ADD_ASSIGN);
4692 }
4693 } else { // Don't do an Export: row Map and range Map are the same.
4694 //
4695 // If Y_in does not have constant stride, or if the column Map
4696 // MV aliases Y_in, then we can't let the kernel write directly
4697 // to Y_in. Instead, we have to use the cached row (== range)
4698 // Map MV as temporary storage.
4699 //
4700 // FIXME (mfh 05 Jun 2014) This test for aliasing only tests if
4701 // the user passed in the same MultiVector for both X and Y. It
4702 // won't detect whether one MultiVector views the other. We
4703 // should also check the MultiVectors' raw data pointers.
4704 if (!Y_in.isConstantStride() || X_colMap.getRawPtr() == &Y_in) {
4705 // Force creating the MV if it hasn't been created already.
4706 // This will reuse a previously created cached MV.
4707 Y_rowMap = getRowMapMultiVector(Y_in, true);
4708
4709 // If beta == 0, we don't need to copy Y_in into Y_rowMap,
4710 // since we're overwriting it anyway.
4711 if (beta != ZERO) {
4712 Tpetra::deep_copy(*Y_rowMap, Y_in);
4713 }
4714 this->localApply(*X_colMap, *Y_rowMap, Teuchos::NO_TRANS, alpha, beta);
4715 Tpetra::deep_copy(Y_in, *Y_rowMap);
4716 } else {
4717 this->localApply(*X_colMap, Y_in, Teuchos::NO_TRANS, alpha, beta);
4718 }
4719 }
4720
4721 // If the range Map is a locally replicated Map, sum up
4722 // contributions from each process. We set beta = 0 on all
4723 // processes but Proc 0 initially, so this will handle the scaling
4724 // factor beta correctly.
4725 if (Y_is_replicated) {
4726 ProfilingRegion regionReduce("Tpetra::CrsMatrix::apply: Reduce Y");
4727 Y_in.reduce();
4728 }
4729}
4730
4731template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4735 const Teuchos::ETransp mode,
4736 Scalar alpha,
4737 Scalar beta) const {
4738 using Teuchos::null;
4739 using Teuchos::RCP;
4740 using Teuchos::rcp;
4741 using Teuchos::rcp_const_cast;
4742 using Teuchos::rcpFromRef;
4744 const Scalar ZERO = Teuchos::ScalarTraits<Scalar>::zero();
4745
4746 // Take shortcuts for alpha == 0.
4747 if (alpha == ZERO) {
4748 // Follow the Sparse BLAS convention by ignoring both the matrix
4749 // and X_in, in this case.
4750 if (beta == ZERO) {
4751 // Follow the Sparse BLAS convention by overwriting any Inf or
4752 // NaN values in Y_in, in this case.
4753 Y_in.putScalar(ZERO);
4754 } else {
4755 Y_in.scale(beta);
4756 }
4757 return;
4758 }
4759
4760 const size_t numVectors = X_in.getNumVectors();
4761
4762 // We don't allow X_in and Y_in to alias one another. It's hard
4763 // to check this, because advanced users could create views from
4764 // raw pointers. However, if X_in and Y_in reference the same
4765 // object, we will do the user a favor by copying X into new
4766 // storage (with a warning). We only need to do this if we have
4767 // trivial importers; otherwise, we don't actually apply the
4768 // operator from X into Y.
4769 RCP<const import_type> importer = this->getGraph()->getImporter();
4770 RCP<const export_type> exporter = this->getGraph()->getExporter();
4771 // access X indirectly, in case we need to create temporary storage
4772 RCP<const MV> X;
4773
4774 // some parameters for below
4775 const bool Y_is_replicated = (!Y_in.isDistributed() && this->getComm()->getSize() != 1);
4776 const bool Y_is_overwritten = (beta == ZERO);
4777 if (Y_is_replicated && this->getComm()->getRank() > 0) {
4778 beta = ZERO;
4779 }
4780
4781 // The kernels do not allow input or output with nonconstant stride.
4782 if (!X_in.isConstantStride() && importer.is_null()) {
4783 X = rcp(new MV(X_in, Teuchos::Copy)); // Constant-stride copy of X_in
4784 } else {
4785 X = rcpFromRef(X_in); // Reference to X_in
4786 }
4787
4788 // Set up temporary multivectors for Import and/or Export.
4789 if (importer != Teuchos::null) {
4790 if (importMV_ != Teuchos::null && importMV_->getNumVectors() != numVectors) {
4791 importMV_ = null;
4792 }
4793 if (importMV_ == null) {
4794 importMV_ = rcp(new MV(this->getColMap(), numVectors));
4795 }
4796 }
4797 if (exporter != Teuchos::null) {
4798 if (exportMV_ != Teuchos::null && exportMV_->getNumVectors() != numVectors) {
4799 exportMV_ = null;
4800 }
4801 if (exportMV_ == null) {
4802 exportMV_ = rcp(new MV(this->getRowMap(), numVectors));
4803 }
4804 }
4805
4806 // If we have a non-trivial exporter, we must import elements that
4807 // are permuted or are on other processors.
4808 if (!exporter.is_null()) {
4809 ProfilingRegion regionImport("Tpetra::CrsMatrix::apply (transpose): Import");
4810 exportMV_->doImport(X_in, *exporter, INSERT);
4811 X = exportMV_; // multiply out of exportMV_
4812 }
4813
4814 // If we have a non-trivial importer, we must export elements that
4815 // are permuted or belong to other processors. We will compute
4816 // solution into the to-be-exported MV; get a view.
4817 if (importer != Teuchos::null) {
4818 ProfilingRegion regionExport("Tpetra::CrsMatrix::apply (transpose): Export");
4819
4820 // FIXME (mfh 18 Apr 2015) Temporary fix suggested by Clark
4821 // Dohrmann on Fri 17 Apr 2015. At some point, we need to go
4822 // back and figure out why this helps. importMV_ SHOULD be
4823 // completely overwritten in the localApply(...) call
4824 // below, because beta == ZERO there.
4825 importMV_->putScalar(ZERO);
4826 // Do the local computation.
4827 this->localApply(*X, *importMV_, mode, alpha, ZERO);
4828
4829 if (Y_is_overwritten) {
4830 Y_in.putScalar(ZERO);
4831 } else {
4832 Y_in.scale(beta);
4833 }
4834 Y_in.doExport(*importMV_, *importer, ADD_ASSIGN);
4835 }
4836 // otherwise, multiply into Y
4837 else {
4838 // can't multiply in-situ; can't multiply into non-strided multivector
4839 //
4840 // FIXME (mfh 05 Jun 2014) This test for aliasing only tests if
4841 // the user passed in the same MultiVector for both X and Y. It
4842 // won't detect whether one MultiVector views the other. We
4843 // should also check the MultiVectors' raw data pointers.
4844 if (!Y_in.isConstantStride() || X.getRawPtr() == &Y_in) {
4845 // Make a deep copy of Y_in, into which to write the multiply result.
4846 MV Y(Y_in, Teuchos::Copy);
4847 this->localApply(*X, Y, mode, alpha, beta);
4848 Tpetra::deep_copy(Y_in, Y);
4849 } else {
4850 this->localApply(*X, Y_in, mode, alpha, beta);
4851 }
4852 }
4853
4854 // If the range Map is a locally replicated map, sum the
4855 // contributions from each process. (That's why we set beta=0
4856 // above for all processes but Proc 0.)
4857 if (Y_is_replicated) {
4858 ProfilingRegion regionReduce("Tpetra::CrsMatrix::apply (transpose): Reduce Y");
4859 Y_in.reduce();
4860 }
4861}
4862
4863template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4867 const Teuchos::ETransp mode,
4868 const Scalar& alpha,
4869 const Scalar& beta) const {
4870 using Teuchos::NO_TRANS;
4872 ProfilingRegion regionLocalApply("Tpetra::CrsMatrix::localApply");
4873
4874 auto X_lcl = X.getLocalViewDevice(Access::ReadOnly);
4875 auto Y_lcl = Y.getLocalViewDevice(Access::ReadWrite);
4876
4877 const bool debug = ::Tpetra::Details::Behavior::debug();
4878 if (debug) {
4879 const char tfecfFuncName[] = "localApply: ";
4880 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(X.getNumVectors() != Y.getNumVectors(), std::runtime_error,
4881 "X.getNumVectors() = " << X.getNumVectors() << " != "
4882 "Y.getNumVectors() = "
4883 << Y.getNumVectors() << ".");
4884 const bool transpose = (mode != Teuchos::NO_TRANS);
4885 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!transpose && X.getLocalLength() !=
4886 getColMap()->getLocalNumElements(),
4887 std::runtime_error,
4888 "NO_TRANS case: X has the wrong number of local rows. "
4889 "X.getLocalLength() = "
4890 << X.getLocalLength() << " != "
4891 "getColMap()->getLocalNumElements() = "
4892 << getColMap()->getLocalNumElements() << ".");
4893 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!transpose && Y.getLocalLength() !=
4894 getRowMap()->getLocalNumElements(),
4895 std::runtime_error,
4896 "NO_TRANS case: Y has the wrong number of local rows. "
4897 "Y.getLocalLength() = "
4898 << Y.getLocalLength() << " != "
4899 "getRowMap()->getLocalNumElements() = "
4900 << getRowMap()->getLocalNumElements() << ".");
4901 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(transpose && X.getLocalLength() !=
4902 getRowMap()->getLocalNumElements(),
4903 std::runtime_error,
4904 "TRANS or CONJ_TRANS case: X has the wrong number of local "
4905 "rows. X.getLocalLength() = "
4906 << X.getLocalLength()
4907 << " != getRowMap()->getLocalNumElements() = "
4908 << getRowMap()->getLocalNumElements() << ".");
4909 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(transpose && Y.getLocalLength() !=
4910 getColMap()->getLocalNumElements(),
4911 std::runtime_error,
4912 "TRANS or CONJ_TRANS case: X has the wrong number of local "
4913 "rows. Y.getLocalLength() = "
4914 << Y.getLocalLength()
4915 << " != getColMap()->getLocalNumElements() = "
4916 << getColMap()->getLocalNumElements() << ".");
4917 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!isFillComplete(), std::runtime_error,
4918 "The matrix is not "
4919 "fill complete. You must call fillComplete() (possibly with "
4920 "domain and range Map arguments) without an intervening "
4921 "resumeFill() call before you may call this method.");
4922 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!X.isConstantStride() || !Y.isConstantStride(),
4923 std::runtime_error, "X and Y must be constant stride.");
4924 // If the two pointers are null, then they don't alias one
4925 // another, even though they are equal.
4926 // Kokkos does not guarantee that zero row-extent vectors
4927 // point to different places, so we have to check that too.
4928 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(X_lcl.data() == Y_lcl.data() && X_lcl.data() != nullptr && X_lcl.extent(0) != 0,
4929 std::runtime_error, "X and Y may not alias one another.");
4930 }
4931
4932 auto A_lcl = getLocalMatrixDevice();
4933
4934 if (!applyHelper.get()) {
4935 // The apply helper does not exist, so create it.
4936 // Decide now whether to use the imbalanced row path, or the default.
4937 bool useMergePath = false;
4938#ifdef KOKKOSKERNELS_ENABLE_TPL_CUSPARSE
4939 // TODO: when https://github.com/kokkos/kokkos-kernels/issues/2166 is fixed and,
4940 // we can use SPMV_MERGE_PATH for the native spmv as well.
4941 // Take out this ifdef to enable that.
4942 //
4943 // Until then, only use SPMV_MERGE_PATH when calling cuSPARSE.
4944 if constexpr (std::is_same_v<execution_space, Kokkos::Cuda>) {
4945 LocalOrdinal nrows = getLocalNumRows();
4946 LocalOrdinal maxRowImbalance = 0;
4947 if (nrows != 0)
4948 maxRowImbalance = getLocalMaxNumRowEntries() - (getLocalNumEntries() / nrows);
4949
4950 if (size_t(maxRowImbalance) >= Tpetra::Details::Behavior::rowImbalanceThreshold())
4951 useMergePath = true;
4952 }
4953#endif
4954 applyHelper = std::make_shared<ApplyHelper>(A_lcl.nnz(), A_lcl.graph.row_map,
4955 useMergePath ? KokkosSparse::SPMV_MERGE_PATH : KokkosSparse::SPMV_DEFAULT);
4956 }
4957
4958 // Translate mode (Teuchos enum) to KokkosKernels (1-character string)
4959 const char* modeKK = nullptr;
4960 switch (mode) {
4961 case Teuchos::NO_TRANS:
4962 modeKK = KokkosSparse::NoTranspose;
4963 break;
4964 case Teuchos::TRANS:
4965 modeKK = KokkosSparse::Transpose;
4966 break;
4967 case Teuchos::CONJ_TRANS:
4968 modeKK = KokkosSparse::ConjugateTranspose;
4969 break;
4970 default:
4971 throw std::invalid_argument("Tpetra::CrsMatrix::localApply: invalid mode");
4972 }
4973
4974 if (applyHelper->shouldUseIntRowptrs()) {
4975 auto A_lcl_int_rowptrs = applyHelper->getIntRowptrMatrix(A_lcl);
4976 KokkosSparse::spmv(
4977 &applyHelper->handle_int, modeKK,
4978 impl_scalar_type(alpha), A_lcl_int_rowptrs, X_lcl, impl_scalar_type(beta), Y_lcl);
4979 } else {
4980 KokkosSparse::spmv(
4981 &applyHelper->handle, modeKK,
4982 impl_scalar_type(alpha), A_lcl, X_lcl, impl_scalar_type(beta), Y_lcl);
4983 }
4984}
4985
4986template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
4990 Teuchos::ETransp mode,
4991 Scalar alpha,
4992 Scalar beta) const {
4994 const char fnName[] = "Tpetra::CrsMatrix::apply";
4995
4996 TEUCHOS_TEST_FOR_EXCEPTION(!isFillComplete(), std::runtime_error,
4997 fnName << ": Cannot call apply() until fillComplete() "
4998 "has been called.");
4999
5000 if (mode == Teuchos::NO_TRANS) {
5001 ProfilingRegion regionNonTranspose(fnName);
5002 this->applyNonTranspose(X, Y, alpha, beta);
5003 } else {
5004 ProfilingRegion regionTranspose("Tpetra::CrsMatrix::apply (transpose)");
5005 this->applyTranspose(X, Y, mode, alpha, beta);
5006 }
5007}
5008
5009template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5010template <class T>
5011Teuchos::RCP<CrsMatrix<T, LocalOrdinal, GlobalOrdinal, Node>>
5013 convert() const {
5014 using Teuchos::RCP;
5015 typedef CrsMatrix<T, LocalOrdinal, GlobalOrdinal, Node> output_matrix_type;
5016 const char tfecfFuncName[] = "convert: ";
5017
5018 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!this->isFillComplete(), std::runtime_error,
5019 "This matrix (the source "
5020 "of the conversion) is not fill complete. You must first call "
5021 "fillComplete() (possibly with the domain and range Map) without an "
5022 "intervening call to resumeFill(), before you may call this method.");
5023
5024 RCP<output_matrix_type> newMatrix(new output_matrix_type(this->getCrsGraph()));
5025 // Copy old values into new values. impl_scalar_type and T may
5026 // differ, so we can't use Kokkos::deep_copy.
5028 copyConvert(newMatrix->getLocalMatrixDevice().values,
5029 this->getLocalMatrixDevice().values);
5030 // Since newmat has a static (const) graph, the graph already has
5031 // a column Map, and Import and Export objects already exist (if
5032 // applicable). Thus, calling fillComplete is cheap.
5033 newMatrix->fillComplete(this->getDomainMap(), this->getRangeMap());
5034
5035 return newMatrix;
5036}
5037
5038template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5040 checkInternalState() const {
5042 const bool debug = ::Tpetra::Details::Behavior::debug("CrsGraph");
5043 if (debug) {
5044 const char tfecfFuncName[] = "checkInternalState: ";
5045 const char err[] =
5046 "Internal state is not consistent. "
5047 "Please report this bug to the Tpetra developers.";
5048 ProfilingRegion("Tpetra::CrsMatrix::checkInternalState");
5049
5050 // This version of the graph (RCP<const crs_graph_type>) must
5051 // always be nonnull.
5052 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(staticGraph_.is_null(), std::logic_error, err);
5053 // myGraph == null means that the matrix has a const ("static")
5054 // graph. Otherwise, the matrix has a dynamic graph (it owns its
5055 // graph).
5056 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!myGraph_.is_null() && myGraph_ != staticGraph_,
5057 std::logic_error, err);
5058 // if matrix is fill complete, then graph must be fill complete
5059 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(isFillComplete() && !staticGraph_->isFillComplete(),
5060 std::logic_error, err << " Specifically, the matrix is fill complete, "
5061 "but its graph is NOT fill complete.");
5062 // if values are allocated and they are non-zero in number, then
5063 // one of the allocations should be present
5064 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(staticGraph_->indicesAreAllocated() &&
5065 staticGraph_->getLocalAllocationSize() > 0 &&
5066 staticGraph_->getLocalNumRows() > 0 &&
5067 valuesUnpacked_wdv.extent(0) == 0,
5068 std::logic_error, err);
5069 }
5070}
5071
5072template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5073std::string
5075 description() const {
5076 std::ostringstream os;
5077
5078 os << "Tpetra::CrsMatrix (Kokkos refactor): {";
5079 if (this->getObjectLabel() != "") {
5080 os << "Label: \"" << this->getObjectLabel() << "\", ";
5081 }
5082 if (isFillComplete()) {
5083 os << "isFillComplete: true"
5084 << ", global dimensions: [" << getGlobalNumRows() << ", "
5085 << getGlobalNumCols() << "]"
5086 << ", global number of entries: " << getGlobalNumEntries()
5087 << "}";
5088 } else {
5089 os << "isFillComplete: false"
5090 << ", global dimensions: [" << getGlobalNumRows() << ", "
5091 << getGlobalNumCols() << "]}";
5092 }
5093 return os.str();
5094}
5095
5096template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5098 describe(Teuchos::FancyOStream& out,
5099 const Teuchos::EVerbosityLevel verbLevel) const {
5100 using std::endl;
5101 using std::setw;
5102 using Teuchos::ArrayView;
5103 using Teuchos::Comm;
5104 using Teuchos::RCP;
5105 using Teuchos::TypeNameTraits;
5106 using Teuchos::VERB_DEFAULT;
5107 using Teuchos::VERB_EXTREME;
5108 using Teuchos::VERB_HIGH;
5109 using Teuchos::VERB_LOW;
5110 using Teuchos::VERB_MEDIUM;
5111 using Teuchos::VERB_NONE;
5112
5113 const Teuchos::EVerbosityLevel vl = (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
5114
5115 if (vl == VERB_NONE) {
5116 return; // Don't print anything at all
5117 }
5118
5119 // By convention, describe() always begins with a tab.
5120 Teuchos::OSTab tab0(out);
5121
5122 RCP<const Comm<int>> comm = this->getComm();
5123 const int myRank = comm->getRank();
5124 const int numProcs = comm->getSize();
5125 size_t width = 1;
5126 for (size_t dec = 10; dec < getGlobalNumRows(); dec *= 10) {
5127 ++width;
5128 }
5129 width = std::max<size_t>(width, static_cast<size_t>(11)) + 2;
5130
5131 // none: print nothing
5132 // low: print O(1) info from node 0
5133 // medium: print O(P) info, num entries per process
5134 // high: print O(N) info, num entries per row
5135 // extreme: print O(NNZ) info: print indices and values
5136 //
5137 // for medium and higher, print constituent objects at specified verbLevel
5138 if (myRank == 0) {
5139 out << "Tpetra::CrsMatrix (Kokkos refactor):" << endl;
5140 }
5141 Teuchos::OSTab tab1(out);
5142
5143 if (myRank == 0) {
5144 if (this->getObjectLabel() != "") {
5145 out << "Label: \"" << this->getObjectLabel() << "\", ";
5146 }
5147 {
5148 out << "Template parameters:" << endl;
5149 Teuchos::OSTab tab2(out);
5150 out << "Scalar: " << TypeNameTraits<Scalar>::name() << endl
5151 << "LocalOrdinal: " << TypeNameTraits<LocalOrdinal>::name() << endl
5152 << "GlobalOrdinal: " << TypeNameTraits<GlobalOrdinal>::name() << endl
5153 << "Node: " << TypeNameTraits<Node>::name() << endl;
5154 }
5155 if (isFillComplete()) {
5156 out << "isFillComplete: true" << endl
5157 << "Global dimensions: [" << getGlobalNumRows() << ", "
5158 << getGlobalNumCols() << "]" << endl
5159 << "Global number of entries: " << getGlobalNumEntries() << endl
5160 << endl
5161 << "Global max number of entries in a row: "
5162 << getGlobalMaxNumRowEntries() << endl;
5163 } else {
5164 out << "isFillComplete: false" << endl
5165 << "Global dimensions: [" << getGlobalNumRows() << ", "
5166 << getGlobalNumCols() << "]" << endl;
5167 }
5168 }
5169
5170 if (vl < VERB_MEDIUM) {
5171 return; // all done!
5172 }
5173
5174 // Describe the row Map.
5175 if (myRank == 0) {
5176 out << endl
5177 << "Row Map:" << endl;
5178 }
5179 if (getRowMap().is_null()) {
5180 if (myRank == 0) {
5181 out << "null" << endl;
5182 }
5183 } else {
5184 if (myRank == 0) {
5185 out << endl;
5186 }
5187 getRowMap()->describe(out, vl);
5188 }
5189
5190 // Describe the column Map.
5191 if (myRank == 0) {
5192 out << "Column Map: ";
5193 }
5194 if (getColMap().is_null()) {
5195 if (myRank == 0) {
5196 out << "null" << endl;
5197 }
5198 } else if (getColMap() == getRowMap()) {
5199 if (myRank == 0) {
5200 out << "same as row Map" << endl;
5201 }
5202 } else {
5203 if (myRank == 0) {
5204 out << endl;
5205 }
5206 getColMap()->describe(out, vl);
5207 }
5208
5209 // Describe the domain Map.
5210 if (myRank == 0) {
5211 out << "Domain Map: ";
5212 }
5213 if (getDomainMap().is_null()) {
5214 if (myRank == 0) {
5215 out << "null" << endl;
5216 }
5217 } else if (getDomainMap() == getRowMap()) {
5218 if (myRank == 0) {
5219 out << "same as row Map" << endl;
5220 }
5221 } else if (getDomainMap() == getColMap()) {
5222 if (myRank == 0) {
5223 out << "same as column Map" << endl;
5224 }
5225 } else {
5226 if (myRank == 0) {
5227 out << endl;
5228 }
5229 getDomainMap()->describe(out, vl);
5230 }
5231
5232 // Describe the range Map.
5233 if (myRank == 0) {
5234 out << "Range Map: ";
5235 }
5236 if (getRangeMap().is_null()) {
5237 if (myRank == 0) {
5238 out << "null" << endl;
5239 }
5240 } else if (getRangeMap() == getDomainMap()) {
5241 if (myRank == 0) {
5242 out << "same as domain Map" << endl;
5243 }
5244 } else if (getRangeMap() == getRowMap()) {
5245 if (myRank == 0) {
5246 out << "same as row Map" << endl;
5247 }
5248 } else {
5249 if (myRank == 0) {
5250 out << endl;
5251 }
5252 getRangeMap()->describe(out, vl);
5253 }
5254
5255 // O(P) data
5256 for (int curRank = 0; curRank < numProcs; ++curRank) {
5257 if (myRank == curRank) {
5258 out << "Process rank: " << curRank << endl;
5259 Teuchos::OSTab tab2(out);
5260 if (!staticGraph_->indicesAreAllocated()) {
5261 out << "Graph indices not allocated" << endl;
5262 } else {
5263 out << "Number of allocated entries: "
5264 << staticGraph_->getLocalAllocationSize() << endl;
5265 }
5266 out << "Number of entries: " << getLocalNumEntries() << endl
5267 << "Max number of entries per row: " << getLocalMaxNumRowEntries()
5268 << endl;
5269 }
5270 // Give output time to complete by executing some barriers.
5271 comm->barrier();
5272 comm->barrier();
5273 comm->barrier();
5274 }
5275
5276 if (vl < VERB_HIGH) {
5277 return; // all done!
5278 }
5279
5280 // O(N) and O(NNZ) data
5281 for (int curRank = 0; curRank < numProcs; ++curRank) {
5282 if (myRank == curRank) {
5283 out << std::setw(width) << "Proc Rank"
5284 << std::setw(width) << "Global Row"
5285 << std::setw(width) << "Num Entries";
5286 if (vl == VERB_EXTREME) {
5287 out << std::setw(width) << "(Index,Value)";
5288 }
5289 out << endl;
5290 for (size_t r = 0; r < getLocalNumRows(); ++r) {
5291 const size_t nE = getNumEntriesInLocalRow(r);
5292 GlobalOrdinal gid = getRowMap()->getGlobalElement(r);
5293 out << std::setw(width) << myRank
5294 << std::setw(width) << gid
5295 << std::setw(width) << nE;
5296 if (vl == VERB_EXTREME) {
5297 if (isGloballyIndexed()) {
5298 global_inds_host_view_type rowinds;
5299 values_host_view_type rowvals;
5300 getGlobalRowView(gid, rowinds, rowvals);
5301 for (size_t j = 0; j < nE; ++j) {
5302 out << " (" << rowinds[j]
5303 << ", " << rowvals[j]
5304 << ") ";
5305 }
5306 } else if (isLocallyIndexed()) {
5307 local_inds_host_view_type rowinds;
5308 values_host_view_type rowvals;
5309 getLocalRowView(r, rowinds, rowvals);
5310 for (size_t j = 0; j < nE; ++j) {
5311 out << " (" << getColMap()->getGlobalElement(rowinds[j])
5312 << ", " << rowvals[j]
5313 << ") ";
5314 }
5315 } // globally or locally indexed
5316 } // vl == VERB_EXTREME
5317 out << endl;
5318 } // for each row r on this process
5319 } // if (myRank == curRank)
5320
5321 // Give output time to complete
5322 comm->barrier();
5323 comm->barrier();
5324 comm->barrier();
5325 } // for each process p
5326}
5327
5328template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5330 checkSizes(const SrcDistObject& source) {
5331 // It's not clear what kind of compatibility checks on sizes can
5332 // be performed here. Epetra_CrsGraph doesn't check any sizes for
5333 // compatibility.
5334
5335 // Currently, the source object must be a RowMatrix with the same
5336 // four template parameters as the target CrsMatrix. We might
5337 // relax this requirement later.
5338 const row_matrix_type* srcRowMat =
5339 dynamic_cast<const row_matrix_type*>(&source);
5340 return (srcRowMat != nullptr);
5341}
5342
5343template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5346 const typename crs_graph_type::padding_type& padding,
5347 const bool verbose) {
5350 using std::endl;
5351 using LO = local_ordinal_type;
5352 using row_ptrs_type =
5353 typename local_graph_device_type::row_map_type::non_const_type;
5354 using range_policy =
5355 Kokkos::RangePolicy<execution_space, Kokkos::IndexType<LO>>;
5356 const char tfecfFuncName[] = "applyCrsPadding";
5357 const char suffix[] =
5358 ". Please report this bug to the Tpetra developers.";
5359 ProfilingRegion regionCAP("Tpetra::CrsMatrix::applyCrsPadding");
5360
5361 std::unique_ptr<std::string> prefix;
5362 if (verbose) {
5363 prefix = this->createPrefix("CrsMatrix", tfecfFuncName);
5364 std::ostringstream os;
5365 os << *prefix << "padding: ";
5366 padding.print(os);
5367 os << endl;
5368 std::cerr << os.str();
5369 }
5370 const int myRank = !verbose ? -1 : [&]() {
5371 auto map = this->getMap();
5372 if (map.is_null()) {
5373 return -1;
5374 }
5375 auto comm = map->getComm();
5376 if (comm.is_null()) {
5377 return -1;
5378 }
5379 return comm->getRank();
5380 }();
5381
5382 // NOTE (mfh 29 Jan 2020) This allocates the values array.
5383 if (!myGraph_->indicesAreAllocated()) {
5384 if (verbose) {
5385 std::ostringstream os;
5386 os << *prefix << "Call allocateIndices" << endl;
5387 std::cerr << os.str();
5388 }
5389 allocateValues(GlobalIndices, GraphNotYetAllocated, verbose);
5390 }
5391
5392 // FIXME (mfh 10 Feb 2020) We shouldn't actually reallocate
5393 // row_ptrs_beg or allocate row_ptrs_end unless the allocation
5394 // size needs to increase. That should be the job of
5395 // padCrsArrays.
5396
5397 // Making copies here because rowPtrsUnpacked_ has a const type. Otherwise, we
5398 // would use it directly.
5399
5400 if (verbose) {
5401 std::ostringstream os;
5402 os << *prefix << "Allocate row_ptrs_beg: "
5403 << myGraph_->getRowPtrsUnpackedHost().extent(0) << endl;
5404 std::cerr << os.str();
5405 }
5406 using Kokkos::view_alloc;
5407 using Kokkos::WithoutInitializing;
5408 row_ptrs_type row_ptr_beg(view_alloc("row_ptr_beg", WithoutInitializing),
5409 myGraph_->rowPtrsUnpacked_dev_.extent(0));
5410 // DEEP_COPY REVIEW - DEVICE-TO-DEVICE
5411 Kokkos::deep_copy(execution_space(), row_ptr_beg, myGraph_->rowPtrsUnpacked_dev_);
5412
5413 const size_t N = row_ptr_beg.extent(0) == 0 ? size_t(0) : size_t(row_ptr_beg.extent(0) - 1);
5414 if (verbose) {
5415 std::ostringstream os;
5416 os << *prefix << "Allocate row_ptrs_end: " << N << endl;
5417 std::cerr << os.str();
5418 }
5419 row_ptrs_type row_ptr_end(
5420 view_alloc("row_ptr_end", WithoutInitializing), N);
5421
5422 row_ptrs_type num_row_entries_d;
5423
5424 const bool refill_num_row_entries =
5425 myGraph_->k_numRowEntries_.extent(0) != 0;
5426
5427 if (refill_num_row_entries) { // unpacked storage
5428 // We can't assume correct *this capture until C++17, and it's
5429 // likely more efficient just to capture what we need anyway.
5430 num_row_entries_d = create_mirror_view_and_copy(memory_space(),
5431 myGraph_->k_numRowEntries_);
5432 Kokkos::parallel_for(
5433 "Fill end row pointers", range_policy(0, N),
5434 KOKKOS_LAMBDA(const size_t i) {
5435 row_ptr_end(i) = row_ptr_beg(i) + num_row_entries_d(i);
5436 });
5437 } else {
5438 // FIXME (mfh 04 Feb 2020) Fix padCrsArrays so that if packed
5439 // storage, we don't need row_ptr_end to be separate allocation;
5440 // could just have it alias row_ptr_beg+1.
5441 Kokkos::parallel_for(
5442 "Fill end row pointers", range_policy(0, N),
5443 KOKKOS_LAMBDA(const size_t i) {
5444 row_ptr_end(i) = row_ptr_beg(i + 1);
5445 });
5446 }
5447
5448 if (myGraph_->isGloballyIndexed()) {
5449 padCrsArrays(row_ptr_beg, row_ptr_end,
5450 myGraph_->gblInds_wdv,
5451 valuesUnpacked_wdv, padding, myRank, verbose);
5452 const auto newValuesLen = valuesUnpacked_wdv.extent(0);
5453 const auto newColIndsLen = myGraph_->gblInds_wdv.extent(0);
5454 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(newValuesLen != newColIndsLen, std::logic_error,
5455 ": After padding, valuesUnpacked_wdv.extent(0)=" << newValuesLen
5456 << " != myGraph_->gblInds_wdv.extent(0)=" << newColIndsLen
5457 << suffix);
5458 } else {
5459 padCrsArrays(row_ptr_beg, row_ptr_end,
5460 myGraph_->lclIndsUnpacked_wdv,
5461 valuesUnpacked_wdv, padding, myRank, verbose);
5462 const auto newValuesLen = valuesUnpacked_wdv.extent(0);
5463 const auto newColIndsLen = myGraph_->lclIndsUnpacked_wdv.extent(0);
5464 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(newValuesLen != newColIndsLen, std::logic_error,
5465 ": After padding, valuesUnpacked_wdv.extent(0)=" << newValuesLen
5466 << " != myGraph_->lclIndsUnpacked_wdv.extent(0)=" << newColIndsLen
5467 << suffix);
5468 }
5469
5470 if (refill_num_row_entries) {
5471 Kokkos::parallel_for(
5472 "Fill num entries", range_policy(0, N),
5473 KOKKOS_LAMBDA(const size_t i) {
5474 num_row_entries_d(i) = row_ptr_end(i) - row_ptr_beg(i);
5475 });
5476 Kokkos::deep_copy(myGraph_->k_numRowEntries_, num_row_entries_d);
5477 }
5478
5479 if (verbose) {
5480 std::ostringstream os;
5481 os << *prefix << "Assign myGraph_->rowPtrsUnpacked_; "
5482 << "old size: " << myGraph_->rowPtrsUnpacked_host_.extent(0)
5483 << ", new size: " << row_ptr_beg.extent(0) << endl;
5484 std::cerr << os.str();
5485 TEUCHOS_ASSERT(myGraph_->getRowPtrsUnpackedHost().extent(0) ==
5486 row_ptr_beg.extent(0));
5487 }
5488 myGraph_->setRowPtrsUnpacked(row_ptr_beg);
5489}
5490
5491template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5492void copyAndPermuteStaticGraphNew(
5495 const size_t numSameIDs,
5496 const LocalOrdinal permuteToLIDs[],
5497 const LocalOrdinal permuteFromLIDs[],
5498 const size_t numPermutes);
5499
5500template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5504 const size_t numSameIDs,
5505 const LocalOrdinal permuteToLIDs[],
5506 const LocalOrdinal permuteFromLIDs[],
5507 const size_t numPermutes) {
5509 using std::endl;
5510 using Teuchos::Array;
5511 using Teuchos::ArrayView;
5512 using LO = LocalOrdinal;
5513 using GO = GlobalOrdinal;
5514 const char tfecfFuncName[] = "copyAndPermuteStaticGraph";
5515 const char suffix[] =
5516 " Please report this bug to the Tpetra developers.";
5517 ProfilingRegion regionCAP("Tpetra::CrsMatrix::copyAndPermuteStaticGraph");
5518
5519 const bool debug = Details::Behavior::debug("CrsGraph");
5520 const bool verbose = Details::Behavior::verbose("CrsGraph");
5521 std::unique_ptr<std::string> prefix;
5522 if (verbose) {
5523 prefix = this->createPrefix("CrsGraph", tfecfFuncName);
5524 std::ostringstream os;
5525 os << *prefix << "Start" << endl;
5526 }
5527 const char* const prefix_raw =
5528 verbose ? prefix.get()->c_str() : nullptr;
5529
5530 const bool sourceIsLocallyIndexed = srcMat.isLocallyIndexed();
5531 //
5532 // Copy the first numSame row from source to target (this matrix).
5533 // This involves copying rows corresponding to LIDs [0, numSame-1].
5534 //
5535 const auto& srcRowMap = *(srcMat.getRowMap());
5536 nonconst_global_inds_host_view_type rowInds;
5537 nonconst_values_host_view_type rowVals;
5538 const LO numSameIDs_as_LID = static_cast<LO>(numSameIDs);
5539 if (sourceIsLocallyIndexed) {
5540 for (LO sourceLID = 0; sourceLID < numSameIDs_as_LID; ++sourceLID) {
5541 // Global ID for the current row index in the source matrix.
5542 // The first numSameIDs GIDs in the two input lists are the
5543 // same, so sourceGID == targetGID in this case.
5544 const GO sourceGID = srcRowMap.getGlobalElement(sourceLID);
5545 const GO targetGID = sourceGID;
5546
5547 ArrayView<const GO> rowIndsConstView;
5548 ArrayView<const Scalar> rowValsConstView;
5549
5550 const size_t rowLength = srcMat.getNumEntriesInGlobalRow(sourceGID);
5551 if (rowLength > static_cast<size_t>(rowInds.size())) {
5552 Kokkos::resize(rowInds, rowLength);
5553 Kokkos::resize(rowVals, rowLength);
5554 }
5555 // Resizing invalidates an Array's views, so we must make new
5556 // ones, even if rowLength hasn't changed.
5557 nonconst_global_inds_host_view_type rowIndsView;
5558 nonconst_values_host_view_type rowValsView;
5559
5560 // The source matrix is locally indexed, so we have to get a
5561 // copy. Really it's the GIDs that have to be copied (because
5562 // they have to be converted from LIDs).
5563 size_t checkRowLength = 0;
5564 {
5566 const crs_matrix_type* srcMatCrsPtr = dynamic_cast<const crs_matrix_type*>(&srcMat);
5567 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(srcMatCrsPtr == nullptr, std::runtime_error, "bad srcMatCrsPtr");
5568 const crs_matrix_type& srcMatCrs = *srcMatCrsPtr;
5569
5570 auto globalRow = sourceGID;
5571 auto StaticGraphRCP = srcMatCrs.getGraph();
5572 const crs_graph_type* StaticGraphPtr = dynamic_cast<const crs_graph_type*>(StaticGraphRCP.get());
5573 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(StaticGraphPtr == nullptr, std::runtime_error, "bad StaticGraphPtr");
5574 const crs_graph_type& StaticGraph = *StaticGraphPtr;
5575 const RowInfo rowinfo = StaticGraph.getRowInfoFromGlobalRowIndex(globalRow);
5576 const size_t theNumEntries = rowinfo.numEntries;
5577 checkRowLength = theNumEntries; // first side effect
5578 auto numEntries = theNumEntries;
5579
5580 if (rowinfo.localRow != Teuchos::OrdinalTraits<size_t>::invalid()) {
5581 if (StaticGraph.isLocallyIndexed()) {
5582 const map_type& colMap = *(StaticGraph.getColMap());
5583 auto curLclInds = StaticGraph.getLocalIndsViewHost(rowinfo);
5584 auto rowValsViewLocal = srcMatCrs.getValuesViewHost(rowinfo);
5585 rowValsConstView = Teuchos::ArrayView<const Scalar>(
5586 reinterpret_cast<const Scalar*>(rowValsViewLocal.data()),
5587 rowValsViewLocal.extent(0),
5588 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5589 auto rowIndsViewLocal = Kokkos::subview(rowInds, std::make_pair((size_t)0, rowLength));
5590 rowIndsConstView = Teuchos::ArrayView<const GO>(
5591 rowIndsViewLocal.data(), rowIndsViewLocal.extent(0), Teuchos::RCP_DISABLE_NODE_LOOKUP);
5592 bool err = colMap.getGlobalElements(curLclInds.data(), numEntries, rowIndsViewLocal.data());
5593 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(err, std::runtime_error, "getGlobalElements error");
5594 } else if (StaticGraph.isGloballyIndexed()) {
5595 auto rowIndsViewLocal = StaticGraph.getGlobalIndsViewHost(rowinfo);
5596 rowIndsConstView = Teuchos::ArrayView<const GO>(
5597 rowIndsViewLocal.data(), rowIndsViewLocal.extent(0), Teuchos::RCP_DISABLE_NODE_LOOKUP);
5598 auto rowValsViewLocal = srcMatCrs.getValuesViewHost(rowinfo);
5599 rowValsConstView = Teuchos::ArrayView<const Scalar>(
5600 reinterpret_cast<const Scalar*>(rowValsViewLocal.data()),
5601 rowValsViewLocal.extent(0),
5602 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5603 }
5604 }
5605 }
5606 if (debug) {
5607 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
5608 rowLength != checkRowLength,
5609 std::logic_error,
5610 "For global row index " << sourceGID << ", the source matrix's getNumEntriesInGlobalRow returns a row length of " << rowLength << ", but getGlobalRowCopy reports a row length of " << checkRowLength << "." << suffix);
5611 }
5612
5613 combineGlobalValues(
5614 targetGID, rowIndsConstView, rowValsConstView, REPLACE, prefix_raw, debug, verbose);
5615 } // for (sourceLID...
5616 } else {
5617 for (LO sourceLID = 0; sourceLID < numSameIDs_as_LID; ++sourceLID) {
5618 // Global ID for the current row index in the source matrix.
5619 // The first numSameIDs GIDs in the two input lists are the
5620 // same, so sourceGID == targetGID in this case.
5621 const GO sourceGID = srcRowMap.getGlobalElement(sourceLID);
5622 const GO targetGID = sourceGID;
5623
5624 ArrayView<const GO> rowIndsConstView;
5625 ArrayView<const Scalar> rowValsConstView;
5626
5627 global_inds_host_view_type rowIndsView;
5628 values_host_view_type rowValsView;
5629 srcMat.getGlobalRowView(sourceGID, rowIndsView, rowValsView);
5630 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
5631 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
5632 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
5633 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
5634 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
5635 rowIndsView.data(), rowIndsView.extent(0),
5636 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5637 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
5638 reinterpret_cast<const Scalar*>(rowValsView.data()), rowValsView.extent(0),
5639 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5640 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
5641 // KDDKDD UVM TEMPORARY: KokkosView interface
5642 // Applying a permutation to a matrix with a static graph
5643 // means REPLACE-ing entries.
5644 combineGlobalValues(targetGID, rowIndsConstView,
5645 rowValsConstView, REPLACE,
5646 prefix_raw, debug, verbose);
5647 }
5648 }
5649
5650 if (verbose) {
5651 std::ostringstream os;
5652 os << *prefix << "Do permutes" << endl;
5653 }
5654
5655 //
5656 // "Permute" part of "copy and permute."
5657 //
5658
5659 const map_type& tgtRowMap = *(this->getRowMap());
5660 for (size_t p = 0; p < numPermutes; ++p) {
5661 const GO sourceGID = srcRowMap.getGlobalElement(permuteFromLIDs[p]);
5662 const GO targetGID = tgtRowMap.getGlobalElement(permuteToLIDs[p]);
5663
5664 ArrayView<const GO> rowIndsConstView;
5665 ArrayView<const Scalar> rowValsConstView;
5666
5667 if (sourceIsLocallyIndexed) {
5668 const size_t rowLength = srcMat.getNumEntriesInGlobalRow(sourceGID);
5669 if (rowLength > static_cast<size_t>(rowInds.size())) {
5670 Kokkos::resize(rowInds, rowLength);
5671 Kokkos::resize(rowVals, rowLength);
5672 }
5673 // Resizing invalidates an Array's views, so we must make new
5674 // ones, even if rowLength hasn't changed.
5675 nonconst_global_inds_host_view_type rowIndsView = Kokkos::subview(rowInds, std::make_pair((size_t)0, rowLength));
5676 nonconst_values_host_view_type rowValsView = Kokkos::subview(rowVals, std::make_pair((size_t)0, rowLength));
5677
5678 // The source matrix is locally indexed, so we have to get a
5679 // copy. Really it's the GIDs that have to be copied (because
5680 // they have to be converted from LIDs).
5681 size_t checkRowLength = 0;
5682 srcMat.getGlobalRowCopy(sourceGID, rowIndsView,
5683 rowValsView, checkRowLength);
5684 if (debug) {
5685 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(rowLength != checkRowLength, std::logic_error,
5686 "For "
5687 "source matrix global row index "
5688 << sourceGID << ", "
5689 "getNumEntriesInGlobalRow returns a row length of "
5690 << rowLength << ", but getGlobalRowCopy a row length of "
5691 << checkRowLength << "." << suffix);
5692 }
5693
5694 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
5695 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
5696 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
5697 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
5698 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
5699 rowIndsView.data(), rowIndsView.extent(0),
5700 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5701 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
5702 reinterpret_cast<const Scalar*>(rowValsView.data()), rowValsView.extent(0),
5703 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5704 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
5705 // KDDKDD UVM TEMPORARY: KokkosView interface
5706 } else {
5707 global_inds_host_view_type rowIndsView;
5708 values_host_view_type rowValsView;
5709 srcMat.getGlobalRowView(sourceGID, rowIndsView, rowValsView);
5710 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
5711 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
5712 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
5713 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
5714 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
5715 rowIndsView.data(), rowIndsView.extent(0),
5716 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5717 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
5718 reinterpret_cast<const Scalar*>(rowValsView.data()), rowValsView.extent(0),
5719 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5720 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
5721 // KDDKDD UVM TEMPORARY: KokkosView interface
5722 }
5723
5724 combineGlobalValues(targetGID, rowIndsConstView,
5725 rowValsConstView, REPLACE,
5726 prefix_raw, debug, verbose);
5727 }
5728
5729 if (verbose) {
5730 std::ostringstream os;
5731 os << *prefix << "Done" << endl;
5732 }
5733}
5734
5735template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5739 const size_t numSameIDs,
5740 const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& permuteToLIDs_dv,
5741 const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& permuteFromLIDs_dv,
5742 const size_t numPermutes) {
5744 using std::endl;
5745 using Teuchos::Array;
5746 using Teuchos::ArrayView;
5747 using LO = LocalOrdinal;
5748 using GO = GlobalOrdinal;
5749 const char tfecfFuncName[] = "copyAndPermuteNonStaticGraph";
5750 const char suffix[] =
5751 " Please report this bug to the Tpetra developers.";
5752 ProfilingRegion regionCAP("Tpetra::CrsMatrix::copyAndPermuteNonStaticGraph");
5753
5754 const bool debug = Details::Behavior::debug("CrsGraph");
5755 const bool verbose = Details::Behavior::verbose("CrsGraph");
5756 std::unique_ptr<std::string> prefix;
5757 if (verbose) {
5758 prefix = this->createPrefix("CrsGraph", tfecfFuncName);
5759 std::ostringstream os;
5760 os << *prefix << "Start" << endl;
5761 }
5762 const char* const prefix_raw =
5763 verbose ? prefix.get()->c_str() : nullptr;
5764
5765 {
5766 using row_graph_type = RowGraph<LO, GO, Node>;
5767 const row_graph_type& srcGraph = *(srcMat.getGraph());
5768 auto padding =
5769 myGraph_->computeCrsPadding(srcGraph, numSameIDs,
5770 permuteToLIDs_dv, permuteFromLIDs_dv, verbose);
5771 applyCrsPadding(*padding, verbose);
5772 }
5773 const bool sourceIsLocallyIndexed = srcMat.isLocallyIndexed();
5774 //
5775 // Copy the first numSame row from source to target (this matrix).
5776 // This involves copying rows corresponding to LIDs [0, numSame-1].
5777 //
5778 const map_type& srcRowMap = *(srcMat.getRowMap());
5779 const LO numSameIDs_as_LID = static_cast<LO>(numSameIDs);
5780 using gids_type = nonconst_global_inds_host_view_type;
5781 using vals_type = nonconst_values_host_view_type;
5782 gids_type rowInds;
5783 vals_type rowVals;
5784 for (LO sourceLID = 0; sourceLID < numSameIDs_as_LID; ++sourceLID) {
5785 // Global ID for the current row index in the source matrix.
5786 // The first numSameIDs GIDs in the two input lists are the
5787 // same, so sourceGID == targetGID in this case.
5788 const GO sourceGID = srcRowMap.getGlobalElement(sourceLID);
5789 const GO targetGID = sourceGID;
5790
5791 ArrayView<const GO> rowIndsConstView;
5792 ArrayView<const Scalar> rowValsConstView;
5793
5794 if (sourceIsLocallyIndexed) {
5795 const size_t rowLength = srcMat.getNumEntriesInGlobalRow(sourceGID);
5796 if (rowLength > static_cast<size_t>(rowInds.extent(0))) {
5797 Kokkos::resize(rowInds, rowLength);
5798 Kokkos::resize(rowVals, rowLength);
5799 }
5800 // Resizing invalidates an Array's views, so we must make new
5801 // ones, even if rowLength hasn't changed.
5802 gids_type rowIndsView = Kokkos::subview(rowInds, std::make_pair((size_t)0, rowLength));
5803 vals_type rowValsView = Kokkos::subview(rowVals, std::make_pair((size_t)0, rowLength));
5804
5805 // The source matrix is locally indexed, so we have to get a
5806 // copy. Really it's the GIDs that have to be copied (because
5807 // they have to be converted from LIDs).
5808 size_t checkRowLength = 0;
5809 srcMat.getGlobalRowCopy(sourceGID, rowIndsView, rowValsView,
5810 checkRowLength);
5811 if (debug) {
5812 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(rowLength != checkRowLength, std::logic_error,
5813 ": For "
5814 "global row index "
5815 << sourceGID << ", the source "
5816 "matrix's getNumEntriesInGlobalRow returns a row length "
5817 "of "
5818 << rowLength << ", but getGlobalRowCopy reports "
5819 "a row length of "
5820 << checkRowLength << "." << suffix);
5821 }
5822 rowIndsConstView = Teuchos::ArrayView<const GO>(rowIndsView.data(), rowLength);
5823 rowValsConstView = Teuchos::ArrayView<const Scalar>(reinterpret_cast<Scalar*>(rowValsView.data()), rowLength);
5824 } else { // source matrix is globally indexed.
5825 global_inds_host_view_type rowIndsView;
5826 values_host_view_type rowValsView;
5827 srcMat.getGlobalRowView(sourceGID, rowIndsView, rowValsView);
5828
5829 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
5830 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
5831 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
5832 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
5833 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
5834 rowIndsView.data(), rowIndsView.extent(0),
5835 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5836 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
5837 reinterpret_cast<const Scalar*>(rowValsView.data()), rowValsView.extent(0),
5838 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5839 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
5840 // KDDKDD UVM TEMPORARY: KokkosView interface
5841 }
5842
5843 // Combine the data into the target matrix.
5844 insertGlobalValuesFilteredChecked(targetGID, rowIndsConstView,
5845 rowValsConstView, prefix_raw, debug, verbose);
5846 }
5847
5848 if (verbose) {
5849 std::ostringstream os;
5850 os << *prefix << "Do permutes" << endl;
5851 }
5852 const LO* const permuteFromLIDs = permuteFromLIDs_dv.view_host().data();
5853 const LO* const permuteToLIDs = permuteToLIDs_dv.view_host().data();
5854
5855 const map_type& tgtRowMap = *(this->getRowMap());
5856 for (size_t p = 0; p < numPermutes; ++p) {
5857 const GO sourceGID = srcRowMap.getGlobalElement(permuteFromLIDs[p]);
5858 const GO targetGID = tgtRowMap.getGlobalElement(permuteToLIDs[p]);
5859
5860 ArrayView<const GO> rowIndsConstView;
5861 ArrayView<const Scalar> rowValsConstView;
5862
5863 if (sourceIsLocallyIndexed) {
5864 const size_t rowLength = srcMat.getNumEntriesInGlobalRow(sourceGID);
5865 if (rowLength > static_cast<size_t>(rowInds.extent(0))) {
5866 Kokkos::resize(rowInds, rowLength);
5867 Kokkos::resize(rowVals, rowLength);
5868 }
5869 // Resizing invalidates an Array's views, so we must make new
5870 // ones, even if rowLength hasn't changed.
5871 gids_type rowIndsView = Kokkos::subview(rowInds, std::make_pair((size_t)0, rowLength));
5872 vals_type rowValsView = Kokkos::subview(rowVals, std::make_pair((size_t)0, rowLength));
5873
5874 // The source matrix is locally indexed, so we have to get a
5875 // copy. Really it's the GIDs that have to be copied (because
5876 // they have to be converted from LIDs).
5877 size_t checkRowLength = 0;
5878 srcMat.getGlobalRowCopy(sourceGID, rowIndsView,
5879 rowValsView, checkRowLength);
5880 if (debug) {
5881 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(rowLength != checkRowLength, std::logic_error,
5882 "For "
5883 "source matrix global row index "
5884 << sourceGID << ", "
5885 "getNumEntriesInGlobalRow returns a row length of "
5886 << rowLength << ", but getGlobalRowCopy a row length of "
5887 << checkRowLength << "." << suffix);
5888 }
5889 rowIndsConstView = Teuchos::ArrayView<const GO>(rowIndsView.data(), rowLength);
5890 rowValsConstView = Teuchos::ArrayView<const Scalar>(reinterpret_cast<Scalar*>(rowValsView.data()), rowLength);
5891 } else {
5892 global_inds_host_view_type rowIndsView;
5893 values_host_view_type rowValsView;
5894 srcMat.getGlobalRowView(sourceGID, rowIndsView, rowValsView);
5895
5896 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
5897 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
5898 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
5899 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
5900 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
5901 rowIndsView.data(), rowIndsView.extent(0),
5902 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5903 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
5904 reinterpret_cast<const Scalar*>(rowValsView.data()), rowValsView.extent(0),
5905 Teuchos::RCP_DISABLE_NODE_LOOKUP);
5906 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
5907 // KDDKDD UVM TEMPORARY: KokkosView interface
5908 }
5909
5910 // Combine the data into the target matrix.
5911 insertGlobalValuesFilteredChecked(targetGID, rowIndsConstView,
5912 rowValsConstView, prefix_raw, debug, verbose);
5913 }
5914
5915 if (verbose) {
5916 std::ostringstream os;
5917 os << *prefix << "Done" << endl;
5918 }
5919}
5920
5921template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
5924 const SrcDistObject& srcObj,
5925 const size_t numSameIDs,
5926 const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& permuteToLIDs,
5927 const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& permuteFromLIDs,
5928 const CombineMode /*CM*/) {
5929 using Details::Behavior;
5932 using std::endl;
5933
5934 // Method name string for TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC.
5935 const char tfecfFuncName[] = "copyAndPermute: ";
5936 ProfilingRegion regionCAP("Tpetra::CrsMatrix::copyAndPermute");
5937
5938 const bool verbose = Behavior::verbose("CrsMatrix");
5939 std::unique_ptr<std::string> prefix;
5940 if (verbose) {
5941 prefix = this->createPrefix("CrsMatrix", "copyAndPermute");
5942 std::ostringstream os;
5943 os << *prefix << endl
5944 << *prefix << " numSameIDs: " << numSameIDs << endl
5945 << *prefix << " numPermute: " << permuteToLIDs.extent(0)
5946 << endl
5947 << *prefix << " "
5948 << dualViewStatusToString(permuteToLIDs, "permuteToLIDs")
5949 << endl
5950 << *prefix << " "
5951 << dualViewStatusToString(permuteFromLIDs, "permuteFromLIDs")
5952 << endl
5953 << *prefix << " "
5954 << "isStaticGraph: " << (isStaticGraph() ? "true" : "false")
5955 << endl;
5956 std::cerr << os.str();
5957 }
5958
5959 const auto numPermute = permuteToLIDs.extent(0);
5960 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(numPermute != permuteFromLIDs.extent(0),
5961 std::invalid_argument, "permuteToLIDs.extent(0) = " << numPermute << "!= permuteFromLIDs.extent(0) = " << permuteFromLIDs.extent(0) << ".");
5962
5963 // This dynamic cast should succeed, because we've already tested
5964 // it in checkSizes().
5966 const RMT& srcMat = dynamic_cast<const RMT&>(srcObj);
5967 if (isStaticGraph()) {
5969 TEUCHOS_ASSERT(!permuteToLIDs.need_sync_device());
5970 auto permuteToLIDs_d = permuteToLIDs.view_device();
5971 TEUCHOS_ASSERT(!permuteFromLIDs.need_sync_device());
5972 auto permuteFromLIDs_d = permuteFromLIDs.view_device();
5973 copyAndPermuteStaticGraphNew(
5974 srcMat, *this, numSameIDs, permuteToLIDs_d.data(), permuteFromLIDs_d.data(), numPermute);
5975
5976 } else {
5977 TEUCHOS_ASSERT(!permuteToLIDs.need_sync_host());
5978 auto permuteToLIDs_h = permuteToLIDs.view_host();
5979 TEUCHOS_ASSERT(!permuteFromLIDs.need_sync_host());
5980 auto permuteFromLIDs_h = permuteFromLIDs.view_host();
5981
5982 copyAndPermuteStaticGraph(srcMat, numSameIDs,
5983 permuteToLIDs_h.data(),
5984 permuteFromLIDs_h.data(),
5985 numPermute);
5986 }
5987 } else {
5988 copyAndPermuteNonStaticGraph(srcMat, numSameIDs, permuteToLIDs,
5989 permuteFromLIDs, numPermute);
5990 }
5991
5992 if (verbose) {
5993 std::ostringstream os;
5994 os << *prefix << "Done" << endl;
5995 std::cerr << os.str();
5996 }
5997}
5998
5999template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6001 packAndPrepare(const SrcDistObject& source,
6002 const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& exportLIDs,
6003 Kokkos::DualView<char*, buffer_device_type>& exports,
6004 Kokkos::DualView<size_t*, buffer_device_type> numPacketsPerLID,
6005 size_t& constantNumPackets) {
6006 using Details::Behavior;
6009 using std::endl;
6010 using Teuchos::outArg;
6011 using Teuchos::REDUCE_MAX;
6012 using Teuchos::reduceAll;
6013 typedef LocalOrdinal LO;
6014 typedef GlobalOrdinal GO;
6015 const char tfecfFuncName[] = "packAndPrepare: ";
6016 ProfilingRegion regionPAP("Tpetra::CrsMatrix::packAndPrepare");
6017
6018 const bool debug = Behavior::debug("CrsMatrix");
6019 const bool verbose = Behavior::verbose("CrsMatrix");
6020
6021 // Processes on which the communicator is null should not participate.
6022 Teuchos::RCP<const Teuchos::Comm<int>> pComm = this->getComm();
6023 if (pComm.is_null()) {
6024 return;
6025 }
6026 const Teuchos::Comm<int>& comm = *pComm;
6027 const int myRank = comm.getSize();
6028
6029 std::unique_ptr<std::string> prefix;
6030 if (verbose) {
6031 prefix = this->createPrefix("CrsMatrix", "packAndPrepare");
6032 std::ostringstream os;
6033 os << *prefix << "Start" << endl
6034 << *prefix << " "
6035 << dualViewStatusToString(exportLIDs, "exportLIDs")
6036 << endl
6037 << *prefix << " "
6038 << dualViewStatusToString(exports, "exports")
6039 << endl
6040 << *prefix << " "
6041 << dualViewStatusToString(numPacketsPerLID, "numPacketsPerLID")
6042 << endl;
6043 std::cerr << os.str();
6044 }
6045
6046 // Attempt to cast the source object to CrsMatrix. If successful,
6047 // use the source object's packNew() method to pack its data for
6048 // communication. Otherwise, attempt to cast to RowMatrix; if
6049 // successful, use the source object's pack() method. Otherwise,
6050 // the source object doesn't have the right type.
6051 //
6052 // FIXME (mfh 30 Jun 2013, 11 Sep 2017) We don't even need the
6053 // RowMatrix to have the same Node type. Unfortunately, we don't
6054 // have a way to ask if the RowMatrix is "a RowMatrix with any
6055 // Node type," since RowMatrix doesn't have a base class. A
6056 // hypothetical RowMatrixBase<Scalar, LO, GO> class, which does
6057 // not currently exist, would satisfy this requirement.
6058 //
6059 // Why RowMatrixBase<Scalar, LO, GO>? The source object's Scalar
6060 // type doesn't technically need to match the target object's
6061 // Scalar type, so we could just have RowMatrixBase<LO, GO>. LO
6062 // and GO need not be the same, as long as there is no overflow of
6063 // the indices. However, checking for index overflow is global
6064 // and therefore undesirable.
6065
6066 std::ostringstream msg; // for collecting error messages
6067 int lclBad = 0; // to be set below
6068
6069 using crs_matrix_type = CrsMatrix<Scalar, LO, GO, Node>;
6070 const crs_matrix_type* srcCrsMat =
6071 dynamic_cast<const crs_matrix_type*>(&source);
6072 if (srcCrsMat != nullptr) {
6073 if (verbose) {
6074 std::ostringstream os;
6075 os << *prefix << "Source matrix same (CrsMatrix) type as target; "
6076 "calling packNew"
6077 << endl;
6078 std::cerr << os.str();
6079 }
6080 try {
6081 srcCrsMat->packNew(exportLIDs, exports, numPacketsPerLID,
6082 constantNumPackets);
6083 } catch (std::exception& e) {
6084 lclBad = 1;
6085 msg << "Proc " << myRank << ": " << e.what() << std::endl;
6086 }
6087 } else {
6088 using Kokkos::HostSpace;
6089 using Kokkos::subview;
6090 using exports_type = Kokkos::DualView<char*, buffer_device_type>;
6091 using range_type = Kokkos::pair<size_t, size_t>;
6092
6093 if (verbose) {
6094 std::ostringstream os;
6095 os << *prefix << "Source matrix NOT same (CrsMatrix) type as target"
6096 << endl;
6097 std::cerr << os.str();
6098 }
6099
6100 const row_matrix_type* srcRowMat =
6101 dynamic_cast<const row_matrix_type*>(&source);
6102 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(srcRowMat == nullptr, std::invalid_argument,
6103 "The source object of the Import or Export operation is neither a "
6104 "CrsMatrix (with the same template parameters as the target object), "
6105 "nor a RowMatrix (with the same first four template parameters as the "
6106 "target object).");
6107
6108 // For the RowMatrix case, we need to convert from
6109 // Kokkos::DualView to Teuchos::Array*. This doesn't need to be
6110 // so terribly efficient, since packing a non-CrsMatrix
6111 // RowMatrix for Import/Export into a CrsMatrix is not a
6112 // critical case. Thus, we may allocate Teuchos::Array objects
6113 // here and copy to and from Kokkos::*View.
6114
6115 // View exportLIDs's host data as a Teuchos::ArrayView.
6116 TEUCHOS_ASSERT(!exportLIDs.need_sync_host());
6117 auto exportLIDs_h = exportLIDs.view_host();
6118 Teuchos::ArrayView<const LO> exportLIDs_av(exportLIDs_h.data(),
6119 exportLIDs_h.size());
6120
6121 // pack() will allocate exports_a as needed. We'll copy back
6122 // into exports (after (re)allocating exports if needed) below.
6123 Teuchos::Array<char> exports_a;
6124
6125 // View exportLIDs' host data as a Teuchos::ArrayView. We don't
6126 // need to sync, since we're doing write-only access, but we do
6127 // need to mark the DualView as modified on host.
6128
6129 numPacketsPerLID.clear_sync_state(); // write-only access
6130 numPacketsPerLID.modify_host();
6131 auto numPacketsPerLID_h = numPacketsPerLID.view_host();
6132 Teuchos::ArrayView<size_t> numPacketsPerLID_av(numPacketsPerLID_h.data(),
6133 numPacketsPerLID_h.size());
6134
6135 // Invoke RowMatrix's legacy pack() interface, using above
6136 // Teuchos::Array* objects.
6137 try {
6138 srcRowMat->pack(exportLIDs_av, exports_a, numPacketsPerLID_av,
6139 constantNumPackets);
6140 } catch (std::exception& e) {
6141 lclBad = 1;
6142 msg << "Proc " << myRank << ": " << e.what() << std::endl;
6143 }
6144
6145 // Allocate 'exports', and copy exports_a back into it.
6146 const size_t newAllocSize = static_cast<size_t>(exports_a.size());
6147 if (static_cast<size_t>(exports.extent(0)) < newAllocSize) {
6148 const std::string oldLabel = exports.view_device().label();
6149 const std::string newLabel = (oldLabel == "") ? "exports" : oldLabel;
6150 exports = exports_type(newLabel, newAllocSize);
6151 }
6152 // It's safe to assume that we're working on host anyway, so
6153 // just keep exports sync'd to host.
6154 // ignore current device contents
6155 exports.modify_host();
6156
6157 auto exports_h = exports.view_host();
6158 auto exports_h_sub = subview(exports_h, range_type(0, newAllocSize));
6159
6160 // Kokkos::deep_copy needs a Kokkos::View input, so turn
6161 // exports_a into a nonowning Kokkos::View first before copying.
6162 typedef typename exports_type::t_host::execution_space HES;
6163 typedef Kokkos::Device<HES, HostSpace> host_device_type;
6164 Kokkos::View<const char*, host_device_type>
6165 exports_a_kv(exports_a.getRawPtr(), newAllocSize);
6166 // DEEP_COPY REVIEW - NOT TESTED
6167 Kokkos::deep_copy(exports_h_sub, exports_a_kv);
6168 }
6169
6170 if (debug) {
6171 int gblBad = 0; // output argument; to be set below
6172 reduceAll<int, int>(comm, REDUCE_MAX, lclBad, outArg(gblBad));
6173 if (gblBad != 0) {
6174 Tpetra::Details::gathervPrint(std::cerr, msg.str(), comm);
6175 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::logic_error,
6176 "packNew() or pack() threw an exception on "
6177 "one or more participating processes.");
6178 }
6179 } else {
6180 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(lclBad != 0, std::logic_error,
6181 "packNew threw an exception on one "
6182 "or more participating processes. Here is this process' error "
6183 "message: "
6184 << msg.str());
6185 }
6186
6187 if (verbose) {
6188 std::ostringstream os;
6189 os << *prefix << "packAndPrepare: Done!" << endl
6190 << *prefix << " "
6191 << dualViewStatusToString(exportLIDs, "exportLIDs")
6192 << endl
6193 << *prefix << " "
6194 << dualViewStatusToString(exports, "exports")
6195 << endl
6196 << *prefix << " "
6197 << dualViewStatusToString(numPacketsPerLID, "numPacketsPerLID")
6198 << endl;
6199 std::cerr << os.str();
6200 }
6201}
6202
6203template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6204size_t
6206 packRow(char exports[],
6207 const size_t offset,
6208 const size_t numEnt,
6209 const GlobalOrdinal gidsIn[],
6210 const impl_scalar_type valsIn[],
6211 const size_t numBytesPerValue) const {
6212 using Kokkos::subview;
6213 using Kokkos::View;
6214 using Tpetra::Details::PackTraits;
6215 typedef LocalOrdinal LO;
6216 typedef GlobalOrdinal GO;
6217 typedef impl_scalar_type ST;
6218
6219 if (numEnt == 0) {
6220 // Empty rows always take zero bytes, to ensure sparsity.
6221 return 0;
6222 }
6223
6224 const GO gid = 0; // packValueCount wants this
6225 const LO numEntLO = static_cast<size_t>(numEnt);
6226
6227 const size_t numEntBeg = offset;
6228 const size_t numEntLen = PackTraits<LO>::packValueCount(numEntLO);
6229 const size_t gidsBeg = numEntBeg + numEntLen;
6230 const size_t gidsLen = numEnt * PackTraits<GO>::packValueCount(gid);
6231 const size_t valsBeg = gidsBeg + gidsLen;
6232 const size_t valsLen = numEnt * numBytesPerValue;
6233
6234 char* const numEntOut = exports + numEntBeg;
6235 char* const gidsOut = exports + gidsBeg;
6236 char* const valsOut = exports + valsBeg;
6237
6238 size_t numBytesOut = 0;
6239 int errorCode = 0;
6240 numBytesOut += PackTraits<LO>::packValue(numEntOut, numEntLO);
6241
6242 {
6243 Kokkos::pair<int, size_t> p;
6244 p = PackTraits<GO>::packArray(gidsOut, gidsIn, numEnt);
6245 errorCode += p.first;
6246 numBytesOut += p.second;
6247
6248 p = PackTraits<ST>::packArray(valsOut, valsIn, numEnt);
6249 errorCode += p.first;
6250 numBytesOut += p.second;
6251 }
6252
6253 const size_t expectedNumBytes = numEntLen + gidsLen + valsLen;
6254 TEUCHOS_TEST_FOR_EXCEPTION(numBytesOut != expectedNumBytes, std::logic_error,
6255 "packRow: "
6256 "numBytesOut = "
6257 << numBytesOut << " != expectedNumBytes = "
6258 << expectedNumBytes << ".");
6259 TEUCHOS_TEST_FOR_EXCEPTION(errorCode != 0, std::runtime_error,
6260 "packRow: "
6261 "PackTraits::packArray returned a nonzero error code");
6262
6263 return numBytesOut;
6264}
6265
6266template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6267size_t
6269 unpackRow(GlobalOrdinal gidsOut[],
6270 impl_scalar_type valsOut[],
6271 const char imports[],
6272 const size_t offset,
6273 const size_t numBytes,
6274 const size_t numEnt,
6275 const size_t numBytesPerValue) {
6276 using Kokkos::subview;
6277 using Kokkos::View;
6278 using Tpetra::Details::PackTraits;
6279 typedef LocalOrdinal LO;
6280 typedef GlobalOrdinal GO;
6281 typedef impl_scalar_type ST;
6282
6283 Details::ProfilingRegion region_upack_row(
6284 "Tpetra::CrsMatrix::unpackRow",
6285 "Import/Export");
6286
6287 if (numBytes == 0) {
6288 // Rows with zero bytes should always have zero entries.
6289 if (numEnt != 0) {
6290 const int myRank = this->getMap()->getComm()->getRank();
6291 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error, "(Proc " << myRank << ") CrsMatrix::"
6292 "unpackRow: The number of bytes to unpack numBytes=0, but the "
6293 "number of entries to unpack (as reported by numPacketsPerLID) "
6294 "for this row numEnt="
6295 << numEnt << " != 0.");
6296 }
6297 return 0;
6298 }
6299
6300 if (numEnt == 0 && numBytes != 0) {
6301 const int myRank = this->getMap()->getComm()->getRank();
6302 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error, "(Proc " << myRank << ") CrsMatrix::"
6303 "unpackRow: The number of entries to unpack (as reported by "
6304 "numPacketsPerLID) numEnt=0, but the number of bytes to unpack "
6305 "numBytes="
6306 << numBytes << " != 0.");
6307 }
6308
6309 const GO gid = 0; // packValueCount wants this
6310 const LO lid = 0; // packValueCount wants this
6311
6312 const size_t numEntBeg = offset;
6313 const size_t numEntLen = PackTraits<LO>::packValueCount(lid);
6314 const size_t gidsBeg = numEntBeg + numEntLen;
6315 const size_t gidsLen = numEnt * PackTraits<GO>::packValueCount(gid);
6316 const size_t valsBeg = gidsBeg + gidsLen;
6317 const size_t valsLen = numEnt * numBytesPerValue;
6318
6319 const char* const numEntIn = imports + numEntBeg;
6320 const char* const gidsIn = imports + gidsBeg;
6321 const char* const valsIn = imports + valsBeg;
6322
6323 size_t numBytesOut = 0;
6324 int errorCode = 0;
6325 LO numEntOut;
6326 numBytesOut += PackTraits<LO>::unpackValue(numEntOut, numEntIn);
6327 if (static_cast<size_t>(numEntOut) != numEnt ||
6328 numEntOut == static_cast<LO>(0)) {
6329 const int myRank = this->getMap()->getComm()->getRank();
6330 std::ostringstream os;
6331 os << "(Proc " << myRank << ") CrsMatrix::unpackRow: ";
6332 bool firstErrorCondition = false;
6333 if (static_cast<size_t>(numEntOut) != numEnt) {
6334 os << "Number of entries from numPacketsPerLID numEnt=" << numEnt
6335 << " does not equal number of entries unpacked from imports "
6336 "buffer numEntOut="
6337 << numEntOut << ".";
6338 firstErrorCondition = true;
6339 }
6340 if (numEntOut == static_cast<LO>(0)) {
6341 if (firstErrorCondition) {
6342 os << " Also, ";
6343 }
6344 os << "Number of entries unpacked from imports buffer numEntOut=0, "
6345 "but number of bytes to unpack for this row numBytes="
6346 << numBytes
6347 << " != 0. This should never happen, since packRow should only "
6348 "ever pack rows with a nonzero number of entries. In this case, "
6349 "the number of entries from numPacketsPerLID is numEnt="
6350 << numEnt
6351 << ".";
6352 }
6353 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error, os.str());
6354 }
6355
6356 {
6357 Kokkos::pair<int, size_t> p;
6358 p = PackTraits<GO>::unpackArray(gidsOut, gidsIn, numEnt);
6359 errorCode += p.first;
6360 numBytesOut += p.second;
6361
6362 p = PackTraits<ST>::unpackArray(valsOut, valsIn, numEnt);
6363 errorCode += p.first;
6364 numBytesOut += p.second;
6365 }
6366
6367 TEUCHOS_TEST_FOR_EXCEPTION(numBytesOut != numBytes, std::logic_error, "unpackRow: numBytesOut = " << numBytesOut << " != numBytes = " << numBytes << ".");
6368
6369 const size_t expectedNumBytes = numEntLen + gidsLen + valsLen;
6370 TEUCHOS_TEST_FOR_EXCEPTION(numBytesOut != expectedNumBytes, std::logic_error,
6371 "unpackRow: "
6372 "numBytesOut = "
6373 << numBytesOut << " != expectedNumBytes = "
6374 << expectedNumBytes << ".");
6375
6376 TEUCHOS_TEST_FOR_EXCEPTION(errorCode != 0, std::runtime_error,
6377 "unpackRow: "
6378 "PackTraits::unpackArray returned a nonzero error code");
6379
6380 return numBytesOut;
6381}
6382
6383template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6385 allocatePackSpaceNew(Kokkos::DualView<char*, buffer_device_type>& exports,
6386 size_t& totalNumEntries,
6387 const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& exportLIDs) const {
6388 using Details::Behavior;
6390 using std::endl;
6391 typedef impl_scalar_type IST;
6392 typedef LocalOrdinal LO;
6393 typedef GlobalOrdinal GO;
6394 // const char tfecfFuncName[] = "allocatePackSpaceNew: ";
6395
6396 // mfh 18 Oct 2017: Set TPETRA_VERBOSE to true for copious debug
6397 // output to std::cerr on every MPI process. This is unwise for
6398 // runs with large numbers of MPI processes.
6399 const bool verbose = Behavior::verbose("CrsMatrix");
6400 std::unique_ptr<std::string> prefix;
6401 if (verbose) {
6402 prefix = this->createPrefix("CrsMatrix", "allocatePackSpaceNew");
6403 std::ostringstream os;
6404 os << *prefix << "Before:"
6405 << endl
6406 << *prefix << " "
6407 << dualViewStatusToString(exports, "exports")
6408 << endl
6409 << *prefix << " "
6410 << dualViewStatusToString(exportLIDs, "exportLIDs")
6411 << endl;
6412 std::cerr << os.str();
6413 }
6414
6415 // The number of export LIDs must fit in LocalOrdinal, assuming
6416 // that the LIDs are distinct and valid on the calling process.
6417 const LO numExportLIDs = static_cast<LO>(exportLIDs.extent(0));
6418
6419 TEUCHOS_ASSERT(!exportLIDs.need_sync_host());
6420 auto exportLIDs_h = exportLIDs.view_host();
6421
6422 // Count the total number of matrix entries to send.
6423 totalNumEntries = 0;
6424 for (LO i = 0; i < numExportLIDs; ++i) {
6425 const LO lclRow = exportLIDs_h[i];
6426 size_t curNumEntries = this->getNumEntriesInLocalRow(lclRow);
6427 // FIXME (mfh 25 Jan 2015) We should actually report invalid row
6428 // indices as an error. Just consider them nonowned for now.
6429 if (curNumEntries == Teuchos::OrdinalTraits<size_t>::invalid()) {
6430 curNumEntries = 0;
6431 }
6432 totalNumEntries += curNumEntries;
6433 }
6434
6435 // FIXME (mfh 24 Feb 2013, 24 Mar 2017) This code is only correct
6436 // if sizeof(IST) is a meaningful representation of the amount of
6437 // data in a Scalar instance. (LO and GO are always built-in
6438 // integer types.)
6439 //
6440 // Allocate the exports array. It does NOT need padding for
6441 // alignment, since we use memcpy to write to / read from send /
6442 // receive buffers.
6443 const size_t allocSize =
6444 static_cast<size_t>(numExportLIDs) * sizeof(LO) +
6445 totalNumEntries * (sizeof(IST) + sizeof(GO));
6446 if (static_cast<size_t>(exports.extent(0)) < allocSize) {
6447 using exports_type = Kokkos::DualView<char*, buffer_device_type>;
6448
6449 const std::string oldLabel = exports.view_device().label();
6450 const std::string newLabel = (oldLabel == "") ? "exports" : oldLabel;
6451 exports = exports_type(newLabel, allocSize);
6452 }
6453
6454 if (verbose) {
6455 std::ostringstream os;
6456 os << *prefix << "After:"
6457 << endl
6458 << *prefix << " "
6459 << dualViewStatusToString(exports, "exports")
6460 << endl
6461 << *prefix << " "
6462 << dualViewStatusToString(exportLIDs, "exportLIDs")
6463 << endl;
6464 std::cerr << os.str();
6465 }
6466}
6467
6468template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6470 packNew(const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& exportLIDs,
6471 Kokkos::DualView<char*, buffer_device_type>& exports,
6472 const Kokkos::DualView<size_t*, buffer_device_type>& numPacketsPerLID,
6473 size_t& constantNumPackets) const {
6474 // The call to packNew in packAndPrepare catches and handles any exceptions.
6475 Details::ProfilingRegion region_pack_new("Tpetra::CrsMatrix::packNew", "Import/Export");
6476 if (this->isStaticGraph()) {
6478 packCrsMatrixNew(*this, exports, numPacketsPerLID, exportLIDs,
6479 constantNumPackets);
6480 } else {
6481 this->packNonStaticNew(exportLIDs, exports, numPacketsPerLID,
6482 constantNumPackets);
6483 }
6484}
6485
6486template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6488 packNonStaticNew(const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& exportLIDs,
6489 Kokkos::DualView<char*, buffer_device_type>& exports,
6490 const Kokkos::DualView<size_t*, buffer_device_type>& numPacketsPerLID,
6491 size_t& constantNumPackets) const {
6492 using Details::Behavior;
6495 using Details::PackTraits;
6496 using Kokkos::View;
6497 using std::endl;
6498 using LO = LocalOrdinal;
6499 using GO = GlobalOrdinal;
6500 using ST = impl_scalar_type;
6501 const char tfecfFuncName[] = "packNonStaticNew: ";
6502
6503 const bool verbose = Behavior::verbose("CrsMatrix");
6504 std::unique_ptr<std::string> prefix;
6505 if (verbose) {
6506 prefix = this->createPrefix("CrsMatrix", "packNonStaticNew");
6507 std::ostringstream os;
6508 os << *prefix << "Start" << endl;
6509 std::cerr << os.str();
6510 }
6511
6512 const size_t numExportLIDs = static_cast<size_t>(exportLIDs.extent(0));
6513 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(numExportLIDs != static_cast<size_t>(numPacketsPerLID.extent(0)),
6514 std::invalid_argument, "exportLIDs.size() = " << numExportLIDs << " != numPacketsPerLID.size() = " << numPacketsPerLID.extent(0) << ".");
6515
6516 // Setting this to zero tells the caller to expect a possibly
6517 // different ("nonconstant") number of packets per local index
6518 // (i.e., a possibly different number of entries per row).
6519 constantNumPackets = 0;
6520
6521 // The pack buffer 'exports' enters this method possibly
6522 // unallocated. Do the first two parts of "Count, allocate, fill,
6523 // compute."
6524 size_t totalNumEntries = 0;
6525 this->allocatePackSpaceNew(exports, totalNumEntries, exportLIDs);
6526 const size_t bufSize = static_cast<size_t>(exports.extent(0));
6527
6528 // Write-only host access
6529 exports.clear_sync_state();
6530 exports.modify_host();
6531 auto exports_h = exports.view_host();
6532 if (verbose) {
6533 std::ostringstream os;
6534 os << *prefix << "After marking exports as modified on host, "
6535 << dualViewStatusToString(exports, "exports") << endl;
6536 std::cerr << os.str();
6537 }
6538
6539 // Read-only host access
6540 auto exportLIDs_h = exportLIDs.view_host();
6541
6542 // Write-only host access
6543 const_cast<Kokkos::DualView<size_t*, buffer_device_type>*>(&numPacketsPerLID)->clear_sync_state();
6544 const_cast<Kokkos::DualView<size_t*, buffer_device_type>*>(&numPacketsPerLID)->modify_host();
6545 auto numPacketsPerLID_h = numPacketsPerLID.view_host();
6546
6547 // Compute the number of "packets" (in this case, bytes) per
6548 // export LID (in this case, local index of the row to send), and
6549 // actually pack the data.
6550 auto maxRowNumEnt = this->getLocalMaxNumRowEntries();
6551
6552 // Temporary buffer for global column indices.
6553 typename global_inds_host_view_type::non_const_type gidsIn_k;
6554 if (this->isLocallyIndexed()) { // Need storage for Global IDs
6555 gidsIn_k =
6556 typename global_inds_host_view_type::non_const_type("packGids",
6557 maxRowNumEnt);
6558 }
6559
6560 size_t offset = 0; // current index into 'exports' array.
6561 for (size_t i = 0; i < numExportLIDs; ++i) {
6562 const LO lclRow = exportLIDs_h[i];
6563
6564 size_t numBytes = 0;
6565 size_t numEnt = this->getNumEntriesInLocalRow(lclRow);
6566
6567 // Only pack this row's data if it has a nonzero number of
6568 // entries. We can do this because receiving processes get the
6569 // number of packets, and will know that zero packets means zero
6570 // entries.
6571 if (numEnt == 0) {
6572 numPacketsPerLID_h[i] = 0;
6573 continue;
6574 }
6575
6576 if (this->isLocallyIndexed()) {
6577 typename global_inds_host_view_type::non_const_type gidsIn;
6578 values_host_view_type valsIn;
6579 // If the matrix is locally indexed on the calling process, we
6580 // have to use its column Map (which it _must_ have in this
6581 // case) to convert to global indices.
6582 local_inds_host_view_type lidsIn;
6583 this->getLocalRowView(lclRow, lidsIn, valsIn);
6584 const map_type& colMap = *(this->getColMap());
6585 for (size_t k = 0; k < numEnt; ++k) {
6586 gidsIn_k[k] = colMap.getGlobalElement(lidsIn[k]);
6587 }
6588 gidsIn = Kokkos::subview(gidsIn_k, Kokkos::make_pair(GO(0), GO(numEnt)));
6589
6590 const size_t numBytesPerValue =
6591 PackTraits<ST>::packValueCount(valsIn[0]);
6592 numBytes = this->packRow(exports_h.data(), offset, numEnt,
6593 gidsIn.data(), valsIn.data(),
6594 numBytesPerValue);
6595 } else if (this->isGloballyIndexed()) {
6596 global_inds_host_view_type gidsIn;
6597 values_host_view_type valsIn;
6598 // If the matrix is globally indexed on the calling process,
6599 // then we can use the column indices directly. However, we
6600 // have to get the global row index. The calling process must
6601 // have a row Map, since otherwise it shouldn't be participating
6602 // in packing operations.
6603 const map_type& rowMap = *(this->getRowMap());
6604 const GO gblRow = rowMap.getGlobalElement(lclRow);
6605 this->getGlobalRowView(gblRow, gidsIn, valsIn);
6606
6607 const size_t numBytesPerValue =
6608 PackTraits<ST>::packValueCount(valsIn[0]);
6609 numBytes = this->packRow(exports_h.data(), offset, numEnt,
6610 gidsIn.data(), valsIn.data(),
6611 numBytesPerValue);
6612 }
6613 // mfh 11 Sep 2017: Currently, if the matrix is neither globally
6614 // nor locally indexed, then it has no entries. Therefore,
6615 // there is nothing to pack. No worries!
6616
6617 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(offset > bufSize || offset + numBytes > bufSize, std::logic_error,
6618 "First invalid offset into 'exports' pack buffer at index i = " << i
6619 << ". exportLIDs_h[i]: " << exportLIDs_h[i] << ", bufSize: " << bufSize << ", offset: " << offset << ", numBytes: " << numBytes << ".");
6620 // numPacketsPerLID_h[i] is the number of "packets" in the
6621 // current local row i. Packet=char (really "byte") so use the
6622 // number of bytes of the packed data for that row.
6623 numPacketsPerLID_h[i] = numBytes;
6624 offset += numBytes;
6625 }
6626
6627 if (verbose) {
6628 std::ostringstream os;
6629 os << *prefix << "Tpetra::CrsMatrix::packNonStaticNew: After:" << endl
6630 << *prefix << " "
6631 << dualViewStatusToString(exports, "exports")
6632 << endl
6633 << *prefix << " "
6634 << dualViewStatusToString(exportLIDs, "exportLIDs")
6635 << endl;
6636 std::cerr << os.str();
6637 }
6638}
6639
6640template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6641LocalOrdinal
6643 combineGlobalValuesRaw(const LocalOrdinal lclRow,
6644 const LocalOrdinal numEnt,
6645 const impl_scalar_type vals[],
6646 const GlobalOrdinal cols[],
6647 const Tpetra::CombineMode combMode,
6648 const char* const prefix,
6649 const bool debug,
6650 const bool verbose) {
6651 using GO = GlobalOrdinal;
6652
6653 // mfh 23 Mar 2017: This branch is not thread safe in a debug
6654 // build, due to use of Teuchos::ArrayView; see #229.
6655 const GO gblRow = myGraph_->rowMap_->getGlobalElement(lclRow);
6656 Teuchos::ArrayView<const GO> cols_av(numEnt == 0 ? nullptr : cols, numEnt);
6657 Teuchos::ArrayView<const Scalar> vals_av(numEnt == 0 ? nullptr : reinterpret_cast<const Scalar*>(vals), numEnt);
6658
6659 // FIXME (mfh 23 Mar 2017) This is a work-around for less common
6660 // combine modes. combineGlobalValues throws on error; it does
6661 // not return an error code. Thus, if it returns, it succeeded.
6662 combineGlobalValues(gblRow, cols_av, vals_av, combMode,
6663 prefix, debug, verbose);
6664 return numEnt;
6665}
6666
6667template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6670 const GlobalOrdinal globalRowIndex,
6671 const Teuchos::ArrayView<const GlobalOrdinal>& columnIndices,
6672 const Teuchos::ArrayView<const Scalar>& values,
6673 const Tpetra::CombineMode combineMode,
6674 const char* const prefix,
6675 const bool debug,
6676 const bool verbose) {
6677 const char tfecfFuncName[] = "combineGlobalValues: ";
6678
6679 if (isStaticGraph()) {
6680 // INSERT doesn't make sense for a static graph, since you
6681 // aren't allowed to change the structure of the graph.
6682 // However, all the other combine modes work.
6683 if (combineMode == ADD) {
6684 sumIntoGlobalValues(globalRowIndex, columnIndices, values);
6685 } else if (combineMode == REPLACE) {
6686 replaceGlobalValues(globalRowIndex, columnIndices, values);
6687 } else if (combineMode == ABSMAX) {
6688 using ::Tpetra::Details::AbsMax;
6689 AbsMax<Scalar> f;
6690 this->template transformGlobalValues<AbsMax<Scalar>>(globalRowIndex,
6691 columnIndices,
6692 values, f);
6693 } else if (combineMode == INSERT) {
6694 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(isStaticGraph() && combineMode == INSERT,
6695 std::invalid_argument,
6696 "INSERT combine mode is forbidden "
6697 "if the matrix has a static (const) graph (i.e., was "
6698 "constructed with the CrsMatrix constructor that takes a "
6699 "const CrsGraph pointer).");
6700 } else {
6701 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::logic_error,
6702 "Invalid combine mode; should "
6703 "never get here! "
6704 "Please report this bug to the Tpetra developers.");
6705 }
6706 } else { // The matrix has a dynamic graph.
6707 if (combineMode == ADD || combineMode == INSERT) {
6708 // For a dynamic graph, all incoming column indices are
6709 // inserted into the target graph. Duplicate indices will
6710 // have their values summed. In this context, ADD and INSERT
6711 // are equivalent. We need to call insertGlobalValues()
6712 // anyway if the column indices don't yet exist in this row,
6713 // so we just call insertGlobalValues() for both cases.
6714 insertGlobalValuesFilteredChecked(globalRowIndex,
6715 columnIndices, values, prefix, debug, verbose);
6716 }
6717 // FIXME (mfh 14 Mar 2012):
6718 //
6719 // Implementing ABSMAX or REPLACE for a dynamic graph would
6720 // require modifying assembly to attach a possibly different
6721 // combine mode to each inserted (i, j, A_ij) entry. For
6722 // example, consider two different Export operations to the same
6723 // target CrsMatrix, the first with ABSMAX combine mode and the
6724 // second with REPLACE. This isn't a common use case, so we
6725 // won't mess with it for now.
6726 else if (combineMode == ABSMAX) {
6727 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
6728 !isStaticGraph() && combineMode == ABSMAX, std::logic_error,
6729 "ABSMAX combine mode when the matrix has a dynamic graph is not yet "
6730 "implemented.");
6731 } else if (combineMode == REPLACE) {
6732 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
6733 !isStaticGraph() && combineMode == REPLACE, std::logic_error,
6734 "REPLACE combine mode when the matrix has a dynamic graph is not yet "
6735 "implemented.");
6736 } else {
6737 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(
6738 true, std::logic_error,
6739 "Should never get here! Please report this "
6740 "bug to the Tpetra developers.");
6741 }
6742 }
6743}
6744
6745template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6747 unpackAndCombine(const Kokkos::DualView<const local_ordinal_type*, buffer_device_type>& importLIDs,
6748 Kokkos::DualView<char*, buffer_device_type> imports,
6749 Kokkos::DualView<size_t*, buffer_device_type> numPacketsPerLID,
6750 const size_t constantNumPackets,
6751 const CombineMode combineMode) {
6752 using Details::Behavior;
6755 using std::endl;
6756 const char tfecfFuncName[] = "unpackAndCombine: ";
6757 ProfilingRegion regionUAC("Tpetra::CrsMatrix::unpackAndCombine");
6758
6759 const bool debug = Behavior::debug("CrsMatrix");
6760 const bool verbose = Behavior::verbose("CrsMatrix");
6761 constexpr int numValidModes = 5;
6762 const CombineMode validModes[numValidModes] =
6764 const char* validModeNames[numValidModes] =
6765 {"ADD", "REPLACE", "ABSMAX", "INSERT", "ZERO"};
6766
6767 std::unique_ptr<std::string> prefix;
6768 if (verbose) {
6769 prefix = this->createPrefix("CrsMatrix", "unpackAndCombine");
6770 std::ostringstream os;
6771 os << *prefix << "Start:" << endl
6772 << *prefix << " "
6773 << dualViewStatusToString(importLIDs, "importLIDs")
6774 << endl
6775 << *prefix << " "
6776 << dualViewStatusToString(imports, "imports")
6777 << endl
6778 << *prefix << " "
6779 << dualViewStatusToString(numPacketsPerLID, "numPacketsPerLID")
6780 << endl
6781 << *prefix << " constantNumPackets: " << constantNumPackets
6782 << endl
6783 << *prefix << " combineMode: " << combineModeToString(combineMode)
6784 << endl;
6785 std::cerr << os.str();
6786 }
6787
6788 if (debug) {
6789 if (std::find(validModes, validModes + numValidModes, combineMode) ==
6790 validModes + numValidModes) {
6791 std::ostringstream os;
6792 os << "Invalid combine mode. Valid modes are {";
6793 for (int k = 0; k < numValidModes; ++k) {
6794 os << validModeNames[k];
6795 if (k < numValidModes - 1) {
6796 os << ", ";
6797 }
6798 }
6799 os << "}.";
6800 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::invalid_argument, os.str());
6801 }
6802 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(importLIDs.extent(0) != numPacketsPerLID.extent(0),
6803 std::invalid_argument, "importLIDs.extent(0)=" << importLIDs.extent(0) << " != numPacketsPerLID.extent(0)=" << numPacketsPerLID.extent(0) << ".");
6804 }
6805
6806 if (combineMode == ZERO) {
6807 return; // nothing to do
6808 }
6809
6810 if (debug) {
6811 using Teuchos::reduceAll;
6812 std::unique_ptr<std::ostringstream> msg(new std::ostringstream());
6813 int lclBad = 0;
6814 try {
6815 unpackAndCombineImpl(importLIDs, imports, numPacketsPerLID,
6816 constantNumPackets, combineMode,
6817 verbose);
6818 } catch (std::exception& e) {
6819 lclBad = 1;
6820 *msg << e.what();
6821 }
6822 int gblBad = 0;
6823 const Teuchos::Comm<int>& comm = *(this->getComm());
6824 reduceAll<int, int>(comm, Teuchos::REDUCE_MAX,
6825 lclBad, Teuchos::outArg(gblBad));
6826 if (gblBad != 0) {
6827 // mfh 22 Oct 2017: 'prefix' might be null, since it is only
6828 // initialized in a debug build. Thus, we get the process
6829 // rank again here. This is an error message, so the small
6830 // run-time cost doesn't matter. See #1887.
6831 std::ostringstream os;
6832 os << "Proc " << comm.getRank() << ": " << msg->str() << endl;
6833 msg = std::unique_ptr<std::ostringstream>(new std::ostringstream());
6834 ::Tpetra::Details::gathervPrint(*msg, os.str(), comm);
6835 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(true, std::logic_error, std::endl
6836 << "unpackAndCombineImpl "
6837 "threw an exception on one or more participating processes: "
6838 << endl
6839 << msg->str());
6840 }
6841 } else {
6842 unpackAndCombineImpl(importLIDs, imports, numPacketsPerLID,
6843 constantNumPackets, combineMode,
6844 verbose);
6845 }
6846
6847 if (verbose) {
6848 std::ostringstream os;
6849 os << *prefix << "Done!" << endl
6850 << *prefix << " "
6851 << dualViewStatusToString(importLIDs, "importLIDs")
6852 << endl
6853 << *prefix << " "
6854 << dualViewStatusToString(imports, "imports")
6855 << endl
6856 << *prefix << " "
6857 << dualViewStatusToString(numPacketsPerLID, "numPacketsPerLID")
6858 << endl;
6859 std::cerr << os.str();
6860 }
6861}
6862
6863template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6866 const Kokkos::DualView<const local_ordinal_type*,
6867 buffer_device_type>& importLIDs,
6868 Kokkos::DualView<char*, buffer_device_type> imports,
6869 Kokkos::DualView<size_t*, buffer_device_type> numPacketsPerLID,
6870 const size_t constantNumPackets,
6871 const CombineMode combineMode,
6872 const bool verbose) {
6873 Details::ProfilingRegion region_unpack_and_combine_impl(
6874 "Tpetra::CrsMatrix::unpackAndCombineImpl",
6875 "Import/Export");
6876 using std::endl;
6877 const char tfecfFuncName[] = "unpackAndCombineImpl";
6878 std::unique_ptr<std::string> prefix;
6879 if (verbose) {
6880 prefix = this->createPrefix("CrsMatrix", tfecfFuncName);
6881 std::ostringstream os;
6882 os << *prefix << "isStaticGraph(): "
6883 << (isStaticGraph() ? "true" : "false")
6884 << ", importLIDs.extent(0): "
6885 << importLIDs.extent(0)
6886 << ", imports.extent(0): "
6887 << imports.extent(0)
6888 << ", numPacketsPerLID.extent(0): "
6889 << numPacketsPerLID.extent(0)
6890 << endl;
6891 std::cerr << os.str();
6892 }
6893
6894 if (isStaticGraph()) {
6895 using Details::unpackCrsMatrixAndCombineNew;
6896 unpackCrsMatrixAndCombineNew(*this, imports, numPacketsPerLID,
6897 importLIDs, constantNumPackets,
6898 combineMode);
6899 } else {
6900 {
6901 using padding_type = typename crs_graph_type::padding_type;
6902 std::unique_ptr<padding_type> padding;
6903 try {
6904 padding = myGraph_->computePaddingForCrsMatrixUnpack(
6905 importLIDs, imports, numPacketsPerLID, verbose);
6906 } catch (std::exception& e) {
6907 const auto rowMap = getRowMap();
6908 const auto comm = rowMap.is_null() ? Teuchos::null : rowMap->getComm();
6909 const int myRank = comm.is_null() ? -1 : comm->getRank();
6910 TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, "Proc " << myRank << ": "
6911 "Tpetra::CrsGraph::computePaddingForCrsMatrixUnpack "
6912 "threw an exception: "
6913 << e.what());
6914 }
6915 if (verbose) {
6916 std::ostringstream os;
6917 os << *prefix << "Call applyCrsPadding" << endl;
6918 std::cerr << os.str();
6919 }
6920 applyCrsPadding(*padding, verbose);
6921 }
6922 if (verbose) {
6923 std::ostringstream os;
6924 os << *prefix << "Call unpackAndCombineImplNonStatic" << endl;
6925 std::cerr << os.str();
6926 }
6927 unpackAndCombineImplNonStatic(importLIDs, imports,
6928 numPacketsPerLID,
6929 constantNumPackets,
6930 combineMode);
6931 }
6932
6933 if (verbose) {
6934 std::ostringstream os;
6935 os << *prefix << "Done" << endl;
6936 std::cerr << os.str();
6937 }
6938}
6939
6940template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
6943 const Kokkos::DualView<const local_ordinal_type*,
6944 buffer_device_type>& importLIDs,
6945 Kokkos::DualView<char*, buffer_device_type> imports,
6946 Kokkos::DualView<size_t*, buffer_device_type> numPacketsPerLID,
6947 const size_t constantNumPackets,
6948 const CombineMode combineMode) {
6949 using Details::Behavior;
6952 using Details::PackTraits;
6954 using Kokkos::MemoryUnmanaged;
6955 using Kokkos::subview;
6956 using Kokkos::View;
6957 using std::endl;
6958 using LO = LocalOrdinal;
6959 using GO = GlobalOrdinal;
6960 using ST = impl_scalar_type;
6961 using size_type = typename Teuchos::ArrayView<LO>::size_type;
6962 using HES =
6963 typename View<int*, device_type>::host_mirror_type::execution_space;
6964 using pair_type = std::pair<typename View<int*, HES>::size_type,
6965 typename View<int*, HES>::size_type>;
6966 using gids_out_type = View<GO*, HES, MemoryUnmanaged>;
6967 using vals_out_type = View<ST*, HES, MemoryUnmanaged>;
6968 const char tfecfFuncName[] = "unpackAndCombineImplNonStatic";
6969
6970 const bool debug = Behavior::debug("CrsMatrix");
6971 const bool verbose = Behavior::verbose("CrsMatrix");
6972 std::unique_ptr<std::string> prefix;
6973 if (verbose) {
6974 prefix = this->createPrefix("CrsMatrix", tfecfFuncName);
6975 std::ostringstream os;
6976 os << *prefix << endl; // we've already printed DualViews' statuses
6977 std::cerr << os.str();
6978 }
6979 const char* const prefix_raw =
6980 verbose ? prefix.get()->c_str() : nullptr;
6981
6982 const size_type numImportLIDs = importLIDs.extent(0);
6983 if (combineMode == ZERO || numImportLIDs == 0) {
6984 return; // nothing to do; no need to combine entries
6985 }
6986
6987 Details::ProfilingRegion region_unpack_and_combine_impl_non_static(
6988 "Tpetra::CrsMatrix::unpackAndCombineImplNonStatic",
6989 "Import/Export");
6990
6991 // We're unpacking on host. This is read-only host access.
6992 if (imports.need_sync_host()) {
6993 imports.sync_host();
6994 }
6995 auto imports_h = imports.view_host();
6996
6997 // Read-only host access.
6998 if (numPacketsPerLID.need_sync_host()) {
6999 numPacketsPerLID.sync_host();
7000 }
7001 auto numPacketsPerLID_h = numPacketsPerLID.view_host();
7002
7003 TEUCHOS_ASSERT(!importLIDs.need_sync_host());
7004 auto importLIDs_h = importLIDs.view_host();
7005
7006 size_t numBytesPerValue;
7007 {
7008 // FIXME (mfh 17 Feb 2015, tjf 2 Aug 2017) What do I do about Scalar types
7009 // with run-time size? We already assume that all entries in both the
7010 // source and target matrices have the same size. If the calling process
7011 // owns at least one entry in either matrix, we can use that entry to set
7012 // the size. However, it is possible that the calling process owns no
7013 // entries. In that case, we're in trouble. One way to fix this would be
7014 // for each row's data to contain the run-time size. This is only
7015 // necessary if the size is not a compile-time constant.
7016 Scalar val;
7017 numBytesPerValue = PackTraits<ST>::packValueCount(val);
7018 }
7019
7020 // Determine the maximum number of entries in any one row
7021 size_t offset = 0;
7022 size_t maxRowNumEnt = 0;
7023 for (size_type i = 0; i < numImportLIDs; ++i) {
7024 const size_t numBytes = numPacketsPerLID_h[i];
7025 if (numBytes == 0) {
7026 continue; // empty buffer for that row means that the row is empty
7027 }
7028 // We need to unpack a nonzero number of entries for this row.
7029 if (debug) {
7030 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(offset + numBytes > size_t(imports_h.extent(0)),
7031 std::logic_error, ": At local row index importLIDs_h[i=" << i << "]=" << importLIDs_h[i] << ", offset (=" << offset << ") + numBytes (=" << numBytes << ") > "
7032 "imports_h.extent(0)="
7033 << imports_h.extent(0) << ".");
7034 }
7035 LO numEntLO = 0;
7036
7037 if (debug) {
7038 const size_t theNumBytes =
7039 PackTraits<LO>::packValueCount(numEntLO);
7040 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(theNumBytes > numBytes, std::logic_error, ": theNumBytes=" << theNumBytes << " > numBytes = " << numBytes << ".");
7041 }
7042 const char* const inBuf = imports_h.data() + offset;
7043 const size_t actualNumBytes =
7044 PackTraits<LO>::unpackValue(numEntLO, inBuf);
7045
7046 if (debug) {
7047 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(actualNumBytes > numBytes, std::logic_error, ": At i=" << i << ", actualNumBytes=" << actualNumBytes << " > numBytes=" << numBytes << ".");
7048 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(numEntLO == 0, std::logic_error,
7049 ": At local row index "
7050 "importLIDs_h[i="
7051 << i << "]=" << importLIDs_h[i] << ", "
7052 "the number of entries read from the packed data is "
7053 "numEntLO="
7054 << numEntLO << ", but numBytes=" << numBytes
7055 << " != 0.");
7056 }
7057
7058 maxRowNumEnt = std::max(size_t(numEntLO), maxRowNumEnt);
7059 offset += numBytes;
7060 }
7061
7062 // Temporary space to cache incoming global column indices and
7063 // values. Column indices come in as global indices, in case the
7064 // source object's column Map differs from the target object's
7065 // (this's) column Map.
7066 View<GO*, HES> gblColInds;
7067 View<LO*, HES> lclColInds;
7068 View<ST*, HES> vals;
7069 {
7070 GO gid = 0;
7071 LO lid = 0;
7072 // FIXME (mfh 17 Feb 2015, tjf 2 Aug 2017) What do I do about Scalar types
7073 // with run-time size? We already assume that all entries in both the
7074 // source and target matrices have the same size. If the calling process
7075 // owns at least one entry in either matrix, we can use that entry to set
7076 // the size. However, it is possible that the calling process owns no
7077 // entries. In that case, we're in trouble. One way to fix this would be
7078 // for each row's data to contain the run-time size. This is only
7079 // necessary if the size is not a compile-time constant.
7080 Scalar val;
7081 gblColInds = ScalarViewTraits<GO, HES>::allocateArray(
7082 gid, maxRowNumEnt, "gids");
7083 lclColInds = ScalarViewTraits<LO, HES>::allocateArray(
7084 lid, maxRowNumEnt, "lids");
7085 vals = ScalarViewTraits<ST, HES>::allocateArray(
7086 val, maxRowNumEnt, "vals");
7087 }
7088
7089 offset = 0;
7090 for (size_type i = 0; i < numImportLIDs; ++i) {
7091 const size_t numBytes = numPacketsPerLID_h[i];
7092 if (numBytes == 0) {
7093 continue; // empty buffer for that row means that the row is empty
7094 }
7095 LO numEntLO = 0;
7096 const char* const inBuf = imports_h.data() + offset;
7097 (void)PackTraits<LO>::unpackValue(numEntLO, inBuf);
7098
7099 const size_t numEnt = static_cast<size_t>(numEntLO);
7100 ;
7101 const LO lclRow = importLIDs_h[i];
7102
7103 gids_out_type gidsOut = subview(gblColInds, pair_type(0, numEnt));
7104 vals_out_type valsOut = subview(vals, pair_type(0, numEnt));
7105
7106 const size_t numBytesOut =
7107 unpackRow(gidsOut.data(), valsOut.data(), imports_h.data(),
7108 offset, numBytes, numEnt, numBytesPerValue);
7109 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(numBytes != numBytesOut, std::logic_error, ": At i=" << i << ", numBytes=" << numBytes << " != numBytesOut=" << numBytesOut << ".");
7110
7111 const ST* const valsRaw = const_cast<const ST*>(valsOut.data());
7112 const GO* const gidsRaw = const_cast<const GO*>(gidsOut.data());
7113 combineGlobalValuesRaw(lclRow, numEnt, valsRaw, gidsRaw,
7114 combineMode, prefix_raw, debug, verbose);
7115 // Don't update offset until current LID has succeeded.
7116 offset += numBytes;
7117 } // for each import LID i
7118
7119 if (verbose) {
7120 std::ostringstream os;
7121 os << *prefix << "Done" << endl;
7122 std::cerr << os.str();
7123 }
7124}
7125
7126template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7127Teuchos::RCP<MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
7129 getColumnMapMultiVector(const MV& X_domainMap,
7130 const bool force) const {
7131 using Teuchos::null;
7132 using Teuchos::RCP;
7133 using Teuchos::rcp;
7134
7135 TEUCHOS_TEST_FOR_EXCEPTION(
7136 !this->hasColMap(), std::runtime_error,
7137 "Tpetra::CrsMatrix::getColumn"
7138 "MapMultiVector: You may only call this method if the matrix has a "
7139 "column Map. If the matrix does not yet have a column Map, you should "
7140 "first call fillComplete (with domain and range Map if necessary).");
7141
7142 // If the graph is not fill complete, then the Import object (if
7143 // one should exist) hasn't been constructed yet.
7144 TEUCHOS_TEST_FOR_EXCEPTION(
7145 !this->getGraph()->isFillComplete(), std::runtime_error,
7146 "Tpetra::"
7147 "CrsMatrix::getColumnMapMultiVector: You may only call this method if "
7148 "this matrix's graph is fill complete.");
7149
7150 const size_t numVecs = X_domainMap.getNumVectors();
7151 RCP<const import_type> importer = this->getGraph()->getImporter();
7152 RCP<const map_type> colMap = this->getColMap();
7153
7154 RCP<MV> X_colMap; // null by default
7155
7156 // If the Import object is trivial (null), then we don't need a
7157 // separate column Map multivector. Just return null in that
7158 // case. The caller is responsible for knowing not to use the
7159 // returned null pointer.
7160 //
7161 // If the Import is nontrivial, then we do need a separate
7162 // column Map multivector for the Import operation. Check in
7163 // that case if we have to (re)create the column Map
7164 // multivector.
7165 if (!importer.is_null() || force) {
7166 if (importMV_.is_null() || importMV_->getNumVectors() != numVecs) {
7167 X_colMap = rcp(new MV(colMap, numVecs));
7168
7169 // Cache the newly created multivector for later reuse.
7170 importMV_ = X_colMap;
7171 } else { // Yay, we can reuse the cached multivector!
7172 X_colMap = importMV_;
7173 // mfh 09 Jan 2013: We don't have to fill with zeros first,
7174 // because the Import uses INSERT combine mode, which overwrites
7175 // existing entries.
7176 //
7177 // X_colMap->putScalar (ZERO);
7178 }
7179 }
7180 return X_colMap;
7181}
7182
7183template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7184Teuchos::RCP<MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
7187 const bool force) const {
7188 using Teuchos::null;
7189 using Teuchos::RCP;
7190 using Teuchos::rcp;
7191
7192 // If the graph is not fill complete, then the Export object (if
7193 // one should exist) hasn't been constructed yet.
7194 TEUCHOS_TEST_FOR_EXCEPTION(
7195 !this->getGraph()->isFillComplete(), std::runtime_error,
7196 "Tpetra::"
7197 "CrsMatrix::getRowMapMultiVector: You may only call this method if this "
7198 "matrix's graph is fill complete.");
7199
7200 const size_t numVecs = Y_rangeMap.getNumVectors();
7201 RCP<const export_type> exporter = this->getGraph()->getExporter();
7202 // Every version of the constructor takes either a row Map, or a
7203 // graph (all of whose constructors take a row Map). Thus, the
7204 // matrix always has a row Map.
7205 RCP<const map_type> rowMap = this->getRowMap();
7206
7207 RCP<MV> Y_rowMap; // null by default
7208
7209 // If the Export object is trivial (null), then we don't need a
7210 // separate row Map multivector. Just return null in that case.
7211 // The caller is responsible for knowing not to use the returned
7212 // null pointer.
7213 //
7214 // If the Export is nontrivial, then we do need a separate row
7215 // Map multivector for the Export operation. Check in that case
7216 // if we have to (re)create the row Map multivector.
7217 if (!exporter.is_null() || force) {
7218 if (exportMV_.is_null() || exportMV_->getNumVectors() != numVecs) {
7219 Y_rowMap = rcp(new MV(rowMap, numVecs));
7220 exportMV_ = Y_rowMap; // Cache the newly created MV for later reuse.
7221 } else { // Yay, we can reuse the cached multivector!
7222 Y_rowMap = exportMV_;
7223 }
7224 }
7225 return Y_rowMap;
7226}
7227
7228template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7230 removeEmptyProcessesInPlace(const Teuchos::RCP<const map_type>& newMap) {
7231 TEUCHOS_TEST_FOR_EXCEPTION(
7232 myGraph_.is_null(), std::logic_error,
7233 "Tpetra::CrsMatrix::"
7234 "removeEmptyProcessesInPlace: This method does not work when the matrix "
7235 "was created with a constant graph (that is, when it was created using "
7236 "the version of its constructor that takes an RCP<const CrsGraph>). "
7237 "This is because the matrix is not allowed to modify the graph in that "
7238 "case, but removing empty processes requires modifying the graph.");
7239 myGraph_->removeEmptyProcessesInPlace(newMap);
7240 // Even though CrsMatrix's row Map (as returned by getRowMap())
7241 // comes from its CrsGraph, CrsMatrix still implements DistObject,
7242 // so we also have to change the DistObject's Map.
7243 this->map_ = this->getRowMap();
7244 // In the nonconst graph case, staticGraph_ is just a const
7245 // pointer to myGraph_. This assignment is probably redundant,
7246 // but it doesn't hurt.
7247 staticGraph_ = Teuchos::rcp_const_cast<const Graph>(myGraph_);
7248}
7249
7250template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7251Teuchos::RCP<RowMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
7253 add(const Scalar& alpha,
7255 const Scalar& beta,
7256 const Teuchos::RCP<const map_type>& domainMap,
7257 const Teuchos::RCP<const map_type>& rangeMap,
7258 const Teuchos::RCP<Teuchos::ParameterList>& params) const {
7259 using std::endl;
7260 using Teuchos::Array;
7261 using Teuchos::ArrayView;
7262 using Teuchos::ParameterList;
7263 using Teuchos::RCP;
7264 using Teuchos::rcp;
7265 using Teuchos::rcp_implicit_cast;
7266 using Teuchos::sublist;
7267 using LO = local_ordinal_type;
7268 using GO = global_ordinal_type;
7269 using crs_matrix_type =
7270 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
7271 const char errPfx[] = "Tpetra::CrsMatrix::add: ";
7272
7273 const bool debug = Details::Behavior::debug("CrsMatrix");
7274 const bool verbose = Details::Behavior::verbose("CrsMatrix");
7275 std::unique_ptr<std::string> prefix;
7276 if (verbose) {
7277 prefix = this->createPrefix("CrsMatrix", "add");
7278 std::ostringstream os;
7279 os << *prefix << "Start" << endl;
7280 std::cerr << os.str();
7281 }
7282
7283 const crs_matrix_type& B = *this; // a convenient abbreviation
7284 const Scalar ZERO = Teuchos::ScalarTraits<Scalar>::zero();
7285 const Scalar ONE = Teuchos::ScalarTraits<Scalar>::one();
7286
7287 // If the user didn't supply a domain or range Map, then try to
7288 // get one from B first (if it has them), then from A (if it has
7289 // them). If we don't have any domain or range Maps, scold the
7290 // user.
7291 RCP<const map_type> A_domainMap = A.getDomainMap();
7292 RCP<const map_type> A_rangeMap = A.getRangeMap();
7293 RCP<const map_type> B_domainMap = B.getDomainMap();
7294 RCP<const map_type> B_rangeMap = B.getRangeMap();
7295
7296 RCP<const map_type> theDomainMap = domainMap;
7297 RCP<const map_type> theRangeMap = rangeMap;
7298
7299 if (domainMap.is_null()) {
7300 if (B_domainMap.is_null()) {
7301 TEUCHOS_TEST_FOR_EXCEPTION(
7302 A_domainMap.is_null(), std::invalid_argument,
7303 "Tpetra::CrsMatrix::add: If neither A nor B have a domain Map, "
7304 "then you must supply a nonnull domain Map to this method.");
7305 theDomainMap = A_domainMap;
7306 } else {
7307 theDomainMap = B_domainMap;
7308 }
7309 }
7310 if (rangeMap.is_null()) {
7311 if (B_rangeMap.is_null()) {
7312 TEUCHOS_TEST_FOR_EXCEPTION(
7313 A_rangeMap.is_null(), std::invalid_argument,
7314 "Tpetra::CrsMatrix::add: If neither A nor B have a range Map, "
7315 "then you must supply a nonnull range Map to this method.");
7316 theRangeMap = A_rangeMap;
7317 } else {
7318 theRangeMap = B_rangeMap;
7319 }
7320 }
7321
7322 if (debug) {
7323 // In debug mode, check that A and B have matching domain and
7324 // range Maps, if they have domain and range Maps at all. (If
7325 // they aren't fill complete, then they may not yet have them.)
7326 if (!A_domainMap.is_null() && !A_rangeMap.is_null()) {
7327 if (!B_domainMap.is_null() && !B_rangeMap.is_null()) {
7328 TEUCHOS_TEST_FOR_EXCEPTION(!B_domainMap->isSameAs(*A_domainMap),
7329 std::invalid_argument,
7330 errPfx << "The input RowMatrix A must have a domain Map "
7331 "which is the same as (isSameAs) this RowMatrix's "
7332 "domain Map.");
7333 TEUCHOS_TEST_FOR_EXCEPTION(!B_rangeMap->isSameAs(*A_rangeMap), std::invalid_argument,
7334 errPfx << "The input RowMatrix A must have a range Map "
7335 "which is the same as (isSameAs) this RowMatrix's range "
7336 "Map.");
7337 TEUCHOS_TEST_FOR_EXCEPTION(!domainMap.is_null() &&
7338 !domainMap->isSameAs(*B_domainMap),
7339 std::invalid_argument,
7340 errPfx << "The input domain Map must be the same as "
7341 "(isSameAs) this RowMatrix's domain Map.");
7342 TEUCHOS_TEST_FOR_EXCEPTION(!rangeMap.is_null() &&
7343 !rangeMap->isSameAs(*B_rangeMap),
7344 std::invalid_argument,
7345 errPfx << "The input range Map must be the same as "
7346 "(isSameAs) this RowMatrix's range Map.");
7347 }
7348 } else if (!B_domainMap.is_null() && !B_rangeMap.is_null()) {
7349 TEUCHOS_TEST_FOR_EXCEPTION(!domainMap.is_null() &&
7350 !domainMap->isSameAs(*B_domainMap),
7351 std::invalid_argument,
7352 errPfx << "The input domain Map must be the same as "
7353 "(isSameAs) this RowMatrix's domain Map.");
7354 TEUCHOS_TEST_FOR_EXCEPTION(!rangeMap.is_null() && !rangeMap->isSameAs(*B_rangeMap),
7355 std::invalid_argument,
7356 errPfx << "The input range Map must be the same as "
7357 "(isSameAs) this RowMatrix's range Map.");
7358 } else {
7359 TEUCHOS_TEST_FOR_EXCEPTION(domainMap.is_null() || rangeMap.is_null(),
7360 std::invalid_argument, errPfx << "If neither A nor B "
7361 "have a domain and range Map, then you must supply a "
7362 "nonnull domain and range Map to this method.");
7363 }
7364 }
7365
7366 // What parameters do we pass to C's constructor? Do we call
7367 // fillComplete on C after filling it? And if so, what parameters
7368 // do we pass to C's fillComplete call?
7369 bool callFillComplete = true;
7370 RCP<ParameterList> constructorSublist;
7371 RCP<ParameterList> fillCompleteSublist;
7372 if (!params.is_null()) {
7373 callFillComplete =
7374 params->get("Call fillComplete", callFillComplete);
7375 constructorSublist = sublist(params, "Constructor parameters");
7376 fillCompleteSublist = sublist(params, "fillComplete parameters");
7377 }
7378
7379 RCP<const map_type> A_rowMap = A.getRowMap();
7380 RCP<const map_type> B_rowMap = B.getRowMap();
7381 RCP<const map_type> C_rowMap = B_rowMap; // see discussion in documentation
7382 RCP<crs_matrix_type> C; // The result matrix.
7383
7384 // If A and B's row Maps are the same, we can compute an upper
7385 // bound on the number of entries in each row of C, before
7386 // actually computing the sum. A reasonable upper bound is the
7387 // sum of the two entry counts in each row.
7388 if (A_rowMap->isSameAs(*B_rowMap)) {
7389 const LO localNumRows = static_cast<LO>(A_rowMap->getLocalNumElements());
7390 Array<size_t> C_maxNumEntriesPerRow(localNumRows, 0);
7391
7392 // Get the number of entries in each row of A.
7393 if (alpha != ZERO) {
7394 for (LO localRow = 0; localRow < localNumRows; ++localRow) {
7395 const size_t A_numEntries = A.getNumEntriesInLocalRow(localRow);
7396 C_maxNumEntriesPerRow[localRow] += A_numEntries;
7397 }
7398 }
7399 // Get the number of entries in each row of B.
7400 if (beta != ZERO) {
7401 for (LO localRow = 0; localRow < localNumRows; ++localRow) {
7402 const size_t B_numEntries = B.getNumEntriesInLocalRow(localRow);
7403 C_maxNumEntriesPerRow[localRow] += B_numEntries;
7404 }
7405 }
7406 // Construct the result matrix C.
7407 if (constructorSublist.is_null()) {
7408 C = rcp(new crs_matrix_type(C_rowMap, C_maxNumEntriesPerRow()));
7409 } else {
7410 C = rcp(new crs_matrix_type(C_rowMap, C_maxNumEntriesPerRow(),
7411 constructorSublist));
7412 }
7413 // Since A and B have the same row Maps, we could add them
7414 // together all at once and merge values before we call
7415 // insertGlobalValues. However, we don't really need to, since
7416 // we've already allocated enough space in each row of C for C
7417 // to do the merge itself.
7418 } else { // the row Maps of A and B are not the same
7419 // Construct the result matrix C.
7420 // true: !A_rowMap->isSameAs (*B_rowMap)
7421 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument, errPfx << "The row maps must "
7422 "be the same for statically allocated matrices, to ensure "
7423 "that there is sufficient space to do the addition.");
7424 }
7425
7426 TEUCHOS_TEST_FOR_EXCEPTION(C.is_null(), std::logic_error,
7427 errPfx << "C should not be null at this point. "
7428 "Please report this bug to the Tpetra developers.");
7429
7430 if (verbose) {
7431 std::ostringstream os;
7432 os << *prefix << "Compute C = alpha*A + beta*B" << endl;
7433 std::cerr << os.str();
7434 }
7435 using gids_type = nonconst_global_inds_host_view_type;
7436 using vals_type = nonconst_values_host_view_type;
7437 gids_type ind;
7438 vals_type val;
7439
7440 if (alpha != ZERO) {
7441 const LO A_localNumRows = static_cast<LO>(A_rowMap->getLocalNumElements());
7442 for (LO localRow = 0; localRow < A_localNumRows; ++localRow) {
7443 size_t A_numEntries = A.getNumEntriesInLocalRow(localRow);
7444 const GO globalRow = A_rowMap->getGlobalElement(localRow);
7445 if (A_numEntries > static_cast<size_t>(ind.size())) {
7446 Kokkos::resize(ind, A_numEntries);
7447 Kokkos::resize(val, A_numEntries);
7448 }
7449 gids_type indView = Kokkos::subview(ind, std::make_pair((size_t)0, A_numEntries));
7450 vals_type valView = Kokkos::subview(val, std::make_pair((size_t)0, A_numEntries));
7451 A.getGlobalRowCopy(globalRow, indView, valView, A_numEntries);
7452
7453 if (alpha != ONE) {
7454 for (size_t k = 0; k < A_numEntries; ++k) {
7455 valView[k] *= alpha;
7456 }
7457 }
7458 C->insertGlobalValues(globalRow, A_numEntries,
7459 reinterpret_cast<Scalar*>(valView.data()),
7460 indView.data());
7461 }
7462 }
7463
7464 if (beta != ZERO) {
7465 const LO B_localNumRows = static_cast<LO>(B_rowMap->getLocalNumElements());
7466 for (LO localRow = 0; localRow < B_localNumRows; ++localRow) {
7467 size_t B_numEntries = B.getNumEntriesInLocalRow(localRow);
7468 const GO globalRow = B_rowMap->getGlobalElement(localRow);
7469 if (B_numEntries > static_cast<size_t>(ind.size())) {
7470 Kokkos::resize(ind, B_numEntries);
7471 Kokkos::resize(val, B_numEntries);
7472 }
7473 gids_type indView = Kokkos::subview(ind, std::make_pair((size_t)0, B_numEntries));
7474 vals_type valView = Kokkos::subview(val, std::make_pair((size_t)0, B_numEntries));
7475 B.getGlobalRowCopy(globalRow, indView, valView, B_numEntries);
7476
7477 if (beta != ONE) {
7478 for (size_t k = 0; k < B_numEntries; ++k) {
7479 valView[k] *= beta;
7480 }
7481 }
7482 C->insertGlobalValues(globalRow, B_numEntries,
7483 reinterpret_cast<Scalar*>(valView.data()),
7484 indView.data());
7485 }
7486 }
7487
7488 if (callFillComplete) {
7489 if (verbose) {
7490 std::ostringstream os;
7491 os << *prefix << "Call fillComplete on C" << endl;
7492 std::cerr << os.str();
7493 }
7494 if (fillCompleteSublist.is_null()) {
7495 C->fillComplete(theDomainMap, theRangeMap);
7496 } else {
7497 C->fillComplete(theDomainMap, theRangeMap, fillCompleteSublist);
7498 }
7499 } else if (verbose) {
7500 std::ostringstream os;
7501 os << *prefix << "Do NOT call fillComplete on C" << endl;
7502 std::cerr << os.str();
7503 }
7504
7505 if (verbose) {
7506 std::ostringstream os;
7507 os << *prefix << "Done" << endl;
7508 std::cerr << os.str();
7509 }
7510 return rcp_implicit_cast<row_matrix_type>(C);
7511}
7512
7513template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
7516 const ::Tpetra::Details::Transfer<LocalOrdinal, GlobalOrdinal, Node>& rowTransfer,
7517 const Teuchos::RCP<const ::Tpetra::Details::Transfer<LocalOrdinal, GlobalOrdinal, Node>>& domainTransfer,
7518 const Teuchos::RCP<const map_type>& domainMap,
7519 const Teuchos::RCP<const map_type>& rangeMap,
7520 const Teuchos::RCP<Teuchos::ParameterList>& params) const {
7521 using Details::Behavior;
7526 using std::endl;
7527 using Teuchos::ArrayRCP;
7528 using Teuchos::ArrayView;
7529 using Teuchos::Comm;
7530 using Teuchos::ParameterList;
7531 using Teuchos::RCP;
7532 typedef LocalOrdinal LO;
7533 typedef GlobalOrdinal GO;
7534 typedef node_type NT;
7535 typedef CrsMatrix<Scalar, LO, GO, NT> this_CRS_type;
7536 typedef Vector<int, LO, GO, NT> IntVectorType;
7537 using Teuchos::as;
7538
7539 const bool debug = Behavior::debug("CrsMatrix");
7540 const bool verbose = Behavior::verbose("CrsMatrix");
7541 int MyPID = getComm()->getRank();
7542
7543 std::unique_ptr<std::string> verbosePrefix;
7544 if (verbose) {
7545 verbosePrefix =
7546 this->createPrefix("CrsMatrix", "transferAndFillComplete");
7547 std::ostringstream os;
7548 os << "Start" << endl;
7549 std::cerr << os.str();
7550 }
7551
7552 //
7553 // Get the caller's parameters
7554 //
7555 bool isMM = false; // optimize for matrix-matrix ops.
7556 bool reverseMode = false; // Are we in reverse mode?
7557 bool restrictComm = false; // Do we need to restrict the communicator?
7558
7559 int mm_optimization_core_count =
7560 Behavior::TAFC_OptimizationCoreCount();
7561 RCP<ParameterList> matrixparams; // parameters for the destination matrix
7562 bool overrideAllreduce = false;
7563 bool useKokkosPath = false;
7564 if (!params.is_null()) {
7565 matrixparams = sublist(params, "CrsMatrix");
7566 reverseMode = params->get("Reverse Mode", reverseMode);
7567 useKokkosPath = params->get("TAFC: use kokkos path", useKokkosPath);
7568 restrictComm = params->get("Restrict Communicator", restrictComm);
7569 auto& slist = params->sublist("matrixmatrix: kernel params", false);
7570 isMM = slist.get("isMatrixMatrix_TransferAndFillComplete", false);
7571 mm_optimization_core_count = slist.get("MM_TAFC_OptimizationCoreCount", mm_optimization_core_count);
7572
7573 overrideAllreduce = slist.get("MM_TAFC_OverrideAllreduceCheck", false);
7574 if (getComm()->getSize() < mm_optimization_core_count && isMM) isMM = false;
7575 if (reverseMode) isMM = false;
7576 }
7577
7578 // Only used in the sparse matrix-matrix multiply (isMM) case.
7579 std::shared_ptr<::Tpetra::Details::CommRequest> iallreduceRequest;
7580 int mismatch = 0;
7581 int reduced_mismatch = 0;
7582 if (isMM && !overrideAllreduce) {
7583 // Test for pathological matrix transfer
7584 const bool source_vals = !getGraph()->getImporter().is_null();
7585 const bool target_vals = !(rowTransfer.getExportLIDs().size() == 0 ||
7586 rowTransfer.getRemoteLIDs().size() == 0);
7587 mismatch = (source_vals != target_vals) ? 1 : 0;
7588 iallreduceRequest =
7589 ::Tpetra::Details::iallreduce(mismatch, reduced_mismatch,
7590 Teuchos::REDUCE_MAX, *(getComm()));
7591 }
7592
7593 std::string tlstr = isMM ? "Tpetra: TAFC All:MMOpt" : "Tpetra: TAFC All:MMLegacy";
7594 Tpetra::Details::ProfilingRegion MMall(tlstr.c_str());
7595
7596 // Make sure that the input argument rowTransfer is either an
7597 // Import or an Export. Import and Export are the only two
7598 // subclasses of Transfer that we defined, but users might
7599 // (unwisely, for now at least) decide to implement their own
7600 // subclasses. Exclude this possibility.
7601 const import_type* xferAsImport = dynamic_cast<const import_type*>(&rowTransfer);
7602 const export_type* xferAsExport = dynamic_cast<const export_type*>(&rowTransfer);
7603 TEUCHOS_TEST_FOR_EXCEPTION(
7604 xferAsImport == nullptr && xferAsExport == nullptr, std::invalid_argument,
7605 "Tpetra::CrsMatrix::transferAndFillComplete: The 'rowTransfer' input "
7606 "argument must be either an Import or an Export, and its template "
7607 "parameters must match the corresponding template parameters of the "
7608 "CrsMatrix.");
7609
7610 // Make sure that the input argument domainTransfer is either an
7611 // Import or an Export. Import and Export are the only two
7612 // subclasses of Transfer that we defined, but users might
7613 // (unwisely, for now at least) decide to implement their own
7614 // subclasses. Exclude this possibility.
7615 Teuchos::RCP<const import_type> xferDomainAsImport = Teuchos::rcp_dynamic_cast<const import_type>(domainTransfer);
7616 Teuchos::RCP<const export_type> xferDomainAsExport = Teuchos::rcp_dynamic_cast<const export_type>(domainTransfer);
7617
7618 if (!domainTransfer.is_null()) {
7619 TEUCHOS_TEST_FOR_EXCEPTION(
7620 (xferDomainAsImport.is_null() && xferDomainAsExport.is_null()), std::invalid_argument,
7621 "Tpetra::CrsMatrix::transferAndFillComplete: The 'domainTransfer' input "
7622 "argument must be either an Import or an Export, and its template "
7623 "parameters must match the corresponding template parameters of the "
7624 "CrsMatrix.");
7625
7626 TEUCHOS_TEST_FOR_EXCEPTION(
7627 (xferAsImport != nullptr || !xferDomainAsImport.is_null()) &&
7628 ((xferAsImport != nullptr && xferDomainAsImport.is_null()) ||
7629 (xferAsImport == nullptr && !xferDomainAsImport.is_null())),
7630 std::invalid_argument,
7631 "Tpetra::CrsMatrix::transferAndFillComplete: The 'rowTransfer' and 'domainTransfer' input "
7632 "arguments must be of the same type (either Import or Export).");
7633
7634 TEUCHOS_TEST_FOR_EXCEPTION(
7635 (xferAsExport != nullptr || !xferDomainAsExport.is_null()) &&
7636 ((xferAsExport != nullptr && xferDomainAsExport.is_null()) ||
7637 (xferAsExport == nullptr && !xferDomainAsExport.is_null())),
7638 std::invalid_argument,
7639 "Tpetra::CrsMatrix::transferAndFillComplete: The 'rowTransfer' and 'domainTransfer' input "
7640 "arguments must be of the same type (either Import or Export).");
7641 } // domainTransfer != null
7642
7643 // FIXME (mfh 15 May 2014) Wouldn't communication still be needed,
7644 // if the source Map is not distributed but the target Map is?
7645 const bool communication_needed = rowTransfer.getSourceMap()->isDistributed();
7646
7647 // Get the new domain and range Maps. We need some of them for
7648 // error checking, now that we have the reverseMode parameter.
7649 RCP<const map_type> MyRowMap = reverseMode ? rowTransfer.getSourceMap() : rowTransfer.getTargetMap();
7650 RCP<const map_type> MyColMap; // create this below
7651 RCP<const map_type> MyDomainMap = !domainMap.is_null() ? domainMap : getDomainMap();
7652 RCP<const map_type> MyRangeMap = !rangeMap.is_null() ? rangeMap : getRangeMap();
7653 RCP<const map_type> BaseRowMap = MyRowMap;
7654 RCP<const map_type> BaseDomainMap = MyDomainMap;
7655
7656 // If the user gave us a nonnull destMat, then check whether it's
7657 // "pristine." That means that it has no entries.
7658 //
7659 // FIXME (mfh 15 May 2014) If this is not true on all processes,
7660 // then this exception test may hang. It would be better to
7661 // forward an error flag to the next communication phase.
7662 if (!destMat.is_null()) {
7663 // FIXME (mfh 15 May 2014): The Epetra idiom for checking
7664 // whether a graph or matrix has no entries on the calling
7665 // process, is that it is neither locally nor globally indexed.
7666 // This may change eventually with the Kokkos refactor version
7667 // of Tpetra, so it would be better just to check the quantity
7668 // of interest directly. Note that with the Kokkos refactor
7669 // version of Tpetra, asking for the total number of entries in
7670 // a graph or matrix that is not fill complete might require
7671 // computation (kernel launch), since it is not thread scalable
7672 // to update a count every time an entry is inserted.
7673 const bool NewFlag = !destMat->getGraph()->isLocallyIndexed() &&
7674 !destMat->getGraph()->isGloballyIndexed();
7675 TEUCHOS_TEST_FOR_EXCEPTION(
7676 !NewFlag, std::invalid_argument,
7677 "Tpetra::CrsMatrix::"
7678 "transferAndFillComplete: The input argument 'destMat' is only allowed "
7679 "to be nonnull, if its graph is empty (neither locally nor globally "
7680 "indexed).");
7681 // FIXME (mfh 15 May 2014) At some point, we want to change
7682 // graphs and matrices so that their DistObject Map
7683 // (this->getMap()) may differ from their row Map. This will
7684 // make redistribution for 2-D distributions more efficient. I
7685 // hesitate to change this check, because I'm not sure how much
7686 // the code here depends on getMap() and getRowMap() being the
7687 // same.
7688 TEUCHOS_TEST_FOR_EXCEPTION(
7689 !destMat->getRowMap()->isSameAs(*MyRowMap), std::invalid_argument,
7690 "Tpetra::CrsMatrix::transferAndFillComplete: The (row) Map of the "
7691 "input argument 'destMat' is not the same as the (row) Map specified "
7692 "by the input argument 'rowTransfer'.");
7693 TEUCHOS_TEST_FOR_EXCEPTION(
7694 !destMat->checkSizes(*this), std::invalid_argument,
7695 "Tpetra::CrsMatrix::transferAndFillComplete: You provided a nonnull "
7696 "destination matrix, but checkSizes() indicates that it is not a legal "
7697 "legal target for redistribution from the source matrix (*this). This "
7698 "may mean that they do not have the same dimensions.");
7699 }
7700
7701 // If forward mode (the default), then *this's (row) Map must be
7702 // the same as the source Map of the Transfer. If reverse mode,
7703 // then *this's (row) Map must be the same as the target Map of
7704 // the Transfer.
7705 //
7706 // FIXME (mfh 15 May 2014) At some point, we want to change graphs
7707 // and matrices so that their DistObject Map (this->getMap()) may
7708 // differ from their row Map. This will make redistribution for
7709 // 2-D distributions more efficient. I hesitate to change this
7710 // check, because I'm not sure how much the code here depends on
7711 // getMap() and getRowMap() being the same.
7712 TEUCHOS_TEST_FOR_EXCEPTION(
7713 !(reverseMode || getRowMap()->isSameAs(*rowTransfer.getSourceMap())),
7714 std::invalid_argument,
7715 "Tpetra::CrsMatrix::transferAndFillComplete: "
7716 "rowTransfer->getSourceMap() must match this->getRowMap() in forward mode.");
7717 TEUCHOS_TEST_FOR_EXCEPTION(
7718 !(!reverseMode || getRowMap()->isSameAs(*rowTransfer.getTargetMap())),
7719 std::invalid_argument,
7720 "Tpetra::CrsMatrix::transferAndFillComplete: "
7721 "rowTransfer->getTargetMap() must match this->getRowMap() in reverse mode.");
7722
7723 // checks for domainTransfer
7724 TEUCHOS_TEST_FOR_EXCEPTION(
7725 !xferDomainAsImport.is_null() && !xferDomainAsImport->getTargetMap()->isSameAs(*domainMap),
7726 std::invalid_argument,
7727 "Tpetra::CrsMatrix::transferAndFillComplete: The target map of the 'domainTransfer' input "
7728 "argument must be the same as the rebalanced domain map 'domainMap'");
7729
7730 TEUCHOS_TEST_FOR_EXCEPTION(
7731 !xferDomainAsExport.is_null() && !xferDomainAsExport->getSourceMap()->isSameAs(*domainMap),
7732 std::invalid_argument,
7733 "Tpetra::CrsMatrix::transferAndFillComplete: The source map of the 'domainTransfer' input "
7734 "argument must be the same as the rebalanced domain map 'domainMap'");
7735
7736 // The basic algorithm here is:
7737 //
7738 // 1. Call the moral equivalent of "Distor.do" to handle the import.
7739 // 2. Copy all the Imported and Copy/Permuted data into the raw
7740 // CrsMatrix / CrsGraphData pointers, still using GIDs.
7741 // 3. Call an optimized version of MakeColMap that avoids the
7742 // Directory lookups (since the importer knows who owns all the
7743 // GIDs) AND reindexes to LIDs.
7744 // 4. Call expertStaticFillComplete()
7745
7746 // Get information from the Importer
7747 const size_t NumSameIDs = rowTransfer.getNumSameIDs();
7748 ArrayView<const LO> ExportLIDs = reverseMode ? rowTransfer.getRemoteLIDs() : rowTransfer.getExportLIDs();
7749 auto RemoteLIDs = reverseMode ? rowTransfer.getExportLIDs_dv() : rowTransfer.getRemoteLIDs_dv();
7750 auto PermuteToLIDs = reverseMode ? rowTransfer.getPermuteFromLIDs_dv() : rowTransfer.getPermuteToLIDs_dv();
7751 auto PermuteFromLIDs = reverseMode ? rowTransfer.getPermuteToLIDs_dv() : rowTransfer.getPermuteFromLIDs_dv();
7752 Distributor& Distor = rowTransfer.getDistributor();
7753
7754 // Owning PIDs
7755 Teuchos::Array<int> SourcePids;
7756
7757 // Temp variables for sub-communicators
7758 RCP<const map_type> ReducedRowMap, ReducedColMap,
7759 ReducedDomainMap, ReducedRangeMap;
7760 RCP<const Comm<int>> ReducedComm;
7761
7762 // If the user gave us a null destMat, then construct the new
7763 // destination matrix. We will replace its column Map later.
7764 if (destMat.is_null()) {
7765 destMat = rcp(new this_CRS_type(MyRowMap, 0, matrixparams));
7766 }
7767
7768 /***************************************************/
7769 /***** 1) First communicator restriction phase ****/
7770 /***************************************************/
7771 if (restrictComm) {
7772 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC restrictComm");
7773 ReducedRowMap = MyRowMap->removeEmptyProcesses();
7774 ReducedComm = ReducedRowMap.is_null() ? Teuchos::null : ReducedRowMap->getComm();
7775 destMat->removeEmptyProcessesInPlace(ReducedRowMap);
7776
7777 ReducedDomainMap = MyRowMap.getRawPtr() == MyDomainMap.getRawPtr() ? ReducedRowMap : MyDomainMap->replaceCommWithSubset(ReducedComm);
7778 ReducedRangeMap = MyRowMap.getRawPtr() == MyRangeMap.getRawPtr() ? ReducedRowMap : MyRangeMap->replaceCommWithSubset(ReducedComm);
7779
7780 // Reset the "my" maps
7781 MyRowMap = ReducedRowMap;
7782 MyDomainMap = ReducedDomainMap;
7783 MyRangeMap = ReducedRangeMap;
7784
7785 // Update my PID, if we've restricted the communicator
7786 if (!ReducedComm.is_null()) {
7787 MyPID = ReducedComm->getRank();
7788 } else {
7789 MyPID = -2; // For debugging
7790 }
7791 } else {
7792 ReducedComm = MyRowMap->getComm();
7793 }
7794
7795 /***************************************************/
7796 /***** 2) From Tpetra::DistObject::doTransfer() ****/
7797 /***************************************************/
7798 // Get the owning PIDs
7799 RCP<const import_type> MyImporter = getGraph()->getImporter();
7800
7801 // check whether domain maps of source matrix and base domain map is the same
7802 bool bSameDomainMap = BaseDomainMap->isSameAs(*getDomainMap());
7803
7804 if (!restrictComm && !MyImporter.is_null() && bSameDomainMap) {
7805 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC getOwningPIDs same map");
7806 // Same domain map as source matrix
7807 //
7808 // NOTE: This won't work for restrictComm (because the Import
7809 // doesn't know the restricted PIDs), though writing an
7810 // optimized version for that case would be easy (Import an
7811 // IntVector of the new PIDs). Might want to add this later.
7812 Import_Util::getPids(*MyImporter, SourcePids, false);
7813 } else if (restrictComm && !MyImporter.is_null() && bSameDomainMap) {
7814 // Same domain map as source matrix (restricted communicator)
7815 // We need one import from the domain to the column map
7816 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC getOwningPIDs restricted comm");
7817 IntVectorType SourceDomain_pids(getDomainMap(), true);
7818 IntVectorType SourceCol_pids(getColMap());
7819 // SourceDomain_pids contains the restricted pids
7820 SourceDomain_pids.putScalar(MyPID);
7821
7822 SourceCol_pids.doImport(SourceDomain_pids, *MyImporter, INSERT);
7823 SourcePids.resize(getColMap()->getLocalNumElements());
7824 SourceCol_pids.get1dCopy(SourcePids());
7825 } else if (MyImporter.is_null()) {
7826 // Matrix has no off-process entries
7827 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC getOwningPIDs all local entries");
7828 SourcePids.resize(getColMap()->getLocalNumElements());
7829 SourcePids.assign(getColMap()->getLocalNumElements(), MyPID);
7830 } else if (!MyImporter.is_null() &&
7831 !domainTransfer.is_null()) {
7832 // general implementation for rectangular matrices with
7833 // domain map different than SourceMatrix domain map.
7834 // User has to provide a DomainTransfer object. We need
7835 // to communications (import/export)
7836 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC getOwningPIDs rectangular case");
7837
7838 // TargetDomain_pids lives on the rebalanced new domain map
7839 IntVectorType TargetDomain_pids(domainMap);
7840 TargetDomain_pids.putScalar(MyPID);
7841
7842 // SourceDomain_pids lives on the non-rebalanced old domain map
7843 IntVectorType SourceDomain_pids(getDomainMap());
7844
7845 // SourceCol_pids lives on the non-rebalanced old column map
7846 IntVectorType SourceCol_pids(getColMap());
7847
7848 if (!reverseMode && !xferDomainAsImport.is_null()) {
7849 SourceDomain_pids.doExport(TargetDomain_pids, *xferDomainAsImport, INSERT);
7850 } else if (reverseMode && !xferDomainAsExport.is_null()) {
7851 SourceDomain_pids.doExport(TargetDomain_pids, *xferDomainAsExport, INSERT);
7852 } else if (!reverseMode && !xferDomainAsExport.is_null()) {
7853 SourceDomain_pids.doImport(TargetDomain_pids, *xferDomainAsExport, INSERT);
7854 } else if (reverseMode && !xferDomainAsImport.is_null()) {
7855 SourceDomain_pids.doImport(TargetDomain_pids, *xferDomainAsImport, INSERT);
7856 } else {
7857 TEUCHOS_TEST_FOR_EXCEPTION(
7858 true, std::logic_error,
7859 "Tpetra::CrsMatrix::"
7860 "transferAndFillComplete: Should never get here! "
7861 "Please report this bug to a Tpetra developer.");
7862 }
7863 SourceCol_pids.doImport(SourceDomain_pids, *MyImporter, INSERT);
7864 SourcePids.resize(getColMap()->getLocalNumElements());
7865 SourceCol_pids.get1dCopy(SourcePids());
7866 } else if (!MyImporter.is_null() &&
7867 BaseDomainMap->isSameAs(*BaseRowMap) &&
7868 getDomainMap()->isSameAs(*getRowMap())) {
7869 // We can use the rowTransfer + SourceMatrix's Import to find out who owns what.
7870 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC getOwningPIDs query import");
7871
7872 IntVectorType TargetRow_pids(domainMap);
7873 IntVectorType SourceRow_pids(getRowMap());
7874 IntVectorType SourceCol_pids(getColMap());
7875
7876 TargetRow_pids.putScalar(MyPID);
7877 if (!reverseMode && xferAsImport != nullptr) {
7878 SourceRow_pids.doExport(TargetRow_pids, *xferAsImport, INSERT);
7879 } else if (reverseMode && xferAsExport != nullptr) {
7880 SourceRow_pids.doExport(TargetRow_pids, *xferAsExport, INSERT);
7881 } else if (!reverseMode && xferAsExport != nullptr) {
7882 SourceRow_pids.doImport(TargetRow_pids, *xferAsExport, INSERT);
7883 } else if (reverseMode && xferAsImport != nullptr) {
7884 SourceRow_pids.doImport(TargetRow_pids, *xferAsImport, INSERT);
7885 } else {
7886 TEUCHOS_TEST_FOR_EXCEPTION(
7887 true, std::logic_error,
7888 "Tpetra::CrsMatrix::"
7889 "transferAndFillComplete: Should never get here! "
7890 "Please report this bug to a Tpetra developer.");
7891 }
7892
7893 SourceCol_pids.doImport(SourceRow_pids, *MyImporter, INSERT);
7894 SourcePids.resize(getColMap()->getLocalNumElements());
7895 SourceCol_pids.get1dCopy(SourcePids());
7896 } else {
7897 TEUCHOS_TEST_FOR_EXCEPTION(
7898 true, std::invalid_argument,
7899 "Tpetra::CrsMatrix::"
7900 "transferAndFillComplete: This method only allows either domainMap == "
7901 "getDomainMap (), or (domainMap == rowTransfer.getTargetMap () and "
7902 "getDomainMap () == getRowMap ()).");
7903 }
7904
7905 // Tpetra-specific stuff
7906 size_t constantNumPackets = destMat->constantNumberOfPackets();
7907 {
7908 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC reallocate buffers");
7909 if (constantNumPackets == 0) {
7910 destMat->reallocArraysForNumPacketsPerLid(ExportLIDs.size(),
7911 RemoteLIDs.view_host().size());
7912 } else {
7913 // There are a constant number of packets per element. We
7914 // already know (from the number of "remote" (incoming)
7915 // elements) how many incoming elements we expect, so we can
7916 // resize the buffer accordingly.
7917 const size_t rbufLen = RemoteLIDs.view_host().size() * constantNumPackets;
7918 destMat->reallocImportsIfNeeded(rbufLen, false, nullptr);
7919 }
7920 }
7921
7922 // Pack & Prepare w/ owning PIDs
7923 {
7924 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC pack and prepare");
7925 if (debug) {
7926 using std::cerr;
7927 using std::endl;
7928 using Teuchos::outArg;
7929 using Teuchos::REDUCE_MAX;
7930 using Teuchos::reduceAll;
7931 RCP<const Teuchos::Comm<int>> comm = this->getComm();
7932 const int myRank = comm->getRank();
7933
7934 std::ostringstream errStrm;
7935 int lclErr = 0;
7936 int gblErr = 0;
7937
7938 Teuchos::ArrayView<size_t> numExportPacketsPerLID;
7939 try {
7940 // packAndPrepare* methods modify numExportPacketsPerLID_.
7941 destMat->numExportPacketsPerLID_.modify_host();
7942 numExportPacketsPerLID =
7943 getArrayViewFromDualView(destMat->numExportPacketsPerLID_);
7944 } catch (std::exception& e) {
7945 errStrm << "Proc " << myRank << ": getArrayViewFromDualView threw: "
7946 << e.what() << std::endl;
7947 lclErr = 1;
7948 } catch (...) {
7949 errStrm << "Proc " << myRank << ": getArrayViewFromDualView threw "
7950 "an exception not a subclass of std::exception"
7951 << std::endl;
7952 lclErr = 1;
7953 }
7954
7955 if (!comm.is_null()) {
7956 reduceAll<int, int>(*comm, REDUCE_MAX, lclErr, outArg(gblErr));
7957 }
7958 if (gblErr != 0) {
7959 ::Tpetra::Details::gathervPrint(cerr, errStrm.str(), *comm);
7960 TEUCHOS_TEST_FOR_EXCEPTION(
7961 true, std::runtime_error,
7962 "getArrayViewFromDualView threw an "
7963 "exception on at least one process.");
7964 }
7965
7966 if (verbose) {
7967 std::ostringstream os;
7968 os << *verbosePrefix << "Calling packCrsMatrixWithOwningPIDs"
7969 << std::endl;
7970 std::cerr << os.str();
7971 }
7972 try {
7974 destMat->exports_,
7975 numExportPacketsPerLID,
7976 ExportLIDs,
7977 SourcePids,
7978 constantNumPackets);
7979 } catch (std::exception& e) {
7980 errStrm << "Proc " << myRank << ": packCrsMatrixWithOwningPIDs threw: "
7981 << e.what() << std::endl;
7982 lclErr = 1;
7983 } catch (...) {
7984 errStrm << "Proc " << myRank << ": packCrsMatrixWithOwningPIDs threw "
7985 "an exception not a subclass of std::exception"
7986 << std::endl;
7987 lclErr = 1;
7988 }
7989
7990 if (verbose) {
7991 std::ostringstream os;
7992 os << *verbosePrefix << "Done with packCrsMatrixWithOwningPIDs"
7993 << std::endl;
7994 std::cerr << os.str();
7995 }
7996
7997 if (!comm.is_null()) {
7998 reduceAll<int, int>(*comm, REDUCE_MAX, lclErr, outArg(gblErr));
7999 }
8000 if (gblErr != 0) {
8001 ::Tpetra::Details::gathervPrint(cerr, errStrm.str(), *comm);
8002 TEUCHOS_TEST_FOR_EXCEPTION(
8003 true, std::runtime_error,
8004 "packCrsMatrixWithOwningPIDs threw an "
8005 "exception on at least one process.");
8006 }
8007 } else {
8008 // packAndPrepare* methods modify numExportPacketsPerLID_.
8009 destMat->numExportPacketsPerLID_.modify_host();
8010 Teuchos::ArrayView<size_t> numExportPacketsPerLID =
8011 getArrayViewFromDualView(destMat->numExportPacketsPerLID_);
8012 if (verbose) {
8013 std::ostringstream os;
8014 os << *verbosePrefix << "Calling packCrsMatrixWithOwningPIDs"
8015 << std::endl;
8016 std::cerr << os.str();
8017 }
8019 destMat->exports_,
8020 numExportPacketsPerLID,
8021 ExportLIDs,
8022 SourcePids,
8023 constantNumPackets);
8024 if (verbose) {
8025 std::ostringstream os;
8026 os << *verbosePrefix << "Done with packCrsMatrixWithOwningPIDs"
8027 << std::endl;
8028 std::cerr << os.str();
8029 }
8030 }
8031 }
8032
8033 // Do the exchange of remote data.
8034 {
8035 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC getOwningPIDs exchange remote data");
8036 if (!communication_needed) {
8037 if (verbose) {
8038 std::ostringstream os;
8039 os << *verbosePrefix << "Communication not needed" << std::endl;
8040 std::cerr << os.str();
8041 }
8042 } else {
8043 if (reverseMode) {
8044 if (constantNumPackets == 0) { // variable number of packets per LID
8045 if (verbose) {
8046 std::ostringstream os;
8047 os << *verbosePrefix << "Reverse mode, variable # packets / LID"
8048 << std::endl;
8049 std::cerr << os.str();
8050 }
8051 // Make sure that host has the latest version, since we're
8052 // using the version on host. If host has the latest
8053 // version, syncing to host does nothing.
8054 destMat->numExportPacketsPerLID_.sync_host();
8055 Teuchos::ArrayView<const size_t> numExportPacketsPerLID =
8056 getArrayViewFromDualView(destMat->numExportPacketsPerLID_);
8057 destMat->numImportPacketsPerLID_.sync_host();
8058 Teuchos::ArrayView<size_t> numImportPacketsPerLID =
8059 getArrayViewFromDualView(destMat->numImportPacketsPerLID_);
8060
8061 if (verbose) {
8062 std::ostringstream os;
8063 os << *verbosePrefix << "Calling 3-arg doReversePostsAndWaits"
8064 << std::endl;
8065 std::cerr << os.str();
8066 }
8067 Distor.doReversePostsAndWaits(destMat->numExportPacketsPerLID_.view_host(), 1,
8068 destMat->numImportPacketsPerLID_.view_host());
8069 if (verbose) {
8070 std::ostringstream os;
8071 os << *verbosePrefix << "Finished 3-arg doReversePostsAndWaits"
8072 << std::endl;
8073 std::cerr << os.str();
8074 }
8075
8076 size_t totalImportPackets = 0;
8077 for (Array_size_type i = 0; i < numImportPacketsPerLID.size(); ++i) {
8078 totalImportPackets += numImportPacketsPerLID[i];
8079 }
8080
8081 // Reallocation MUST go before setting the modified flag,
8082 // because it may clear out the flags.
8083 destMat->reallocImportsIfNeeded(totalImportPackets, verbose,
8084 verbosePrefix.get());
8085 destMat->imports_.modify_host();
8086 auto hostImports = destMat->imports_.view_host();
8087 // This is a legacy host pack/unpack path, so use the host
8088 // version of exports_.
8089 destMat->exports_.sync_host();
8090 auto hostExports = destMat->exports_.view_host();
8091 if (verbose) {
8092 std::ostringstream os;
8093 os << *verbosePrefix << "Calling 4-arg doReversePostsAndWaits"
8094 << std::endl;
8095 std::cerr << os.str();
8096 }
8097 Distor.doReversePostsAndWaits(hostExports,
8098 numExportPacketsPerLID,
8099 hostImports,
8100 numImportPacketsPerLID);
8101 if (verbose) {
8102 std::ostringstream os;
8103 os << *verbosePrefix << "Finished 4-arg doReversePostsAndWaits"
8104 << std::endl;
8105 std::cerr << os.str();
8106 }
8107 } else { // constant number of packets per LID
8108 if (verbose) {
8109 std::ostringstream os;
8110 os << *verbosePrefix << "Reverse mode, constant # packets / LID"
8111 << std::endl;
8112 std::cerr << os.str();
8113 }
8114 destMat->imports_.modify_host();
8115 auto hostImports = destMat->imports_.view_host();
8116 // This is a legacy host pack/unpack path, so use the host
8117 // version of exports_.
8118 destMat->exports_.sync_host();
8119 auto hostExports = destMat->exports_.view_host();
8120 if (verbose) {
8121 std::ostringstream os;
8122 os << *verbosePrefix << "Calling 3-arg doReversePostsAndWaits"
8123 << std::endl;
8124 std::cerr << os.str();
8125 }
8126 Distor.doReversePostsAndWaits(hostExports,
8127 constantNumPackets,
8128 hostImports);
8129 if (verbose) {
8130 std::ostringstream os;
8131 os << *verbosePrefix << "Finished 3-arg doReversePostsAndWaits"
8132 << std::endl;
8133 std::cerr << os.str();
8134 }
8135 }
8136 } else { // forward mode (the default)
8137 if (constantNumPackets == 0) { // variable number of packets per LID
8138 if (verbose) {
8139 std::ostringstream os;
8140 os << *verbosePrefix << "Forward mode, variable # packets / LID"
8141 << std::endl;
8142 std::cerr << os.str();
8143 }
8144 // Make sure that host has the latest version, since we're
8145 // using the version on host. If host has the latest
8146 // version, syncing to host does nothing.
8147 destMat->numExportPacketsPerLID_.sync_host();
8148 Teuchos::ArrayView<const size_t> numExportPacketsPerLID =
8149 getArrayViewFromDualView(destMat->numExportPacketsPerLID_);
8150 destMat->numImportPacketsPerLID_.sync_host();
8151 Teuchos::ArrayView<size_t> numImportPacketsPerLID =
8152 getArrayViewFromDualView(destMat->numImportPacketsPerLID_);
8153 if (verbose) {
8154 std::ostringstream os;
8155 os << *verbosePrefix << "Calling 3-arg doPostsAndWaits"
8156 << std::endl;
8157 std::cerr << os.str();
8158 }
8159 Distor.doPostsAndWaits(destMat->numExportPacketsPerLID_.view_host(), 1,
8160 destMat->numImportPacketsPerLID_.view_host());
8161 if (verbose) {
8162 std::ostringstream os;
8163 os << *verbosePrefix << "Finished 3-arg doPostsAndWaits"
8164 << std::endl;
8165 std::cerr << os.str();
8166 }
8167
8168 size_t totalImportPackets = 0;
8169 for (Array_size_type i = 0; i < numImportPacketsPerLID.size(); ++i) {
8170 totalImportPackets += numImportPacketsPerLID[i];
8171 }
8172
8173 // Reallocation MUST go before setting the modified flag,
8174 // because it may clear out the flags.
8175 destMat->reallocImportsIfNeeded(totalImportPackets, verbose,
8176 verbosePrefix.get());
8177 destMat->imports_.modify_host();
8178 auto hostImports = destMat->imports_.view_host();
8179 // This is a legacy host pack/unpack path, so use the host
8180 // version of exports_.
8181 destMat->exports_.sync_host();
8182 auto hostExports = destMat->exports_.view_host();
8183 if (verbose) {
8184 std::ostringstream os;
8185 os << *verbosePrefix << "Calling 4-arg doPostsAndWaits"
8186 << std::endl;
8187 std::cerr << os.str();
8188 }
8189 Distor.doPostsAndWaits(hostExports,
8190 numExportPacketsPerLID,
8191 hostImports,
8192 numImportPacketsPerLID);
8193 if (verbose) {
8194 std::ostringstream os;
8195 os << *verbosePrefix << "Finished 4-arg doPostsAndWaits"
8196 << std::endl;
8197 std::cerr << os.str();
8198 }
8199 } else { // constant number of packets per LID
8200 if (verbose) {
8201 std::ostringstream os;
8202 os << *verbosePrefix << "Forward mode, constant # packets / LID"
8203 << std::endl;
8204 std::cerr << os.str();
8205 }
8206 destMat->imports_.modify_host();
8207 auto hostImports = destMat->imports_.view_host();
8208 // This is a legacy host pack/unpack path, so use the host
8209 // version of exports_.
8210 destMat->exports_.sync_host();
8211 auto hostExports = destMat->exports_.view_host();
8212 if (verbose) {
8213 std::ostringstream os;
8214 os << *verbosePrefix << "Calling 3-arg doPostsAndWaits"
8215 << std::endl;
8216 std::cerr << os.str();
8217 }
8218 Distor.doPostsAndWaits(hostExports,
8219 constantNumPackets,
8220 hostImports);
8221 if (verbose) {
8222 std::ostringstream os;
8223 os << *verbosePrefix << "Finished 3-arg doPostsAndWaits"
8224 << std::endl;
8225 std::cerr << os.str();
8226 }
8227 }
8228 }
8229 }
8230 }
8231
8232 /*********************************************************************/
8233 /**** 3) Copy all of the Same/Permute/Remote data into CSR_arrays ****/
8234 /*********************************************************************/
8235
8236 bool runOnHost = std::is_same_v<typename device_type::memory_space, Kokkos::HostSpace> && !useKokkosPath;
8237
8238 Teuchos::Array<int> RemotePids;
8239 if (runOnHost) {
8240 Teuchos::Array<int> TargetPids;
8241 // Backwards compatibility measure. We'll use this again below.
8242
8243 // TODO JHU Need to track down why numImportPacketsPerLID_ has not been corrently marked as modified on host (which it has been)
8244 // TODO JHU somewhere above, e.g., call to Distor.doPostsAndWaits().
8245 // TODO JHU This only becomes apparent as we begin to convert TAFC to run on device.
8246 destMat->numImportPacketsPerLID_.modify_host(); // FIXME
8247
8248 auto tmCopySPRdata = Teuchos::rcp(new Tpetra::Details::ProfilingRegion("Tpetra TAFC unpack-count-resize + copy same-perm-remote data"));
8249 ArrayRCP<size_t> CSR_rowptr;
8250 ArrayRCP<GO> CSR_colind_GID;
8251 ArrayRCP<LO> CSR_colind_LID;
8252 ArrayRCP<Scalar> CSR_vals;
8253
8254 destMat->imports_.sync_device();
8255 destMat->numImportPacketsPerLID_.sync_device();
8256
8257 size_t N = BaseRowMap->getLocalNumElements();
8258
8259 auto RemoteLIDs_d = RemoteLIDs.view_device();
8260 auto PermuteToLIDs_d = PermuteToLIDs.view_device();
8261 auto PermuteFromLIDs_d = PermuteFromLIDs.view_device();
8262
8264 *this,
8265 RemoteLIDs_d,
8266 destMat->imports_.view_device(), // hostImports
8267 destMat->numImportPacketsPerLID_.view_device(), // numImportPacketsPerLID
8268 NumSameIDs,
8269 PermuteToLIDs_d,
8270 PermuteFromLIDs_d,
8271 N,
8272 MyPID,
8273 CSR_rowptr,
8274 CSR_colind_GID,
8275 CSR_vals,
8276 SourcePids(),
8277 TargetPids);
8278
8279 // If LO and GO are the same, we can reuse memory when
8280 // converting the column indices from global to local indices.
8281 if (typeid(LO) == typeid(GO)) {
8282 CSR_colind_LID = Teuchos::arcp_reinterpret_cast<LO>(CSR_colind_GID);
8283 } else {
8284 CSR_colind_LID.resize(CSR_colind_GID.size());
8285 }
8286 CSR_colind_LID.resize(CSR_colind_GID.size());
8287
8288 // On return from unpackAndCombineIntoCrsArrays TargetPids[i] == -1 for locally
8289 // owned entries. Convert them to the actual PID.
8290 // JHU FIXME This can be done within unpackAndCombineIntoCrsArrays with a parallel_for.
8291 for (size_t i = 0; i < static_cast<size_t>(TargetPids.size()); i++) {
8292 if (TargetPids[i] == -1) TargetPids[i] = MyPID;
8293 }
8294 tmCopySPRdata = Teuchos::null;
8295 /**************************************************************/
8296 /**** 4) Call Optimized MakeColMap w/ no Directory Lookups ****/
8297 /**************************************************************/
8298 // Call an optimized version of makeColMap that avoids the
8299 // Directory lookups (since the Import object knows who owns all
8300 // the GIDs).
8301 if (verbose) {
8302 std::ostringstream os;
8303 os << *verbosePrefix << "Calling lowCommunicationMakeColMapAndReindex"
8304 << std::endl;
8305 std::cerr << os.str();
8306 }
8307 {
8308 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC makeColMap");
8309 Import_Util::lowCommunicationMakeColMapAndReindexSerial(CSR_rowptr(),
8310 CSR_colind_LID(),
8311 CSR_colind_GID(),
8312 BaseDomainMap,
8313 TargetPids,
8314 RemotePids,
8315 MyColMap,
8316 params);
8317 }
8318
8319 if (verbose) {
8320 std::ostringstream os;
8321 os << *verbosePrefix << "restrictComm="
8322 << (restrictComm ? "true" : "false") << std::endl;
8323 std::cerr << os.str();
8324 }
8325
8326 /*******************************************************/
8327 /**** 4) Second communicator restriction phase ****/
8328 /*******************************************************/
8329 {
8330 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC restrict colmap");
8331 if (restrictComm) {
8332 ReducedColMap = (MyRowMap.getRawPtr() == MyColMap.getRawPtr()) ? ReducedRowMap : MyColMap->replaceCommWithSubset(ReducedComm);
8333 MyColMap = ReducedColMap; // Reset the "my" maps
8334 }
8335
8336 // Replace the col map
8337 if (verbose) {
8338 std::ostringstream os;
8339 os << *verbosePrefix << "Calling replaceColMap" << std::endl;
8340 std::cerr << os.str();
8341 }
8342 destMat->replaceColMap(MyColMap);
8343
8344 // Short circuit if the processor is no longer in the communicator
8345 //
8346 // NOTE: Epetra replaces modifies all "removed" processes so they
8347 // have a dummy (serial) Map that doesn't touch the original
8348 // communicator. Duplicating that here might be a good idea.
8349 if (ReducedComm.is_null()) {
8350 if (verbose) {
8351 std::ostringstream os;
8352 os << *verbosePrefix << "I am no longer in the communicator; "
8353 "returning"
8354 << std::endl;
8355 std::cerr << os.str();
8356 }
8357 return;
8358 }
8359 }
8360
8361 /***************************************************/
8362 /**** 5) Sort ****/
8363 /***************************************************/
8364 if ((!reverseMode && xferAsImport != nullptr) ||
8365 (reverseMode && xferAsExport != nullptr)) {
8366 if (verbose) {
8367 std::ostringstream os;
8368 os << *verbosePrefix << "Calling sortCrsEntries" << endl;
8369 std::cerr << os.str();
8370 }
8371 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC sortCrsEntries");
8372 Import_Util::sortCrsEntries(CSR_rowptr(),
8373 CSR_colind_LID(),
8374 CSR_vals());
8375 } else if ((!reverseMode && xferAsExport != nullptr) ||
8376 (reverseMode && xferAsImport != nullptr)) {
8377 if (verbose) {
8378 std::ostringstream os;
8379 os << *verbosePrefix << "Calling sortAndMergeCrsEntries"
8380 << endl;
8381 std::cerr << os.str();
8382 }
8383 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC sortAndMergeCrsEntries");
8385 CSR_colind_LID(),
8386 CSR_vals());
8387 if (CSR_rowptr[N] != static_cast<size_t>(CSR_vals.size())) {
8388 CSR_colind_LID.resize(CSR_rowptr[N]);
8389 CSR_vals.resize(CSR_rowptr[N]);
8390 }
8391 } else {
8392 TEUCHOS_TEST_FOR_EXCEPTION(
8393 true, std::logic_error,
8394 "Tpetra::CrsMatrix::"
8395 "transferAndFillComplete: Should never get here! "
8396 "Please report this bug to a Tpetra developer.");
8397 }
8398 /***************************************************/
8399 /**** 6) Reset the colmap and the arrays ****/
8400 /***************************************************/
8401
8402 if (verbose) {
8403 std::ostringstream os;
8404 os << *verbosePrefix << "Calling destMat->setAllValues" << endl;
8405 std::cerr << os.str();
8406 }
8407
8408 // Call constructor for the new matrix (restricted as needed)
8409 //
8410 // NOTE (mfh 15 May 2014) This should work fine for the Kokkos
8411 // refactor version of CrsMatrix, though it reserves the right to
8412 // make a deep copy of the arrays.
8413 {
8414 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC setAllValues");
8415 destMat->setAllValues(CSR_rowptr, CSR_colind_LID, CSR_vals);
8416 }
8417
8418 } else {
8419 // run on device
8420
8421 // Backwards compatibility measure. We'll use this again below.
8422
8423 // TODO JHU Need to track down why numImportPacketsPerLID_ has not been corrently marked as modified on host (which it has been)
8424 // TODO JHU somewhere above, e.g., call to Distor.doPostsAndWaits().
8425 // TODO JHU This only becomes apparent as we begin to convert TAFC to run on device.
8426 destMat->numImportPacketsPerLID_.modify_host(); // FIXME
8427
8428 auto tmCopySPRdata = Teuchos::rcp(new Tpetra::Details::ProfilingRegion("Tpetra TAFC unpack-count-resize + copy same-perm-remote data"));
8429 ArrayRCP<size_t> CSR_rowptr;
8430 ArrayRCP<GO> CSR_colind_GID;
8431 ArrayRCP<LO> CSR_colind_LID;
8432 ArrayRCP<Scalar> CSR_vals;
8433
8434 destMat->imports_.sync_device();
8435 destMat->numImportPacketsPerLID_.sync_device();
8436
8437 size_t N = BaseRowMap->getLocalNumElements();
8438
8439 auto RemoteLIDs_d = RemoteLIDs.view_device();
8440 auto PermuteToLIDs_d = PermuteToLIDs.view_device();
8441 auto PermuteFromLIDs_d = PermuteFromLIDs.view_device();
8442
8443 Kokkos::View<size_t*, device_type> CSR_rowptr_d;
8444 Kokkos::View<GO*, device_type> CSR_colind_GID_d;
8445 Kokkos::View<LO*, device_type> CSR_colind_LID_d;
8446 Kokkos::View<impl_scalar_type*, device_type> CSR_vals_d;
8447 Kokkos::View<int*, device_type> TargetPids_d;
8448
8450 *this,
8451 RemoteLIDs_d,
8452 destMat->imports_.view_device(), // hostImports
8453 destMat->numImportPacketsPerLID_.view_device(), // numImportPacketsPerLID
8454 NumSameIDs,
8455 PermuteToLIDs_d,
8456 PermuteFromLIDs_d,
8457 N,
8458 MyPID,
8459 CSR_rowptr_d,
8460 CSR_colind_GID_d,
8461 CSR_vals_d,
8462 SourcePids(),
8463 TargetPids_d);
8464
8465 Kokkos::resize(CSR_colind_LID_d, CSR_colind_GID_d.size());
8466
8467 tmCopySPRdata = Teuchos::null;
8468 /**************************************************************/
8469 /**** 4) Call Optimized MakeColMap w/ no Directory Lookups ****/
8470 /**************************************************************/
8471 // Call an optimized version of makeColMap that avoids the
8472 // Directory lookups (since the Import object knows who owns all
8473 // the GIDs).
8474 if (verbose) {
8475 std::ostringstream os;
8476 os << *verbosePrefix << "Calling lowCommunicationMakeColMapAndReindex"
8477 << std::endl;
8478 std::cerr << os.str();
8479 }
8480 {
8481 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC makeColMap");
8483 CSR_colind_LID_d,
8484 CSR_colind_GID_d,
8485 BaseDomainMap,
8486 TargetPids_d,
8487 RemotePids,
8488 MyColMap,
8489 params);
8490 }
8491
8492 if (verbose) {
8493 std::ostringstream os;
8494 os << *verbosePrefix << "restrictComm="
8495 << (restrictComm ? "true" : "false") << std::endl;
8496 std::cerr << os.str();
8497 }
8498
8499 /*******************************************************/
8500 /**** 4) Second communicator restriction phase ****/
8501 /*******************************************************/
8502 {
8503 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC restrict colmap");
8504 if (restrictComm) {
8505 ReducedColMap = (MyRowMap.getRawPtr() == MyColMap.getRawPtr()) ? ReducedRowMap : MyColMap->replaceCommWithSubset(ReducedComm);
8506 MyColMap = ReducedColMap; // Reset the "my" maps
8507 }
8508
8509 // Replace the col map
8510 if (verbose) {
8511 std::ostringstream os;
8512 os << *verbosePrefix << "Calling replaceColMap" << std::endl;
8513 std::cerr << os.str();
8514 }
8515 destMat->replaceColMap(MyColMap);
8516
8517 // Short circuit if the processor is no longer in the communicator
8518 //
8519 // NOTE: Epetra replaces modifies all "removed" processes so they
8520 // have a dummy (serial) Map that doesn't touch the original
8521 // communicator. Duplicating that here might be a good idea.
8522 if (ReducedComm.is_null()) {
8523 if (verbose) {
8524 std::ostringstream os;
8525 os << *verbosePrefix << "I am no longer in the communicator; "
8526 "returning"
8527 << std::endl;
8528 std::cerr << os.str();
8529 }
8530 return;
8531 }
8532 }
8533
8534 /***************************************************/
8535 /**** 5) Sort ****/
8536 /***************************************************/
8537
8538 if ((!reverseMode && xferAsImport != nullptr) ||
8539 (reverseMode && xferAsExport != nullptr)) {
8540 if (verbose) {
8541 std::ostringstream os;
8542 os << *verbosePrefix << "Calling sortCrsEntries" << endl;
8543 std::cerr << os.str();
8544 }
8545 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC sortCrsEntries");
8546 Import_Util::sortCrsEntries(CSR_rowptr_d,
8547 CSR_colind_LID_d,
8548 CSR_vals_d);
8549 } else if ((!reverseMode && xferAsExport != nullptr) ||
8550 (reverseMode && xferAsImport != nullptr)) {
8551 if (verbose) {
8552 std::ostringstream os;
8553 os << *verbosePrefix << "Calling sortAndMergeCrsEntries"
8554 << endl;
8555 std::cerr << os.str();
8556 }
8557 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC sortAndMergeCrsEntries");
8559 CSR_colind_LID_d,
8560 CSR_vals_d,
8561 ::KokkosSparse::SortAlgorithm::DEFAULT);
8562 } else {
8563 TEUCHOS_TEST_FOR_EXCEPTION(
8564 true, std::logic_error,
8565 "Tpetra::CrsMatrix::"
8566 "transferAndFillComplete: Should never get here! "
8567 "Please report this bug to a Tpetra developer.");
8568 }
8569
8570 /***************************************************/
8571 /**** 6) Reset the colmap and the arrays ****/
8572 /***************************************************/
8573
8574 if (verbose) {
8575 std::ostringstream os;
8576 os << *verbosePrefix << "Calling destMat->setAllValues" << endl;
8577 std::cerr << os.str();
8578 }
8579
8580 {
8581 Tpetra::Details::ProfilingRegion MMrc("Tpetra TAFC setAllValues");
8582 destMat->setAllValues(CSR_rowptr_d, CSR_colind_LID_d, CSR_vals_d);
8583 }
8584
8585 } // if (runOnHost) .. else ..
8586
8587 /***************************************************/
8588 /**** 7) Build Importer & Call ESFC ****/
8589 /***************************************************/
8590 auto tmIESFC = Teuchos::rcp(new Tpetra::Details::ProfilingRegion("Tpetra TAFC build importer and esfc"));
8591 // Pre-build the importer using the existing PIDs
8592 Teuchos::ParameterList esfc_params;
8593
8594 RCP<import_type> MyImport;
8595
8596 // Fulfull the non-blocking allreduce on reduced_mismatch.
8597 if (iallreduceRequest.get() != nullptr) {
8598 if (verbose) {
8599 std::ostringstream os;
8600 os << *verbosePrefix << "Calling iallreduceRequest->wait()"
8601 << endl;
8602 std::cerr << os.str();
8603 }
8604 iallreduceRequest->wait();
8605 if (reduced_mismatch != 0) {
8606 isMM = false;
8607 }
8608 }
8609
8610 if (isMM) {
8611 Tpetra::Details::ProfilingRegion MMisMM("Tpetra isMM Block");
8612 // Combine all type1/2/3 lists, [filter them], then call the expert import constructor.
8613
8614 if (verbose) {
8615 std::ostringstream os;
8616 os << *verbosePrefix << "Getting CRS pointers" << endl;
8617 std::cerr << os.str();
8618 }
8619
8620 Teuchos::ArrayRCP<LocalOrdinal> type3LIDs;
8621 Teuchos::ArrayRCP<int> type3PIDs;
8622 auto rowptr = getCrsGraph()->getLocalRowPtrsHost();
8623 auto colind = getCrsGraph()->getLocalIndicesHost();
8624
8625 if (verbose) {
8626 std::ostringstream os;
8627 os << *verbosePrefix << "Calling reverseNeighborDiscovery" << std::endl;
8628 std::cerr << os.str();
8629 }
8630
8631 {
8632 Tpetra::Details::ProfilingRegion MMrc("Tpetra isMMrevNeighDis");
8633 Import_Util::reverseNeighborDiscovery(*this,
8634 rowptr,
8635 colind,
8636 rowTransfer,
8637 MyImporter,
8638 MyDomainMap,
8639 type3PIDs,
8640 type3LIDs,
8641 ReducedComm);
8642 }
8643
8644 if (verbose) {
8645 std::ostringstream os;
8646 os << *verbosePrefix << "Done with reverseNeighborDiscovery" << std::endl;
8647 std::cerr << os.str();
8648 }
8649
8650 Teuchos::ArrayView<const int> EPID1 = MyImporter.is_null() ? Teuchos::ArrayView<const int>() : MyImporter->getExportPIDs();
8651 Teuchos::ArrayView<const LO> ELID1 = MyImporter.is_null() ? Teuchos::ArrayView<const LO>() : MyImporter->getExportLIDs();
8652
8653 Teuchos::ArrayView<const int> TEPID2 = rowTransfer.getExportPIDs(); // row matrix
8654 Teuchos::ArrayView<const LO> TELID2 = rowTransfer.getExportLIDs();
8655
8656 const int numCols = getGraph()->getColMap()->getLocalNumElements(); // may be dup
8657 // from EpetraExt_MMHelpers.cpp: build_type2_exports
8658 std::vector<bool> IsOwned(numCols, true);
8659 std::vector<int> SentTo(numCols, -1);
8660 if (!MyImporter.is_null()) {
8661 for (auto&& rlid : MyImporter->getRemoteLIDs()) { // the remoteLIDs must be from sourcematrix
8662 IsOwned[rlid] = false;
8663 }
8664 }
8665
8666 std::vector<std::pair<int, GO>> usrtg;
8667 usrtg.reserve(TEPID2.size());
8668
8669 {
8670 const auto& colMap = *(this->getColMap()); // *this is sourcematrix
8671 for (Array_size_type i = 0; i < TEPID2.size(); ++i) {
8672 const LO row = TELID2[i];
8673 const int pid = TEPID2[i];
8674 for (auto j = rowptr[row]; j < rowptr[row + 1]; ++j) {
8675 const int col = colind[j];
8676 if (IsOwned[col] && SentTo[col] != pid) {
8677 SentTo[col] = pid;
8678 GO gid = colMap.getGlobalElement(col);
8679 usrtg.push_back(std::pair<int, GO>(pid, gid));
8680 }
8681 }
8682 }
8683 }
8684
8685 // This sort can _not_ be omitted.[
8686 std::sort(usrtg.begin(), usrtg.end()); // default comparator does the right thing, now sorted in gid order
8687 auto eopg = std ::unique(usrtg.begin(), usrtg.end());
8688 // 25 Jul 2018: Could just ignore the entries at and after eopg.
8689 usrtg.erase(eopg, usrtg.end());
8690
8691 const Array_size_type type2_us_size = usrtg.size();
8692 Teuchos::ArrayRCP<int> EPID2 = Teuchos::arcp(new int[type2_us_size], 0, type2_us_size, true);
8693 Teuchos::ArrayRCP<LO> ELID2 = Teuchos::arcp(new LO[type2_us_size], 0, type2_us_size, true);
8694
8695 int pos = 0;
8696 for (auto&& p : usrtg) {
8697 EPID2[pos] = p.first;
8698 ELID2[pos] = this->getDomainMap()->getLocalElement(p.second);
8699 pos++;
8700 }
8701
8702 Teuchos::ArrayView<int> EPID3 = type3PIDs();
8703 Teuchos::ArrayView<LO> ELID3 = type3LIDs();
8704 GO InfGID = std::numeric_limits<GO>::max();
8705 int InfPID = INT_MAX;
8706#ifdef TPETRA_MIN3
8707#undef TPETRA_MIN3
8708#endif // TPETRA_MIN3
8709#define TPETRA_MIN3(x, y, z) ((x) < (y) ? (std::min(x, z)) : (std::min(y, z)))
8710 int i1 = 0, i2 = 0, i3 = 0;
8711 int Len1 = EPID1.size();
8712 int Len2 = EPID2.size();
8713 int Len3 = EPID3.size();
8714
8715 int MyLen = Len1 + Len2 + Len3;
8716 Teuchos::ArrayRCP<LO> userExportLIDs = Teuchos::arcp(new LO[MyLen], 0, MyLen, true);
8717 Teuchos::ArrayRCP<int> userExportPIDs = Teuchos::arcp(new int[MyLen], 0, MyLen, true);
8718 int iloc = 0; // will be the size of the userExportLID/PIDs
8719
8720 while (i1 < Len1 || i2 < Len2 || i3 < Len3) {
8721 int PID1 = (i1 < Len1) ? (EPID1[i1]) : InfPID;
8722 int PID2 = (i2 < Len2) ? (EPID2[i2]) : InfPID;
8723 int PID3 = (i3 < Len3) ? (EPID3[i3]) : InfPID;
8724
8725 GO GID1 = (i1 < Len1) ? getDomainMap()->getGlobalElement(ELID1[i1]) : InfGID;
8726 GO GID2 = (i2 < Len2) ? getDomainMap()->getGlobalElement(ELID2[i2]) : InfGID;
8727 GO GID3 = (i3 < Len3) ? getDomainMap()->getGlobalElement(ELID3[i3]) : InfGID;
8728
8729 int MIN_PID = TPETRA_MIN3(PID1, PID2, PID3);
8730 GO MIN_GID = TPETRA_MIN3(((PID1 == MIN_PID) ? GID1 : InfGID), ((PID2 == MIN_PID) ? GID2 : InfGID), ((PID3 == MIN_PID) ? GID3 : InfGID));
8731#ifdef TPETRA_MIN3
8732#undef TPETRA_MIN3
8733#endif // TPETRA_MIN3
8734 bool added_entry = false;
8735
8736 if (PID1 == MIN_PID && GID1 == MIN_GID) {
8737 userExportLIDs[iloc] = ELID1[i1];
8738 userExportPIDs[iloc] = EPID1[i1];
8739 i1++;
8740 added_entry = true;
8741 iloc++;
8742 }
8743 if (PID2 == MIN_PID && GID2 == MIN_GID) {
8744 if (!added_entry) {
8745 userExportLIDs[iloc] = ELID2[i2];
8746 userExportPIDs[iloc] = EPID2[i2];
8747 added_entry = true;
8748 iloc++;
8749 }
8750 i2++;
8751 }
8752 if (PID3 == MIN_PID && GID3 == MIN_GID) {
8753 if (!added_entry) {
8754 userExportLIDs[iloc] = ELID3[i3];
8755 userExportPIDs[iloc] = EPID3[i3];
8756 iloc++;
8757 }
8758 i3++;
8759 }
8760 }
8761
8762 if (verbose) {
8763 std::ostringstream os;
8764 os << *verbosePrefix << "Create Import" << std::endl;
8765 std::cerr << os.str();
8766 }
8767
8768 Tpetra::Details::ProfilingRegion ismmIctor("Tpetra isMMIportCtor");
8769 Teuchos::RCP<Teuchos::ParameterList> plist = rcp(new Teuchos::ParameterList());
8770 // 25 Jul 2018: Test for equality with the non-isMM path's Import object.
8771 if ((MyDomainMap != MyColMap) && (!MyDomainMap->isSameAs(*MyColMap)))
8772 MyImport = rcp(new import_type(MyDomainMap,
8773 MyColMap,
8774 RemotePids,
8775 userExportLIDs.view(0, iloc).getConst(),
8776 userExportPIDs.view(0, iloc).getConst(),
8777 plist));
8778
8779 if (verbose) {
8780 std::ostringstream os;
8781 os << *verbosePrefix << "Call expertStaticFillComplete" << std::endl;
8782 std::cerr << os.str();
8783 }
8784
8785 {
8786 Tpetra::Details::ProfilingRegion esfc("Tpetra isMM::destMat->eSFC");
8787 if (!params.is_null())
8788 esfc_params.set("compute global constants", params->get("compute global constants", true));
8789 destMat->expertStaticFillComplete(MyDomainMap, MyRangeMap, MyImport, Teuchos::null, rcp(new Teuchos::ParameterList(esfc_params)));
8790 }
8791
8792 } // if(isMM)
8793 else {
8794 Tpetra::Details::ProfilingRegion MMnotMMblock("Tpetra TAFC notMMblock");
8795 if (verbose) {
8796 std::ostringstream os;
8797 os << *verbosePrefix << "Create Import" << std::endl;
8798 std::cerr << os.str();
8799 }
8800
8801 Tpetra::Details::ProfilingRegion notMMIcTor("Tpetra TAFC notMMCreateImporter");
8802 if ((MyDomainMap != MyColMap) && (!MyDomainMap->isSameAs(*MyColMap)))
8803 MyImport = rcp(new import_type(MyDomainMap, MyColMap, RemotePids, Teuchos::null));
8804
8805 if (verbose) {
8806 std::ostringstream os;
8807 os << *verbosePrefix << "Call expertStaticFillComplete" << endl;
8808 std::cerr << os.str();
8809 }
8810
8811 Tpetra::Details::ProfilingRegion esfcnotmm("Tpetra notMMdestMat->expertStaticFillComplete");
8812
8813 if (!params.is_null()) {
8814 esfc_params.set("compute global constants",
8815 params->get("compute global constants", true));
8816 }
8817 destMat->expertStaticFillComplete(MyDomainMap, MyRangeMap,
8818 MyImport, Teuchos::null,
8819 rcp(new Teuchos::ParameterList(esfc_params)));
8820 }
8821
8822 tmIESFC = Teuchos::null;
8823
8824 if (verbose) {
8825 std::ostringstream os;
8826 os << *verbosePrefix << "Done" << endl;
8827 std::cerr << os.str();
8828 }
8829} // transferAndFillComplete
8830
8831template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
8833 importAndFillComplete(Teuchos::RCP<CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& destMatrix,
8834 const import_type& importer,
8835 const Teuchos::RCP<const map_type>& domainMap,
8836 const Teuchos::RCP<const map_type>& rangeMap,
8837 const Teuchos::RCP<Teuchos::ParameterList>& params) const {
8838 transferAndFillComplete(destMatrix, importer, Teuchos::null, domainMap, rangeMap, params);
8839}
8840
8841template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
8843 importAndFillComplete(Teuchos::RCP<CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& destMatrix,
8844 const import_type& rowImporter,
8845 const import_type& domainImporter,
8846 const Teuchos::RCP<const map_type>& domainMap,
8847 const Teuchos::RCP<const map_type>& rangeMap,
8848 const Teuchos::RCP<Teuchos::ParameterList>& params) const {
8849 transferAndFillComplete(destMatrix, rowImporter, Teuchos::rcpFromRef(domainImporter), domainMap, rangeMap, params);
8850}
8851
8852template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
8854 exportAndFillComplete(Teuchos::RCP<CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& destMatrix,
8855 const export_type& exporter,
8856 const Teuchos::RCP<const map_type>& domainMap,
8857 const Teuchos::RCP<const map_type>& rangeMap,
8858 const Teuchos::RCP<Teuchos::ParameterList>& params) const {
8859 transferAndFillComplete(destMatrix, exporter, Teuchos::null, domainMap, rangeMap, params);
8860}
8861
8862template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
8864 exportAndFillComplete(Teuchos::RCP<CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& destMatrix,
8865 const export_type& rowExporter,
8866 const export_type& domainExporter,
8867 const Teuchos::RCP<const map_type>& domainMap,
8868 const Teuchos::RCP<const map_type>& rangeMap,
8869 const Teuchos::RCP<Teuchos::ParameterList>& params) const {
8870 transferAndFillComplete(destMatrix, rowExporter, Teuchos::rcpFromRef(domainExporter), domainMap, rangeMap, params);
8871}
8872
8873template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
8874void copyAndPermuteStaticGraphNew(
8877 const size_t numSameIDs,
8878 const LocalOrdinal permuteToLIDs[],
8879 const LocalOrdinal permuteFromLIDs[],
8880 const size_t numPermutes) {
8882 using std::endl;
8883 using Teuchos::Array;
8884 using LO = LocalOrdinal;
8885 using GO = GlobalOrdinal;
8886
8887 using impl_scalar_type = typename KokkosKernels::ArithTraits<Scalar>::val_type;
8888
8890
8891 typedef typename crs_matrix_type::local_inds_device_view_type::non_const_value_type local_inds_device_value_t;
8892
8893 typedef typename Node::execution_space exec_space;
8894 typedef Kokkos::RangePolicy<exec_space, LO> range_type;
8895
8896 const LocalOrdinal LINV = Teuchos::OrdinalTraits<LocalOrdinal>::invalid();
8897
8898 ProfilingRegion regionCAP("Tpetra::CrsMatrix::copyAndPermuteStaticGraphNew");
8899
8900 const crs_matrix_type* srcMatCrsPtr = dynamic_cast<const crs_matrix_type*>(&srcMat);
8901 TEUCHOS_TEST_FOR_EXCEPTION(srcMatCrsPtr == nullptr, std::runtime_error, "bad srcMatCrsPtr");
8902 const crs_matrix_type& srcMatCrs = *srcMatCrsPtr;
8903
8904 crs_matrix_type* tgtMatCrsPtr = dynamic_cast<crs_matrix_type*>(&tgtMat);
8905 TEUCHOS_TEST_FOR_EXCEPTION(tgtMatCrsPtr == nullptr, std::runtime_error, "bad tgtMatCrsPtr");
8906 crs_matrix_type& tgtMatCrs = *tgtMatCrsPtr;
8907
8908 const bool sourceIsLocallyIndexed = srcMat.isLocallyIndexed();
8909
8910 //
8911 // Copy the first numSame row from source to target (this matrix).
8912 // This involves copying rows corresponding to LIDs [0, numSame-1].
8913 //
8914 const auto& srcRowMap = *(srcMat.getRowMap());
8915 auto comm = srcRowMap.getComm();
8916
8917 const LO numSameIDs_as_LID = static_cast<LO>(numSameIDs);
8918
8919 auto my_replaceGlobalValuesImpl_scalar = KOKKOS_LAMBDA(
8920 const bool sorted, const bool atomic, size_t hint[],
8921 const size_t numInTgtRow, const local_inds_device_value_t tgtColInds[], impl_scalar_type tgtRowVals[],
8922 const local_inds_device_value_t lclColInd, const impl_scalar_type newVals)
8923 ->LO {
8924 LO numValid = 0; // number of valid input column indices
8925
8926 if (atomic) {
8927 if (lclColInd != LINV) {
8928 const size_t offset = KokkosSparse::findRelOffset(tgtColInds, numInTgtRow, lclColInd, hint[0], sorted);
8929 if (offset != numInTgtRow) {
8930 Kokkos::atomic_store(&tgtRowVals[offset], newVals);
8931 hint[0] = offset + 1;
8932 numValid++;
8933 }
8934 }
8935 } else {
8936 if (lclColInd != LINV) {
8937 const size_t offset = KokkosSparse::findRelOffset(tgtColInds, numInTgtRow, lclColInd, hint[0], sorted);
8938 if (offset != numInTgtRow) {
8939 tgtRowVals[offset] = newVals;
8940 hint[0] = offset + 1;
8941 numValid++;
8942 }
8943 }
8944 }
8945 return numValid;
8946 };
8947
8948 if (sourceIsLocallyIndexed) {
8949 typename crs_matrix_type::row_ptrs_device_view_type tgtLocalRowPtrsDevice = tgtMatCrs.getLocalRowPtrsDevice();
8950 typename crs_matrix_type::local_inds_device_view_type tgtLocalColIndsDevice = tgtMatCrs.getLocalIndicesDevice();
8951 typename crs_matrix_type::row_ptrs_host_view_type srcLocalRowPtrsHost = srcMatCrs.getLocalRowPtrsHost();
8952 typename crs_matrix_type::row_ptrs_device_view_type srcLocalRowPtrsDevice = srcMatCrs.getLocalRowPtrsDevice();
8953 typename crs_matrix_type::local_inds_device_view_type srcLocalColIndsDevice = srcMatCrs.getLocalIndicesDevice();
8954
8955 bool tgtMatIsSorted = tgtMatCrs.getCrsGraph()->isSorted();
8956
8957 using local_map_type = typename crs_matrix_type::map_type::local_map_type;
8958
8959 local_map_type local_map = srcMat.getRowMap()->getLocalMap();
8960 local_map_type local_col_map = srcMat.getColMap()->getLocalMap();
8961 local_map_type tgt_local_map = tgtMatCrs.getRowMap()->getLocalMap();
8962 local_map_type tgt_local_col_map = tgtMatCrs.getColMap()->getLocalMap();
8963
8964 auto vals = srcMatCrs.getLocalValuesDevice(Access::ReadOnly);
8965 auto tvals = tgtMatCrs.getLocalValuesDevice(Access::ReadWrite);
8966
8967 Kokkos::parallel_for(
8968 "Tpetra_CrsMatrix::copyAndPermuteStaticGraph",
8969 range_type(0, numSameIDs_as_LID),
8970 KOKKOS_LAMBDA(const LO sourceLID) {
8971 local_inds_device_value_t start = srcLocalRowPtrsDevice(sourceLID);
8972 local_inds_device_value_t end = srcLocalRowPtrsDevice(sourceLID + 1);
8973 local_inds_device_value_t rowLength = (end - start);
8974
8975 local_inds_device_value_t tstart = tgtLocalRowPtrsDevice(sourceLID);
8976 local_inds_device_value_t tend = tgtLocalRowPtrsDevice(sourceLID + 1);
8977 local_inds_device_value_t numInTgtRow = (tend - tstart);
8978
8979 KOKKOS_ASSERT(static_cast<size_t>(tstart) < tvals.extent(0));
8980 impl_scalar_type* tgtRowVals = reinterpret_cast<impl_scalar_type*>(&tvals(tstart));
8981 const local_inds_device_value_t* tgtColInds = &tgtLocalColIndsDevice(tstart);
8982
8983 size_t hint = 0;
8984 for (LO j = 0; j < rowLength; j++) {
8985 local_inds_device_value_t ci = srcLocalColIndsDevice(start + j);
8986 GO gi = local_col_map.getGlobalElement(ci);
8987 const local_inds_device_value_t lclColInd = tgt_local_col_map.getLocalElement(gi);
8988 my_replaceGlobalValuesImpl_scalar(
8989 tgtMatIsSorted, false, &hint, numInTgtRow, tgtColInds, tgtRowVals, lclColInd, vals(start + j));
8990 }
8991 }); // kokkos parallel_for
8992 } else {
8993 for (LO sourceLID = 0; sourceLID < numSameIDs_as_LID; ++sourceLID) {
8994 // Global ID for the current row index in the source matrix.
8995 // The first numSameIDs GIDs in the two input lists are the
8996 // same, so sourceGID == targetGID in this case.
8997 const GO sourceGID = srcRowMap.getGlobalElement(sourceLID);
8998 const GO targetGID = sourceGID;
8999
9000 Teuchos::ArrayView<const GO> rowIndsConstView;
9001 Teuchos::ArrayView<const Scalar> rowValsConstView;
9002
9003 typename crs_matrix_type::global_inds_host_view_type rowIndsView;
9004 typename crs_matrix_type::values_host_view_type rowValsView;
9005 srcMat.getGlobalRowView(sourceGID, rowIndsView, rowValsView);
9006 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
9007 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
9008 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
9009 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
9010 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
9011 rowIndsView.data(), rowIndsView.extent(0), Teuchos::RCP_DISABLE_NODE_LOOKUP);
9012 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
9013 reinterpret_cast<const Scalar*>(rowValsView.data()),
9014 rowValsView.extent(0),
9015 Teuchos::RCP_DISABLE_NODE_LOOKUP);
9016 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
9017 // KDDKDD UVM TEMPORARY: KokkosView interface
9018
9019 // Applying a permutation to a matrix with a static graph
9020 // means REPLACE-ing entries.
9021 // FIXME - need to apply the same approach as above, maybe reuse my_replaceGlobalValuesImpl_scalar?
9022 tgtMatCrs.replaceGlobalValues(targetGID, rowIndsConstView, rowValsConstView);
9023 }
9024 }
9025
9026 // FIXME - need to apply the same approach as above to the permutes, see #14708
9027 //
9028 // "Permute" part of "copy and permute."
9029 //
9030 typename crs_matrix_type::nonconst_global_inds_host_view_type rowInds;
9031 typename crs_matrix_type::nonconst_values_host_view_type rowVals;
9032
9033 const auto& tgtRowMap = *(tgtMat.getRowMap());
9034 for (size_t p = 0; p < numPermutes; ++p) {
9035 const GO sourceGID = srcRowMap.getGlobalElement(permuteFromLIDs[p]);
9036 const GO targetGID = tgtRowMap.getGlobalElement(permuteToLIDs[p]);
9037
9038 Teuchos::ArrayView<const GO> rowIndsConstView;
9039 Teuchos::ArrayView<const Scalar> rowValsConstView;
9040
9041 if (sourceIsLocallyIndexed) {
9042 const size_t rowLength = srcMat.getNumEntriesInGlobalRow(sourceGID);
9043 if (rowLength > static_cast<size_t>(rowInds.size())) {
9044 Kokkos::resize(rowInds, rowLength);
9045 Kokkos::resize(rowVals, rowLength);
9046 }
9047 // Resizing invalidates an Array's views, so we must make new
9048 // ones, even if rowLength hasn't changed.
9049 typename crs_matrix_type::nonconst_global_inds_host_view_type rowIndsView = Kokkos::subview(
9050 rowInds, std::make_pair((size_t)0, rowLength));
9051 typename crs_matrix_type::nonconst_values_host_view_type rowValsView = Kokkos::subview(
9052 rowVals, std::make_pair((size_t)0, rowLength));
9053
9054 // The source matrix is locally indexed, so we have to get a
9055 // copy. Really it's the GIDs that have to be copied (because
9056 // they have to be converted from LIDs).
9057 size_t checkRowLength = 0;
9058 srcMat.getGlobalRowCopy(sourceGID, rowIndsView, rowValsView, checkRowLength);
9059
9060 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
9061 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
9062 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
9063 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
9064 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
9065 rowIndsView.data(), rowIndsView.extent(0), Teuchos::RCP_DISABLE_NODE_LOOKUP);
9066 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
9067 reinterpret_cast<const Scalar*>(rowValsView.data()),
9068 rowValsView.extent(0),
9069 Teuchos::RCP_DISABLE_NODE_LOOKUP);
9070 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
9071 // KDDKDD UVM TEMPORARY: KokkosView interface
9072 } else {
9073 typename crs_matrix_type::global_inds_host_view_type rowIndsView;
9074 typename crs_matrix_type::values_host_view_type rowValsView;
9075 srcMat.getGlobalRowView(sourceGID, rowIndsView, rowValsView);
9076 // KDDKDD UVM TEMPORARY: refactor combineGlobalValues to take
9077 // KDDKDD UVM TEMPORARY: Kokkos::View instead of ArrayView
9078 // KDDKDD UVM TEMPORARY: For now, wrap the view in ArrayViews
9079 // KDDKDD UVM TEMPORARY: Should be safe because we hold the KokkosViews
9080 rowIndsConstView = Teuchos::ArrayView<const GO>( // BAD BAD BAD
9081 rowIndsView.data(), rowIndsView.extent(0), Teuchos::RCP_DISABLE_NODE_LOOKUP);
9082 rowValsConstView = Teuchos::ArrayView<const Scalar>( // BAD BAD BAD
9083 reinterpret_cast<const Scalar*>(rowValsView.data()),
9084 rowValsView.extent(0),
9085 Teuchos::RCP_DISABLE_NODE_LOOKUP);
9086 // KDDKDD UVM TEMPORARY: Add replace, sum, transform methods with
9087 // KDDKDD UVM TEMPORARY: KokkosView interface
9088 }
9089
9090 tgtMatCrs.replaceGlobalValues(targetGID, rowIndsConstView, rowValsConstView);
9091 }
9092}
9093
9094} // namespace Tpetra
9095
9096//
9097// Explicit instantiation macro
9098//
9099// Must be expanded from within the Tpetra namespace!
9100//
9101
9102#define TPETRA_CRSMATRIX_MATRIX_INSTANT(SCALAR, LO, GO, NODE) \
9103 \
9104 template class CrsMatrix<SCALAR, LO, GO, NODE>;
9105
9106#define TPETRA_CRSMATRIX_CONVERT_INSTANT(SO, SI, LO, GO, NODE) \
9107 \
9108 template Teuchos::RCP<CrsMatrix<SO, LO, GO, NODE>> \
9109 CrsMatrix<SI, LO, GO, NODE>::convert<SO>() const;
9110
9111#define TPETRA_CRSMATRIX_IMPORT_AND_FILL_COMPLETE_INSTANT(SCALAR, LO, GO, NODE) \
9112 template <> \
9113 Teuchos::RCP<CrsMatrix<SCALAR, LO, GO, NODE>> \
9114 importAndFillCompleteCrsMatrix(const Teuchos::RCP<const CrsMatrix<SCALAR, LO, GO, NODE>>& sourceMatrix, \
9115 const Import<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9116 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9117 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>& importer, \
9118 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9119 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9120 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& domainMap, \
9121 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9122 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9123 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& rangeMap, \
9124 const Teuchos::RCP<Teuchos::ParameterList>& params);
9125
9126#define TPETRA_CRSMATRIX_IMPORT_AND_FILL_COMPLETE_INSTANT_TWO(SCALAR, LO, GO, NODE) \
9127 template <> \
9128 Teuchos::RCP<CrsMatrix<SCALAR, LO, GO, NODE>> \
9129 importAndFillCompleteCrsMatrix(const Teuchos::RCP<const CrsMatrix<SCALAR, LO, GO, NODE>>& sourceMatrix, \
9130 const Import<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9131 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9132 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>& rowImporter, \
9133 const Import<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9134 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9135 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>& domainImporter, \
9136 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9137 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9138 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& domainMap, \
9139 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9140 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9141 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& rangeMap, \
9142 const Teuchos::RCP<Teuchos::ParameterList>& params);
9143
9144#define TPETRA_CRSMATRIX_EXPORT_AND_FILL_COMPLETE_INSTANT(SCALAR, LO, GO, NODE) \
9145 template <> \
9146 Teuchos::RCP<CrsMatrix<SCALAR, LO, GO, NODE>> \
9147 exportAndFillCompleteCrsMatrix(const Teuchos::RCP<const CrsMatrix<SCALAR, LO, GO, NODE>>& sourceMatrix, \
9148 const Export<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9149 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9150 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>& exporter, \
9151 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9152 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9153 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& domainMap, \
9154 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9155 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9156 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& rangeMap, \
9157 const Teuchos::RCP<Teuchos::ParameterList>& params);
9158
9159#define TPETRA_CRSMATRIX_EXPORT_AND_FILL_COMPLETE_INSTANT_TWO(SCALAR, LO, GO, NODE) \
9160 template <> \
9161 Teuchos::RCP<CrsMatrix<SCALAR, LO, GO, NODE>> \
9162 exportAndFillCompleteCrsMatrix(const Teuchos::RCP<const CrsMatrix<SCALAR, LO, GO, NODE>>& sourceMatrix, \
9163 const Export<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9164 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9165 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>& rowExporter, \
9166 const Export<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9167 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9168 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>& domainExporter, \
9169 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9170 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9171 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& domainMap, \
9172 const Teuchos::RCP<const Map<CrsMatrix<SCALAR, LO, GO, NODE>::local_ordinal_type, \
9173 CrsMatrix<SCALAR, LO, GO, NODE>::global_ordinal_type, \
9174 CrsMatrix<SCALAR, LO, GO, NODE>::node_type>>& rangeMap, \
9175 const Teuchos::RCP<Teuchos::ParameterList>& params);
9176
9177#define TPETRA_CRSMATRIX_INSTANT(SCALAR, LO, GO, NODE) \
9178 TPETRA_CRSMATRIX_MATRIX_INSTANT(SCALAR, LO, GO, NODE) \
9179 TPETRA_CRSMATRIX_IMPORT_AND_FILL_COMPLETE_INSTANT(SCALAR, LO, GO, NODE) \
9180 TPETRA_CRSMATRIX_EXPORT_AND_FILL_COMPLETE_INSTANT(SCALAR, LO, GO, NODE) \
9181 TPETRA_CRSMATRIX_IMPORT_AND_FILL_COMPLETE_INSTANT_TWO(SCALAR, LO, GO, NODE) \
9182 TPETRA_CRSMATRIX_EXPORT_AND_FILL_COMPLETE_INSTANT_TWO(SCALAR, LO, GO, NODE)
9183
9184#endif // TPETRA_CRSMATRIX_DEF_HPP
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra's behavior.
Declaration of Tpetra::Details::EquilibrationInfo.
Declaration of Tpetra::Details::Profiling, a scope guard for Kokkos Profiling.
Declaration and generic definition of traits class that tells Tpetra::CrsMatrix how to pack and unpac...
Declaration and definition of Tpetra::Details::castAwayConstDualView, an implementation detail of Tpe...
Declare and define the functions Tpetra::Details::computeOffsetsFromCounts and Tpetra::computeOffsets...
Declare and define Tpetra::Details::copyConvert, an implementation detail of Tpetra (in particular,...
Declare and define Tpetra::Details::copyOffsets, an implementation detail of Tpetra (in particular,...
Functions that wrap Kokkos::create_mirror_view, in order to avoid deep copies when not necessary,...
Functions for manipulating CRS arrays.
Declaration of a function that prints strings from each process.
Declaration and definition of Tpetra::Details::getEntryOnHost.
Declaration of Tpetra::Details::iallreduce.
Declaration and definition of Tpetra::Details::leftScaleLocalCrsMatrix.
KOKKOS_FUNCTION size_t packRow(const LocalMapType &col_map, const Kokkos::View< Packet *, BufferDeviceType > &exports, const InputLidsType &lids_in, const InputPidsType &pids_in, const size_t offset, const size_t num_ent, const bool pack_pids)
Packs a single row of the CrsGraph.
Declaration and definition of Tpetra::Details::rightScaleLocalCrsMatrix.
KOKKOS_FUNCTION int unpackRow(const Kokkos::View< GO *, Device, Kokkos::MemoryUnmanaged > &gids_out, const Kokkos::View< int *, Device, Kokkos::MemoryUnmanaged > &pids_out, const Kokkos::View< const Packet *, BufferDevice > &imports, const size_t offset, const size_t num_ent)
Unpack a single row of a CrsGraph.
Utility functions for packing and unpacking sparse matrix entries.
void sortAndMergeCrsEntries(const Teuchos::ArrayView< size_t > &CRS_rowptr, const Teuchos::ArrayView< Ordinal > &CRS_colind, const Teuchos::ArrayView< Scalar > &CRS_vals)
Sort and merge the entries of the (raw CSR) matrix by column index within each row.
void lowCommunicationMakeColMapAndReindex(const Teuchos::ArrayView< const size_t > &rowptr, const Teuchos::ArrayView< LocalOrdinal > &colind_LID, const Teuchos::ArrayView< GlobalOrdinal > &colind_GID, const Teuchos::RCP< const Tpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > &domainMapRCP, const Teuchos::ArrayView< const int > &owningPIDs, Teuchos::Array< int > &remotePIDs, Teuchos::RCP< const Tpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > &colMap, const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
lowCommunicationMakeColMapAndReindex
void sortCrsEntries(const Teuchos::ArrayView< size_t > &CRS_rowptr, const Teuchos::ArrayView< Ordinal > &CRS_colind, const Teuchos::ArrayView< Scalar > &CRS_vals)
Sort the entries of the (raw CSR) matrix by column index within each row.
Internal functions and macros designed for use with Tpetra::Import and Tpetra::Export objects.
void getPids(const Tpetra::Import< LocalOrdinal, GlobalOrdinal, Node > &Importer, Teuchos::Array< int > &pids, bool use_minus_one_for_local)
Like getPidGidPairs, but just gets the PIDs, ordered by the column Map.
#define TPETRA_ABUSE_WARNING(throw_exception_test, Exception, msg)
Handle an abuse warning, according to HAVE_TPETRA_THROW_ABUSE_WARNINGS and HAVE_TPETRA_PRINT_ABUSE_WA...
Declaration of Tpetra::computeRowAndColumnOneNorms.
A distributed graph accessed by rows (adjacency lists) and stored sparsely.
void reindexColumns(const Teuchos::RCP< const map_type > &newColMap, const Teuchos::RCP< const import_type > &newImport=Teuchos::null, const bool sortIndicesInEachRow=true)
Reindex the column indices in place, and replace the column Map. Optionally, replace the Import objec...
Kokkos::View< size_t *, Kokkos::LayoutLeft, device_type >::host_mirror_type num_row_entries_type
Row offsets for "1-D" storage.
global_inds_dualv_type::t_host::const_type getGlobalIndsViewHost(const RowInfo &rowinfo) const
Get a const, globally indexed view of the locally owned row myRow, such that rowinfo = getRowInfo(myR...
size_t getNumEntriesInLocalRow(local_ordinal_type localRow) const override
Get the number of entries in the given row (local index).
local_inds_wdv_type lclIndsUnpacked_wdv
Local ordinals of column indices for all rows Valid when isLocallyIndexed is true If OptimizedStorage...
RowInfo getRowInfoFromGlobalRowIndex(const global_ordinal_type gblRow) const
Get information about the locally owned row with global index gblRow.
size_t findGlobalIndices(const RowInfo &rowInfo, const Teuchos::ArrayView< const global_ordinal_type > &indices, std::function< void(const size_t, const size_t, const size_t)> fun) const
Finds indices in the given row.
num_row_entries_type k_numRowEntries_
The number of local entries in each locally owned row.
Teuchos::RCP< const map_type > getDomainMap() const override
Returns the Map associated with the domain of this graph.
RowInfo getRowInfo(const local_ordinal_type myRow) const
Get information about the locally owned row with local index myRow.
Teuchos::RCP< const map_type > colMap_
The Map describing the distribution of columns of the graph.
bool noRedundancies_
Whether the graph's indices are non-redundant (merged) in each row, on this process.
bool isSorted() const
Whether graph indices in all rows are known to be sorted.
bool isFillComplete() const override
Whether fillComplete() has been called and the graph is in compute mode.
Teuchos::RCP< const map_type > getRangeMap() const override
Returns the Map associated with the domain of this graph.
local_inds_dualv_type::t_host::const_type getLocalIndsViewHost(const RowInfo &rowinfo) const
Get a const, locally indexed view of the locally owned row myRow, such that rowinfo = getRowInfo(myRo...
Teuchos::RCP< const map_type > getRowMap() const override
Returns the Map that describes the row distribution in this graph.
size_t insertGlobalIndicesImpl(const local_ordinal_type lclRow, const global_ordinal_type inputGblColInds[], const size_t numInputInds)
Insert global indices, using an input local row index.
bool indicesAreSorted_
Whether the graph's indices are sorted in each row, on this process.
local_inds_dualv_type::t_host getLocalIndsViewHostNonConst(const RowInfo &rowinfo)
Get a ReadWrite locally indexed view of the locally owned row myRow, such that rowinfo = getRowInfo(m...
Teuchos::RCP< const map_type > rowMap_
The Map describing the distribution of rows of the graph.
bool isGloballyIndexed() const override
Whether the graph's column indices are stored as global indices.
bool isLocallyIndexed() const override
Whether the graph's column indices are stored as local indices.
size_t getLocalNumRows() const override
Returns the number of graph rows owned on the calling node.
Sparse matrix that presents a row-oriented interface that lets users read or modify entries.
virtual void insertGlobalValuesImpl(crs_graph_type &graph, RowInfo &rowInfo, const GlobalOrdinal gblColInds[], const impl_scalar_type vals[], const size_t numInputEnt)
Common implementation detail of insertGlobalValues and insertGlobalValuesFiltered.
bool isGloballyIndexed() const override
Whether the matrix is globally indexed on the calling process.
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const override
Print this object with the given verbosity level to the given output stream.
std::map< GlobalOrdinal, std::pair< Teuchos::Array< GlobalOrdinal >, Teuchos::Array< Scalar > > > nonlocals_
Nonlocal data added using insertGlobalValues().
void localApply(const MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &X, MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Y, const Teuchos::ETransp mode=Teuchos::NO_TRANS, const Scalar &alpha=Teuchos::ScalarTraits< Scalar >::one(), const Scalar &beta=Teuchos::ScalarTraits< Scalar >::zero()) const
Compute the local part of a sparse matrix-(Multi)Vector multiply.
void unpackAndCombine(const Kokkos::DualView< const local_ordinal_type *, buffer_device_type > &importLIDs, Kokkos::DualView< char *, buffer_device_type > imports, Kokkos::DualView< size_t *, buffer_device_type > numPacketsPerLID, const size_t constantNumPackets, const CombineMode CM) override
Unpack the imported column indices and values, and combine into matrix.
void replaceRangeMap(const Teuchos::RCP< const map_type > &newRangeMap)
Replace the current range Map with the given objects.
Details::EStorageStatus storageStatus_
Status of the matrix's storage, when not in a fill-complete state.
void applyNonTranspose(const MV &X_in, MV &Y_in, Scalar alpha, Scalar beta) const
void importAndFillComplete(Teuchos::RCP< CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &destMatrix, const import_type &importer, const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null) const
Import from this to the given destination matrix, and make the result fill complete.
CrsGraph< LocalOrdinal, GlobalOrdinal, Node > crs_graph_type
The CrsGraph specialization suitable for this CrsMatrix specialization.
local_ordinal_type replaceGlobalValues(const global_ordinal_type globalRow, const Kokkos::View< const global_ordinal_type *, Kokkos::AnonymousSpace > &inputInds, const Kokkos::View< const impl_scalar_type *, Kokkos::AnonymousSpace > &inputVals)
Replace one or more entries' values, using global indices.
bool haveGlobalConstants() const
Returns true if globalConstants have been computed; false otherwise.
size_t getGlobalMaxNumRowEntries() const override
Maximum number of entries in any row of the matrix, over all processes in the matrix's communicator.
void getGlobalRowCopy(GlobalOrdinal GlobalRow, nonconst_global_inds_host_view_type &Indices, nonconst_values_host_view_type &Values, size_t &NumEntries) const override
Fill given arrays with a deep copy of the locally owned entries of the matrix in a given row,...
size_t getNumEntriesInGlobalRow(GlobalOrdinal globalRow) const override
Number of entries in the sparse matrix in the given global row, on the calling (MPI) process.
void scale(const Scalar &alpha)
Scale the matrix's values: this := alpha*this.
GlobalOrdinal global_ordinal_type
The type of each global index in the matrix.
void sortAndMergeIndicesAndValues(const bool sorted, const bool merged)
Sort and merge duplicate local column indices in all rows on the calling process, along with their co...
void packNew(const Kokkos::DualView< const local_ordinal_type *, buffer_device_type > &exportLIDs, Kokkos::DualView< char *, buffer_device_type > &exports, const Kokkos::DualView< size_t *, buffer_device_type > &numPacketsPerLID, size_t &constantNumPackets) const
Pack this object's data for an Import or Export.
size_t getLocalNumCols() const override
The number of columns connected to the locally owned rows of this matrix.
Teuchos::RCP< const map_type > getDomainMap() const override
The domain Map of this matrix.
bool hasColMap() const override
Whether the matrix has a well-defined column Map.
mag_type getNormInf() const
Compute and return the infinity norm of the matrix.
Teuchos::RCP< CrsMatrix< T, LocalOrdinal, GlobalOrdinal, Node > > convert() const
Return another CrsMatrix with the same entries, but converted to a different Scalar type T.
values_dualv_type::t_dev getValuesViewDeviceNonConst(const RowInfo &rowinfo)
Get a non-const Device view of the locally owned values row myRow, such that rowinfo = getRowInfo(myR...
void expertStaticFillComplete(const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const Teuchos::RCP< const import_type > &importer=Teuchos::null, const Teuchos::RCP< const export_type > &exporter=Teuchos::null, const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Perform a fillComplete on a matrix that already has data.
local_ordinal_type sumIntoLocalValues(const local_ordinal_type localRow, const Kokkos::View< const local_ordinal_type *, Kokkos::AnonymousSpace > &inputInds, const Kokkos::View< const impl_scalar_type *, Kokkos::AnonymousSpace > &inputVals, const bool atomic=useAtomicUpdatesByDefault)
Sum into one or more sparse matrix entries, using local row and column indices.
virtual Teuchos::RCP< RowMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > add(const Scalar &alpha, const RowMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, const Scalar &beta, const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &domainMap, const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &rangeMap, const Teuchos::RCP< Teuchos::ParameterList > &params) const override
Implementation of RowMatrix::add: return alpha*A + beta*this.
void applyTranspose(const MV &X_in, MV &Y_in, const Teuchos::ETransp mode, Scalar alpha, Scalar beta) const
size_t getNumEntriesInLocalRow(local_ordinal_type localRow) const override
Number of entries in the sparse matrix in the given local row, on the calling (MPI) process.
Teuchos::RCP< MV > exportMV_
Row Map MultiVector used in apply().
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const override
The communicator over which the matrix is distributed.
bool isFillActive() const
Whether the matrix is not fill complete.
RowMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > row_matrix_type
The RowMatrix representing the base class of CrsMatrix.
void replaceDomainMapAndImporter(const Teuchos::RCP< const map_type > &newDomainMap, Teuchos::RCP< const import_type > &newImporter)
Replace the current domain Map and Import with the given objects.
LocalOrdinal sumIntoGlobalValues(const GlobalOrdinal globalRow, const Teuchos::ArrayView< const GlobalOrdinal > &cols, const Teuchos::ArrayView< const Scalar > &vals, const bool atomic=useAtomicUpdatesByDefault)
Sum into one or more sparse matrix entries, using global indices.
mag_type getNorm1(bool assumeSymmetric=false) const
Compute and return the 1-norm of the matrix.
void apply(const MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &X, MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Y, Teuchos::ETransp mode=Teuchos::NO_TRANS, Scalar alpha=Teuchos::ScalarTraits< Scalar >::one(), Scalar beta=Teuchos::ScalarTraits< Scalar >::zero()) const override
Compute a sparse matrix-MultiVector multiply.
mag_type getFrobeniusNorm() const override
Compute and return the Frobenius norm of the matrix.
void insertLocalValues(const LocalOrdinal localRow, const Teuchos::ArrayView< const LocalOrdinal > &cols, const Teuchos::ArrayView< const Scalar > &vals, const CombineMode CM=ADD)
Insert one or more entries into the matrix, using local column indices.
global_size_t getGlobalNumCols() const override
The number of global columns in the matrix.
Teuchos::RCP< const map_type > getRangeMap() const override
The range Map of this matrix.
Teuchos::RCP< MV > importMV_
Column Map MultiVector used in apply().
void allocateValues(ELocalGlobal lg, GraphAllocationStatus gas, const bool verbose)
Allocate values (and optionally indices) using the Node.
size_t getLocalNumEntries() const override
The local number of entries in this matrix.
Map< LocalOrdinal, GlobalOrdinal, Node > map_type
The Map specialization suitable for this CrsMatrix specialization.
typename KokkosKernels::ArithTraits< impl_scalar_type >::mag_type mag_type
Type of a norm result.
bool fillComplete_
Whether the matrix is fill complete.
virtual LocalOrdinal sumIntoGlobalValuesImpl(impl_scalar_type rowVals[], const crs_graph_type &graph, const RowInfo &rowInfo, const GlobalOrdinal inds[], const impl_scalar_type newVals[], const LocalOrdinal numElts, const bool atomic=useAtomicUpdatesByDefault)
Implementation detail of sumIntoGlobalValues.
void replaceDomainMap(const Teuchos::RCP< const map_type > &newDomainMap)
Replace the current domain Map with the given objects.
std::string description() const override
A one-line description of this object.
void reindexColumns(crs_graph_type *const graph, const Teuchos::RCP< const map_type > &newColMap, const Teuchos::RCP< const import_type > &newImport=Teuchos::null, const bool sortEachRow=true)
Reindex the column indices in place, and replace the column Map. Optionally, replace the Import objec...
Teuchos::RCP< MV > getColumnMapMultiVector(const MV &X_domainMap, const bool force=false) const
Create a (or fetch a cached) column Map MultiVector.
void replaceRangeMapAndExporter(const Teuchos::RCP< const map_type > &newRangeMap, Teuchos::RCP< const export_type > &newExporter)
Replace the current Range Map and Export with the given objects.
size_t getLocalMaxNumRowEntries() const override
Maximum number of entries in any row of the matrix, on this process.
void replaceColMap(const Teuchos::RCP< const map_type > &newColMap)
Replace the matrix's column Map with the given Map.
global_size_t getGlobalNumRows() const override
Number of global elements in the row map of this matrix.
void globalAssemble()
Communicate nonlocal contributions to other processes.
void checkInternalState() const
Check that this object's state is sane; throw if it's not.
bool hasTransposeApply() const override
Whether apply() allows applying the transpose or conjugate transpose.
GlobalOrdinal getIndexBase() const override
The index base for global indices for this matrix.
Scalar scalar_type
The type of each entry in the matrix.
void getLocalDiagCopy(Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &diag) const override
Get a constant, nonpersisting view of a row of this matrix, using local row and column indices,...
void setAllToScalar(const Scalar &alpha)
Set all matrix entries equal to alpha.
void fillLocalGraphAndMatrix(const Teuchos::RCP< Teuchos::ParameterList > &params)
Fill data into the local graph and matrix.
Export< LocalOrdinal, GlobalOrdinal, Node > export_type
The Export specialization suitable for this CrsMatrix specialization.
TPETRA_DETAILS_ALWAYS_INLINE local_matrix_device_type getLocalMatrixDevice() const
The local sparse matrix.
void getLocalRowView(LocalOrdinal LocalRow, local_inds_host_view_type &indices, values_host_view_type &values) const override
Get a constant view of a row of this matrix, using local row and column indices.
Teuchos::RCP< const map_type > getColMap() const override
The Map that describes the column distribution in this matrix.
void insertGlobalValues(const GlobalOrdinal globalRow, const Teuchos::ArrayView< const GlobalOrdinal > &cols, const Teuchos::ArrayView< const Scalar > &vals)
Insert one or more entries into the matrix, using global column indices.
void fillComplete(const Teuchos::RCP< const map_type > &domainMap, const Teuchos::RCP< const map_type > &rangeMap, const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Tell the matrix that you are done changing its structure or values, and that you are ready to do comp...
void getGlobalRowView(GlobalOrdinal GlobalRow, global_inds_host_view_type &indices, values_host_view_type &values) const override
Get a constant, nonpersisting view of a row of this matrix, using global row and column indices.
void setAllValues(const typename local_graph_device_type::row_map_type &ptr, const typename local_graph_device_type::entries_type::non_const_type &ind, const typename local_matrix_device_type::values_type &val)
Set the local matrix using three (compressed sparse row) arrays.
Teuchos::RCP< const RowGraph< LocalOrdinal, GlobalOrdinal, Node > > getGraph() const override
This matrix's graph, as a RowGraph.
virtual void copyAndPermute(const SrcDistObject &source, const size_t numSameIDs, const Kokkos::DualView< const local_ordinal_type *, buffer_device_type > &permuteToLIDs, const Kokkos::DualView< const local_ordinal_type *, buffer_device_type > &permuteFromLIDs, const CombineMode CM) override
virtual void removeEmptyProcessesInPlace(const Teuchos::RCP< const map_type > &newMap) override
Remove processes owning zero rows from the Maps and their communicator.
virtual LocalOrdinal sumIntoLocalValuesImpl(impl_scalar_type rowVals[], const crs_graph_type &graph, const RowInfo &rowInfo, const LocalOrdinal inds[], const impl_scalar_type newVals[], const LocalOrdinal numElts, const bool atomic=useAtomicUpdatesByDefault)
Implementation detail of sumIntoLocalValues.
void swap(CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &matrix)
Swaps the data from *this with the data and maps from crsMatrix.
bool isStaticGraph() const
Indicates that the graph is static, so that new entries cannot be added to this matrix.
global_size_t getGlobalNumEntries() const override
The global number of entries in this matrix.
virtual LocalOrdinal replaceLocalValuesImpl(impl_scalar_type rowVals[], const crs_graph_type &graph, const RowInfo &rowInfo, const LocalOrdinal inds[], const impl_scalar_type newVals[], const LocalOrdinal numElts)
Implementation detail of replaceLocalValues.
KokkosSparse::CrsMatrix< impl_scalar_type, local_ordinal_type, device_type, void, typename local_graph_device_type::size_type > local_matrix_device_type
The specialization of Kokkos::CrsMatrix that represents the part of the sparse matrix on each MPI pro...
size_t getLocalNumRows() const override
The number of matrix rows owned by the calling process.
bool isFillComplete() const override
Whether the matrix is fill complete.
virtual bool checkSizes(const SrcDistObject &source) override
Compare the source and target (this) objects for compatibility.
Teuchos::RCP< const map_type > getRowMap() const override
The Map that describes the row distribution in this matrix.
local_ordinal_type replaceLocalValues(const local_ordinal_type localRow, const Kokkos::View< const local_ordinal_type *, Kokkos::AnonymousSpace > &inputInds, const Kokkos::View< const impl_scalar_type *, Kokkos::AnonymousSpace > &inputVals)
Replace one or more entries' values, using local row and column indices.
void exportAndFillComplete(Teuchos::RCP< CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &destMatrix, const export_type &exporter, const Teuchos::RCP< const map_type > &domainMap=Teuchos::null, const Teuchos::RCP< const map_type > &rangeMap=Teuchos::null, const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null) const
Export from this to the given destination matrix, and make the result fill complete.
values_dualv_type::t_host::const_type getValuesViewHost(const RowInfo &rowinfo) const
Get a const Host view of the locally owned values row myRow, such that rowinfo = getRowInfo(myRow).
bool isLocallyIndexed() const override
Whether the matrix is locally indexed on the calling process.
typename row_matrix_type::impl_scalar_type impl_scalar_type
The type used internally in place of Scalar.
Teuchos::RCP< MV > getRowMapMultiVector(const MV &Y_rangeMap, const bool force=false) const
Create a (or fetch a cached) row Map MultiVector.
local_matrix_device_type::values_type::const_type getLocalValuesDevice(Access::ReadOnlyStruct s) const
Get the Kokkos local values on device, read only.
virtual LocalOrdinal replaceGlobalValuesImpl(impl_scalar_type rowVals[], const crs_graph_type &graph, const RowInfo &rowInfo, const GlobalOrdinal inds[], const impl_scalar_type newVals[], const LocalOrdinal numElts)
Implementation detail of replaceGlobalValues.
values_dualv_type::t_host getValuesViewHostNonConst(const RowInfo &rowinfo)
Get a non-const Host view of the locally owned values row myRow, such that rowinfo = getRowInfo(myRow...
void resumeFill(const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Resume operations that may change the values or structure of the matrix.
void getLocalDiagOffsets(Teuchos::ArrayRCP< size_t > &offsets) const
Get offsets of the diagonal entries in the matrix.
void fillLocalMatrix(const Teuchos::RCP< Teuchos::ParameterList > &params)
Fill data into the local matrix.
void rightScale(const Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &x) override
Scale the matrix on the right with the given Vector.
bool isStorageOptimized() const
Returns true if storage has been optimized.
Import< LocalOrdinal, GlobalOrdinal, Node > import_type
The Import specialization suitable for this CrsMatrix specialization.
void getLocalRowCopy(LocalOrdinal LocalRow, nonconst_local_inds_host_view_type &Indices, nonconst_values_host_view_type &Values, size_t &NumEntries) const override
Fill given arrays with a deep copy of the locally owned entries of the matrix in a given row,...
values_dualv_type::t_dev::const_type getValuesViewDevice(const RowInfo &rowinfo) const
Get a const Device view of the locally owned values row myRow, such that rowinfo = getRowInfo(myRow).
void leftScale(const Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &x) override
Scale the matrix on the left with the given Vector.
virtual bool supportsRowViews() const override
Return true if getLocalRowView() and getGlobalRowView() are valid for this object.
Teuchos::RCP< const crs_graph_type > getCrsGraph() const
This matrix's graph, as a CrsGraph.
Description of Tpetra's behavior.
static bool useNewCopyAndPermute()
Use new implementation of copyAndPermute.
static bool debug()
Whether Tpetra is in debug mode.
static bool verbose()
Whether Tpetra is in verbose mode.
static size_t verbosePrintCountThreshold()
Number of entries below which arrays, lists, etc. will be printed in debug mode.
static size_t rowImbalanceThreshold()
Threshold for deciding if a local matrix is "imbalanced" in the number of entries per row....
void doExport(const SrcDistObject &source, const Export< LocalOrdinal, GlobalOrdinal, Node > &exporter, const CombineMode CM, const bool restrictedMode=false)
Export data into this object using an Export object ("forward mode").
virtual Teuchos::RCP< const map_type > getMap() const
The Map describing the parallel distribution of this object.
bool isDistributed() const
Whether this is a globally distributed object.
Sets up and executes a communication plan for a Tpetra DistObject.
global_ordinal_type getGlobalElement(local_ordinal_type localIndex) const
The global index corresponding to the given local index.
bool isNodeLocalElement(local_ordinal_type localIndex) const
Whether the given local index is valid for this Map on the calling process.
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const
Accessors for the Teuchos::Comm and Kokkos Node objects.
local_ordinal_type getLocalElement(global_ordinal_type globalIndex) const
The local index corresponding to the given global index.
bool isNodeGlobalElement(global_ordinal_type globalIndex) const
Whether the given global index is owned by this Map on the calling process.
local_map_type getLocalMap() const
Get the LocalMap for Kokkos-Kernels.
One or more distributed dense vectors.
void reduce()
Sum values of a locally replicated multivector across all processes.
void scale(const Scalar &alpha)
Scale in place: this = alpha*this.
size_t getLocalLength() const
Local number of rows on the calling process.
size_t getNumVectors() const
Number of columns in the multivector.
dual_view_type::t_dev::const_type getLocalViewDevice(Access::ReadOnlyStruct) const
Return a read-only, up-to-date view of this MultiVector's local data on device. This requires that th...
dual_view_type::t_host::const_type getLocalViewHost(Access::ReadOnlyStruct) const
Return a read-only, up-to-date view of this MultiVector's local data on host. This requires that ther...
bool isConstantStride() const
Whether this multivector has constant stride between columns.
void putScalar(const Scalar &value)
Set all values in the multivector with the given value.
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getRangeMap() const =0
The Map associated with the range of this operator, which must be compatible with Y....
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getDomainMap() const =0
The Map associated with the domain of this operator, which must be compatible with X....
An abstract interface for graphs accessed by rows.
A read-only, row-oriented interface to a sparse matrix.
virtual bool isLocallyIndexed() const =0
Whether matrix indices are locally indexed.
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getColMap() const =0
The Map that describes the distribution of columns over processes.
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getRowMap() const =0
The Map that describes the distribution of rows over processes.
virtual void getGlobalRowCopy(GlobalOrdinal GlobalRow, nonconst_global_inds_host_view_type &Indices, nonconst_values_host_view_type &Values, size_t &NumEntries) const =0
Get a copy of the given global row's entries.
virtual size_t getNumEntriesInLocalRow(LocalOrdinal localRow) const =0
The current number of entries on the calling process in the specified local row.
virtual void getGlobalRowView(GlobalOrdinal GlobalRow, global_inds_host_view_type &indices, values_host_view_type &values) const =0
Get a constant, nonpersisting, globally indexed view of the given row of the matrix.
virtual size_t getNumEntriesInGlobalRow(GlobalOrdinal globalRow) const =0
The current number of entries on the calling process in the specified global row.
Abstract base class for objects that can be the source of an Import or Export operation.
A distributed dense vector.
Implementation details of Tpetra.
void start()
Start the deep_copy counter.
Nonmember function that computes a residual Computes R = B - A * X.
void padCrsArrays(const RowPtr &rowPtrBeg, const RowPtr &rowPtrEnd, Indices &indices_wdv, const Padding &padding, const int my_rank, const bool verbose)
Determine if the row pointers and indices arrays need to be resized to accommodate new entries....
void verbosePrintArray(std::ostream &out, const ArrayType &x, const char name[], const size_t maxNumToPrint)
Print min(x.size(), maxNumToPrint) entries of x.
void copyOffsets(const OutputViewType &dst, const InputViewType &src)
Copy row offsets (in a sparse graph or matrix) from src to dst. The offsets may have different types.
void leftScaleLocalCrsMatrix(const LocalSparseMatrixType &A_lcl, const ScalingFactorsViewType &scalingFactors, const bool assumeSymmetric, const bool divide=true)
Left-scale a KokkosSparse::CrsMatrix.
Kokkos::DualView< ValueType *, DeviceType > castAwayConstDualView(const Kokkos::DualView< const ValueType *, DeviceType > &input_dv)
Cast away const-ness of a 1-D Kokkos::DualView.
void unpackAndCombineIntoCrsArrays(const CrsGraph< LO, GO, NT > &sourceGraph, const Teuchos::ArrayView< const LO > &importLIDs, const Teuchos::ArrayView< const typename CrsGraph< LO, GO, NT >::packet_type > &imports, const Teuchos::ArrayView< const size_t > &numPacketsPerLID, const size_t constantNumPackets, const CombineMode combineMode, const size_t numSameIDs, const Teuchos::ArrayView< const LO > &permuteToLIDs, const Teuchos::ArrayView< const LO > &permuteFromLIDs, size_t TargetNumRows, size_t TargetNumNonzeros, const int MyTargetPID, const Teuchos::ArrayView< size_t > &CRS_rowptr, const Teuchos::ArrayView< GO > &CRS_colind, const Teuchos::ArrayView< const int > &SourcePids, Teuchos::Array< int > &TargetPids)
unpackAndCombineIntoCrsArrays
Impl::CreateMirrorViewFromUnmanagedHostArray< ValueType, OutputDeviceType >::output_view_type create_mirror_view_from_raw_host_array(const OutputDeviceType &, ValueType *inPtr, const size_t inSize, const bool copy=true, const char label[]="")
Variant of Kokkos::create_mirror_view that takes a raw host 1-d array as input.
size_t unpackAndCombineWithOwningPIDsCount(const CrsGraph< LO, GO, NT > &sourceGraph, const Teuchos::ArrayView< const LO > &importLIDs, const Teuchos::ArrayView< const typename CrsGraph< LO, GO, NT >::packet_type > &imports, const Teuchos::ArrayView< const size_t > &numPacketsPerLID, size_t constantNumPackets, CombineMode combineMode, size_t numSameIDs, const Teuchos::ArrayView< const LO > &permuteToLIDs, const Teuchos::ArrayView< const LO > &permuteFromLIDs)
Special version of Tpetra::Details::unpackCrsGraphAndCombine that also unpacks owning process ranks.
Teuchos::ArrayView< typename DualViewType::t_dev::value_type > getArrayViewFromDualView(const DualViewType &x)
Get a Teuchos::ArrayView which views the host Kokkos::View of the input 1-D Kokkos::DualView.
void copyConvert(const OutputViewType &dst, const InputViewType &src)
Copy values from the 1-D Kokkos::View src, to the 1-D Kokkos::View dst, of the same length....
void packCrsMatrixWithOwningPIDs(const CrsMatrix< ST, LO, GO, NT > &sourceMatrix, Kokkos::DualView< char *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &exports_dv, const Teuchos::ArrayView< size_t > &numPacketsPerLID, const Teuchos::ArrayView< const LO > &exportLIDs, const Teuchos::ArrayView< const int > &sourcePIDs, size_t &constantNumPackets)
Pack specified entries of the given local sparse matrix for communication.
void rightScaleLocalCrsMatrix(const LocalSparseMatrixType &A_lcl, const ScalingFactorsViewType &scalingFactors, const bool assumeSymmetric, const bool divide=true)
Right-scale a KokkosSparse::CrsMatrix.
std::unique_ptr< std::string > createPrefix(const int myRank, const char prefix[])
Create string prefix for each line of verbose output.
OffsetsViewType::non_const_value_type computeOffsetsFromCounts(const ExecutionSpace &execSpace, const OffsetsViewType &ptr, const CountsViewType &counts)
Compute offsets from counts.
std::string dualViewStatusToString(const DualViewType &dv, const char name[])
Return the status of the given Kokkos::DualView, as a human-readable string.
static LocalMapType::local_ordinal_type getDiagCopyWithoutOffsets(const DiagType &D, const LocalMapType &rowMap, const LocalMapType &colMap, const CrsMatrixType &A)
Given a locally indexed, local sparse matrix, and corresponding local row and column Maps,...
void packCrsMatrixNew(const CrsMatrix< ST, LO, GO, NT > &sourceMatrix, Kokkos::DualView< char *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &exports, const Kokkos::DualView< size_t *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &numPacketsPerLID, const Kokkos::DualView< const LO *, typename DistObject< char, LO, GO, NT >::buffer_device_type > &exportLIDs, size_t &constantNumPackets)
Pack specified entries of the given local sparse matrix for communication, for "new" DistObject inter...
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.
void deep_copy(MultiVector< DS, DL, DG, DN > &dst, const MultiVector< SS, SL, SG, SN > &src)
Copy the contents of the MultiVector src into dst.
Details::EquilibrationInfo< typename KokkosKernels::ArithTraits< SC >::val_type, typename NT::device_type > computeRowOneNorms(const Tpetra::RowMatrix< SC, LO, GO, NT > &A)
Compute global row one-norms ("row sums") of the input sparse matrix A, in a way suitable for one-sid...
Details::EquilibrationInfo< typename KokkosKernels::ArithTraits< SC >::val_type, typename NT::device_type > computeRowAndColumnOneNorms(const Tpetra::RowMatrix< SC, LO, GO, NT > &A, const bool assumeSymmetric)
Compute global row and column one-norms ("row sums" and "column sums") of the input sparse matrix A,...
void sort2(const IT1 &first1, const IT1 &last1, const IT2 &first2, const bool stableSort=false)
Sort the first array, and apply the resulting permutation to the second array.
Teuchos_Ordinal Array_size_type
Size type for Teuchos Array objects.
size_t global_size_t
Global size_t object.
std::string combineModeToString(const CombineMode combineMode)
Human-readable string representation of the given CombineMode.
Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > createOneToOne(const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &M)
Nonmember constructor for a contiguous Map with user-defined weights and a user-specified,...
void merge2(IT1 &indResultOut, IT2 &valResultOut, IT1 indBeg, IT1 indEnd, IT2 valBeg, IT2)
Merge values in place, additively, with the same index.
CombineMode
Rule for combining data in an Import or Export.
@ REPLACE
Replace existing values with new values.
@ ADD
Sum new values.
@ ABSMAX
Replace old value with maximum of magnitudes of old and new values.
@ ADD_ASSIGN
Accumulate new values into existing values (may not be supported in all classes).
@ INSERT
Insert new values that don't currently exist.
@ ZERO
Replace old values with zero.
Functor for the the ABSMAX CombineMode of Import and Export operations.
Scalar operator()(const Scalar &x, const Scalar &y)
Return the maximum of the magnitudes (absolute values) of x and y.
Traits class for packing / unpacking data of type T.
Traits class for allocating a Kokkos::View<T*, D>.
Allocation information for a locally owned row in a CrsGraph or CrsMatrix.