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