Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
TpetraExt_MatrixMatrix_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_MATRIXMATRIX_DEF_HPP
11#define TPETRA_MATRIXMATRIX_DEF_HPP
13#include "KokkosSparse_Utils.hpp"
14#include "Tpetra_ConfigDefs.hpp"
16#include "Teuchos_VerboseObject.hpp"
17#include "Teuchos_Array.hpp"
18#include "Tpetra_Util.hpp"
19#include "Tpetra_CrsMatrix.hpp"
20#include "Tpetra_BlockCrsMatrix.hpp"
22#include "Tpetra_RowMatrixTransposer.hpp"
25#include "Tpetra_Details_makeColMap.hpp"
26#include "Tpetra_ConfigDefs.hpp"
27#include "Tpetra_Map.hpp"
28#include "Tpetra_Export.hpp"
32
33#include <algorithm>
34#include <type_traits>
35#include "Teuchos_FancyOStream.hpp"
36
37#include "TpetraExt_MatrixMatrix_ExtraKernels_def.hpp"
39
40#include "KokkosSparse_spgemm.hpp"
41#include "KokkosSparse_spadd.hpp"
42#include "Kokkos_Bitset.hpp"
43
44#include <MatrixMarket_Tpetra.hpp>
45
51/*********************************************************************************************************/
52// Include the architecture-specific kernel partial specializations here
53// NOTE: This needs to be outside all namespaces
54#include "TpetraExt_MatrixMatrix_OpenMP.hpp"
55#include "TpetraExt_MatrixMatrix_Cuda.hpp"
56#include "TpetraExt_MatrixMatrix_HIP.hpp"
57#include "TpetraExt_MatrixMatrix_SYCL.hpp"
58
59namespace Tpetra {
60
61namespace MatrixMatrix {
62
63//
64// This method forms the matrix-matrix product C = op(A) * op(B), where
65// op(A) == A if transposeA is false,
66// op(A) == A^T if transposeA is true,
67// and similarly for op(B).
68//
69template <class Scalar,
70 class LocalOrdinal,
71 class GlobalOrdinal,
72 class Node>
75 bool transposeA,
77 bool transposeB,
80 const std::string& label,
81 const Teuchos::RCP<Teuchos::ParameterList>& params) {
82 using Teuchos::null;
83 using Teuchos::RCP;
84 using Teuchos::rcp;
85 typedef Scalar SC;
86 typedef LocalOrdinal LO;
87 typedef GlobalOrdinal GO;
88 typedef Node NO;
89 typedef CrsMatrix<SC, LO, GO, NO> crs_matrix_type;
90 typedef Import<LO, GO, NO> import_type;
92 typedef Map<LO, GO, NO> map_type;
94
96
97 const std::string prefix = "TpetraExt::MatrixMatrix::Multiply(): ";
98
99 // TEUCHOS_FUNC_TIME_MONITOR_DIFF("My Matrix Mult", mmm_multiply);
100
101 // The input matrices A and B must both be fillComplete.
102 TEUCHOS_TEST_FOR_EXCEPTION(!A.isFillComplete(), std::runtime_error, prefix << "Matrix A is not fill complete.");
103 TEUCHOS_TEST_FOR_EXCEPTION(!B.isFillComplete(), std::runtime_error, prefix << "Matrix B is not fill complete.");
104
105 // If transposeA is true, then Aprime will be the transpose of A
106 // (computed explicitly via RowMatrixTransposer). Otherwise, Aprime
107 // will just be a pointer to A.
109 // If transposeB is true, then Bprime will be the transpose of B
110 // (computed explicitly via RowMatrixTransposer). Otherwise, Bprime
111 // will just be a pointer to B.
113
114 // Is this a "clean" matrix?
115 //
116 // mfh 27 Sep 2016: Historically, if Epetra_CrsMatrix was neither
117 // locally nor globally indexed, then it was empty. I don't like
118 // this, because the most straightforward implementation presumes
119 // lazy allocation of indices. However, historical precedent
120 // demands that we keep around this predicate as a way to test
121 // whether the matrix is empty.
122 const bool newFlag = !C.getGraph()->isLocallyIndexed() && !C.getGraph()->isGloballyIndexed();
123
124 bool use_optimized_ATB = false;
126 use_optimized_ATB = true;
127
128#ifdef USE_OLD_TRANSPOSE // NOTE: For Grey Ballard's use. Remove this later.
129 use_optimized_ATB = false;
130#endif
131
132 using Teuchos::ParameterList;
134 transposeParams->set("sort", true); // Kokkos Kernels spgemm requires inputs to be sorted
135
138 Aprime = transposer.createTranspose(transposeParams);
139 } else {
141 }
142
143 if (transposeB) {
145 Bprime = transposer.createTranspose(transposeParams);
146 } else {
148 }
149
150 // Check size compatibility
151 global_size_t numACols = A.getDomainMap()->getGlobalNumElements();
152 global_size_t numBCols = B.getDomainMap()->getGlobalNumElements();
153 global_size_t Aouter = transposeA ? numACols : A.getGlobalNumRows();
154 global_size_t Bouter = transposeB ? B.getGlobalNumRows() : numBCols;
155 global_size_t Ainner = transposeA ? A.getGlobalNumRows() : numACols;
156 global_size_t Binner = transposeB ? numBCols : B.getGlobalNumRows();
157 TEUCHOS_TEST_FOR_EXCEPTION(Ainner != Binner, std::runtime_error,
158 prefix << "ERROR, inner dimensions of op(A) and op(B) "
159 "must match for matrix-matrix product. op(A) is "
160 << Aouter << "x" << Ainner << ", op(B) is " << Binner << "x" << Bouter);
161
162 // The result matrix C must at least have a row-map that reflects the correct
163 // row-size. Don't check the number of columns because rectangular matrices
164 // which were constructed with only one map can still end up having the
165 // correct capacity and dimensions when filled.
166 TEUCHOS_TEST_FOR_EXCEPTION(Aouter > C.getGlobalNumRows(), std::runtime_error,
167 prefix << "ERROR, dimensions of result C must "
168 "match dimensions of op(A) * op(B). C has "
169 << C.getGlobalNumRows()
170 << " rows, should have at least " << Aouter << std::endl);
171
172 // It doesn't matter whether C is already Filled or not. If it is already
173 // Filled, it must have space allocated for the positions that will be
174 // referenced in forming C = op(A)*op(B). If it doesn't have enough space,
175 // we'll error out later when trying to store result values.
176
177 // CGB: However, matrix must be in active-fill
178 if (!C.isFillActive()) C.resumeFill();
179
180 // We're going to need to import remotely-owned sections of A and/or B if
181 // more than one processor is performing this run, depending on the scenario.
182 int numProcs = A.getComm()->getSize();
183
184 // Declare a couple of structs that will be used to hold views of the data
185 // of A and B, to be used for fast access during the matrix-multiplication.
188
191
192 {
193 Tpetra::Details::ProfilingRegion r("TpetraExt: MMM: All I&X");
194
195 // Now import any needed remote rows and populate the Aview struct
196 // NOTE: We assert that an import isn't needed --- since we do the transpose
197 // above to handle that.
198 if (!use_optimized_ATB) {
200 MMdetails::import_and_extract_views(*Aprime, targetMap_A, Aview, dummyImporter, true, label, params);
201 }
202
203 // We will also need local access to all rows of B that correspond to the
204 // column-map of op(A).
205 if (numProcs > 1)
206 targetMap_B = Aprime->getColMap();
207
208 // Import any needed remote rows and populate the Bview struct.
210 MMdetails::import_and_extract_views(*Bprime, targetMap_B, Bview, Aprime->getGraph()->getImporter(), Aprime->getGraph()->getImporter().is_null(), label, params);
211
212 } // stop MM_importExtract here
213
214 // stop the setup timer, and start the multiply timer
215 MM = Teuchos::null;
216 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: All Multiply"));
217
218 // Call the appropriate method to perform the actual multiplication.
219 if (use_optimized_ATB) {
220 MMdetails::mult_AT_B_newmatrix(A, B, C, label, params);
221
222 } else if (call_FillComplete_on_result && newFlag) {
223 MMdetails::mult_A_B_newmatrix(Aview, Bview, C, label, params);
224
225 } else if (call_FillComplete_on_result) {
226 MMdetails::mult_A_B_reuse(Aview, Bview, C, label, params);
227
228 } else {
229 // mfh 27 Sep 2016: Is this the "slow" case? This
230 // "CrsWrapper_CrsMatrix" thing could perhaps be made to support
231 // thread-parallel inserts, but that may take some effort.
233
234 MMdetails::mult_A_B(Aview, Bview, crsmat, label, params);
235 }
236
237 MM = Teuchos::null;
238 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: All FillComplete"));
239 if (call_FillComplete_on_result && !C.isFillComplete()) {
240 // We'll call FillComplete on the C matrix before we exit, and give it a
241 // domain-map and a range-map.
242 // The domain-map will be the domain-map of B, unless
243 // op(B)==transpose(B), in which case the range-map of B will be used.
244 // The range-map will be the range-map of A, unless op(A)==transpose(A),
245 // in which case the domain-map of A will be used.
246 C.fillComplete(Bprime->getDomainMap(), Aprime->getRangeMap());
247 }
248}
249
250//
251// This method forms the matrix-matrix product C = op(A) * op(B), where
252// op(A) == A and similarly for op(B). op(A) = A^T is not yet implemented.
253//
254template <class Scalar,
255 class LocalOrdinal,
256 class GlobalOrdinal,
257 class Node>
260 bool transposeA,
262 bool transposeB,
264 const std::string& label) {
265 using Teuchos::null;
266 using Teuchos::RCP;
267 using Teuchos::rcp;
268 typedef Scalar SC;
269 typedef LocalOrdinal LO;
270 typedef GlobalOrdinal GO;
271 typedef Node NO;
273 typedef Map<LO, GO, NO> map_type;
274 typedef Import<LO, GO, NO> import_type;
275
276 std::string prefix = std::string("TpetraExt ") + label + std::string(": ");
277
278 TEUCHOS_TEST_FOR_EXCEPTION(transposeA == true, std::runtime_error, prefix << "Matrix A cannot be transposed.");
279 TEUCHOS_TEST_FOR_EXCEPTION(transposeB == true, std::runtime_error, prefix << "Matrix B cannot be transposed.");
280
281 // Check size compatibility
282 global_size_t numACols = A->getGlobalNumCols();
283 global_size_t numBCols = B->getGlobalNumCols();
284 global_size_t numARows = A->getGlobalNumRows();
285 global_size_t numBRows = B->getGlobalNumRows();
286
291 TEUCHOS_TEST_FOR_EXCEPTION(Ainner != Binner, std::runtime_error,
292 prefix << "ERROR, inner dimensions of op(A) and op(B) "
293 "must match for matrix-matrix product. op(A) is "
294 << Aouter << "x" << Ainner << ", op(B) is " << Binner << "x" << Bouter);
295
296 // We're going to need to import remotely-owned sections of A and/or B if
297 // more than one processor is performing this run, depending on the scenario.
298 int numProcs = A->getComm()->getSize();
299
300 const LO blocksize = A->getBlockSize();
301 TEUCHOS_TEST_FOR_EXCEPTION(blocksize != B->getBlockSize(), std::runtime_error,
302 prefix << "ERROR, Blocksizes do not match. A.blocksize = " << blocksize << ", B.blocksize = " << B->getBlockSize());
303
304 // Declare a couple of structs that will be used to hold views of the data
305 // of A and B, to be used for fast access during the matrix-multiplication.
308
309 RCP<const map_type> targetMap_A = A->getRowMap();
310 RCP<const map_type> targetMap_B = B->getRowMap();
311
312 // Populate the Aview struct. No remotes are needed.
314 MMdetails::import_and_extract_views(*A, targetMap_A, Aview, dummyImporter, true);
315
316 // We will also need local access to all rows of B that correspond to the
317 // column-map of op(A).
318 if (numProcs > 1)
319 targetMap_B = A->getColMap();
320
321 // Import any needed remote rows and populate the Bview struct.
322 MMdetails::import_and_extract_views(*B, targetMap_B, Bview, A->getGraph()->getImporter(),
323 A->getGraph()->getImporter().is_null());
324
325 // Call the appropriate method to perform the actual multiplication.
326 MMdetails::mult_A_B_newmatrix(Aview, Bview, C);
327}
328
329template <class Scalar,
330 class LocalOrdinal,
331 class GlobalOrdinal,
332 class Node>
333void Jacobi(typename Teuchos::ScalarTraits<Scalar>::magnitudeType omega,
339 const std::string& label,
340 const Teuchos::RCP<Teuchos::ParameterList>& params) {
341 using Teuchos::RCP;
342 using Teuchos::rcp;
343 typedef Scalar SC;
344 typedef LocalOrdinal LO;
345 typedef GlobalOrdinal GO;
346 typedef Node NO;
347 typedef Import<LO, GO, NO> import_type;
349 typedef Map<LO, GO, NO> map_type;
350 typedef CrsMatrix<SC, LO, GO, NO> crs_matrix_type;
351
353
354 const std::string prefix = "TpetraExt::MatrixMatrix::Jacobi(): ";
355
356 // A and B should already be Filled.
357 // Should we go ahead and call FillComplete() on them if necessary or error
358 // out? For now, we choose to error out.
359 TEUCHOS_TEST_FOR_EXCEPTION(!A.isFillComplete(), std::runtime_error, prefix << "Matrix A is not fill complete.");
360 TEUCHOS_TEST_FOR_EXCEPTION(!B.isFillComplete(), std::runtime_error, prefix << "Matrix B is not fill complete.");
361
364
365 // Now check size compatibility
366 global_size_t numACols = A.getDomainMap()->getGlobalNumElements();
367 global_size_t numBCols = B.getDomainMap()->getGlobalNumElements();
368 global_size_t Aouter = A.getGlobalNumRows();
371 global_size_t Binner = B.getGlobalNumRows();
372 TEUCHOS_TEST_FOR_EXCEPTION(Ainner != Binner, std::runtime_error,
373 prefix << "ERROR, inner dimensions of op(A) and op(B) "
374 "must match for matrix-matrix product. op(A) is "
375 << Aouter << "x" << Ainner << ", op(B) is " << Binner << "x" << Bouter);
376
377 // The result matrix C must at least have a row-map that reflects the correct
378 // row-size. Don't check the number of columns because rectangular matrices
379 // which were constructed with only one map can still end up having the
380 // correct capacity and dimensions when filled.
381 TEUCHOS_TEST_FOR_EXCEPTION(Aouter > C.getGlobalNumRows(), std::runtime_error,
382 prefix << "ERROR, dimensions of result C must "
383 "match dimensions of op(A) * op(B). C has "
384 << C.getGlobalNumRows()
385 << " rows, should have at least " << Aouter << std::endl);
386
387 // It doesn't matter whether C is already Filled or not. If it is already
388 // Filled, it must have space allocated for the positions that will be
389 // referenced in forming C = op(A)*op(B). If it doesn't have enough space,
390 // we'll error out later when trying to store result values.
391
392 // CGB: However, matrix must be in active-fill
393 TEUCHOS_TEST_FOR_EXCEPT(C.isFillActive() == false);
394
395 // We're going to need to import remotely-owned sections of A and/or B if
396 // more than one processor is performing this run, depending on the scenario.
397 int numProcs = A.getComm()->getSize();
398
399 // Declare a couple of structs that will be used to hold views of the data of
400 // A and B, to be used for fast access during the matrix-multiplication.
403
406
407 {
408 Tpetra::Details::ProfilingRegion r("TpetraExt: Jacobi: All I&X");
409 // Disable globalConstants by default
410 // NOTE: the I&X routine sticks an importer on the paramlist as output, so we have to use a unique guy here
411 RCP<Teuchos::ParameterList> importParams = Teuchos::rcp(new Teuchos::ParameterList);
412 importParams->set("compute global constants", false);
413 if (!params.is_null()) {
414 importParams->setParameters(*params);
415 importParams->set("compute global constants", params->get("compute global constants: temporaries", false));
416 }
417
418 // Now import any needed remote rows and populate the Aview struct.
420 MMdetails::import_and_extract_views(*Aprime, targetMap_A, Aview, dummyImporter, true, label, importParams);
421
422 // We will also need local access to all rows of B that correspond to the
423 // column-map of op(A).
424 if (numProcs > 1)
425 targetMap_B = Aprime->getColMap();
426
427 // Now import any needed remote rows and populate the Bview struct.
428 MMdetails::import_and_extract_views(*Bprime, targetMap_B, Bview, Aprime->getGraph()->getImporter(), Aprime->getGraph()->getImporter().is_null(), label, importParams);
429 }
430 MM = Teuchos::null;
431 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: Jacobi All Multiply"));
432
433 // Now call the appropriate method to perform the actual multiplication.
435
436 // Is this a "clean" matrix
437 bool newFlag = !C.getGraph()->isLocallyIndexed() && !C.getGraph()->isGloballyIndexed();
438
440 MMdetails::jacobi_A_B_newmatrix(omega, Dinv, Aview, Bview, C, label, params);
441
442 } else if (call_FillComplete_on_result) {
443 MMdetails::jacobi_A_B_reuse(omega, Dinv, Aview, Bview, C, label, params);
444
445 } else {
446 TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, "jacobi_A_B_general not implemented");
447 }
448
449 if (!params.is_null()) {
450 bool removeZeroEntries = params->get("remove zeros", false);
451 if (removeZeroEntries) {
452 typedef Teuchos::ScalarTraits<Scalar> STS;
453 typename STS::magnitudeType threshold = params->get("remove zeros threshold", STS::magnitude(STS::zero()));
455 }
456 }
457}
458
459template <class Scalar,
460 class LocalOrdinal,
461 class GlobalOrdinal,
462 class Node>
463void Add(
465 bool transposeA,
468 Scalar scalarB) {
469 using Teuchos::Array;
470 using Teuchos::null;
471 using Teuchos::RCP;
472 typedef Scalar SC;
473 typedef LocalOrdinal LO;
474 typedef GlobalOrdinal GO;
475 typedef Node NO;
476 typedef CrsMatrix<SC, LO, GO, NO> crs_matrix_type;
478
479 const std::string prefix = "TpetraExt::MatrixMatrix::Add(): ";
480
481 TEUCHOS_TEST_FOR_EXCEPTION(!A.isFillComplete(), std::runtime_error,
482 prefix << "ERROR, input matrix A.isFillComplete() is false; it is required to be true. "
483 "(Result matrix B is not required to be isFillComplete()).");
484 TEUCHOS_TEST_FOR_EXCEPTION(B.isFillComplete(), std::runtime_error,
485 prefix << "ERROR, input matrix B must not be fill complete!");
486 TEUCHOS_TEST_FOR_EXCEPTION(B.isStaticGraph(), std::runtime_error,
487 prefix << "ERROR, input matrix B must not have static graph!");
488 TEUCHOS_TEST_FOR_EXCEPTION(B.isLocallyIndexed(), std::runtime_error,
489 prefix << "ERROR, input matrix B must not be locally indexed!");
490
491 using Teuchos::ParameterList;
493 transposeParams->set("sort", false);
494
496 if (transposeA) {
498 Aprime = transposer.createTranspose(transposeParams);
499 } else {
501 }
502
503 size_t a_numEntries;
504 typename crs_matrix_type::nonconst_global_inds_host_view_type a_inds("a_inds", A.getLocalMaxNumRowEntries());
505 typename crs_matrix_type::nonconst_values_host_view_type a_vals("a_vals", A.getLocalMaxNumRowEntries());
506 GO row;
507
508 if (scalarB != Teuchos::ScalarTraits<SC>::one())
509 B.scale(scalarB);
510
511 size_t numMyRows = B.getLocalNumRows();
512 if (scalarA != Teuchos::ScalarTraits<SC>::zero()) {
513 for (LO i = 0; (size_t)i < numMyRows; ++i) {
514 row = B.getRowMap()->getGlobalElement(i);
515 Aprime->getGlobalRowCopy(row, a_inds, a_vals, a_numEntries);
516
517 if (scalarA != Teuchos::ScalarTraits<SC>::one()) {
518 for (size_t j = 0; j < a_numEntries; ++j)
519 a_vals[j] *= scalarA;
520 }
521 B.insertGlobalValues(row, a_numEntries, reinterpret_cast<Scalar*>(a_vals.data()), a_inds.data());
522 }
523 }
524}
525
526template <class Scalar,
527 class LocalOrdinal,
528 class GlobalOrdinal,
529 class Node>
530Teuchos::RCP<CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>
532 const bool transposeA,
534 const Scalar& beta,
535 const bool transposeB,
537 const Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>& domainMap,
538 const Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>& rangeMap,
539 const Teuchos::RCP<Teuchos::ParameterList>& params) {
540 using Teuchos::ParameterList;
541 using Teuchos::RCP;
542 using Teuchos::rcp;
543 using Teuchos::rcpFromRef;
545 if (!params.is_null()) {
547 params->isParameter("Call fillComplete") && !params->get<bool>("Call fillComplete"),
548 std::invalid_argument,
549 "Tpetra::MatrixMatrix::add(): this version of add() always calls fillComplete\n"
550 "on the result, but you explicitly set 'Call fillComplete' = false in the parameter list. Don't set this explicitly.");
551 params->set("Call fillComplete", true);
552 }
553 // If transposeB, must compute B's explicit transpose to
554 // get the correct row map for C.
556 if (transposeB) {
558 Brcp = transposer.createTranspose();
559 }
560 // Check that A,B are fillComplete before getting B's column map
561 TEUCHOS_TEST_FOR_EXCEPTION(!A.isFillComplete() || !Brcp->isFillComplete(), std::invalid_argument,
562 "TpetraExt::MatrixMatrix::add(): A and B must both be fill complete.");
563 RCP<crs_matrix_type> C = rcp(new crs_matrix_type(Brcp->getRowMap(), 0));
564 // this version of add() always fill completes the result, no matter what is in params on input
565 add(alpha, transposeA, A, beta, false, *Brcp, *C, domainMap, rangeMap, params);
566 return C;
567}
568
569// This functor does the same thing as CrsGraph::convertColumnIndicesFromGlobalToLocal,
570// but since the spadd() output is always packed there is no need for a separate
571// numRowEntries here.
572//
573template <class LO, class GO, class LOView, class GOView, class LocalMap>
574struct ConvertGlobalToLocalFunctor {
575 ConvertGlobalToLocalFunctor(LOView& lids_, const GOView& gids_, const LocalMap localColMap_)
576 : lids(lids_)
577 , gids(gids_)
578 , localColMap(localColMap_) {}
579
580 KOKKOS_FUNCTION void operator()(const GO i) const {
581 lids(i) = localColMap.getLocalElement(gids(i));
582 }
583
584 LOView lids;
585 const GOView gids;
586 const LocalMap localColMap;
587};
588
589template <class Scalar,
590 class LocalOrdinal,
591 class GlobalOrdinal,
592 class Node>
593void add(const Scalar& alpha,
594 const bool transposeA,
596 const Scalar& beta,
597 const bool transposeB,
600 const Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>& domainMap,
601 const Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>>& rangeMap,
602 const Teuchos::RCP<Teuchos::ParameterList>& params) {
603 using Teuchos::RCP;
604 using Teuchos::rcp;
605 using Teuchos::rcp_dynamic_cast;
606 using Teuchos::rcp_implicit_cast;
607 using Teuchos::rcpFromRef;
608 using Teuchos::TimeMonitor;
609 using SC = Scalar;
610 using LO = LocalOrdinal;
611 using GO = GlobalOrdinal;
612 using NO = Node;
613 using crs_matrix_type = CrsMatrix<SC, LO, GO, NO>;
614 using crs_graph_type = CrsGraph<LO, GO, NO>;
615 using map_type = Map<LO, GO, NO>;
617 using import_type = Import<LO, GO, NO>;
618 using export_type = Export<LO, GO, NO>;
619 using exec_space = typename crs_graph_type::execution_space;
620 using AddKern = MMdetails::AddKernels<SC, LO, GO, NO>;
621 const char* prefix_mmm = "TpetraExt::MatrixMatrix::add: ";
622 constexpr bool debug = false;
623
625
626 if (debug) {
627 std::ostringstream os;
628 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
629 << "TpetraExt::MatrixMatrix::add" << std::endl;
630 std::cerr << os.str();
631 }
632
633 TEUCHOS_TEST_FOR_EXCEPTION(C.isLocallyIndexed() || C.isGloballyIndexed(), std::invalid_argument,
634 prefix_mmm << "C must be a 'new' matrix (neither locally nor globally indexed).");
635 TEUCHOS_TEST_FOR_EXCEPTION(!A.isFillComplete() || !B.isFillComplete(), std::invalid_argument,
636 prefix_mmm << "A and B must both be fill complete.");
637#ifdef HAVE_TPETRA_DEBUG
638 // The matrices don't have domain or range Maps unless they are fill complete.
639 if (A.isFillComplete() && B.isFillComplete()) {
640 const bool domainMapsSame =
641 (!transposeA && !transposeB &&
642 !A.getDomainMap()->locallySameAs(*B.getDomainMap())) ||
643 (!transposeA && transposeB &&
644 !A.getDomainMap()->isSameAs(*B.getRangeMap())) ||
645 (transposeA && !transposeB &&
646 !A.getRangeMap()->isSameAs(*B.getDomainMap()));
647 TEUCHOS_TEST_FOR_EXCEPTION(domainMapsSame, std::invalid_argument,
648 prefix_mmm << "The domain Maps of Op(A) and Op(B) are not the same.");
649
650 const bool rangeMapsSame =
651 (!transposeA && !transposeB &&
652 !A.getRangeMap()->isSameAs(*B.getRangeMap())) ||
653 (!transposeA && transposeB &&
654 !A.getRangeMap()->isSameAs(*B.getDomainMap())) ||
655 (transposeA && !transposeB &&
656 !A.getDomainMap()->isSameAs(*B.getRangeMap()));
657 TEUCHOS_TEST_FOR_EXCEPTION(rangeMapsSame, std::invalid_argument,
658 prefix_mmm << "The range Maps of Op(A) and Op(B) are not the same.");
659 }
660#endif // HAVE_TPETRA_DEBUG
661
662 using Teuchos::ParameterList;
663 // Form the explicit transpose of A if necessary.
665 if (transposeA) {
667 Aprime = transposer.createTranspose();
668 }
669
670#ifdef HAVE_TPETRA_DEBUG
671 TEUCHOS_TEST_FOR_EXCEPTION(Aprime.is_null(), std::logic_error,
672 prefix_mmm << "Failed to compute Op(A). "
673 "Please report this bug to the Tpetra developers.");
674#endif // HAVE_TPETRA_DEBUG
675
676 // Form the explicit transpose of B if necessary.
678 if (transposeB) {
679 if (debug) {
680 std::ostringstream os;
681 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
682 << "Form explicit xpose of B" << std::endl;
683 std::cerr << os.str();
684 }
686 Bprime = transposer.createTranspose();
687 }
688#ifdef HAVE_TPETRA_DEBUG
689 TEUCHOS_TEST_FOR_EXCEPTION(Bprime.is_null(), std::logic_error,
690 prefix_mmm << "Failed to compute Op(B). Please report this bug to the Tpetra developers.");
692 !Aprime->isFillComplete() || !Bprime->isFillComplete(), std::invalid_argument,
693 prefix_mmm << "Aprime and Bprime must both be fill complete. "
694 "Please report this bug to the Tpetra developers.");
695#endif // HAVE_TPETRA_DEBUG
698 if (CDomainMap.is_null()) {
699 CDomainMap = Bprime->getDomainMap();
700 }
701 if (CRangeMap.is_null()) {
702 CRangeMap = Bprime->getRangeMap();
703 }
704 assert(!(CDomainMap.is_null()));
705 assert(!(CRangeMap.is_null()));
706 typedef typename AddKern::values_array values_array;
707 typedef typename AddKern::row_ptrs_array row_ptrs_array;
708 typedef typename AddKern::col_inds_array col_inds_array;
709 bool AGraphSorted = Aprime->getCrsGraph()->isSorted();
710 bool BGraphSorted = Bprime->getCrsGraph()->isSorted();
711 values_array vals;
712 row_ptrs_array rowptrs;
713 col_inds_array colinds;
714
715 MM = Teuchos::null;
716 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: rowmap check/import"));
717
718 if (!(Aprime->getRowMap()->isSameAs(*(Bprime->getRowMap())))) {
719 // import Aprime into Bprime's row map so the local matrices have same # of rows
720 auto import = rcp(new import_type(Aprime->getRowMap(), Bprime->getRowMap()));
721 // cbl do not set
722 // parameterlist "isMatrixMatrix_TransferAndFillComplete" true here as
723 // this import _may_ take the form of a transfer. In practice it would be unlikely,
724 // but the general case is not so forgiving.
725 Aprime = importAndFillCompleteCrsMatrix<crs_matrix_type>(Aprime, *import, Bprime->getDomainMap(), Bprime->getRangeMap());
726 }
727 bool matchingColMaps = Aprime->getColMap()->isSameAs(*(Bprime->getColMap()));
729 RCP<const import_type> Cimport = Teuchos::null;
730 RCP<export_type> Cexport = Teuchos::null;
731 bool doFillComplete = true;
732 if (Teuchos::nonnull(params) && params->isParameter("Call fillComplete")) {
733 doFillComplete = params->get<bool>("Call fillComplete");
734 }
735 auto Alocal = Aprime->getLocalMatrixDevice();
736 auto Blocal = Bprime->getLocalMatrixDevice();
737 LO numLocalRows = Alocal.numRows();
738 if (numLocalRows == 0) {
739 // KokkosKernels spadd assumes rowptrs.extent(0) + 1 == nrows,
740 // but an empty Tpetra matrix is allowed to have rowptrs.extent(0) == 0.
741 // Handle this case now
742 //(without interfering with collective operations, since it's possible for
743 // some ranks to have 0 local rows and others not).
744 rowptrs = row_ptrs_array("C rowptrs", 0);
745 }
746 auto Acolmap = Aprime->getColMap();
747 auto Bcolmap = Bprime->getColMap();
748 if (!matchingColMaps) {
749 using global_col_inds_array = typename AddKern::global_col_inds_array;
750 MM = Teuchos::null;
751 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: mismatched col map full kernel"));
752
753 // use kernel that converts col indices in both A and B to common domain map before adding
754 auto AlocalColmap = Acolmap->getLocalMap();
755 auto BlocalColmap = Bcolmap->getLocalMap();
756 global_col_inds_array globalColinds;
757 if (debug) {
758 std::ostringstream os;
759 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
760 << "Call AddKern::convertToGlobalAndAdd(...)" << std::endl;
761 std::cerr << os.str();
762 }
763 AddKern::convertToGlobalAndAdd(
766 if (debug) {
767 std::ostringstream os;
768 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
769 << "Finished AddKern::convertToGlobalAndAdd(...)" << std::endl;
770 std::cerr << os.str();
771 }
772 MM = Teuchos::null;
773 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: Constructing graph"));
774
776 Tpetra::Details::makeColMap<LocalOrdinal, GlobalOrdinal, Node>(CcolMap, CDomainMap, globalColinds);
777 C.replaceColMap(CcolMap);
778 col_inds_array localColinds("C colinds", globalColinds.extent(0));
779 Kokkos::parallel_for(Kokkos::RangePolicy<exec_space>(0, globalColinds.extent(0)),
780 ConvertGlobalToLocalFunctor<LocalOrdinal, GlobalOrdinal,
781 col_inds_array, global_col_inds_array,
782 typename map_type::local_map_type>(localColinds, globalColinds, CcolMap->getLocalMap()));
783 Import_Util::sortCrsEntries(rowptrs, localColinds, vals);
784 C.setAllValues(rowptrs, localColinds, vals);
785 C.fillComplete(CDomainMap, CRangeMap, params);
786 if (!doFillComplete)
787 C.resumeFill();
788 } else {
789 // Aprime, Bprime and C all have the same column maps
790 auto Avals = Alocal.values;
791 auto Bvals = Blocal.values;
792 auto Arowptrs = Alocal.graph.row_map;
793 auto Browptrs = Blocal.graph.row_map;
794 auto Acolinds = Alocal.graph.entries;
795 auto Bcolinds = Blocal.graph.entries;
796 if (sorted) {
797 // use sorted kernel
798 MM = Teuchos::null;
799 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: sorted entries full kernel"));
800 if (debug) {
801 std::ostringstream os;
802 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
803 << "Call AddKern::addSorted(...)" << std::endl;
804 std::cerr << os.str();
805 }
806 AddKern::addSorted(Avals, Arowptrs, Acolinds, alpha, Bvals, Browptrs, Bcolinds, beta, Aprime->getGlobalNumCols(), vals, rowptrs, colinds);
807 } else {
808 // use unsorted kernel
809 MM = Teuchos::null;
810 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: unsorted entries full kernel"));
811
812 if (debug) {
813 std::ostringstream os;
814 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
815 << "Call AddKern::addUnsorted(...)" << std::endl;
816 std::cerr << os.str();
817 }
818 AddKern::addUnsorted(Avals, Arowptrs, Acolinds, alpha, Bvals, Browptrs, Bcolinds, beta, Aprime->getGlobalNumCols(), vals, rowptrs, colinds);
819 }
820 // Bprime col map works as C's row map, since Aprime and Bprime have the same colmaps.
822 C.replaceColMap(Ccolmap);
823 C.setAllValues(rowptrs, colinds, vals);
824 if (doFillComplete) {
825 MM = Teuchos::null;
826 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: expertStaticFillComplete"));
827 if (!CDomainMap->isSameAs(*Ccolmap)) {
828 if (debug) {
829 std::ostringstream os;
830 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
831 << "Create Cimport" << std::endl;
832 std::cerr << os.str();
833 }
834 Cimport = rcp(new import_type(CDomainMap, Ccolmap));
835 }
836 if (!C.getRowMap()->isSameAs(*CRangeMap)) {
837 if (debug) {
838 std::ostringstream os;
839 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
840 << "Create Cexport" << std::endl;
841 std::cerr << os.str();
842 }
843 Cexport = rcp(new export_type(C.getRowMap(), CRangeMap));
844 }
845
846 if (debug) {
847 std::ostringstream os;
848 os << "Proc " << A.getMap()->getComm()->getRank() << ": "
849 << "Call C->expertStaticFillComplete(...)" << std::endl;
850 std::cerr << os.str();
851 }
852 C.expertStaticFillComplete(CDomainMap, CRangeMap, Cimport, Cexport, params);
853 }
854 }
855}
856
857// This version of Add takes C as RCP&, so C may be null on input (in this case,
858// it is allocated and constructed in this function).
859template <class Scalar,
860 class LocalOrdinal,
861 class GlobalOrdinal,
862 class Node>
863void Add(
865 bool transposeA,
868 bool transposeB,
871 using std::endl;
872 using Teuchos::Array;
873 using Teuchos::ArrayRCP;
874 using Teuchos::ArrayView;
875 using Teuchos::RCP;
876 using Teuchos::rcp;
877 using Teuchos::rcp_dynamic_cast;
878 using Teuchos::rcpFromRef;
879 using Teuchos::tuple;
880 // typedef typename ArrayView<const Scalar>::size_type size_type;
881 typedef Teuchos::ScalarTraits<Scalar> STS;
883 // typedef Import<LocalOrdinal, GlobalOrdinal, Node> import_type;
884 // typedef RowGraph<LocalOrdinal, GlobalOrdinal, Node> row_graph_type;
885 // typedef CrsGraph<LocalOrdinal, GlobalOrdinal, Node> crs_graph_type;
888
889 std::string prefix = "TpetraExt::MatrixMatrix::Add(): ";
890
892 !A.isFillComplete() || !B.isFillComplete(), std::invalid_argument,
893 prefix << "A and B must both be fill complete before calling this function.");
894
895 if (C.is_null()) {
896 TEUCHOS_TEST_FOR_EXCEPTION(!A.haveGlobalConstants(), std::logic_error,
897 prefix << "C is null (must be allocated), but A.haveGlobalConstants() is false. "
898 "Please report this bug to the Tpetra developers.");
899 TEUCHOS_TEST_FOR_EXCEPTION(!B.haveGlobalConstants(), std::logic_error,
900 prefix << "C is null (must be allocated), but B.haveGlobalConstants() is false. "
901 "Please report this bug to the Tpetra developers.");
902 }
903
904#ifdef HAVE_TPETRA_DEBUG
905 {
906 const bool domainMapsSame =
907 (!transposeA && !transposeB && !A.getDomainMap()->isSameAs(*(B.getDomainMap()))) ||
908 (!transposeA && transposeB && !A.getDomainMap()->isSameAs(*(B.getRangeMap()))) ||
909 (transposeA && !transposeB && !A.getRangeMap()->isSameAs(*(B.getDomainMap())));
910 TEUCHOS_TEST_FOR_EXCEPTION(domainMapsSame, std::invalid_argument,
911 prefix << "The domain Maps of Op(A) and Op(B) are not the same.");
912
913 const bool rangeMapsSame =
914 (!transposeA && !transposeB && !A.getRangeMap()->isSameAs(*(B.getRangeMap()))) ||
915 (!transposeA && transposeB && !A.getRangeMap()->isSameAs(*(B.getDomainMap()))) ||
916 (transposeA && !transposeB && !A.getDomainMap()->isSameAs(*(B.getRangeMap())));
917 TEUCHOS_TEST_FOR_EXCEPTION(rangeMapsSame, std::invalid_argument,
918 prefix << "The range Maps of Op(A) and Op(B) are not the same.");
919 }
920#endif // HAVE_TPETRA_DEBUG
921
922 using Teuchos::ParameterList;
924 transposeParams->set("sort", false);
925
926 // Form the explicit transpose of A if necessary.
928 if (transposeA) {
930 Aprime = theTransposer.createTranspose(transposeParams);
931 } else {
933 }
934
935#ifdef HAVE_TPETRA_DEBUG
936 TEUCHOS_TEST_FOR_EXCEPTION(Aprime.is_null(), std::logic_error,
937 prefix << "Failed to compute Op(A). Please report this bug to the Tpetra developers.");
938#endif // HAVE_TPETRA_DEBUG
939
940 // Form the explicit transpose of B if necessary.
942 if (transposeB) {
944 Bprime = theTransposer.createTranspose(transposeParams);
945 } else {
947 }
948
949#ifdef HAVE_TPETRA_DEBUG
950 TEUCHOS_TEST_FOR_EXCEPTION(Bprime.is_null(), std::logic_error,
951 prefix << "Failed to compute Op(B). Please report this bug to the Tpetra developers.");
952#endif // HAVE_TPETRA_DEBUG
953
954 bool CwasFillComplete = false;
955
956 // Allocate or zero the entries of the result matrix.
957 if (!C.is_null()) {
958 CwasFillComplete = C->isFillComplete();
960 C->resumeFill();
961 C->setAllToScalar(STS::zero());
962 } else {
963 // FIXME (mfh 08 May 2013) When I first looked at this method, I
964 // noticed that C was being given the row Map of Aprime (the
965 // possibly transposed version of A). Is this what we want?
966
967 // It is a precondition that Aprime and Bprime have the same domain and range maps.
968 // However, they may have different row maps. In this case, it's difficult to
969 // get a precise upper bound on the number of entries in each local row of C, so
970 // just use the looser upper bound based on the max number of entries in any row of Aprime and Bprime.
971 if (Aprime->getRowMap()->isSameAs(*Bprime->getRowMap())) {
972 LocalOrdinal numLocalRows = Aprime->getLocalNumRows();
974 for (LocalOrdinal i = 0; i < numLocalRows; i++) {
975 CmaxEntriesPerRow[i] = Aprime->getNumEntriesInLocalRow(i) + Bprime->getNumEntriesInLocalRow(i);
976 }
977 C = rcp(new crs_matrix_type(Aprime->getRowMap(), CmaxEntriesPerRow()));
978 } else {
979 // Note: above we checked that Aprime and Bprime have global constants, so it's safe to ask for max entries per row.
980 C = rcp(new crs_matrix_type(Aprime->getRowMap(), Aprime->getGlobalMaxNumRowEntries() + Bprime->getGlobalMaxNumRowEntries()));
981 }
982 }
983
984#ifdef HAVE_TPETRA_DEBUG
985 TEUCHOS_TEST_FOR_EXCEPTION(Aprime.is_null(), std::logic_error,
986 prefix << "At this point, Aprime is null. Please report this bug to the Tpetra developers.");
987 TEUCHOS_TEST_FOR_EXCEPTION(Bprime.is_null(), std::logic_error,
988 prefix << "At this point, Bprime is null. Please report this bug to the Tpetra developers.");
989 TEUCHOS_TEST_FOR_EXCEPTION(C.is_null(), std::logic_error,
990 prefix << "At this point, C is null. Please report this bug to the Tpetra developers.");
991#endif // HAVE_TPETRA_DEBUG
992
996
997 // do a loop over each matrix to add: A reordering might be more efficient
998 for (int k = 0; k < 2; ++k) {
999 typename crs_matrix_type::nonconst_global_inds_host_view_type Indices;
1000 typename crs_matrix_type::nonconst_values_host_view_type Values;
1001
1002 // Loop over each locally owned row of the current matrix (either
1003 // Aprime or Bprime), and sum its entries into the corresponding
1004 // row of C. This works regardless of whether Aprime or Bprime
1005 // has the same row Map as C, because both sumIntoGlobalValues and
1006 // insertGlobalValues allow summing resp. inserting into nonowned
1007 // rows of C.
1008#ifdef HAVE_TPETRA_DEBUG
1009 TEUCHOS_TEST_FOR_EXCEPTION(Mat[k].is_null(), std::logic_error,
1010 prefix << "At this point, curRowMap is null. Please report this bug to the Tpetra developers.");
1011#endif // HAVE_TPETRA_DEBUG
1012 RCP<const map_type> curRowMap = Mat[k]->getRowMap();
1013#ifdef HAVE_TPETRA_DEBUG
1014 TEUCHOS_TEST_FOR_EXCEPTION(curRowMap.is_null(), std::logic_error,
1015 prefix << "At this point, curRowMap is null. Please report this bug to the Tpetra developers.");
1016#endif // HAVE_TPETRA_DEBUG
1017
1018 const size_t localNumRows = Mat[k]->getLocalNumRows();
1019 for (size_t i = 0; i < localNumRows; ++i) {
1020 const GlobalOrdinal globalRow = curRowMap->getGlobalElement(i);
1021 size_t numEntries = Mat[k]->getNumEntriesInGlobalRow(globalRow);
1022 if (numEntries > 0) {
1023 if (numEntries > Indices.extent(0)) {
1024 Kokkos::resize(Indices, numEntries);
1025 Kokkos::resize(Values, numEntries);
1026 }
1027 Mat[k]->getGlobalRowCopy(globalRow, Indices, Values, numEntries);
1028
1029 if (scalar[k] != STS::one()) {
1030 for (size_t j = 0; j < numEntries; ++j) {
1031 Values[j] *= scalar[k];
1032 }
1033 }
1034
1035 if (CwasFillComplete) {
1036 size_t result = C->sumIntoGlobalValues(globalRow, numEntries,
1037 reinterpret_cast<Scalar*>(Values.data()), Indices.data());
1038 TEUCHOS_TEST_FOR_EXCEPTION(result != numEntries, std::logic_error,
1039 prefix << "sumIntoGlobalValues failed to add entries from A or B into C.");
1040 } else {
1041 C->insertGlobalValues(globalRow, numEntries,
1042 reinterpret_cast<Scalar*>(Values.data()), Indices.data());
1043 }
1044 }
1045 }
1046 }
1047 if (CwasFillComplete) {
1048 C->fillComplete(C->getDomainMap(),
1049 C->getRangeMap());
1050 }
1051}
1052
1053// This version of Add takes C as const RCP&, so C must not be null on input. Otherwise, its behavior is identical
1054// to the above version where C is RCP&.
1055template <class Scalar,
1056 class LocalOrdinal,
1057 class GlobalOrdinal,
1058 class Node>
1059void Add(
1061 bool transposeA,
1064 bool transposeB,
1067 std::string prefix = "TpetraExt::MatrixMatrix::Add(): ";
1068
1069 TEUCHOS_TEST_FOR_EXCEPTION(C.is_null(), std::invalid_argument,
1070 prefix << "C must not be null");
1071
1072 Teuchos::RCP<CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> C_ = C;
1074}
1075
1076} // End namespace MatrixMatrix
1077
1078namespace MMdetails {
1079
1080// nvcc 12.3 and 12.4 report getApplyHelper() as inaccessible even if
1081// CrsMatrix friends kokkos_kernels_mult_A_B_newmatrix. Use this non-template
1082// wrapper instead. Someone had the same problem in 2011 here:
1083// https://forums.developer.nvidia.com/t/24378.
1084struct CrsMatrixApplyHelperAccess {
1085 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1086 static auto get(const CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& matrix) {
1087 return matrix.getApplyHelper();
1088 }
1089};
1090
1091template <class Scalar,
1092 class LocalOrdinal,
1093 class GlobalOrdinal,
1094 class Node,
1095 class LocalOrdinalViewType>
1096void kokkos_kernels_mult_A_B_newmatrix(
1097 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
1098 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
1099 const LocalOrdinalViewType& Acol2Brow,
1100 const LocalOrdinalViewType& Acol2Irow,
1101 const LocalOrdinalViewType& Bcol2Ccol,
1102 const LocalOrdinalViewType& Icol2Ccol,
1103 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
1104 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
1105 const std::string& label,
1106 const Teuchos::RCP<Teuchos::ParameterList>& params) {
1107 using backend_type = KokkosKernelsSPGEMMBackend<Node>;
1108 using Teuchos::RCP;
1109 using Teuchos::rcp;
1110
1112 using device_t = typename KCRS::device_type;
1113 using graph_t = typename KCRS::StaticCrsGraphType;
1114 using lno_view_t = typename graph_t::row_map_type::non_const_type;
1115 using int_view_t = Kokkos::View<int*, typename lno_view_t::array_layout, typename lno_view_t::memory_space, typename lno_view_t::memory_traits>;
1116 using lno_nnz_view_t = typename graph_t::entries_type::non_const_type;
1117 using scalar_view_t = typename KCRS::values_type::non_const_type;
1118 using KernelHandle = KokkosKernels::Experimental::KokkosKernelsHandle<
1119 typename lno_view_t::const_value_type, typename lno_nnz_view_t::const_value_type, typename scalar_view_t::const_value_type,
1120 typename device_t::execution_space, typename device_t::memory_space, typename device_t::memory_space>;
1121 using IntKernelHandle = KokkosKernels::Experimental::KokkosKernelsHandle<
1122 typename int_view_t::const_value_type, typename lno_nnz_view_t::const_value_type, typename scalar_view_t::const_value_type,
1123 typename device_t::execution_space, typename device_t::memory_space, typename device_t::memory_space>;
1124
1125 const std::string wrapperLabel = "TpetraExt: MMM: Newmatrix " + backend_type::algorithm_label() + "Wrapper";
1126 RCP<Tpetra::Details::ProfilingRegion> MM =
1127 rcp(new Tpetra::Details::ProfilingRegion(wrapperLabel));
1128
1129 int team_work_size = 16;
1130 std::string myalg("SPGEMM_DEFAULT");
1131 if (!params.is_null()) {
1132 const std::string prefixedAlg = backend_type::parameter_prefix() + ": algorithm";
1133 const std::string prefixedTeam = backend_type::parameter_prefix() + ": team work size";
1134 if (params->isParameter(prefixedAlg))
1135 myalg = params->get(prefixedAlg, myalg);
1136 if (params->isParameter(prefixedTeam))
1137 team_work_size = params->get(prefixedTeam, team_work_size);
1138 }
1139
1140 const KCRS Amat = Aview.origMatrix->getLocalMatrixDevice();
1141
1142 const std::string genericAlg = backend_type::algorithm_label() + " algorithm";
1143 if (!params.is_null() && params->isParameter(genericAlg))
1144 myalg = params->get(genericAlg, myalg);
1145 KokkosSparse::SPGEMMAlgorithm alg_enum = KokkosSparse::StringToSPGEMMAlgorithm(myalg);
1146
1147 KCRS Bmerged = Tpetra::MMdetails::merge_matrices(
1148 Aview, Bview, Acol2Brow, Acol2Irow, Bcol2Ccol, Icol2Ccol, C.getColMap()->getLocalNumElements());
1149 backend_type::pre_spgemm(Bmerged);
1150
1151 const std::string coreLabel = "TpetraExt: MMM: Newmatrix " + backend_type::algorithm_label() + "Core";
1152 MM = Teuchos::null;
1153 MM = rcp(new Tpetra::Details::ProfilingRegion(coreLabel));
1154
1155 typename KernelHandle::nnz_lno_t AnumRows = Amat.numRows();
1156 typename KernelHandle::nnz_lno_t BnumRows = Bmerged.numRows();
1157 typename KernelHandle::nnz_lno_t BnumCols = Bmerged.numCols();
1158
1159 lno_view_t row_mapC(Kokkos::ViewAllocateWithoutInitializing("non_const_lno_row"), AnumRows + 1);
1160 lno_nnz_view_t entriesC;
1161 scalar_view_t valuesC;
1162
1163 Tpetra::Details::IntRowPtrHelper<decltype(Bmerged)> irph(Bmerged.nnz(), Bmerged.graph.row_map);
1164 const bool useIntRowptrs =
1165 irph.shouldUseIntRowptrs() &&
1166 CrsMatrixApplyHelperAccess::get(*Aview.origMatrix)->shouldUseIntRowptrs();
1167
1168 if (useIntRowptrs) {
1169 IntKernelHandle kh;
1170 kh.create_spgemm_handle(alg_enum);
1171 kh.set_team_work_size(team_work_size);
1172
1173 int_view_t int_row_mapC(Kokkos::ViewAllocateWithoutInitializing("non_const_int_row"), AnumRows + 1);
1174
1175 auto Aint = CrsMatrixApplyHelperAccess::get(*Aview.origMatrix)->getIntRowptrMatrix(Amat);
1176 auto Bint = irph.getIntRowptrMatrix(Bmerged);
1177
1178 {
1179 Tpetra::Details::ProfilingRegion MM2("TpetraExt: MMM: Newmatrix KokkosKernels symbolic int");
1180 KokkosSparse::spgemm_symbolic(
1181 &kh, AnumRows, BnumRows, BnumCols, Aint.graph.row_map, Aint.graph.entries, false, Bint.graph.row_map, Bint.graph.entries, false, int_row_mapC);
1182 }
1183
1184 Tpetra::Details::ProfilingRegion MM2("TpetraExt: MMM: Newmatrix KokkosKernels numeric int");
1185 size_t c_nnz_size = kh.get_spgemm_handle()->get_c_nnz();
1186 if (c_nnz_size) {
1187 entriesC = lno_nnz_view_t(Kokkos::ViewAllocateWithoutInitializing("entriesC"), c_nnz_size);
1188 valuesC = scalar_view_t(Kokkos::ViewAllocateWithoutInitializing("valuesC"), c_nnz_size);
1189 }
1190 KokkosSparse::spgemm_numeric(
1191 &kh, AnumRows, BnumRows, BnumCols, Aint.graph.row_map, Aint.graph.entries, Aint.values, false,
1192 Bint.graph.row_map, Bint.graph.entries, Bint.values, false, int_row_mapC, entriesC, valuesC);
1193 Kokkos::parallel_for(
1194 Kokkos::RangePolicy<typename device_t::execution_space>(0, int_row_mapC.size()),
1195 KOKKOS_LAMBDA(const int i) { row_mapC(i) = int_row_mapC(i); });
1196 kh.destroy_spgemm_handle();
1197
1198 } else {
1199 KernelHandle kh;
1200 kh.create_spgemm_handle(alg_enum);
1201 kh.set_team_work_size(team_work_size);
1202
1203 {
1204 Tpetra::Details::ProfilingRegion MM2("TpetraExt: MMM: Newmatrix KokkosKernels symbolic non-int");
1205 KokkosSparse::spgemm_symbolic(
1206 &kh, AnumRows, BnumRows, BnumCols, Amat.graph.row_map, Amat.graph.entries, false, Bmerged.graph.row_map, Bmerged.graph.entries, false, row_mapC);
1207 }
1208
1209 Tpetra::Details::ProfilingRegion MM2("TpetraExt: MMM: Newmatrix KokkosKernels numeric non-int");
1210 size_t c_nnz_size = kh.get_spgemm_handle()->get_c_nnz();
1211 if (c_nnz_size) {
1212 entriesC = lno_nnz_view_t(Kokkos::ViewAllocateWithoutInitializing("entriesC"), c_nnz_size);
1213 valuesC = scalar_view_t(Kokkos::ViewAllocateWithoutInitializing("valuesC"), c_nnz_size);
1214 }
1215 KokkosSparse::spgemm_numeric(
1216 &kh, AnumRows, BnumRows, BnumCols, Amat.graph.row_map, Amat.graph.entries, Amat.values, false,
1217 Bmerged.graph.row_map, Bmerged.graph.entries, Bmerged.values, false, row_mapC, entriesC, valuesC);
1218 kh.destroy_spgemm_handle();
1219 }
1220
1221 const std::string sortLabel = "TpetraExt: MMM: Newmatrix " + backend_type::algorithm_label() + "Sort";
1222 MM = Teuchos::null;
1223 MM = rcp(new Tpetra::Details::ProfilingRegion(sortLabel));
1224
1225 if (params.is_null() || params->get("sort entries", true))
1226 Import_Util::sortCrsEntries(row_mapC, entriesC, valuesC);
1227 C.setAllValues(row_mapC, entriesC, valuesC);
1228
1229 const std::string esfcLabel = "TpetraExt: MMM: Newmatrix " + backend_type::algorithm_label() + "ESFC";
1230 MM = Teuchos::null;
1231 MM = rcp(new Tpetra::Details::ProfilingRegion(esfcLabel));
1232
1233 RCP<Teuchos::ParameterList> labelList = rcp(new Teuchos::ParameterList);
1234 labelList->set("Timer Label", label);
1235 if (!params.is_null())
1236 labelList->set("compute global constants", params->get("compute global constants", true));
1237 RCP<const Export<LocalOrdinal, GlobalOrdinal, Node>> dummyExport;
1238 C.expertStaticFillComplete(Bview.origMatrix->getDomainMap(), Aview.origMatrix->getRangeMap(), Cimport, dummyExport, labelList);
1239}
1240
1241template <class Scalar,
1242 class LocalOrdinal,
1243 class GlobalOrdinal,
1244 class Node,
1245 class LocalOrdinalViewType>
1246void host_mult_A_B_reuse(
1247 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
1248 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
1249 const LocalOrdinalViewType& targetMapToOrigRow_dev,
1250 const LocalOrdinalViewType& targetMapToImportRow_dev,
1251 const LocalOrdinalViewType& Bcol2Ccol_dev,
1252 const LocalOrdinalViewType& Icol2Ccol_dev,
1253 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
1254 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
1255 const std::string& label,
1256 const Teuchos::RCP<Teuchos::ParameterList>& params) {
1257 // FIXME: Right now, this is a cut-and-paste of the serial kernel
1258
1259 using Teuchos::RCP;
1260 using Teuchos::rcp;
1261
1262 // By default, if A*B results in an entry that is not supported by the graph of C, we throw.
1263 // This option allows to override this behavior and silently ignores such entries.
1264 bool throwOnInsert = true;
1265 if (!params.is_null() && params->isType<bool>("MM Throw For Non-Existent Entries"))
1266 throwOnInsert = params->get<bool>("MM Throw For Non-Existent Entries");
1267
1268 RCP<Tpetra::Details::ProfilingRegion> MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: Reuse SerialCore"));
1269
1270 // Lots and lots of typedefs
1271 typedef typename Tpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_host_type KCRS;
1272 typedef typename KCRS::StaticCrsGraphType graph_t;
1273 typedef typename graph_t::row_map_type::const_type c_lno_view_t;
1274 typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
1275 typedef typename KCRS::values_type::non_const_type scalar_view_t;
1276
1277 typedef Scalar SC;
1278 typedef LocalOrdinal LO;
1279 typedef GlobalOrdinal GO;
1280 typedef Node NO;
1281 typedef Map<LO, GO, NO> map_type;
1282 const size_t ST_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
1283 const LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
1284 const SC SC_ZERO = Teuchos::ScalarTraits<Scalar>::zero();
1285
1286 // Since this is being run on Cuda, we need to fence because the below code will use UVM
1287 // typename graph_t::execution_space().fence();
1288
1289 // KDDKDD UVM Without UVM, need to copy targetMap arrays to host.
1290 // KDDKDD UVM Ideally, this function would run on device and use
1291 // KDDKDD UVM KokkosKernels instead of this host implementation.
1292 auto targetMapToOrigRow =
1293 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1294 targetMapToOrigRow_dev);
1295 auto targetMapToImportRow =
1296 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1297 targetMapToImportRow_dev);
1298 auto Bcol2Ccol =
1299 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1300 Bcol2Ccol_dev);
1301 auto Icol2Ccol =
1302 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1303 Icol2Ccol_dev);
1304
1305 // Sizes
1306 RCP<const map_type> Ccolmap = C.getColMap();
1307 size_t m = Aview.origMatrix->getLocalNumRows();
1308 size_t n = Ccolmap->getLocalNumElements();
1309
1310 // Grab the Kokkos::SparseCrsMatrices & inner stuff
1311 const KCRS Amat = Aview.origMatrix->getLocalMatrixHost();
1312 const KCRS Bmat = Bview.origMatrix->getLocalMatrixHost();
1313 const KCRS Cmat = C.getLocalMatrixHost();
1314
1315 c_lno_view_t Arowptr = Amat.graph.row_map,
1316 Browptr = Bmat.graph.row_map,
1317 Crowptr = Cmat.graph.row_map;
1318 const lno_nnz_view_t Acolind = Amat.graph.entries,
1319 Bcolind = Bmat.graph.entries,
1320 Ccolind = Cmat.graph.entries;
1321 const scalar_view_t Avals = Amat.values, Bvals = Bmat.values;
1322 scalar_view_t Cvals = Cmat.values;
1323
1324 c_lno_view_t Irowptr;
1325 lno_nnz_view_t Icolind;
1326 scalar_view_t Ivals;
1327 if (!Bview.importMatrix.is_null()) {
1328 auto lclB = Bview.importMatrix->getLocalMatrixHost();
1329 Irowptr = lclB.graph.row_map;
1330 Icolind = lclB.graph.entries;
1331 Ivals = lclB.values;
1332 }
1333
1334 // Classic csr assembly (low memory edition)
1335 // mfh 27 Sep 2016: The c_status array is an implementation detail
1336 // of the local sparse matrix-matrix multiply routine.
1337
1338 // The status array will contain the index into colind where this entry was last deposited.
1339 // c_status[i] < CSR_ip - not in the row yet
1340 // c_status[i] >= CSR_ip - this is the entry where you can find the data
1341 // We start with this filled with INVALID's indicating that there are no entries yet.
1342 // Sadly, this complicates the code due to the fact that size_t's are unsigned.
1343 std::vector<size_t> c_status(n, ST_INVALID);
1344
1345 // For each row of A/C
1346 size_t CSR_ip = 0, OLD_ip = 0;
1347 for (size_t i = 0; i < m; i++) {
1348 // First fill the c_status array w/ locations where we're allowed to
1349 // generate nonzeros for this row
1350 OLD_ip = Crowptr[i];
1351 CSR_ip = Crowptr[i + 1];
1352 for (size_t k = OLD_ip; k < CSR_ip; k++) {
1353 c_status[Ccolind[k]] = k;
1354
1355 // Reset values in the row of C
1356 Cvals[k] = SC_ZERO;
1357 }
1358
1359 for (size_t k = Arowptr[i]; k < Arowptr[i + 1]; k++) {
1360 LO Aik = Acolind[k];
1361 const SC Aval = Avals[k];
1362 if (Aval == SC_ZERO)
1363 continue;
1364
1365 if (targetMapToOrigRow[Aik] != LO_INVALID) {
1366 // Local matrix
1367 size_t Bk = Teuchos::as<size_t>(targetMapToOrigRow[Aik]);
1368
1369 for (size_t j = Browptr[Bk]; j < Browptr[Bk + 1]; ++j) {
1370 LO Bkj = Bcolind[j];
1371 LO Cij = Bcol2Ccol[Bkj];
1372
1373 const bool badInsert = (Cij == LO_INVALID) || (c_status[Cij] < OLD_ip) || (c_status[Cij] >= CSR_ip);
1374 if (!badInsert)
1375 Cvals[c_status[Cij]] += Aval * Bvals[j];
1376 else if (throwOnInsert)
1377 TEUCHOS_TEST_FOR_EXCEPTION(badInsert,
1378 std::runtime_error, "Trying to insert a new entry (" << i << "," << Cij << ") into a static graph "
1379 << "(c_status = " << c_status[Cij] << " of [" << OLD_ip << "," << CSR_ip << "))");
1380 }
1381
1382 } else {
1383 // Remote matrix
1384 size_t Ik = Teuchos::as<size_t>(targetMapToImportRow[Aik]);
1385 for (size_t j = Irowptr[Ik]; j < Irowptr[Ik + 1]; ++j) {
1386 LO Ikj = Icolind[j];
1387 LO Cij = Icol2Ccol[Ikj];
1388
1389 const bool badInsert = (Cij == LO_INVALID) || (c_status[Cij] < OLD_ip) || (c_status[Cij] >= CSR_ip);
1390 if (!badInsert)
1391 Cvals[c_status[Cij]] += Aval * Ivals[j];
1392 else if (throwOnInsert)
1393 TEUCHOS_TEST_FOR_EXCEPTION(badInsert,
1394 std::runtime_error, "Trying to insert a new entry (" << i << "," << Cij << ") into a static graph "
1395 << "(c_status = " << c_status[Cij] << " of [" << OLD_ip << "," << CSR_ip << "))");
1396 }
1397 }
1398 }
1399 }
1400
1401 C.fillComplete(C.getDomainMap(), C.getRangeMap());
1402}
1403
1404template <class Scalar,
1405 class LocalOrdinal,
1406 class GlobalOrdinal,
1407 class Node,
1408 class LocalOrdinalViewType>
1409void kokkos_kernels_jacobi_A_B_newmatrix(typename Teuchos::ScalarTraits<Scalar>::magnitudeType omega,
1410 const Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Dinv,
1411 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
1412 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
1413 const LocalOrdinalViewType& Acol2Brow,
1414 const LocalOrdinalViewType& Acol2Irow,
1415 const LocalOrdinalViewType& Bcol2Ccol,
1416 const LocalOrdinalViewType& Icol2Ccol,
1417 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
1418 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
1419 const std::string& label,
1420 const Teuchos::RCP<Teuchos::ParameterList>& params) {
1421 using backend_type = KokkosKernelsSPGEMMBackend<Node>;
1422 // Check if the diagonal entries exist in debug mode
1423 const bool debug = Tpetra::Details::Behavior::debug();
1424 if (debug) {
1425 auto rowMap = Aview.origMatrix->getRowMap();
1427 Aview.origMatrix->getLocalDiagCopy(diags);
1428 size_t diagLength = rowMap->getLocalNumElements();
1429 Teuchos::Array<Scalar> diagonal(diagLength);
1430 diags.get1dCopy(diagonal());
1431
1432 for (size_t i = 0; i < diagLength; ++i) {
1433 TEUCHOS_TEST_FOR_EXCEPTION(diagonal[i] == Teuchos::ScalarTraits<Scalar>::zero(),
1434 std::runtime_error,
1435 "Matrix A has a zero/missing diagonal: " << diagonal[i] << std::endl
1436 << "KokkosKernels Jacobi-fused SpGEMM requires nonzero diagonal entries in A" << std::endl);
1437 }
1438 }
1439
1440 using Teuchos::RCP;
1441 using Teuchos::rcp;
1442 RCP<Tpetra::Details::ProfilingRegion> MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: Jacobi: Newmatrix KokkosKernels"));
1443
1444 // Usings
1445 using device_t = typename Node::device_type;
1447 using graph_t = typename matrix_t::StaticCrsGraphType;
1448 using lno_view_t = typename graph_t::row_map_type::non_const_type;
1449 using int_view_t = Kokkos::View<int*,
1450 typename lno_view_t::array_layout,
1451 typename lno_view_t::memory_space,
1452 typename lno_view_t::memory_traits>;
1453 using lno_nnz_view_t = typename graph_t::entries_type::non_const_type;
1454 using scalar_view_t = typename matrix_t::values_type::non_const_type;
1455
1456 // KokkosKernels handle
1457 using handle_t = typename KokkosKernels::Experimental::KokkosKernelsHandle<
1458 typename lno_view_t::const_value_type, typename lno_nnz_view_t::const_value_type, typename scalar_view_t::const_value_type,
1459 typename device_t::execution_space, typename device_t::memory_space, typename device_t::memory_space>;
1460
1461 using int_handle_t = typename KokkosKernels::Experimental::KokkosKernelsHandle<
1462 typename int_view_t::const_value_type, typename lno_nnz_view_t::const_value_type, typename scalar_view_t::const_value_type,
1463 typename device_t::execution_space, typename device_t::memory_space, typename device_t::memory_space>;
1464
1465 // Merge the B and Bimport matrices
1466 const matrix_t Bmerged = Tpetra::MMdetails::merge_matrices(Aview, Bview, Acol2Brow, Acol2Irow, Bcol2Ccol, Icol2Ccol, C.getColMap()->getLocalNumElements());
1467
1468 // Get the properties and arrays of input matrices
1469 const matrix_t Amat = Aview.origMatrix->getLocalMatrixDevice();
1470 const matrix_t Bmat = Bview.origMatrix->getLocalMatrixDevice();
1471
1472 typename handle_t::nnz_lno_t AnumRows = Amat.numRows();
1473 typename handle_t::nnz_lno_t BnumRows = Bmerged.numRows();
1474 typename handle_t::nnz_lno_t BnumCols = Bmerged.numCols();
1475
1476 // Arrays of the output matrix
1477 lno_view_t row_mapC(Kokkos::ViewAllocateWithoutInitializing("row_mapC"), AnumRows + 1);
1478 lno_nnz_view_t entriesC;
1479 scalar_view_t valuesC;
1480
1481 // Options
1482 int team_work_size = 16;
1483 std::string myalg("SPGEMM_DEFAULT");
1484 if (!params.is_null()) {
1485 const std::string prefixedAlg = backend_type::parameter_prefix() + ": algorithm";
1486 const std::string prefixedTeam = backend_type::parameter_prefix() + ": team work size";
1487 if (params->isParameter(prefixedAlg))
1488 myalg = params->get(prefixedAlg, myalg);
1489 if (params->isParameter(prefixedTeam))
1490 team_work_size = params->get(prefixedTeam, team_work_size);
1491 }
1492
1493 // Get the algorithm mode
1494 const std::string genericAlg = backend_type::algorithm_label() + " algorithm";
1495 if (!params.is_null() && params->isParameter(genericAlg))
1496 myalg = params->get(genericAlg, myalg);
1497 KokkosSparse::SPGEMMAlgorithm alg_enum = KokkosSparse::StringToSPGEMMAlgorithm(myalg);
1498
1499 // decide whether to use integer-typed row pointers for this spgemm
1500 Tpetra::Details::IntRowPtrHelper<decltype(Bmerged)> irph(Bmerged.nnz(), Bmerged.graph.row_map);
1501 const bool useIntRowptrs =
1502 irph.shouldUseIntRowptrs() &&
1503 CrsMatrixApplyHelperAccess::get(*Aview.origMatrix)->shouldUseIntRowptrs();
1504
1505 const Scalar jacobiOmega = omega * Teuchos::ScalarTraits<Scalar>::one();
1506
1507 if (useIntRowptrs) {
1508 int_handle_t kh;
1509 kh.create_spgemm_handle(alg_enum);
1510 kh.set_team_work_size(team_work_size);
1511
1512 int_view_t int_row_mapC(Kokkos::ViewAllocateWithoutInitializing("int_row_mapC"), AnumRows + 1);
1513
1514 auto Aint = CrsMatrixApplyHelperAccess::get(*Aview.origMatrix)->getIntRowptrMatrix(Amat);
1515 auto Bint = irph.getIntRowptrMatrix(Bmerged);
1516
1517 {
1518 Tpetra::Details::ProfilingRegion MM2("TpetraExt: Jacobi: KokkosKernels symbolic int");
1519 KokkosSparse::spgemm_symbolic(&kh, AnumRows, BnumRows, BnumCols,
1520 Aint.graph.row_map, Aint.graph.entries, false,
1521 Bint.graph.row_map, Bint.graph.entries, false,
1522 int_row_mapC);
1523 }
1524
1525 size_t c_nnz_size = kh.get_spgemm_handle()->get_c_nnz();
1526 if (c_nnz_size) {
1527 entriesC = lno_nnz_view_t(Kokkos::ViewAllocateWithoutInitializing("entriesC"), c_nnz_size);
1528 valuesC = scalar_view_t(Kokkos::ViewAllocateWithoutInitializing("valuesC"), c_nnz_size);
1529 }
1530 Tpetra::Details::ProfilingRegion MM2("TpetraExt: Jacobi: KokkosKernels numeric int");
1531
1532 if (c_nnz_size) {
1533 // even though there is no TPL for this, we have to use the same handle that was used in the symbolic phase,
1534 // so need to have a special int-typed call for this as well.
1535 KokkosSparse::Experimental::spgemm_jacobi(&kh, AnumRows, BnumRows, BnumCols,
1536 Aint.graph.row_map, Aint.graph.entries, Amat.values, false,
1537 Bint.graph.row_map, Bint.graph.entries, Bint.values, false,
1538 int_row_mapC, entriesC, valuesC,
1539 jacobiOmega, Dinv.getLocalViewDevice(Access::ReadOnly));
1540 }
1541 // transfer the integer rowptrs back to the correct rowptr type
1542 Kokkos::parallel_for(
1543 Kokkos::RangePolicy<typename device_t::execution_space>(0, int_row_mapC.size()),
1544 KOKKOS_LAMBDA(int i) { row_mapC(i) = int_row_mapC(i); });
1545 kh.destroy_spgemm_handle();
1546 } else {
1547 handle_t kh;
1548 kh.create_spgemm_handle(alg_enum);
1549 kh.set_team_work_size(team_work_size);
1550
1551 {
1552 Tpetra::Details::ProfilingRegion MM2("TpetraExt: Jacobi: KokkosKernels symbolic non-int");
1553 KokkosSparse::spgemm_symbolic(&kh, AnumRows, BnumRows, BnumCols,
1554 Amat.graph.row_map, Amat.graph.entries, false,
1555 Bmerged.graph.row_map, Bmerged.graph.entries, false,
1556 row_mapC);
1557 }
1558
1559 size_t c_nnz_size = kh.get_spgemm_handle()->get_c_nnz();
1560 if (c_nnz_size) {
1561 entriesC = lno_nnz_view_t(Kokkos::ViewAllocateWithoutInitializing("entriesC"), c_nnz_size);
1562 valuesC = scalar_view_t(Kokkos::ViewAllocateWithoutInitializing("valuesC"), c_nnz_size);
1563 }
1564
1565 Tpetra::Details::ProfilingRegion MM2("TpetraExt: Jacobi: KokkosKernels numeric non-int");
1566 if (c_nnz_size) {
1567 KokkosSparse::Experimental::spgemm_jacobi(&kh, AnumRows, BnumRows, BnumCols,
1568 Amat.graph.row_map, Amat.graph.entries, Amat.values, false,
1569 Bmerged.graph.row_map, Bmerged.graph.entries, Bmerged.values, false,
1570 row_mapC, entriesC, valuesC,
1571 jacobiOmega, Dinv.getLocalViewDevice(Access::ReadOnly));
1572 }
1573 kh.destroy_spgemm_handle();
1574 }
1575
1576 const std::string sortLabel = "TpetraExt: Jacobi: Newmatrix " + backend_type::algorithm_label() + "Sort";
1577 MM = Teuchos::null;
1578 MM = rcp(new Tpetra::Details::ProfilingRegion(sortLabel));
1579
1580 // Sort & set values
1581 if (params.is_null() || params->get("sort entries", true))
1582 Import_Util::sortCrsEntries(row_mapC, entriesC, valuesC);
1583 C.setAllValues(row_mapC, entriesC, valuesC);
1584
1585 const std::string esfcLabel = "TpetraExt: Jacobi: Newmatrix " + backend_type::algorithm_label() + "ESFC";
1586 MM = Teuchos::null;
1587 MM = rcp(new Tpetra::Details::ProfilingRegion(esfcLabel));
1588
1589 // Final Fillcomplete
1590 Teuchos::RCP<Teuchos::ParameterList> labelList = rcp(new Teuchos::ParameterList);
1591 labelList->set("Timer Label", label);
1592 if (!params.is_null()) labelList->set("compute global constants", params->get("compute global constants", true));
1593 Teuchos::RCP<const Export<LocalOrdinal, GlobalOrdinal, Node>> dummyExport;
1594 C.expertStaticFillComplete(Bview.origMatrix->getDomainMap(), Aview.origMatrix->getRangeMap(), Cimport, dummyExport, labelList);
1595}
1596
1597template <class Scalar,
1598 class LocalOrdinal,
1599 class GlobalOrdinal,
1600 class Node,
1601 class LocalOrdinalViewType>
1602void host_jacobi_A_B_reuse(typename Teuchos::ScalarTraits<Scalar>::magnitudeType omega,
1603 const Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Dinv,
1604 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
1605 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
1606 const LocalOrdinalViewType& targetMapToOrigRow_dev,
1607 const LocalOrdinalViewType& targetMapToImportRow_dev,
1608 const LocalOrdinalViewType& Bcol2Ccol_dev,
1609 const LocalOrdinalViewType& Icol2Ccol_dev,
1610 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
1611 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
1612 const std::string& label,
1613 const Teuchos::RCP<Teuchos::ParameterList>& params) {
1614 // FIXME: Right now, this is a cut-and-paste of the serial kernel
1615 using Teuchos::RCP;
1616 using Teuchos::rcp;
1617 RCP<Tpetra::Details::ProfilingRegion> MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: Jacobi: Reuse CudaCore"));
1618
1619 // Lots and lots of typedefs
1620 typedef typename Tpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_host_type KCRS;
1621 typedef typename KCRS::StaticCrsGraphType graph_t;
1622 typedef typename graph_t::row_map_type::const_type c_lno_view_t;
1623 typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
1624 typedef typename KCRS::values_type::non_const_type scalar_view_t;
1625 typedef typename scalar_view_t::memory_space scalar_memory_space;
1626
1627 typedef Scalar SC;
1628 typedef LocalOrdinal LO;
1629 typedef GlobalOrdinal GO;
1630 typedef Node NO;
1631 typedef Map<LO, GO, NO> map_type;
1632 const size_t ST_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
1633 const LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
1634 const SC SC_ZERO = Teuchos::ScalarTraits<Scalar>::zero();
1635
1636 // Since this is being run on Cuda, we need to fence because the below host code will use UVM
1637 // KDDKDD typename graph_t::execution_space().fence();
1638
1639 // KDDKDD UVM Without UVM, need to copy targetMap arrays to host.
1640 // KDDKDD UVM Ideally, this function would run on device and use
1641 // KDDKDD UVM KokkosKernels instead of this host implementation.
1642 auto targetMapToOrigRow =
1643 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1644 targetMapToOrigRow_dev);
1645 auto targetMapToImportRow =
1646 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1647 targetMapToImportRow_dev);
1648 auto Bcol2Ccol =
1649 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1650 Bcol2Ccol_dev);
1651 auto Icol2Ccol =
1652 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
1653 Icol2Ccol_dev);
1654
1655 // Sizes
1656 RCP<const map_type> Ccolmap = C.getColMap();
1657 size_t m = Aview.origMatrix->getLocalNumRows();
1658 size_t n = Ccolmap->getLocalNumElements();
1659
1660 // Grab the Kokkos::SparseCrsMatrices & inner stuff
1661 const KCRS Amat = Aview.origMatrix->getLocalMatrixHost();
1662 const KCRS Bmat = Bview.origMatrix->getLocalMatrixHost();
1663 const KCRS Cmat = C.getLocalMatrixHost();
1664
1665 c_lno_view_t Arowptr = Amat.graph.row_map, Browptr = Bmat.graph.row_map, Crowptr = Cmat.graph.row_map;
1666 const lno_nnz_view_t Acolind = Amat.graph.entries, Bcolind = Bmat.graph.entries, Ccolind = Cmat.graph.entries;
1667 const scalar_view_t Avals = Amat.values, Bvals = Bmat.values;
1668 scalar_view_t Cvals = Cmat.values;
1669
1670 c_lno_view_t Irowptr;
1671 lno_nnz_view_t Icolind;
1672 scalar_view_t Ivals;
1673 if (!Bview.importMatrix.is_null()) {
1674 auto lclB = Bview.importMatrix->getLocalMatrixHost();
1675 Irowptr = lclB.graph.row_map;
1676 Icolind = lclB.graph.entries;
1677 Ivals = lclB.values;
1678 }
1679
1680 // Jacobi-specific inner stuff
1681 auto Dvals =
1682 Dinv.template getLocalView<scalar_memory_space>(Access::ReadOnly);
1683
1684 // The status array will contain the index into colind where this entry was last deposited.
1685 // c_status[i] < CSR_ip - not in the row yet
1686 // c_status[i] >= CSR_ip - this is the entry where you can find the data
1687 // We start with this filled with INVALID's indicating that there are no entries yet.
1688 // Sadly, this complicates the code due to the fact that size_t's are unsigned.
1689 std::vector<size_t> c_status(n, ST_INVALID);
1690
1691 // For each row of A/C
1692 size_t CSR_ip = 0, OLD_ip = 0;
1693 for (size_t i = 0; i < m; i++) {
1694 // First fill the c_status array w/ locations where we're allowed to
1695 // generate nonzeros for this row
1696 OLD_ip = Crowptr[i];
1697 CSR_ip = Crowptr[i + 1];
1698 for (size_t k = OLD_ip; k < CSR_ip; k++) {
1699 c_status[Ccolind[k]] = k;
1700
1701 // Reset values in the row of C
1702 Cvals[k] = SC_ZERO;
1703 }
1704
1705 SC minusOmegaDval = -omega * Dvals(i, 0);
1706
1707 // Entries of B
1708 for (size_t j = Browptr[i]; j < Browptr[i + 1]; j++) {
1709 Scalar Bval = Bvals[j];
1710 if (Bval == SC_ZERO)
1711 continue;
1712 LO Bij = Bcolind[j];
1713 LO Cij = Bcol2Ccol[Bij];
1714
1715 TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip,
1716 std::runtime_error, "Trying to insert a new entry into a static graph");
1717
1718 Cvals[c_status[Cij]] = Bvals[j];
1719 }
1720
1721 // Entries of -omega * Dinv * A * B
1722 for (size_t k = Arowptr[i]; k < Arowptr[i + 1]; k++) {
1723 LO Aik = Acolind[k];
1724 const SC Aval = Avals[k];
1725 if (Aval == SC_ZERO)
1726 continue;
1727
1728 if (targetMapToOrigRow[Aik] != LO_INVALID) {
1729 // Local matrix
1730 size_t Bk = Teuchos::as<size_t>(targetMapToOrigRow[Aik]);
1731
1732 for (size_t j = Browptr[Bk]; j < Browptr[Bk + 1]; ++j) {
1733 LO Bkj = Bcolind[j];
1734 LO Cij = Bcol2Ccol[Bkj];
1735
1736 TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip,
1737 std::runtime_error, "Trying to insert a new entry into a static graph");
1738
1739 Cvals[c_status[Cij]] += minusOmegaDval * Aval * Bvals[j];
1740 }
1741
1742 } else {
1743 // Remote matrix
1744 size_t Ik = Teuchos::as<size_t>(targetMapToImportRow[Aik]);
1745 for (size_t j = Irowptr[Ik]; j < Irowptr[Ik + 1]; ++j) {
1746 LO Ikj = Icolind[j];
1747 LO Cij = Icol2Ccol[Ikj];
1748
1749 TEUCHOS_TEST_FOR_EXCEPTION(c_status[Cij] < OLD_ip || c_status[Cij] >= CSR_ip,
1750 std::runtime_error, "Trying to insert a new entry into a static graph");
1751
1752 Cvals[c_status[Cij]] += minusOmegaDval * Aval * Ivals[j];
1753 }
1754 }
1755 }
1756 }
1757
1758 MM = Teuchos::null;
1759 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: Jacobi: Reuse ESFC"));
1760
1761 C.fillComplete(C.getDomainMap(), C.getRangeMap());
1762}
1763
1764/*********************************************************************************************************/
1766// template <class TransferType>
1767// void printMultiplicationStatistics(Teuchos::RCP<TransferType > Transfer, const std::string &label) {
1768// if (Transfer.is_null())
1769// return;
1770//
1771// const Distributor & Distor = Transfer->getDistributor();
1772// Teuchos::RCP<const Teuchos::Comm<int> > Comm = Transfer->getSourceMap()->getComm();
1773//
1774// size_t rows_send = Transfer->getNumExportIDs();
1775// size_t rows_recv = Transfer->getNumRemoteIDs();
1776//
1777// size_t round1_send = Transfer->getNumExportIDs() * sizeof(size_t);
1778// size_t round1_recv = Transfer->getNumRemoteIDs() * sizeof(size_t);
1779// size_t num_send_neighbors = Distor.getNumSends();
1780// size_t num_recv_neighbors = Distor.getNumReceives();
1781// size_t round2_send, round2_recv;
1782// Distor.getLastDoStatistics(round2_send,round2_recv);
1783//
1784// int myPID = Comm->getRank();
1785// int NumProcs = Comm->getSize();
1786//
1787// // Processor by processor statistics
1788// // printf("[%d] %s Statistics: neigh[s/r]=%d/%d rows[s/r]=%d/%d r1bytes[s/r]=%d/%d r2bytes[s/r]=%d/%d\n",
1789// // myPID, label.c_str(),num_send_neighbors,num_recv_neighbors,rows_send,rows_recv,round1_send,round1_recv,round2_send,round2_recv);
1790//
1791// // Global statistics
1792// size_t lstats[8] = {num_send_neighbors,num_recv_neighbors,rows_send,rows_recv,round1_send,round1_recv,round2_send,round2_recv};
1793// size_t gstats_min[8], gstats_max[8];
1794//
1795// double lstats_avg[8], gstats_avg[8];
1796// for(int i=0; i<8; i++)
1797// lstats_avg[i] = ((double)lstats[i])/NumProcs;
1798//
1799// Teuchos::reduceAll(*Comm(),Teuchos::REDUCE_MIN,8,lstats,gstats_min);
1800// Teuchos::reduceAll(*Comm(),Teuchos::REDUCE_MAX,8,lstats,gstats_max);
1801// Teuchos::reduceAll(*Comm(),Teuchos::REDUCE_SUM,8,lstats_avg,gstats_avg);
1802//
1803// if(!myPID) {
1804// printf("%s Send Statistics[min/avg/max]: neigh=%d/%4.1f/%d rows=%d/%4.1f/%d round1=%d/%4.1f/%d round2=%d/%4.1f/%d\n", label.c_str(),
1805// (int)gstats_min[0],gstats_avg[0],(int)gstats_max[0], (int)gstats_min[2],gstats_avg[2],(int)gstats_max[2],
1806// (int)gstats_min[4],gstats_avg[4],(int)gstats_max[4], (int)gstats_min[6],gstats_avg[6],(int)gstats_max[6]);
1807// printf("%s Recv Statistics[min/avg/max]: neigh=%d/%4.1f/%d rows=%d/%4.1f/%d round1=%d/%4.1f/%d round2=%d/%4.1f/%d\n", label.c_str(),
1808// (int)gstats_min[1],gstats_avg[1],(int)gstats_max[1], (int)gstats_min[3],gstats_avg[3],(int)gstats_max[3],
1809// (int)gstats_min[5],gstats_avg[5],(int)gstats_max[5], (int)gstats_min[7],gstats_avg[7],(int)gstats_max[7]);
1810// }
1811// }
1812
1813// Kernel method for computing the local portion of C = A*B
1814template <class Scalar,
1815 class LocalOrdinal,
1816 class GlobalOrdinal,
1817 class Node>
1818void mult_AT_B_newmatrix(
1819 const CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
1820 const CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& B,
1821 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
1822 const std::string& label,
1823 const Teuchos::RCP<Teuchos::ParameterList>& params) {
1824 using Teuchos::RCP;
1825 using Teuchos::rcp;
1826 typedef Scalar SC;
1827 typedef LocalOrdinal LO;
1828 typedef GlobalOrdinal GO;
1829 typedef Node NO;
1830 typedef CrsMatrixStruct<SC, LO, GO, NO> crs_matrix_struct_type;
1831 typedef RowMatrixTransposer<SC, LO, GO, NO> transposer_type;
1832
1833 RCP<Tpetra::Details::ProfilingRegion> MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM-T: Transpose"));
1834
1835 /*************************************************************/
1836 /* 1) Local Transpose of A */
1837 /*************************************************************/
1838 transposer_type transposer(rcpFromRef(A), label + std::string("XP: "));
1839
1840 using Teuchos::ParameterList;
1841 RCP<ParameterList> transposeParams(new ParameterList);
1842 transposeParams->set("sort", true); // Kokkos Kernels spgemm requires inputs to be sorted
1843 if (!params.is_null()) {
1844 transposeParams->set("compute global constants",
1845 params->get("compute global constants: temporaries",
1846 false));
1847 }
1848 RCP<Tpetra::CrsMatrix<SC, LO, GO, NO>> Atrans =
1849 transposer.createTransposeLocal(transposeParams);
1850
1851 /*************************************************************/
1852 /* 2/3) Call mult_A_B_newmatrix w/ fillComplete */
1853 /*************************************************************/
1854 MM = Teuchos::null;
1855 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM-T: I&X"));
1856
1857 // Get views, asserting that no import is required to speed up computation
1858 crs_matrix_struct_type Aview;
1859 crs_matrix_struct_type Bview;
1860 RCP<const Import<LO, GO, NO>> dummyImporter;
1861
1862 // NOTE: the I&X routine sticks an importer on the paramlist as output, so we have to use a unique guy here
1863 RCP<Teuchos::ParameterList> importParams = Teuchos::rcp(new ParameterList);
1864 importParams->set("compute global constants", false);
1865 if (!params.is_null()) {
1866 importParams->setParameters(*params);
1867 if (params->isParameter("compute global constants: temporaries"))
1868 importParams->set("compute global constants",
1869 params->get<bool>("compute global constants: temporaries"));
1870 }
1871 MMdetails::import_and_extract_views(*Atrans, Atrans->getRowMap(),
1872 Aview, dummyImporter, true,
1873 label, importParams);
1874
1875 if (B.getRowMap()->isSameAs(*Atrans->getColMap())) {
1876 MMdetails::import_and_extract_views(B, B.getRowMap(), Bview, dummyImporter, true, label, importParams);
1877 } else {
1878 MMdetails::import_and_extract_views(B, Atrans->getColMap(), Bview, dummyImporter, false, label, importParams);
1879 }
1880
1881 MM = Teuchos::null;
1882 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM-T: AB-core"));
1883
1884 RCP<Tpetra::CrsMatrix<SC, LO, GO, NO>> Ctemp;
1885
1886 // If Atrans has no Exporter, we can use C instead of having to create a temp matrix
1887 bool needs_final_export = !Atrans->getGraph()->getExporter().is_null();
1888 if (needs_final_export) {
1889 Ctemp = rcp(new Tpetra::CrsMatrix<SC, LO, GO, NO>(Atrans->getRowMap(), 0));
1890 } else {
1891 Ctemp = rcp(&C, false);
1892 }
1893
1894 RCP<Teuchos::ParameterList> multParams = Teuchos::rcp(new ParameterList);
1895 if (!params.is_null()) {
1896 multParams->setParameters(*params);
1897 }
1898 multParams->set("compute global constants", !needs_final_export);
1899 mult_A_B_newmatrix(Aview, Bview, *Ctemp, label, multParams);
1900
1901 /*************************************************************/
1902 /* 4) exportAndFillComplete matrix */
1903 /*************************************************************/
1904 MM = Teuchos::null;
1905 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM-T: exportAndFillComplete"));
1906
1907 RCP<Tpetra::CrsMatrix<SC, LO, GO, NO>> Crcp(&C, false);
1908
1909 if (needs_final_export) {
1910 ParameterList labelList;
1911 labelList.set("Timer Label", label);
1912 if (!params.is_null()) {
1913 labelList.setParameters(*params);
1914 }
1915 ParameterList& labelList_subList = labelList.sublist("matrixmatrix: kernel params", false);
1916 labelList_subList.set("isMatrixMatrix_TransferAndFillComplete", true,
1917 "This parameter should be set to true only for MatrixMatrix operations: the optimization in Epetra that was ported to Tpetra does _not_ take into account the possibility that for any given source PID, a particular GID may not exist on the target PID: i.e. a transfer operation. A fix for this general case is in development.");
1918
1919 Ctemp->exportAndFillComplete(Crcp,
1920 *Ctemp->getGraph()->getExporter(),
1921 B.getDomainMap(),
1922 A.getDomainMap(),
1923 rcp(&labelList, false));
1924 }
1925#ifdef HAVE_TPETRA_MMM_STATISTICS
1926 printMultiplicationStatistics(Ctemp->getGraph()->getExporter(), label + std::string(" AT_B MMM"));
1927#endif
1928}
1929
1930/*********************************************************************************************************/
1931// Kernel method for computing the local portion of C = A*B
1932template <class Scalar,
1933 class LocalOrdinal,
1934 class GlobalOrdinal,
1935 class Node>
1936void mult_A_B(
1937 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
1938 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
1939 CrsWrapper<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
1940 const std::string& /* label */,
1941 const Teuchos::RCP<Teuchos::ParameterList>& params) {
1942 using Teuchos::Array;
1943 using Teuchos::ArrayRCP;
1944 using Teuchos::ArrayView;
1945 using Teuchos::null;
1946 using Teuchos::OrdinalTraits;
1947
1948 bool skipExplicitZero = true;
1949 if (params && params->isParameter("MM Skip Explicit Zeros")) {
1950 skipExplicitZero = params->get<bool>("MM Skip Explicit Zeros");
1951 }
1952
1953 typedef Teuchos::ScalarTraits<Scalar> STS;
1954 // TEUCHOS_FUNC_TIME_MONITOR_DIFF("mult_A_B", mult_A_B);
1955 LocalOrdinal C_firstCol = Bview.colMap->getMinLocalIndex();
1956 LocalOrdinal C_lastCol = Bview.colMap->getMaxLocalIndex();
1957
1958 LocalOrdinal C_firstCol_import = OrdinalTraits<LocalOrdinal>::zero();
1959 LocalOrdinal C_lastCol_import = OrdinalTraits<LocalOrdinal>::invalid();
1960
1961 ArrayView<const GlobalOrdinal> bcols = Bview.colMap->getLocalElementList();
1962 ArrayView<const GlobalOrdinal> bcols_import = null;
1963 if (Bview.importColMap != null) {
1964 C_firstCol_import = Bview.importColMap->getMinLocalIndex();
1965 C_lastCol_import = Bview.importColMap->getMaxLocalIndex();
1966
1967 bcols_import = Bview.importColMap->getLocalElementList();
1968 }
1969
1970 size_t C_numCols = C_lastCol - C_firstCol +
1971 OrdinalTraits<LocalOrdinal>::one();
1972 size_t C_numCols_import = C_lastCol_import - C_firstCol_import +
1973 OrdinalTraits<LocalOrdinal>::one();
1974
1975 if (C_numCols_import > C_numCols)
1976 C_numCols = C_numCols_import;
1977
1978 Array<Scalar> dwork = Array<Scalar>(C_numCols);
1979 Array<GlobalOrdinal> iwork = Array<GlobalOrdinal>(C_numCols);
1980 Array<size_t> iwork2 = Array<size_t>(C_numCols);
1981
1982 Array<Scalar> C_row_i = dwork;
1983 Array<GlobalOrdinal> C_cols = iwork;
1984 Array<size_t> c_index = iwork2;
1985 Array<GlobalOrdinal> combined_index = Array<GlobalOrdinal>(2 * C_numCols);
1986 Array<Scalar> combined_values = Array<Scalar>(2 * C_numCols);
1987
1988 size_t C_row_i_length, j, k, last_index;
1989
1990 // Run through all the hash table lookups once and for all
1991 LocalOrdinal LO_INVALID = OrdinalTraits<LocalOrdinal>::invalid();
1992 Array<LocalOrdinal> Acol2Brow(Aview.colMap->getLocalNumElements(), LO_INVALID);
1993 Array<LocalOrdinal> Acol2Irow(Aview.colMap->getLocalNumElements(), LO_INVALID);
1994 if (Aview.colMap->isSameAs(*Bview.origMatrix->getRowMap())) {
1995 // Maps are the same: Use local IDs as the hash
1996 for (LocalOrdinal i = Aview.colMap->getMinLocalIndex(); i <=
1997 Aview.colMap->getMaxLocalIndex();
1998 i++)
1999 Acol2Brow[i] = i;
2000 } else {
2001 // Maps are not the same: Use the map's hash
2002 for (LocalOrdinal i = Aview.colMap->getMinLocalIndex(); i <=
2003 Aview.colMap->getMaxLocalIndex();
2004 i++) {
2005 GlobalOrdinal GID = Aview.colMap->getGlobalElement(i);
2006 LocalOrdinal BLID = Bview.origMatrix->getRowMap()->getLocalElement(GID);
2007 if (BLID != LO_INVALID)
2008 Acol2Brow[i] = BLID;
2009 else
2010 Acol2Irow[i] = Bview.importMatrix->getRowMap()->getLocalElement(GID);
2011 }
2012 }
2013
2014 // To form C = A*B we're going to execute this expression:
2015 //
2016 // C(i,j) = sum_k( A(i,k)*B(k,j) )
2017 //
2018 // Our goal, of course, is to navigate the data in A and B once, without
2019 // performing searches for column-indices, etc.
2020 auto Arowptr = Aview.origMatrix->getLocalRowPtrsHost();
2021 auto Acolind = Aview.origMatrix->getLocalIndicesHost();
2022 auto Avals = Aview.origMatrix->getLocalValuesHost(Tpetra::Access::ReadOnly);
2023 auto Browptr = Bview.origMatrix->getLocalRowPtrsHost();
2024 auto Bcolind = Bview.origMatrix->getLocalIndicesHost();
2025 auto Bvals = Bview.origMatrix->getLocalValuesHost(Tpetra::Access::ReadOnly);
2026 decltype(Browptr) Irowptr;
2027 decltype(Bcolind) Icolind;
2028 decltype(Bvals) Ivals;
2029 if (!Bview.importMatrix.is_null()) {
2030 Irowptr = Bview.importMatrix->getLocalRowPtrsHost();
2031 Icolind = Bview.importMatrix->getLocalIndicesHost();
2032 Ivals = Bview.importMatrix->getLocalValuesHost(Tpetra::Access::ReadOnly);
2033 }
2034
2035 bool C_filled = C.isFillComplete();
2036
2037 for (size_t i = 0; i < C_numCols; i++)
2038 c_index[i] = OrdinalTraits<size_t>::invalid();
2039
2040 // Loop over the rows of A.
2041 size_t Arows = Aview.rowMap->getLocalNumElements();
2042 for (size_t i = 0; i < Arows; ++i) {
2043 // Only navigate the local portion of Aview... which is, thankfully, all of
2044 // A since this routine doesn't do transpose modes
2045 GlobalOrdinal global_row = Aview.rowMap->getGlobalElement(i);
2046
2047 // Loop across the i-th row of A and for each corresponding row in B, loop
2048 // across columns and accumulate product A(i,k)*B(k,j) into our partial sum
2049 // quantities C_row_i. In other words, as we stride across B(k,:) we're
2050 // calculating updates for row i of the result matrix C.
2051 C_row_i_length = OrdinalTraits<size_t>::zero();
2052
2053 for (k = Arowptr[i]; k < Arowptr[i + 1]; ++k) {
2054 LocalOrdinal Ak = Acol2Brow[Acolind[k]];
2055 const Scalar Aval = Avals[k];
2056 if (Aval == STS::zero() && skipExplicitZero)
2057 continue;
2058
2059 if (Ak == LO_INVALID)
2060 continue;
2061
2062 for (j = Browptr[Ak]; j < Browptr[Ak + 1]; ++j) {
2063 LocalOrdinal col = Bcolind[j];
2064 // assert(col >= 0 && col < C_numCols);
2065
2066 if (c_index[col] == OrdinalTraits<size_t>::invalid()) {
2067 // assert(C_row_i_length >= 0 && C_row_i_length < C_numCols);
2068 // This has to be a += so insertGlobalValue goes out
2069 C_row_i[C_row_i_length] = Aval * Bvals[j];
2070 C_cols[C_row_i_length] = col;
2071 c_index[col] = C_row_i_length;
2072 C_row_i_length++;
2073
2074 } else {
2075 // static cast from impl_scalar_type to Scalar needed for complex
2076 C_row_i[c_index[col]] += Aval * static_cast<Scalar>(Bvals[j]);
2077 }
2078 }
2079 }
2080
2081 for (size_t ii = 0; ii < C_row_i_length; ii++) {
2082 c_index[C_cols[ii]] = OrdinalTraits<size_t>::invalid();
2083 C_cols[ii] = bcols[C_cols[ii]];
2084 combined_index[ii] = C_cols[ii];
2085 combined_values[ii] = C_row_i[ii];
2086 }
2087 last_index = C_row_i_length;
2088
2089 //
2090 // Now put the C_row_i values into C.
2091 //
2092 // We might have to revamp this later.
2093 C_row_i_length = OrdinalTraits<size_t>::zero();
2094
2095 for (k = Arowptr[i]; k < Arowptr[i + 1]; ++k) {
2096 LocalOrdinal Ak = Acol2Brow[Acolind[k]];
2097 const Scalar Aval = Avals[k];
2098 if (Aval == STS::zero() && skipExplicitZero)
2099 continue;
2100
2101 if (Ak != LO_INVALID) continue;
2102
2103 Ak = Acol2Irow[Acolind[k]];
2104 for (j = Irowptr[Ak]; j < Irowptr[Ak + 1]; ++j) {
2105 LocalOrdinal col = Icolind[j];
2106 // assert(col >= 0 && col < C_numCols);
2107
2108 if (c_index[col] == OrdinalTraits<size_t>::invalid()) {
2109 // assert(C_row_i_length >= 0 && C_row_i_length < C_numCols);
2110 // This has to be a += so insertGlobalValue goes out
2111 C_row_i[C_row_i_length] = Aval * Ivals[j];
2112 C_cols[C_row_i_length] = col;
2113 c_index[col] = C_row_i_length;
2114 C_row_i_length++;
2115
2116 } else {
2117 // This has to be a += so insertGlobalValue goes out
2118 // static cast from impl_scalar_type to Scalar needed for complex
2119 C_row_i[c_index[col]] += Aval * static_cast<Scalar>(Ivals[j]);
2120 }
2121 }
2122 }
2123
2124 for (size_t ii = 0; ii < C_row_i_length; ii++) {
2125 c_index[C_cols[ii]] = OrdinalTraits<size_t>::invalid();
2126 C_cols[ii] = bcols_import[C_cols[ii]];
2127 combined_index[last_index] = C_cols[ii];
2128 combined_values[last_index] = C_row_i[ii];
2129 last_index++;
2130 }
2131
2132 // Now put the C_row_i values into C.
2133 // We might have to revamp this later.
2134 C_filled ? C.sumIntoGlobalValues(
2135 global_row,
2136 combined_index.view(OrdinalTraits<size_t>::zero(), last_index),
2137 combined_values.view(OrdinalTraits<size_t>::zero(), last_index))
2138 : C.insertGlobalValues(
2139 global_row,
2140 combined_index.view(OrdinalTraits<size_t>::zero(), last_index),
2141 combined_values.view(OrdinalTraits<size_t>::zero(), last_index));
2142 }
2143}
2144
2145/*********************************************************************************************************/
2146template <class Scalar,
2147 class LocalOrdinal,
2148 class GlobalOrdinal,
2149 class Node>
2150void setMaxNumEntriesPerRow(CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Mview) {
2151 typedef typename Teuchos::Array<Teuchos::ArrayView<const LocalOrdinal>>::size_type local_length_size;
2152 Mview.maxNumRowEntries = Teuchos::OrdinalTraits<local_length_size>::zero();
2153
2154 if (Mview.indices.size() > Teuchos::OrdinalTraits<local_length_size>::zero()) {
2155 Mview.maxNumRowEntries = Mview.indices[0].size();
2156
2157 for (local_length_size i = 1; i < Mview.indices.size(); ++i)
2158 if (Mview.indices[i].size() > Mview.maxNumRowEntries)
2159 Mview.maxNumRowEntries = Mview.indices[i].size();
2160 }
2161}
2162
2163/*********************************************************************************************************/
2164template <class CrsMatrixType>
2165size_t C_estimate_nnz(CrsMatrixType& A, CrsMatrixType& B) {
2166 // Follows the NZ estimate in ML's ml_matmatmult.c
2167 size_t Aest = 100, Best = 100;
2168 if (A.getLocalNumEntries() >= A.getLocalNumRows())
2169 Aest = (A.getLocalNumRows() > 0) ? A.getLocalNumEntries() / A.getLocalNumRows() : 100;
2170 if (B.getLocalNumEntries() >= B.getLocalNumRows())
2171 Best = (B.getLocalNumRows() > 0) ? B.getLocalNumEntries() / B.getLocalNumRows() : 100;
2172
2173 size_t nnzperrow = (size_t)(sqrt((double)Aest) + sqrt((double)Best) - 1);
2174 nnzperrow *= nnzperrow;
2175
2176 return (size_t)(A.getLocalNumRows() * nnzperrow * 0.75 + 100);
2177}
2178
2179/*********************************************************************************************************/
2180// Kernel method for computing the local portion of C = A*B for CrsMatrix
2181//
2182// mfh 27 Sep 2016: Currently, mult_AT_B_newmatrix() also calls this
2183// function, so this is probably the function we want to
2184// thread-parallelize.
2185template <class Scalar,
2186 class LocalOrdinal,
2187 class GlobalOrdinal,
2188 class Node>
2189void mult_A_B_newmatrix(
2190 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
2191 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
2192 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
2193 const std::string& label,
2194 const Teuchos::RCP<Teuchos::ParameterList>& params) {
2195 using Teuchos::Array;
2196 using Teuchos::ArrayRCP;
2197 using Teuchos::ArrayView;
2198 using Teuchos::RCP;
2199 using Teuchos::rcp;
2200
2201 // Tpetra typedefs
2202 typedef LocalOrdinal LO;
2203 typedef GlobalOrdinal GO;
2204 typedef Node NO;
2205 typedef Import<LO, GO, NO> import_type;
2206 typedef Map<LO, GO, NO> map_type;
2207
2208 // Kokkos typedefs
2209 typedef typename map_type::local_map_type local_map_type;
2211 typedef typename KCRS::StaticCrsGraphType graph_t;
2212 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
2213 typedef typename NO::execution_space execution_space;
2214 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
2215 typedef Kokkos::View<LO*, typename lno_view_t::array_layout, typename lno_view_t::device_type> lo_view_t;
2216
2217 Tpetra::Details::ProfilingRegion MM("TpetraExt: MMM: M5 Cmap");
2218
2219 LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
2220
2221 // Build the final importer / column map, hash table lookups for C
2222 RCP<const import_type> Cimport;
2223 RCP<const map_type> Ccolmap;
2224 RCP<const import_type> Bimport = Bview.origMatrix->getGraph()->getImporter();
2225 RCP<const import_type> Iimport = Bview.importMatrix.is_null() ? Teuchos::null : Bview.importMatrix->getGraph()->getImporter();
2226 local_map_type Acolmap_local = Aview.colMap->getLocalMap();
2227 local_map_type Browmap_local = Bview.origMatrix->getRowMap()->getLocalMap();
2228 local_map_type Irowmap_local;
2229 if (!Bview.importMatrix.is_null()) Irowmap_local = Bview.importMatrix->getRowMap()->getLocalMap();
2230 local_map_type Bcolmap_local = Bview.origMatrix->getColMap()->getLocalMap();
2231 local_map_type Icolmap_local;
2232 if (!Bview.importMatrix.is_null()) Icolmap_local = Bview.importMatrix->getColMap()->getLocalMap();
2233
2234 // mfh 27 Sep 2016: Bcol2Ccol is a table that maps from local column
2235 // indices of B, to local column indices of C. (B and C have the
2236 // same number of columns.) The kernel uses this, instead of
2237 // copying the entire input matrix B and converting its column
2238 // indices to those of C.
2239 lo_view_t Bcol2Ccol(Kokkos::ViewAllocateWithoutInitializing("Bcol2Ccol"), Bview.colMap->getLocalNumElements()), Icol2Ccol;
2240
2241 if (Bview.importMatrix.is_null()) {
2242 // mfh 27 Sep 2016: B has no "remotes," so B and C have the same column Map.
2243 Cimport = Bimport;
2244 Ccolmap = Bview.colMap;
2245 const LO colMapSize = static_cast<LO>(Bview.colMap->getLocalNumElements());
2246 // Bcol2Ccol is trivial
2247 Kokkos::parallel_for(
2248 "Tpetra::mult_A_B_newmatrix::Bcol2Ccol_fill",
2249 Kokkos::RangePolicy<execution_space, LO>(0, colMapSize),
2250 KOKKOS_LAMBDA(const LO i) {
2251 Bcol2Ccol(i) = i;
2252 });
2253 } else {
2254 // mfh 27 Sep 2016: B has "remotes," so we need to build the
2255 // column Map of C, as well as C's Import object (from its domain
2256 // Map to its column Map). C's column Map is the union of the
2257 // column Maps of (the local part of) B, and the "remote" part of
2258 // B. Ditto for the Import. We have optimized this "setUnion"
2259 // operation on Import objects and Maps.
2260
2261 // Choose the right variant of setUnion
2262 if (!Bimport.is_null() && !Iimport.is_null()) {
2263 Cimport = Bimport->setUnion(*Iimport, params);
2264 } else if (!Bimport.is_null() && Iimport.is_null()) {
2265 Cimport = Bimport->setUnion(params);
2266 } else if (Bimport.is_null() && !Iimport.is_null()) {
2267 Cimport = Iimport->setUnion(params);
2268 } else {
2269 throw std::runtime_error("TpetraExt::MMM status of matrix importers is nonsensical");
2270 }
2271 Ccolmap = Cimport->getTargetMap();
2272
2273 // FIXME (mfh 27 Sep 2016) This error check requires an all-reduce
2274 // in general. We should get rid of it in order to reduce
2275 // communication costs of sparse matrix-matrix multiply.
2276 TEUCHOS_TEST_FOR_EXCEPTION(!Cimport->getSourceMap()->isSameAs(*Bview.origMatrix->getDomainMap()),
2277 std::runtime_error, "Tpetra::MMM: Import setUnion messed with the DomainMap in an unfortunate way");
2278
2279 // NOTE: This is not efficient and should be folded into setUnion
2280 //
2281 // mfh 27 Sep 2016: What the above comment means, is that the
2282 // setUnion operation on Import objects could also compute these
2283 // local index - to - local index look-up tables.
2284 Kokkos::resize(Icol2Ccol, Bview.importMatrix->getColMap()->getLocalNumElements());
2285 local_map_type Ccolmap_local = Ccolmap->getLocalMap();
2286 Kokkos::parallel_for(
2287 "Tpetra::mult_A_B_newmatrix::Bcol2Ccol_getGlobalElement", range_type(0, Bview.origMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
2288 Bcol2Ccol(i) = Ccolmap_local.getLocalElement(Bcolmap_local.getGlobalElement(i));
2289 });
2290 Kokkos::parallel_for(
2291 "Tpetra::mult_A_B_newmatrix::Icol2Ccol_getGlobalElement", range_type(0, Bview.importMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
2292 Icol2Ccol(i) = Ccolmap_local.getLocalElement(Icolmap_local.getGlobalElement(i));
2293 });
2294 }
2295
2296 // Replace the column map
2297 //
2298 // mfh 27 Sep 2016: We do this because C was originally created
2299 // without a column Map. Now we have its column Map.
2300 C.replaceColMap(Ccolmap);
2301
2302 // mfh 27 Sep 2016: Construct tables that map from local column
2303 // indices of A, to local row indices of either B_local (the locally
2304 // owned part of B), or B_remote (the "imported" remote part of B).
2305 //
2306 // For column index Aik in row i of A, if the corresponding row of B
2307 // exists in the local part of B ("orig") (which I'll call B_local),
2308 // then targetMapToOrigRow[Aik] is the local index of that row of B.
2309 // Otherwise, targetMapToOrigRow[Aik] is "invalid" (a flag value).
2310 //
2311 // For column index Aik in row i of A, if the corresponding row of B
2312 // exists in the remote part of B ("Import") (which I'll call
2313 // B_remote), then targetMapToImportRow[Aik] is the local index of
2314 // that row of B. Otherwise, targetMapToOrigRow[Aik] is "invalid"
2315 // (a flag value).
2316
2317 // Run through all the hash table lookups once and for all
2318 lo_view_t targetMapToOrigRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToOrigRow"), Aview.colMap->getLocalNumElements());
2319 lo_view_t targetMapToImportRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToImportRow"), Aview.colMap->getLocalNumElements());
2320
2321 Kokkos::parallel_for(
2322 "Tpetra::mult_A_B_newmatrix::construct_tables", range_type(Aview.colMap->getMinLocalIndex(), Aview.colMap->getMaxLocalIndex() + 1), KOKKOS_LAMBDA(const LO i) {
2323 GO aidx = Acolmap_local.getGlobalElement(i);
2324 LO B_LID = Browmap_local.getLocalElement(aidx);
2325 if (B_LID != LO_INVALID) {
2326 targetMapToOrigRow(i) = B_LID;
2327 targetMapToImportRow(i) = LO_INVALID;
2328 } else {
2329 LO I_LID = Irowmap_local.getLocalElement(aidx);
2330 targetMapToOrigRow(i) = LO_INVALID;
2331 targetMapToImportRow(i) = I_LID;
2332 }
2333 });
2334
2335 // Call the actual kernel. We'll rely on partial template specialization to call the correct one ---
2336 // Either the straight-up Tpetra code (SerialNode) or the KokkosKernels one (other NGP node types)
2337 KernelWrappers<Scalar, LocalOrdinal, GlobalOrdinal, Node, lo_view_t>::mult_A_B_newmatrix_kernel_wrapper(Aview, Bview, targetMapToOrigRow, targetMapToImportRow, Bcol2Ccol, Icol2Ccol, C, Cimport, label, params);
2338}
2339
2340/*********************************************************************************************************/
2341// Kernel method for computing the local portion of C = A*B for BlockCrsMatrix
2342template <class Scalar,
2343 class LocalOrdinal,
2344 class GlobalOrdinal,
2345 class Node>
2346void mult_A_B_newmatrix(BlockCrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
2347 BlockCrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
2348 Teuchos::RCP<BlockCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>>& C) {
2349 using Teuchos::Array;
2350 using Teuchos::ArrayRCP;
2351 using Teuchos::ArrayView;
2352 using Teuchos::null;
2353 using Teuchos::RCP;
2354 using Teuchos::rcp;
2355
2356 // Tpetra typedefs
2357 typedef LocalOrdinal LO;
2358 typedef GlobalOrdinal GO;
2359 typedef Node NO;
2360 typedef Import<LO, GO, NO> import_type;
2361 typedef Map<LO, GO, NO> map_type;
2362 typedef BlockCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node> block_crs_matrix_type;
2363 typedef typename block_crs_matrix_type::crs_graph_type graph_t;
2364
2365 // Kokkos typedefs
2366 typedef typename map_type::local_map_type local_map_type;
2367 typedef typename block_crs_matrix_type::local_matrix_device_type KBSR;
2368 typedef typename KBSR::device_type device_t;
2369 typedef typename KBSR::StaticCrsGraphType static_graph_t;
2370 typedef typename static_graph_t::row_map_type::non_const_type lno_view_t;
2371 typedef typename static_graph_t::entries_type::non_const_type lno_nnz_view_t;
2372 typedef typename KBSR::values_type::non_const_type scalar_view_t;
2373 typedef typename NO::execution_space execution_space;
2374 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
2375 typedef Kokkos::View<LO*, typename lno_view_t::array_layout, typename lno_view_t::device_type> lo_view_t;
2376
2377 LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
2378
2379 // Build the final importer / column map, hash table lookups for C
2380 RCP<const import_type> Cimport;
2381 RCP<const map_type> Ccolmap;
2382 RCP<const import_type> Bimport = Bview.origMatrix->getGraph()->getImporter();
2383 RCP<const import_type> Iimport = Bview.importMatrix.is_null() ? Teuchos::null : Bview.importMatrix->getGraph()->getImporter();
2384 local_map_type Acolmap_local = Aview.colMap->getLocalMap();
2385 local_map_type Browmap_local = Bview.origMatrix->getRowMap()->getLocalMap();
2386 local_map_type Irowmap_local;
2387 if (!Bview.importMatrix.is_null()) Irowmap_local = Bview.importMatrix->getRowMap()->getLocalMap();
2388 local_map_type Bcolmap_local = Bview.origMatrix->getColMap()->getLocalMap();
2389 local_map_type Icolmap_local;
2390 if (!Bview.importMatrix.is_null()) Icolmap_local = Bview.importMatrix->getColMap()->getLocalMap();
2391
2392 // Bcol2Ccol is a table that maps from local column
2393 // indices of B, to local column indices of C. (B and C have the
2394 // same number of columns.) The kernel uses this, instead of
2395 // copying the entire input matrix B and converting its column
2396 // indices to those of C.
2397 lo_view_t Bcol2Ccol(Kokkos::ViewAllocateWithoutInitializing("Bcol2Ccol"), Bview.colMap->getLocalNumElements()), Icol2Ccol;
2398
2399 if (Bview.importMatrix.is_null()) {
2400 // mfh 27 Sep 2016: B has no "remotes," so B and C have the same column Map.
2401 Cimport = Bimport;
2402 Ccolmap = Bview.colMap;
2403 const LO colMapSize = static_cast<LO>(Bview.colMap->getLocalNumElements());
2404 // Bcol2Ccol is trivial
2405 Kokkos::parallel_for(
2406 "Tpetra::mult_A_B_newmatrix::Bcol2Ccol_fill",
2407 Kokkos::RangePolicy<execution_space, LO>(0, colMapSize),
2408 KOKKOS_LAMBDA(const LO i) {
2409 Bcol2Ccol(i) = i;
2410 });
2411 } else {
2412 // B has "remotes," so we need to build the
2413 // column Map of C, as well as C's Import object (from its domain
2414 // Map to its column Map). C's column Map is the union of the
2415 // column Maps of (the local part of) B, and the "remote" part of
2416 // B. Ditto for the Import. We have optimized this "setUnion"
2417 // operation on Import objects and Maps.
2418
2419 // Choose the right variant of setUnion
2420 if (!Bimport.is_null() && !Iimport.is_null()) {
2421 Cimport = Bimport->setUnion(*Iimport);
2422 } else if (!Bimport.is_null() && Iimport.is_null()) {
2423 Cimport = Bimport->setUnion();
2424 } else if (Bimport.is_null() && !Iimport.is_null()) {
2425 Cimport = Iimport->setUnion();
2426 } else {
2427 throw std::runtime_error("TpetraExt::MMM status of matrix importers is nonsensical");
2428 }
2429 Ccolmap = Cimport->getTargetMap();
2430
2431 // NOTE: This is not efficient and should be folded into setUnion
2432 //
2433 // What the above comment means, is that the
2434 // setUnion operation on Import objects could also compute these
2435 // local index - to - local index look-up tables.
2436 Kokkos::resize(Icol2Ccol, Bview.importMatrix->getColMap()->getLocalNumElements());
2437 local_map_type Ccolmap_local = Ccolmap->getLocalMap();
2438 Kokkos::parallel_for(
2439 "Tpetra::mult_A_B_newmatrix::Bcol2Ccol_getGlobalElement",
2440 range_type(0, Bview.origMatrix->getColMap()->getLocalNumElements()),
2441 KOKKOS_LAMBDA(const LO i) {
2442 Bcol2Ccol(i) = Ccolmap_local.getLocalElement(Bcolmap_local.getGlobalElement(i));
2443 });
2444 Kokkos::parallel_for(
2445 "Tpetra::mult_A_B_newmatrix::Icol2Ccol_getGlobalElement",
2446 range_type(0, Bview.importMatrix->getColMap()->getLocalNumElements()),
2447 KOKKOS_LAMBDA(const LO i) {
2448 Icol2Ccol(i) = Ccolmap_local.getLocalElement(Icolmap_local.getGlobalElement(i));
2449 });
2450 }
2451
2452 // Construct tables that map from local column
2453 // indices of A, to local row indices of either B_local (the locally
2454 // owned part of B), or B_remote (the "imported" remote part of B).
2455 //
2456 // For column index Aik in row i of A, if the corresponding row of B
2457 // exists in the local part of B ("orig") (which I'll call B_local),
2458 // then targetMapToOrigRow[Aik] is the local index of that row of B.
2459 // Otherwise, targetMapToOrigRow[Aik] is "invalid" (a flag value).
2460 //
2461 // For column index Aik in row i of A, if the corresponding row of B
2462 // exists in the remote part of B ("Import") (which I'll call
2463 // B_remote), then targetMapToImportRow[Aik] is the local index of
2464 // that row of B. Otherwise, targetMapToOrigRow[Aik] is "invalid"
2465 // (a flag value).
2466
2467 // Run through all the hash table lookups once and for all
2468 lo_view_t targetMapToOrigRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToOrigRow"),
2469 Aview.colMap->getLocalNumElements());
2470 lo_view_t targetMapToImportRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToImportRow"),
2471 Aview.colMap->getLocalNumElements());
2472
2473 Kokkos::parallel_for(
2474 "Tpetra::mult_A_B_newmatrix::construct_tables",
2475 range_type(Aview.colMap->getMinLocalIndex(), Aview.colMap->getMaxLocalIndex() + 1),
2476 KOKKOS_LAMBDA(const LO i) {
2477 GO aidx = Acolmap_local.getGlobalElement(i);
2478 LO B_LID = Browmap_local.getLocalElement(aidx);
2479 if (B_LID != LO_INVALID) {
2480 targetMapToOrigRow(i) = B_LID;
2481 targetMapToImportRow(i) = LO_INVALID;
2482 } else {
2483 LO I_LID = Irowmap_local.getLocalElement(aidx);
2484 targetMapToOrigRow(i) = LO_INVALID;
2485 targetMapToImportRow(i) = I_LID;
2486 }
2487 });
2488
2489 // Create the KernelHandle
2490 using KernelHandle =
2491 KokkosKernels::Experimental::KokkosKernelsHandle<typename lno_view_t::const_value_type,
2492 typename lno_nnz_view_t::const_value_type,
2493 typename scalar_view_t::const_value_type,
2494 typename device_t::execution_space,
2495 typename device_t::memory_space,
2496 typename device_t::memory_space>;
2497 int team_work_size = 16; // Defaults to 16 as per Deveci 12/7/16 - csiefer
2498 std::string myalg("SPGEMM_DEFAULT");
2499 KokkosSparse::SPGEMMAlgorithm alg_enum = KokkosSparse::StringToSPGEMMAlgorithm(myalg);
2500
2501 KernelHandle kh;
2502 kh.create_spgemm_handle(alg_enum);
2503 kh.set_team_work_size(team_work_size);
2504
2505 // Get KokkosSparse::BsrMatrix for A and Bmerged (B and BImport)
2506 const KBSR Amat = Aview.origMatrix->getLocalMatrixDevice();
2507 const KBSR Bmerged = Tpetra::MMdetails::merge_matrices(Aview, Bview,
2508 targetMapToOrigRow, targetMapToImportRow,
2509 Bcol2Ccol, Icol2Ccol,
2510 Ccolmap.getConst()->getLocalNumElements());
2511
2512 RCP<graph_t> graphC;
2513 typename KBSR::values_type values;
2514 {
2515 // Call KokkosSparse routines to calculate Amat*Bmerged on device.
2516 // NOTE: Need to scope guard this since the BlockCrs constructor will need to copy the host graph
2517 KBSR Cmat;
2518 KokkosSparse::block_spgemm_symbolic(kh, Amat, false, Bmerged, false, Cmat);
2519 KokkosSparse::block_spgemm_numeric(kh, Amat, false, Bmerged, false, Cmat);
2520 kh.destroy_spgemm_handle();
2521
2522 // Build Tpetra::BlockCrsMatrix from KokkosSparse::BsrMatrix
2523 graphC = rcp(new graph_t(Cmat.graph, Aview.origMatrix->getRowMap(), Ccolmap.getConst()));
2524 values = Cmat.values;
2525 }
2526 C = rcp(new block_crs_matrix_type(*graphC, values, Aview.blocksize));
2527}
2528
2529/*********************************************************************************************************/
2530// AB NewMatrix Kernel wrappers (Default non-threaded version for CrsMatrix)
2531template <class Scalar,
2532 class LocalOrdinal,
2533 class GlobalOrdinal,
2534 class Node,
2535 class LocalOrdinalViewType>
2536void KernelWrappers<Scalar, LocalOrdinal, GlobalOrdinal, Node, LocalOrdinalViewType>::mult_A_B_newmatrix_kernel_wrapper(CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
2537 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
2538 const LocalOrdinalViewType& targetMapToOrigRow,
2539 const LocalOrdinalViewType& targetMapToImportRow,
2540 const LocalOrdinalViewType& Bcol2Ccol,
2541 const LocalOrdinalViewType& Icol2Ccol,
2542 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
2543 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
2544 const std::string& label,
2545 const Teuchos::RCP<Teuchos::ParameterList>& params) {
2546 using Teuchos::Array;
2547 using Teuchos::ArrayRCP;
2548 using Teuchos::ArrayView;
2549 using Teuchos::RCP;
2550 using Teuchos::rcp;
2551
2552 Tpetra::Details::ProfilingRegion MM("TpetraExt: MMM: Newmatrix SerialCore");
2553
2554 // Lots and lots of typedefs
2555 typedef typename Tpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_host_type KCRS;
2556 typedef typename KCRS::StaticCrsGraphType graph_t;
2557 typedef typename graph_t::row_map_type::const_type c_lno_view_t;
2558 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
2559 typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
2560 typedef typename KCRS::values_type::non_const_type scalar_view_t;
2561
2562 typedef Scalar SC;
2563 typedef LocalOrdinal LO;
2564 typedef GlobalOrdinal GO;
2565 typedef Node NO;
2566 typedef Map<LO, GO, NO> map_type;
2567 const size_t ST_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
2568 const LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
2569 const SC SC_ZERO = Teuchos::ScalarTraits<Scalar>::zero();
2570
2571 bool skipExplicitZero = true;
2572 if (params && params->isParameter("MM Skip Explicit Zeros")) {
2573 skipExplicitZero = params->get<bool>("MM Skip Explicit Zeros");
2574 }
2575
2576 // Sizes
2577 RCP<const map_type> Ccolmap = C.getColMap();
2578 size_t m = Aview.origMatrix->getLocalNumRows();
2579 size_t n = Ccolmap->getLocalNumElements();
2580 size_t b_max_nnz_per_row = Bview.origMatrix->getLocalMaxNumRowEntries();
2581
2582 // Grab the Kokkos::SparseCrsMatrices & inner stuff
2583 const KCRS Amat = Aview.origMatrix->getLocalMatrixHost();
2584 const KCRS Bmat = Bview.origMatrix->getLocalMatrixHost();
2585
2586 c_lno_view_t Arowptr = Amat.graph.row_map, Browptr = Bmat.graph.row_map;
2587 const lno_nnz_view_t Acolind = Amat.graph.entries, Bcolind = Bmat.graph.entries;
2588 const scalar_view_t Avals = Amat.values, Bvals = Bmat.values;
2589
2590 c_lno_view_t Irowptr;
2591 lno_nnz_view_t Icolind;
2592 scalar_view_t Ivals;
2593 if (!Bview.importMatrix.is_null()) {
2594 auto lclB = Bview.importMatrix->getLocalMatrixHost();
2595 Irowptr = lclB.graph.row_map;
2596 Icolind = lclB.graph.entries;
2597 Ivals = lclB.values;
2598 b_max_nnz_per_row = std::max(b_max_nnz_per_row, Bview.importMatrix->getLocalMaxNumRowEntries());
2599 }
2600
2601 // Classic csr assembly (low memory edition)
2602 //
2603 // mfh 27 Sep 2016: C_estimate_nnz does not promise an upper bound.
2604 // The method loops over rows of A, and may resize after processing
2605 // each row. Chris Siefert says that this reflects experience in
2606 // ML; for the non-threaded case, ML found it faster to spend less
2607 // effort on estimation and risk an occasional reallocation.
2608 size_t CSR_alloc = std::max(C_estimate_nnz(*Aview.origMatrix, *Bview.origMatrix), n);
2609 lno_view_t Crowptr(Kokkos::ViewAllocateWithoutInitializing("Crowptr"), m + 1);
2610 lno_nnz_view_t Ccolind(Kokkos::ViewAllocateWithoutInitializing("Ccolind"), CSR_alloc);
2611 scalar_view_t Cvals(Kokkos::ViewAllocateWithoutInitializing("Cvals"), CSR_alloc);
2612
2613 // mfh 27 Sep 2016: The c_status array is an implementation detail
2614 // of the local sparse matrix-matrix multiply routine.
2615
2616 // The status array will contain the index into colind where this entry was last deposited.
2617 // c_status[i] < CSR_ip - not in the row yet
2618 // c_status[i] >= CSR_ip - this is the entry where you can find the data
2619 // We start with this filled with INVALID's indicating that there are no entries yet.
2620 // Sadly, this complicates the code due to the fact that size_t's are unsigned.
2621 size_t INVALID = Teuchos::OrdinalTraits<size_t>::invalid();
2622 std::vector<size_t> c_status(n, ST_INVALID);
2623
2624 // mfh 27 Sep 2016: Here is the local sparse matrix-matrix multiply
2625 // routine. The routine computes C := A * (B_local + B_remote).
2626 //
2627 // For column index Aik in row i of A, targetMapToOrigRow[Aik] tells
2628 // you whether the corresponding row of B belongs to B_local
2629 // ("orig") or B_remote ("Import").
2630
2631 // For each row of A/C
2632 size_t CSR_ip = 0, OLD_ip = 0;
2633 for (size_t i = 0; i < m; i++) {
2634 // mfh 27 Sep 2016: m is the number of rows in the input matrix A
2635 // on the calling process.
2636 Crowptr[i] = CSR_ip;
2637
2638 // mfh 27 Sep 2016: For each entry of A in the current row of A
2639 for (size_t k = Arowptr[i]; k < Arowptr[i + 1]; k++) {
2640 LO Aik = Acolind[k]; // local column index of current entry of A
2641 const SC Aval = Avals[k]; // value of current entry of A
2642 if (Aval == SC_ZERO && skipExplicitZero)
2643 continue; // skip explicitly stored zero values in A
2644
2645 if (targetMapToOrigRow[Aik] != LO_INVALID) {
2646 // mfh 27 Sep 2016: If the entry of targetMapToOrigRow
2647 // corresponding to the current entry of A is populated, then
2648 // the corresponding row of B is in B_local (i.e., it lives on
2649 // the calling process).
2650
2651 // Local matrix
2652 size_t Bk = static_cast<size_t>(targetMapToOrigRow[Aik]);
2653
2654 // mfh 27 Sep 2016: Go through all entries in that row of B_local.
2655 for (size_t j = Browptr[Bk]; j < Browptr[Bk + 1]; ++j) {
2656 LO Bkj = Bcolind[j];
2657 LO Cij = Bcol2Ccol[Bkj];
2658
2659 if (c_status[Cij] == INVALID || c_status[Cij] < OLD_ip) {
2660 // New entry
2661 c_status[Cij] = CSR_ip;
2662 Ccolind[CSR_ip] = Cij;
2663 Cvals[CSR_ip] = Aval * Bvals[j];
2664 CSR_ip++;
2665
2666 } else {
2667 Cvals[c_status[Cij]] += Aval * Bvals[j];
2668 }
2669 }
2670
2671 } else {
2672 // mfh 27 Sep 2016: If the entry of targetMapToOrigRow
2673 // corresponding to the current entry of A NOT populated (has
2674 // a flag "invalid" value), then the corresponding row of B is
2675 // in B_local (i.e., it lives on the calling process).
2676
2677 // Remote matrix
2678 size_t Ik = static_cast<size_t>(targetMapToImportRow[Aik]);
2679 for (size_t j = Irowptr[Ik]; j < Irowptr[Ik + 1]; ++j) {
2680 LO Ikj = Icolind[j];
2681 LO Cij = Icol2Ccol[Ikj];
2682
2683 if (c_status[Cij] == INVALID || c_status[Cij] < OLD_ip) {
2684 // New entry
2685 c_status[Cij] = CSR_ip;
2686 Ccolind[CSR_ip] = Cij;
2687 Cvals[CSR_ip] = Aval * Ivals[j];
2688 CSR_ip++;
2689 } else {
2690 Cvals[c_status[Cij]] += Aval * Ivals[j];
2691 }
2692 }
2693 }
2694 }
2695
2696 // Resize for next pass if needed
2697 if (i + 1 < m && CSR_ip + std::min(n, (Arowptr[i + 2] - Arowptr[i + 1]) * b_max_nnz_per_row) > CSR_alloc) {
2698 CSR_alloc *= 2;
2699 Kokkos::resize(Ccolind, CSR_alloc);
2700 Kokkos::resize(Cvals, CSR_alloc);
2701 }
2702 OLD_ip = CSR_ip;
2703 }
2704
2705 Crowptr[m] = CSR_ip;
2706
2707 // Downward resize
2708 Kokkos::resize(Ccolind, CSR_ip);
2709 Kokkos::resize(Cvals, CSR_ip);
2710
2711 {
2712 Tpetra::Details::ProfilingRegion MM3("TpetraExt: MMM: Newmatrix Final Sort");
2713
2714 // Final sort & set of CRS arrays
2715 if (params.is_null() || params->get("sort entries", true)) {
2716 // Tpetra's serial SpGEMM results in almost sorted matrices. Use shell sort.
2717 Import_Util::sortCrsEntries(Crowptr, Ccolind, Cvals);
2718 }
2719 C.setAllValues(Crowptr, Ccolind, Cvals);
2720 }
2721
2722 Tpetra::Details::ProfilingRegion MM4("TpetraExt: MMM: Newmatrix ESCC");
2723 {
2724 // Final FillComplete
2725 //
2726 // mfh 27 Sep 2016: So-called "expert static fill complete" bypasses
2727 // Import (from domain Map to column Map) construction (which costs
2728 // lots of communication) by taking the previously constructed
2729 // Import object. We should be able to do this without interfering
2730 // with the implementation of the local part of sparse matrix-matrix
2731 // multply above.
2732 RCP<Teuchos::ParameterList> labelList = rcp(new Teuchos::ParameterList);
2733 labelList->set("Timer Label", label);
2734 if (!params.is_null()) labelList->set("compute global constants", params->get("compute global constants", true));
2735 RCP<const Export<LO, GO, NO>> dummyExport;
2736 C.expertStaticFillComplete(Bview.origMatrix->getDomainMap(), Aview.origMatrix->getRangeMap(), Cimport, dummyExport, labelList);
2737 }
2738}
2739/*********************************************************************************************************/
2740// Kernel method for computing the local portion of C = A*B (reuse)
2741template <class Scalar,
2742 class LocalOrdinal,
2743 class GlobalOrdinal,
2744 class Node>
2745void mult_A_B_reuse(
2746 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
2747 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
2748 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
2749 const std::string& label,
2750 const Teuchos::RCP<Teuchos::ParameterList>& params) {
2751 using Teuchos::Array;
2752 using Teuchos::ArrayRCP;
2753 using Teuchos::ArrayView;
2754 using Teuchos::RCP;
2755 using Teuchos::rcp;
2756
2757 // Tpetra typedefs
2758 typedef LocalOrdinal LO;
2759 typedef GlobalOrdinal GO;
2760 typedef Node NO;
2761 typedef Import<LO, GO, NO> import_type;
2762 typedef Map<LO, GO, NO> map_type;
2763
2764 // Kokkos typedefs
2765 typedef typename map_type::local_map_type local_map_type;
2767 typedef typename KCRS::StaticCrsGraphType graph_t;
2768 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
2769 typedef typename NO::execution_space execution_space;
2770 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
2771 typedef Kokkos::View<LO*, typename lno_view_t::array_layout, typename lno_view_t::device_type> lo_view_t;
2772
2773 Tpetra::Details::ProfilingRegion MM("TpetraExt: MMM: Reuse Cmap");
2774 (void)label;
2775
2776 LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
2777
2778 // Grab all the maps
2779 RCP<const import_type> Cimport = C.getGraph()->getImporter();
2780 RCP<const map_type> Ccolmap = C.getColMap();
2781 local_map_type Acolmap_local = Aview.colMap->getLocalMap();
2782 local_map_type Browmap_local = Bview.origMatrix->getRowMap()->getLocalMap();
2783 local_map_type Irowmap_local;
2784 if (!Bview.importMatrix.is_null()) Irowmap_local = Bview.importMatrix->getRowMap()->getLocalMap();
2785 local_map_type Bcolmap_local = Bview.origMatrix->getColMap()->getLocalMap();
2786 local_map_type Icolmap_local;
2787 if (!Bview.importMatrix.is_null()) Icolmap_local = Bview.importMatrix->getColMap()->getLocalMap();
2788 local_map_type Ccolmap_local = Ccolmap->getLocalMap();
2789
2790 // Build the final importer / column map, hash table lookups for C
2791 lo_view_t Bcol2Ccol(Kokkos::ViewAllocateWithoutInitializing("Bcol2Ccol"), Bview.colMap->getLocalNumElements()), Icol2Ccol;
2792 {
2793 // Bcol2Col may not be trivial, as Ccolmap is compressed during fillComplete in newmatrix
2794 // So, column map of C may be a strict subset of the column map of B
2795 Kokkos::parallel_for(
2796 range_type(0, Bview.origMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
2797 Bcol2Ccol(i) = Ccolmap_local.getLocalElement(Bcolmap_local.getGlobalElement(i));
2798 });
2799
2800 if (!Bview.importMatrix.is_null()) {
2801 TEUCHOS_TEST_FOR_EXCEPTION(!Cimport->getSourceMap()->isSameAs(*Bview.origMatrix->getDomainMap()),
2802 std::runtime_error, "Tpetra::MMM: Import setUnion messed with the DomainMap in an unfortunate way");
2803
2804 Kokkos::resize(Icol2Ccol, Bview.importMatrix->getColMap()->getLocalNumElements());
2805 Kokkos::parallel_for(
2806 range_type(0, Bview.importMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
2807 Icol2Ccol(i) = Ccolmap_local.getLocalElement(Icolmap_local.getGlobalElement(i));
2808 });
2809 }
2810 }
2811
2812 // Run through all the hash table lookups once and for all
2813 lo_view_t targetMapToOrigRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToOrigRow"), Aview.colMap->getLocalNumElements());
2814 lo_view_t targetMapToImportRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToImportRow"), Aview.colMap->getLocalNumElements());
2815 Kokkos::parallel_for(
2816 range_type(Aview.colMap->getMinLocalIndex(), Aview.colMap->getMaxLocalIndex() + 1), KOKKOS_LAMBDA(const LO i) {
2817 GO aidx = Acolmap_local.getGlobalElement(i);
2818 LO B_LID = Browmap_local.getLocalElement(aidx);
2819 if (B_LID != LO_INVALID) {
2820 targetMapToOrigRow(i) = B_LID;
2821 targetMapToImportRow(i) = LO_INVALID;
2822 } else {
2823 LO I_LID = Irowmap_local.getLocalElement(aidx);
2824 targetMapToOrigRow(i) = LO_INVALID;
2825 targetMapToImportRow(i) = I_LID;
2826 }
2827 });
2828
2829 // Call the actual kernel. We'll rely on partial template specialization to call the correct one ---
2830 // Either the straight-up Tpetra code (SerialNode) or the KokkosKernels one (other NGP node types)
2831 KernelWrappers<Scalar, LocalOrdinal, GlobalOrdinal, Node, lo_view_t>::mult_A_B_reuse_kernel_wrapper(Aview, Bview, targetMapToOrigRow, targetMapToImportRow, Bcol2Ccol, Icol2Ccol, C, Cimport, label, params);
2832}
2833
2834/*********************************************************************************************************/
2835template <class Scalar,
2836 class LocalOrdinal,
2837 class GlobalOrdinal,
2838 class Node,
2839 class LocalOrdinalViewType>
2840void KernelWrappers<Scalar, LocalOrdinal, GlobalOrdinal, Node, LocalOrdinalViewType>::mult_A_B_reuse_kernel_wrapper(CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
2841 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
2842 const LocalOrdinalViewType& targetMapToOrigRow,
2843 const LocalOrdinalViewType& targetMapToImportRow,
2844 const LocalOrdinalViewType& Bcol2Ccol,
2845 const LocalOrdinalViewType& Icol2Ccol,
2846 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
2847 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
2848 const std::string& label,
2849 const Teuchos::RCP<Teuchos::ParameterList>& params) {
2850 Tpetra::MMdetails::host_mult_A_B_reuse(
2851 Aview, Bview, targetMapToOrigRow, targetMapToImportRow,
2852 Bcol2Ccol, Icol2Ccol, C, Cimport, label, params);
2853}
2854
2855/*********************************************************************************************************/
2856// Kernel method for computing the local portion of C = (I-omega D^{-1} A)*B
2857template <class Scalar,
2858 class LocalOrdinal,
2859 class GlobalOrdinal,
2860 class Node>
2861void jacobi_A_B_newmatrix(
2862 typename Teuchos::ScalarTraits<Scalar>::magnitudeType omega,
2863 const Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Dinv,
2864 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
2865 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
2866 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
2867 const std::string& label,
2868 const Teuchos::RCP<Teuchos::ParameterList>& params) {
2869 using Teuchos::Array;
2870 using Teuchos::ArrayRCP;
2871 using Teuchos::ArrayView;
2872 using Teuchos::RCP;
2873 using Teuchos::rcp;
2874 // typedef Scalar SC;
2875 typedef LocalOrdinal LO;
2876 typedef GlobalOrdinal GO;
2877 typedef Node NO;
2878
2879 typedef Import<LO, GO, NO> import_type;
2880 typedef Map<LO, GO, NO> map_type;
2881 typedef typename map_type::local_map_type local_map_type;
2882
2883 // All of the Kokkos typedefs
2885 typedef typename KCRS::StaticCrsGraphType graph_t;
2886 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
2887 typedef typename NO::execution_space execution_space;
2888 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
2889 typedef Kokkos::View<LO*, typename lno_view_t::array_layout, typename lno_view_t::device_type> lo_view_t;
2890
2891 Tpetra::Details::ProfilingRegion MM3("TpetraExt: Jacobi: M5 Cmap");
2892
2893 LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
2894
2895 // Build the final importer / column map, hash table lookups for C
2896 RCP<const import_type> Cimport;
2897 RCP<const map_type> Ccolmap;
2898 RCP<const import_type> Bimport = Bview.origMatrix->getGraph()->getImporter();
2899 RCP<const import_type> Iimport = Bview.importMatrix.is_null() ? Teuchos::null : Bview.importMatrix->getGraph()->getImporter();
2900 local_map_type Acolmap_local = Aview.colMap->getLocalMap();
2901 local_map_type Browmap_local = Bview.origMatrix->getRowMap()->getLocalMap();
2902 local_map_type Irowmap_local;
2903 if (!Bview.importMatrix.is_null()) Irowmap_local = Bview.importMatrix->getRowMap()->getLocalMap();
2904 local_map_type Bcolmap_local = Bview.origMatrix->getColMap()->getLocalMap();
2905 local_map_type Icolmap_local;
2906 if (!Bview.importMatrix.is_null()) Icolmap_local = Bview.importMatrix->getColMap()->getLocalMap();
2907
2908 // mfh 27 Sep 2016: Bcol2Ccol is a table that maps from local column
2909 // indices of B, to local column indices of C. (B and C have the
2910 // same number of columns.) The kernel uses this, instead of
2911 // copying the entire input matrix B and converting its column
2912 // indices to those of C.
2913 lo_view_t Bcol2Ccol(Kokkos::ViewAllocateWithoutInitializing("Bcol2Ccol"), Bview.colMap->getLocalNumElements()), Icol2Ccol;
2914
2915 if (Bview.importMatrix.is_null()) {
2916 // mfh 27 Sep 2016: B has no "remotes," so B and C have the same column Map.
2917 Cimport = Bimport;
2918 Ccolmap = Bview.colMap;
2919 // Bcol2Ccol is trivial
2920 // Bcol2Ccol is trivial
2921
2922 Kokkos::RangePolicy<execution_space, LO> range(0, static_cast<LO>(Bview.colMap->getLocalNumElements()));
2923 Kokkos::parallel_for(
2924 range, KOKKOS_LAMBDA(const size_t i) {
2925 Bcol2Ccol(i) = static_cast<LO>(i);
2926 });
2927 } else {
2928 // mfh 27 Sep 2016: B has "remotes," so we need to build the
2929 // column Map of C, as well as C's Import object (from its domain
2930 // Map to its column Map). C's column Map is the union of the
2931 // column Maps of (the local part of) B, and the "remote" part of
2932 // B. Ditto for the Import. We have optimized this "setUnion"
2933 // operation on Import objects and Maps.
2934
2935 // Choose the right variant of setUnion
2936 if (!Bimport.is_null() && !Iimport.is_null()) {
2937 Cimport = Bimport->setUnion(*Iimport, params);
2938 Ccolmap = Cimport->getTargetMap();
2939
2940 } else if (!Bimport.is_null() && Iimport.is_null()) {
2941 Cimport = Bimport->setUnion(params);
2942
2943 } else if (Bimport.is_null() && !Iimport.is_null()) {
2944 Cimport = Iimport->setUnion(params);
2945
2946 } else
2947 throw std::runtime_error("TpetraExt::Jacobi status of matrix importers is nonsensical");
2948
2949 Ccolmap = Cimport->getTargetMap();
2950
2951 TEUCHOS_TEST_FOR_EXCEPTION(!Cimport->getSourceMap()->isSameAs(*Bview.origMatrix->getDomainMap()),
2952 std::runtime_error, "Tpetra:Jacobi Import setUnion messed with the DomainMap in an unfortunate way");
2953
2954 // NOTE: This is not efficient and should be folded into setUnion
2955 //
2956 // mfh 27 Sep 2016: What the above comment means, is that the
2957 // setUnion operation on Import objects could also compute these
2958 // local index - to - local index look-up tables.
2959 Kokkos::resize(Icol2Ccol, Bview.importMatrix->getColMap()->getLocalNumElements());
2960 local_map_type Ccolmap_local = Ccolmap->getLocalMap();
2961 Kokkos::parallel_for(
2962 range_type(0, Bview.origMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
2963 Bcol2Ccol(i) = Ccolmap_local.getLocalElement(Bcolmap_local.getGlobalElement(i));
2964 });
2965 Kokkos::parallel_for(
2966 range_type(0, Bview.importMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
2967 Icol2Ccol(i) = Ccolmap_local.getLocalElement(Icolmap_local.getGlobalElement(i));
2968 });
2969 }
2970
2971 // Replace the column map
2972 //
2973 // mfh 27 Sep 2016: We do this because C was originally created
2974 // without a column Map. Now we have its column Map.
2975 C.replaceColMap(Ccolmap);
2976
2977 // mfh 27 Sep 2016: Construct tables that map from local column
2978 // indices of A, to local row indices of either B_local (the locally
2979 // owned part of B), or B_remote (the "imported" remote part of B).
2980 //
2981 // For column index Aik in row i of A, if the corresponding row of B
2982 // exists in the local part of B ("orig") (which I'll call B_local),
2983 // then targetMapToOrigRow[Aik] is the local index of that row of B.
2984 // Otherwise, targetMapToOrigRow[Aik] is "invalid" (a flag value).
2985 //
2986 // For column index Aik in row i of A, if the corresponding row of B
2987 // exists in the remote part of B ("Import") (which I'll call
2988 // B_remote), then targetMapToImportRow[Aik] is the local index of
2989 // that row of B. Otherwise, targetMapToOrigRow[Aik] is "invalid"
2990 // (a flag value).
2991
2992 // Run through all the hash table lookups once and for all
2993 lo_view_t targetMapToOrigRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToOrigRow"), Aview.colMap->getLocalNumElements());
2994 lo_view_t targetMapToImportRow(Kokkos::ViewAllocateWithoutInitializing("targetMapToImportRow"), Aview.colMap->getLocalNumElements());
2995 Kokkos::parallel_for(
2996 range_type(Aview.colMap->getMinLocalIndex(), Aview.colMap->getMaxLocalIndex() + 1), KOKKOS_LAMBDA(const LO i) {
2997 GO aidx = Acolmap_local.getGlobalElement(i);
2998 LO B_LID = Browmap_local.getLocalElement(aidx);
2999 if (B_LID != LO_INVALID) {
3000 targetMapToOrigRow(i) = B_LID;
3001 targetMapToImportRow(i) = LO_INVALID;
3002 } else {
3003 LO I_LID = Irowmap_local.getLocalElement(aidx);
3004 targetMapToOrigRow(i) = LO_INVALID;
3005 targetMapToImportRow(i) = I_LID;
3006 }
3007 });
3008
3009 // Call the actual kernel. We'll rely on partial template specialization to call the correct one ---
3010 // Either the straight-up Tpetra code (SerialNode) or the KokkosKernels one (other NGP node types)
3011 KernelWrappers2<Scalar, LocalOrdinal, GlobalOrdinal, Node, lo_view_t>::jacobi_A_B_newmatrix_kernel_wrapper(omega, Dinv, Aview, Bview, targetMapToOrigRow, targetMapToImportRow, Bcol2Ccol, Icol2Ccol, C, Cimport, label, params);
3012}
3013
3014/*********************************************************************************************************/
3015// Jacobi AB NewMatrix Kernel wrappers (Default non-threaded version)
3016// Kernel method for computing the local portion of C = (I-omega D^{-1} A)*B
3017
3018template <class Scalar,
3019 class LocalOrdinal,
3020 class GlobalOrdinal,
3021 class Node,
3022 class LocalOrdinalViewType>
3023void KernelWrappers2<Scalar, LocalOrdinal, GlobalOrdinal, Node, LocalOrdinalViewType>::jacobi_A_B_newmatrix_kernel_wrapper(typename Teuchos::ScalarTraits<Scalar>::magnitudeType omega,
3024 const Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Dinv,
3025 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
3026 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
3027 const LocalOrdinalViewType& targetMapToOrigRow,
3028 const LocalOrdinalViewType& targetMapToImportRow,
3029 const LocalOrdinalViewType& Bcol2Ccol,
3030 const LocalOrdinalViewType& Icol2Ccol,
3031 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
3032 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
3033 const std::string& label,
3034 const Teuchos::RCP<Teuchos::ParameterList>& params) {
3035 Tpetra::Details::ProfilingRegion MM("TpetraExt: Jacobi: Newmatrix SerialCore");
3036
3037 using Teuchos::Array;
3038 using Teuchos::ArrayRCP;
3039 using Teuchos::ArrayView;
3040 using Teuchos::RCP;
3041 using Teuchos::rcp;
3042
3043 // Lots and lots of typedefs
3044 typedef typename Tpetra::CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_host_type KCRS;
3045 typedef typename KCRS::StaticCrsGraphType graph_t;
3046 typedef typename graph_t::row_map_type::const_type c_lno_view_t;
3047 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
3048 typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
3049 typedef typename KCRS::values_type::non_const_type scalar_view_t;
3050
3051 // Jacobi-specific
3052 typedef typename scalar_view_t::memory_space scalar_memory_space;
3053
3054 typedef Scalar SC;
3055 typedef LocalOrdinal LO;
3056 typedef GlobalOrdinal GO;
3057 typedef Node NO;
3058
3059 typedef Map<LO, GO, NO> map_type;
3060 size_t ST_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
3061 LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
3062
3063 // Sizes
3064 RCP<const map_type> Ccolmap = C.getColMap();
3065 size_t m = Aview.origMatrix->getLocalNumRows();
3066 size_t n = Ccolmap->getLocalNumElements();
3067 size_t b_max_nnz_per_row = Bview.origMatrix->getLocalMaxNumRowEntries();
3068
3069 // Grab the Kokkos::SparseCrsMatrices & inner stuff
3070 const KCRS Amat = Aview.origMatrix->getLocalMatrixHost();
3071 const KCRS Bmat = Bview.origMatrix->getLocalMatrixHost();
3072
3073 c_lno_view_t Arowptr = Amat.graph.row_map, Browptr = Bmat.graph.row_map;
3074 const lno_nnz_view_t Acolind = Amat.graph.entries, Bcolind = Bmat.graph.entries;
3075 const scalar_view_t Avals = Amat.values, Bvals = Bmat.values;
3076
3077 c_lno_view_t Irowptr;
3078 lno_nnz_view_t Icolind;
3079 scalar_view_t Ivals;
3080 if (!Bview.importMatrix.is_null()) {
3081 auto lclB = Bview.importMatrix->getLocalMatrixHost();
3082 Irowptr = lclB.graph.row_map;
3083 Icolind = lclB.graph.entries;
3084 Ivals = lclB.values;
3085 b_max_nnz_per_row = std::max(b_max_nnz_per_row, Bview.importMatrix->getLocalMaxNumRowEntries());
3086 }
3087
3088 // Jacobi-specific inner stuff
3089 auto Dvals =
3090 Dinv.template getLocalView<scalar_memory_space>(Access::ReadOnly);
3091
3092 // Teuchos::ArrayView::operator[].
3093 // The status array will contain the index into colind where this entry was last deposited.
3094 // c_status[i] < CSR_ip - not in the row yet.
3095 // c_status[i] >= CSR_ip, this is the entry where you can find the data
3096 // We start with this filled with INVALID's indicating that there are no entries yet.
3097 // Sadly, this complicates the code due to the fact that size_t's are unsigned.
3098 size_t INVALID = Teuchos::OrdinalTraits<size_t>::invalid();
3099 Array<size_t> c_status(n, ST_INVALID);
3100
3101 // Classic csr assembly (low memory edition)
3102 //
3103 // mfh 27 Sep 2016: C_estimate_nnz does not promise an upper bound.
3104 // The method loops over rows of A, and may resize after processing
3105 // each row. Chris Siefert says that this reflects experience in
3106 // ML; for the non-threaded case, ML found it faster to spend less
3107 // effort on estimation and risk an occasional reallocation.
3108 size_t CSR_alloc = std::max(C_estimate_nnz(*Aview.origMatrix, *Bview.origMatrix), n);
3109 lno_view_t Crowptr(Kokkos::ViewAllocateWithoutInitializing("Crowptr"), m + 1);
3110 lno_nnz_view_t Ccolind(Kokkos::ViewAllocateWithoutInitializing("Ccolind"), CSR_alloc);
3111 scalar_view_t Cvals(Kokkos::ViewAllocateWithoutInitializing("Cvals"), CSR_alloc);
3112 size_t CSR_ip = 0, OLD_ip = 0;
3113
3114 const SC SC_ZERO = Teuchos::ScalarTraits<Scalar>::zero();
3115
3116 // mfh 27 Sep 2016: Here is the local sparse matrix-matrix multiply
3117 // routine. The routine computes
3118 //
3119 // C := (I - omega * D^{-1} * A) * (B_local + B_remote)).
3120 //
3121 // This corresponds to one sweep of (weighted) Jacobi.
3122 //
3123 // For column index Aik in row i of A, targetMapToOrigRow[Aik] tells
3124 // you whether the corresponding row of B belongs to B_local
3125 // ("orig") or B_remote ("Import").
3126
3127 // For each row of A/C
3128 for (size_t i = 0; i < m; i++) {
3129 // mfh 27 Sep 2016: m is the number of rows in the input matrix A
3130 // on the calling process.
3131 Crowptr[i] = CSR_ip;
3132 SC minusOmegaDval = -omega * Dvals(i, 0);
3133
3134 // Entries of B
3135 for (size_t j = Browptr[i]; j < Browptr[i + 1]; j++) {
3136 Scalar Bval = Bvals[j];
3137 if (Bval == SC_ZERO)
3138 continue;
3139 LO Bij = Bcolind[j];
3140 LO Cij = Bcol2Ccol[Bij];
3141
3142 // Assume no repeated entries in B
3143 c_status[Cij] = CSR_ip;
3144 Ccolind[CSR_ip] = Cij;
3145 Cvals[CSR_ip] = Bvals[j];
3146 CSR_ip++;
3147 }
3148
3149 // Entries of -omega * Dinv * A * B
3150 for (size_t k = Arowptr[i]; k < Arowptr[i + 1]; k++) {
3151 LO Aik = Acolind[k];
3152 const SC Aval = Avals[k];
3153 if (Aval == SC_ZERO)
3154 continue;
3155
3156 if (targetMapToOrigRow[Aik] != LO_INVALID) {
3157 // Local matrix
3158 size_t Bk = static_cast<size_t>(targetMapToOrigRow[Aik]);
3159
3160 for (size_t j = Browptr[Bk]; j < Browptr[Bk + 1]; ++j) {
3161 LO Bkj = Bcolind[j];
3162 LO Cij = Bcol2Ccol[Bkj];
3163
3164 if (c_status[Cij] == INVALID || c_status[Cij] < OLD_ip) {
3165 // New entry
3166 c_status[Cij] = CSR_ip;
3167 Ccolind[CSR_ip] = Cij;
3168 Cvals[CSR_ip] = minusOmegaDval * Aval * Bvals[j];
3169 CSR_ip++;
3170
3171 } else {
3172 Cvals[c_status[Cij]] += minusOmegaDval * Aval * Bvals[j];
3173 }
3174 }
3175
3176 } else {
3177 // Remote matrix
3178 size_t Ik = static_cast<size_t>(targetMapToImportRow[Aik]);
3179 for (size_t j = Irowptr[Ik]; j < Irowptr[Ik + 1]; ++j) {
3180 LO Ikj = Icolind[j];
3181 LO Cij = Icol2Ccol[Ikj];
3182
3183 if (c_status[Cij] == INVALID || c_status[Cij] < OLD_ip) {
3184 // New entry
3185 c_status[Cij] = CSR_ip;
3186 Ccolind[CSR_ip] = Cij;
3187 Cvals[CSR_ip] = minusOmegaDval * Aval * Ivals[j];
3188 CSR_ip++;
3189 } else {
3190 Cvals[c_status[Cij]] += minusOmegaDval * Aval * Ivals[j];
3191 }
3192 }
3193 }
3194 }
3195
3196 // Resize for next pass if needed
3197 if (i + 1 < m && CSR_ip + std::min(n, (Arowptr[i + 2] - Arowptr[i + 1] + 1) * b_max_nnz_per_row) > CSR_alloc) {
3198 CSR_alloc *= 2;
3199 Kokkos::resize(Ccolind, CSR_alloc);
3200 Kokkos::resize(Cvals, CSR_alloc);
3201 }
3202 OLD_ip = CSR_ip;
3203 }
3204 Crowptr[m] = CSR_ip;
3205
3206 // Downward resize
3207 Kokkos::resize(Ccolind, CSR_ip);
3208 Kokkos::resize(Cvals, CSR_ip);
3209
3210 {
3211 Tpetra::Details::ProfilingRegion MM2("TpetraExt: Jacobi: Newmatrix Final Sort");
3212
3213 // Replace the column map
3214 //
3215 // mfh 27 Sep 2016: We do this because C was originally created
3216 // without a column Map. Now we have its column Map.
3217 C.replaceColMap(Ccolmap);
3218
3219 // Final sort & set of CRS arrays
3220 //
3221 // TODO (mfh 27 Sep 2016) Will the thread-parallel "local" sparse
3222 // matrix-matrix multiply routine sort the entries for us?
3223 // Final sort & set of CRS arrays
3224 if (params.is_null() || params->get("sort entries", true)) {
3225 // Tpetra's serial SpGEMM results in almost sorted matrices. Use shell sort.
3226 Import_Util::sortCrsEntries(Crowptr, Ccolind, Cvals);
3227 }
3228 C.setAllValues(Crowptr, Ccolind, Cvals);
3229 }
3230 {
3231 Tpetra::Details::ProfilingRegion MM3("TpetraExt: Jacobi: Newmatrix ESFC");
3232
3233 // Final FillComplete
3234 //
3235 // mfh 27 Sep 2016: So-called "expert static fill complete" bypasses
3236 // Import (from domain Map to column Map) construction (which costs
3237 // lots of communication) by taking the previously constructed
3238 // Import object. We should be able to do this without interfering
3239 // with the implementation of the local part of sparse matrix-matrix
3240 // multply above
3241 RCP<Teuchos::ParameterList> labelList = rcp(new Teuchos::ParameterList);
3242 labelList->set("Timer Label", label);
3243 if (!params.is_null()) labelList->set("compute global constants", params->get("compute global constants", true));
3244 RCP<const Export<LO, GO, NO>> dummyExport;
3245 C.expertStaticFillComplete(Bview.origMatrix->getDomainMap(), Aview.origMatrix->getRangeMap(), Cimport, dummyExport, labelList);
3246 }
3247}
3248
3249/*********************************************************************************************************/
3250// Kernel method for computing the local portion of C = (I-omega D^{-1} A)*B
3251template <class Scalar,
3252 class LocalOrdinal,
3253 class GlobalOrdinal,
3254 class Node>
3255void jacobi_A_B_reuse(
3256 typename Teuchos::ScalarTraits<Scalar>::magnitudeType omega,
3257 const Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Dinv,
3258 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
3259 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
3260 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
3261 const std::string& label,
3262 const Teuchos::RCP<Teuchos::ParameterList>& params) {
3263 using Teuchos::Array;
3264 using Teuchos::ArrayRCP;
3265 using Teuchos::ArrayView;
3266 using Teuchos::RCP;
3267 using Teuchos::rcp;
3268
3269 typedef LocalOrdinal LO;
3270 typedef GlobalOrdinal GO;
3271 typedef Node NO;
3272
3273 typedef Import<LO, GO, NO> import_type;
3274 typedef Map<LO, GO, NO> map_type;
3275
3276 // Kokkos typedefs
3277 typedef typename map_type::local_map_type local_map_type;
3279 typedef typename KCRS::StaticCrsGraphType graph_t;
3280 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
3281 typedef typename NO::execution_space execution_space;
3282 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
3283 typedef Kokkos::View<LO*, typename lno_view_t::array_layout, typename lno_view_t::device_type> lo_view_t;
3284
3285 RCP<const import_type> Cimport = C.getGraph()->getImporter();
3286 lo_view_t Bcol2Ccol, Icol2Ccol;
3287 lo_view_t targetMapToOrigRow;
3288 lo_view_t targetMapToImportRow;
3289 {
3290 Tpetra::Details::ProfilingRegion MM("TpetraExt: Jacobi: Reuse Cmap");
3291
3292 LO LO_INVALID = Teuchos::OrdinalTraits<LO>::invalid();
3293
3294 // Grab all the maps
3295 RCP<const map_type> Ccolmap = C.getColMap();
3296 local_map_type Acolmap_local = Aview.colMap->getLocalMap();
3297 local_map_type Browmap_local = Bview.origMatrix->getRowMap()->getLocalMap();
3298 local_map_type Irowmap_local;
3299 if (!Bview.importMatrix.is_null()) Irowmap_local = Bview.importMatrix->getRowMap()->getLocalMap();
3300 local_map_type Bcolmap_local = Bview.origMatrix->getColMap()->getLocalMap();
3301 local_map_type Icolmap_local;
3302 if (!Bview.importMatrix.is_null()) Icolmap_local = Bview.importMatrix->getColMap()->getLocalMap();
3303 local_map_type Ccolmap_local = Ccolmap->getLocalMap();
3304
3305 // Build the final importer / column map, hash table lookups for C
3306 Bcol2Ccol = lo_view_t(Kokkos::ViewAllocateWithoutInitializing("Bcol2Ccol"), Bview.colMap->getLocalNumElements());
3307 {
3308 // Bcol2Col may not be trivial, as Ccolmap is compressed during fillComplete in newmatrix
3309 // So, column map of C may be a strict subset of the column map of B
3310 Kokkos::parallel_for(
3311 range_type(0, Bview.origMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
3312 Bcol2Ccol(i) = Ccolmap_local.getLocalElement(Bcolmap_local.getGlobalElement(i));
3313 });
3314
3315 if (!Bview.importMatrix.is_null()) {
3316 TEUCHOS_TEST_FOR_EXCEPTION(!Cimport->getSourceMap()->isSameAs(*Bview.origMatrix->getDomainMap()),
3317 std::runtime_error, "Tpetra::Jacobi: Import setUnion messed with the DomainMap in an unfortunate way");
3318
3319 Kokkos::resize(Icol2Ccol, Bview.importMatrix->getColMap()->getLocalNumElements());
3320 Kokkos::parallel_for(
3321 range_type(0, Bview.importMatrix->getColMap()->getLocalNumElements()), KOKKOS_LAMBDA(const LO i) {
3322 Icol2Ccol(i) = Ccolmap_local.getLocalElement(Icolmap_local.getGlobalElement(i));
3323 });
3324 }
3325 }
3326
3327 // Run through all the hash table lookups once and for all
3328 targetMapToOrigRow = lo_view_t(Kokkos::ViewAllocateWithoutInitializing("targetMapToOrigRow"), Aview.colMap->getLocalNumElements());
3329 targetMapToImportRow = lo_view_t(Kokkos::ViewAllocateWithoutInitializing("targetMapToImportRow"), Aview.colMap->getLocalNumElements());
3330 Kokkos::parallel_for(
3331 range_type(Aview.colMap->getMinLocalIndex(), Aview.colMap->getMaxLocalIndex() + 1), KOKKOS_LAMBDA(const LO i) {
3332 GO aidx = Acolmap_local.getGlobalElement(i);
3333 LO B_LID = Browmap_local.getLocalElement(aidx);
3334 if (B_LID != LO_INVALID) {
3335 targetMapToOrigRow(i) = B_LID;
3336 targetMapToImportRow(i) = LO_INVALID;
3337 } else {
3338 LO I_LID = Irowmap_local.getLocalElement(aidx);
3339 targetMapToOrigRow(i) = LO_INVALID;
3340 targetMapToImportRow(i) = I_LID;
3341 }
3342 });
3343 }
3344
3345 // Call the actual kernel. We'll rely on partial template specialization to call the correct one ---
3346 // Either the straight-up Tpetra code (SerialNode) or the KokkosKernels one (other NGP node types)
3347 KernelWrappers2<Scalar, LocalOrdinal, GlobalOrdinal, Node, lo_view_t>::jacobi_A_B_reuse_kernel_wrapper(omega, Dinv, Aview, Bview, targetMapToOrigRow, targetMapToImportRow, Bcol2Ccol, Icol2Ccol, C, Cimport, label, params);
3348}
3349
3350/*********************************************************************************************************/
3351template <class Scalar,
3352 class LocalOrdinal,
3353 class GlobalOrdinal,
3354 class Node,
3355 class LocalOrdinalViewType>
3356void KernelWrappers2<Scalar, LocalOrdinal, GlobalOrdinal, Node, LocalOrdinalViewType>::jacobi_A_B_reuse_kernel_wrapper(typename Teuchos::ScalarTraits<Scalar>::magnitudeType omega,
3357 const Vector<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Dinv,
3358 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
3359 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
3360 const LocalOrdinalViewType& targetMapToOrigRow,
3361 const LocalOrdinalViewType& targetMapToImportRow,
3362 const LocalOrdinalViewType& Bcol2Ccol,
3363 const LocalOrdinalViewType& Icol2Ccol,
3364 CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& C,
3365 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> Cimport,
3366 const std::string& label,
3367 const Teuchos::RCP<Teuchos::ParameterList>& params) {
3368 host_jacobi_A_B_reuse(omega, Dinv, Aview, Bview, targetMapToOrigRow, targetMapToImportRow, Bcol2Ccol, Icol2Ccol, C, Cimport, label, params);
3369}
3370
3371/*********************************************************************************************************/
3372template <class Scalar,
3373 class LocalOrdinal,
3374 class GlobalOrdinal,
3375 class Node>
3376void import_and_extract_views(
3377 const CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& A,
3378 Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>> targetMap,
3379 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
3380 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> prototypeImporter,
3381 bool userAssertsThereAreNoRemotes,
3382 const std::string& label,
3383 const Teuchos::RCP<Teuchos::ParameterList>& params) {
3384 using Teuchos::Array;
3385 using Teuchos::ArrayView;
3386 using Teuchos::null;
3387 using Teuchos::RCP;
3388 using Teuchos::rcp;
3389
3390 typedef Scalar SC;
3391 typedef LocalOrdinal LO;
3392 typedef GlobalOrdinal GO;
3393 typedef Node NO;
3394
3395 typedef Map<LO, GO, NO> map_type;
3396 typedef Import<LO, GO, NO> import_type;
3397 typedef CrsMatrix<SC, LO, GO, NO> crs_matrix_type;
3398
3399 RCP<Tpetra::Details::ProfilingRegion> MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: I&X Alloc"));
3400
3401 // The goal of this method is to populate the 'Aview' struct with views of the
3402 // rows of A, including all rows that correspond to elements in 'targetMap'.
3403 //
3404 // If targetMap includes local elements that correspond to remotely-owned rows
3405 // of A, then those remotely-owned rows will be imported into
3406 // 'Aview.importMatrix', and views of them will be included in 'Aview'.
3407 Aview.deleteContents();
3408
3409 Aview.origMatrix = rcp(&A, false);
3410 // trigger creation of int-typed row pointer array for use in TPLs, but don't actually need it here
3411 Aview.origMatrix->getApplyHelper();
3412 Aview.origRowMap = A.getRowMap();
3413 Aview.rowMap = targetMap;
3414 Aview.colMap = A.getColMap();
3415 Aview.domainMap = A.getDomainMap();
3416 Aview.importColMap = null;
3417 RCP<const map_type> rowMap = A.getRowMap();
3418 const int numProcs = rowMap->getComm()->getSize();
3419
3420 // Short circuit if the user swears there are no remotes (or if we're in serial)
3421 if (userAssertsThereAreNoRemotes || numProcs < 2)
3422 return;
3423
3424 RCP<const import_type> importer;
3425 if (params != null && params->isParameter("importer")) {
3426 importer = params->get<RCP<const import_type>>("importer");
3427
3428 } else {
3429 MM = Teuchos::null;
3430 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: I&X RemoteMap"));
3431
3432 // Mark each row in targetMap as local or remote, and go ahead and get a view
3433 // for the local rows
3434 RCP<const map_type> remoteRowMap;
3435 size_t numRemote = 0;
3436 int mode = 0;
3437 if (!prototypeImporter.is_null() &&
3438 prototypeImporter->getSourceMap()->isSameAs(*rowMap) &&
3439 prototypeImporter->getTargetMap()->isSameAs(*targetMap)) {
3440 // We have a valid prototype importer --- ask it for the remotes
3441 Tpetra::Details::ProfilingRegion MM2("TpetraExt: MMM: I&X RemoteMap: Mode1");
3442
3443 ArrayView<const LO> remoteLIDs = prototypeImporter->getRemoteLIDs();
3444 numRemote = prototypeImporter->getNumRemoteIDs();
3445
3446 Array<GO> remoteRows(numRemote);
3447 for (size_t i = 0; i < numRemote; i++)
3448 remoteRows[i] = targetMap->getGlobalElement(remoteLIDs[i]);
3449
3450 remoteRowMap = rcp(new map_type(Teuchos::OrdinalTraits<global_size_t>::invalid(), remoteRows(),
3451 rowMap->getIndexBase(), rowMap->getComm(), params));
3452 mode = 1;
3453
3454 } else if (prototypeImporter.is_null()) {
3455 // No prototype importer --- count the remotes the hard way
3456 Tpetra::Details::ProfilingRegion MM2("TpetraExt: MMM: I&X RemoteMap: Mode2");
3457
3458 ArrayView<const GO> rows = targetMap->getLocalElementList();
3459 size_t numRows = targetMap->getLocalNumElements();
3460
3461 Array<GO> remoteRows(numRows);
3462 for (size_t i = 0; i < numRows; ++i) {
3463 const LO mlid = rowMap->getLocalElement(rows[i]);
3464
3465 if (mlid == Teuchos::OrdinalTraits<LO>::invalid())
3466 remoteRows[numRemote++] = rows[i];
3467 }
3468 remoteRows.resize(numRemote);
3469 remoteRowMap = rcp(new map_type(Teuchos::OrdinalTraits<global_size_t>::invalid(), remoteRows(),
3470 rowMap->getIndexBase(), rowMap->getComm(), params));
3471 mode = 2;
3472
3473 } else {
3474 // PrototypeImporter is bad. But if we're in serial that's OK.
3475 mode = 3;
3476 }
3477
3478 if (numProcs < 2) {
3479 TEUCHOS_TEST_FOR_EXCEPTION(numRemote > 0, std::runtime_error,
3480 "MatrixMatrix::import_and_extract_views ERROR, numProcs < 2 but attempting to import remote matrix rows.");
3481 // If only one processor we don't need to import any remote rows, so return.
3482 return;
3483 }
3484
3485 //
3486 // Now we will import the needed remote rows of A, if the remoteRowMap has entries
3487 //
3488 if (!remoteRowMap.is_null() && (remoteRowMap->getGlobalNumElements() > 0)) {
3489 MM = Teuchos::null;
3490 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: I&X Import-2"));
3491
3492 // Create an importer with target-map remoteRowMap and source-map rowMap.
3493 if (mode == 1)
3494 importer = prototypeImporter->createRemoteOnlyImport(remoteRowMap);
3495 else if (mode == 2)
3496 importer = rcp(new import_type(rowMap, remoteRowMap));
3497 else
3498 throw std::runtime_error("prototypeImporter->SourceMap() does not match A.getRowMap()!");
3499 }
3500
3501 if (params != null)
3502 params->set("importer", importer);
3503 }
3504
3505 if (importer != null) {
3506 MM = Teuchos::null;
3507 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: I&X Import-3"));
3508
3509 // Now create a new matrix into which we can import the remote rows of A that we need.
3510 Teuchos::ParameterList labelList;
3511 labelList.set("Timer Label", label);
3512 // Minor speedup tweak - avoid computing the global constants
3513 labelList.set("compute global constants", false);
3514 auto& labelList_subList = labelList.sublist("matrixmatrix: kernel params", false);
3515 labelList_subList.set("isMatrixMatrix_TransferAndFillComplete", true);
3516
3517 if (!params.is_null()) {
3518 if (params->isParameter("compute global constants"))
3519 labelList.set("compute global constants", params->get<bool>("compute global constants"));
3520 }
3521
3522 Aview.importMatrix = Tpetra::importAndFillCompleteCrsMatrix<crs_matrix_type>(rcpFromRef(A), *importer,
3523 A.getDomainMap(), importer->getTargetMap(), rcpFromRef(labelList));
3524 // trigger creation of int-typed row pointer array for use in TPLs, but don't actually need it here
3525 Aview.importMatrix->getApplyHelper();
3526
3527#if 0
3528 // Disabled code for dumping input matrices
3529 static int count=0;
3530 char str[80];
3531 sprintf(str,"import_matrix.%d.dat",count);
3533 count++;
3534#endif
3535
3536#ifdef HAVE_TPETRA_MMM_STATISTICS
3537 printMultiplicationStatistics(importer, label + std::string(" I&X MMM"));
3538#endif
3539
3540 MM = Teuchos::null;
3541 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: MMM: I&X Import-4"));
3542
3543 // Save the column map of the imported matrix, so that we can convert indices back to global for arithmetic later
3544 Aview.importColMap = Aview.importMatrix->getColMap();
3545 MM = Teuchos::null;
3546 }
3547}
3548
3549/*********************************************************************************************************/
3550template <class Scalar,
3551 class LocalOrdinal,
3552 class GlobalOrdinal,
3553 class Node>
3554void import_and_extract_views(
3555 const BlockCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& M,
3556 Teuchos::RCP<const Map<LocalOrdinal, GlobalOrdinal, Node>> targetMap,
3557 BlockCrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Mview,
3558 Teuchos::RCP<const Import<LocalOrdinal, GlobalOrdinal, Node>> prototypeImporter,
3559 bool userAssertsThereAreNoRemotes) {
3560 using Teuchos::Array;
3561 using Teuchos::ArrayView;
3562 using Teuchos::null;
3563 using Teuchos::RCP;
3564 using Teuchos::rcp;
3565
3566 typedef Scalar SC;
3567 typedef LocalOrdinal LO;
3568 typedef GlobalOrdinal GO;
3569 typedef Node NO;
3570
3571 typedef Map<LO, GO, NO> map_type;
3572 typedef Import<LO, GO, NO> import_type;
3573 typedef BlockCrsMatrix<SC, LO, GO, NO> blockcrs_matrix_type;
3574
3575 // The goal of this method is to populate the 'Mview' struct with views of the
3576 // rows of M, including all rows that correspond to elements in 'targetMap'.
3577 //
3578 // If targetMap includes local elements that correspond to remotely-owned rows
3579 // of M, then those remotely-owned rows will be imported into
3580 // 'Mview.importMatrix', and views of them will be included in 'Mview'.
3581 Mview.deleteContents();
3582
3583 Mview.origMatrix = rcp(&M, false);
3584 // trigger creation of int-typed row pointer array for use in TPLs, but don't actually need it here
3585 Mview.origMatrix->getApplyHelper();
3586 Mview.origRowMap = M.getRowMap();
3587 Mview.rowMap = targetMap;
3588 Mview.colMap = M.getColMap();
3589 Mview.importColMap = null;
3590 RCP<const map_type> rowMap = M.getRowMap();
3591 const int numProcs = rowMap->getComm()->getSize();
3592
3593 // Short circuit if the user swears there are no remotes (or if we're in serial)
3594 if (userAssertsThereAreNoRemotes || numProcs < 2) return;
3595
3596 // Mark each row in targetMap as local or remote, and go ahead and get a view
3597 // for the local rows
3598 RCP<const map_type> remoteRowMap;
3599 size_t numRemote = 0;
3600 int mode = 0;
3601 if (!prototypeImporter.is_null() &&
3602 prototypeImporter->getSourceMap()->isSameAs(*rowMap) &&
3603 prototypeImporter->getTargetMap()->isSameAs(*targetMap)) {
3604 // We have a valid prototype importer --- ask it for the remotes
3605 ArrayView<const LO> remoteLIDs = prototypeImporter->getRemoteLIDs();
3606 numRemote = prototypeImporter->getNumRemoteIDs();
3607
3608 Array<GO> remoteRows(numRemote);
3609 for (size_t i = 0; i < numRemote; i++)
3610 remoteRows[i] = targetMap->getGlobalElement(remoteLIDs[i]);
3611
3612 remoteRowMap = rcp(new map_type(Teuchos::OrdinalTraits<global_size_t>::invalid(), remoteRows(),
3613 rowMap->getIndexBase(), rowMap->getComm()));
3614 mode = 1;
3615
3616 } else if (prototypeImporter.is_null()) {
3617 // No prototype importer --- count the remotes the hard way
3618 ArrayView<const GO> rows = targetMap->getLocalElementList();
3619 size_t numRows = targetMap->getLocalNumElements();
3620
3621 Array<GO> remoteRows(numRows);
3622 for (size_t i = 0; i < numRows; ++i) {
3623 const LO mlid = rowMap->getLocalElement(rows[i]);
3624
3625 if (mlid == Teuchos::OrdinalTraits<LO>::invalid())
3626 remoteRows[numRemote++] = rows[i];
3627 }
3628 remoteRows.resize(numRemote);
3629 remoteRowMap = rcp(new map_type(Teuchos::OrdinalTraits<global_size_t>::invalid(), remoteRows(),
3630 rowMap->getIndexBase(), rowMap->getComm()));
3631 mode = 2;
3632
3633 } else {
3634 // PrototypeImporter is bad. But if we're in serial that's OK.
3635 mode = 3;
3636 }
3637
3638 if (numProcs < 2) {
3639 TEUCHOS_TEST_FOR_EXCEPTION(numRemote > 0, std::runtime_error,
3640 "MatrixMatrix::import_and_extract_views ERROR, numProcs < 2 but attempting to import remote matrix rows.");
3641 // If only one processor we don't need to import any remote rows, so return.
3642 return;
3643 }
3644
3645 // Now we will import the needed remote rows of M, if the remoteRowMap has entries
3646
3647 RCP<const import_type> importer;
3648
3649 if (!remoteRowMap.is_null() && (remoteRowMap->getGlobalNumElements() > 0)) {
3650 // Create an importer with target-map remoteRowMap and source-map rowMap.
3651 if (mode == 1)
3652 importer = prototypeImporter->createRemoteOnlyImport(remoteRowMap);
3653 else if (mode == 2)
3654 importer = rcp(new import_type(rowMap, remoteRowMap));
3655 else
3656 throw std::runtime_error("prototypeImporter->SourceMap() does not match M.getRowMap()!");
3657 }
3658
3659 if (importer != null) {
3660 // Get import matrix
3661 // TODO: create the int-typed row-pointer here
3662 Mview.importMatrix = Tpetra::importAndFillCompleteBlockCrsMatrix<blockcrs_matrix_type>(rcpFromRef(M), *importer);
3663 // trigger creation of int-typed row pointer array for use in TPLs, but don't actually need it here
3664 Mview.importMatrix->getApplyHelper();
3665 // Save the column map of the imported matrix, so that we can convert indices
3666 // back to global for arithmetic later
3667 Mview.importColMap = Mview.importMatrix->getColMap();
3668 }
3669}
3670
3671/*********************************************************************************************************/
3672// This only merges matrices that look like B & Bimport, aka, they have no overlapping rows
3673template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node, class LocalOrdinalViewType>
3675merge_matrices(CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
3676 CrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
3677 const LocalOrdinalViewType& Acol2Brow,
3678 const LocalOrdinalViewType& Acol2Irow,
3679 const LocalOrdinalViewType& Bcol2Ccol,
3680 const LocalOrdinalViewType& Icol2Ccol,
3681 const size_t mergedNodeNumCols) {
3682 using Teuchos::RCP;
3684 typedef typename KCRS::StaticCrsGraphType graph_t;
3685 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
3686 typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
3687 typedef typename KCRS::values_type::non_const_type scalar_view_t;
3688 // Grab the Kokkos::SparseCrsMatrices
3689 const KCRS Ak = Aview.origMatrix->getLocalMatrixDevice();
3690 const KCRS Bk = Bview.origMatrix->getLocalMatrixDevice();
3691
3692 // We need to do this dance if either (a) We have Bimport or (b) We don't A's colMap is not the same as B's rowMap
3693 if (!Bview.importMatrix.is_null() || (Bview.importMatrix.is_null() && (&*Aview.origMatrix->getGraph()->getColMap() != &*Bview.origMatrix->getGraph()->getRowMap()))) {
3694 // We do have a Bimport
3695 // NOTE: We're going merge Borig and Bimport into a single matrix and reindex the columns *before* we multiply.
3696 // This option was chosen because we know we don't have any duplicate entries, so we can allocate once.
3697
3698 KCRS Iks;
3699 if (!Bview.importMatrix.is_null()) Iks = Bview.importMatrix->getLocalMatrixDevice();
3700
3701 size_t merge_numrows = Ak.numCols();
3702
3703 // The last entry of this at least, need to be initialized
3704 lno_view_t Mrowptr("Mrowptr", merge_numrows + 1);
3705
3706 const LocalOrdinal LO_INVALID = Teuchos::OrdinalTraits<LocalOrdinal>::invalid();
3707
3708 // Use a Kokkos::parallel_scan to build the rowptr
3709 typedef typename Node::execution_space execution_space;
3710 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
3711 Kokkos::parallel_scan(
3712 "Tpetra_MatrixMatrix_merge_matrices_buildRowptr", range_type(0, merge_numrows),
3713 KOKKOS_LAMBDA(const size_t i, size_t& update, const bool final) {
3714 if (final) Mrowptr(i) = update;
3715 // Get the row count
3716 size_t ct = 0;
3717 if (Acol2Brow(i) != LO_INVALID)
3718 ct = Bk.graph.row_map(Acol2Brow(i) + 1) - Bk.graph.row_map(Acol2Brow(i));
3719 else
3720 ct = Iks.graph.row_map(Acol2Irow(i) + 1) - Iks.graph.row_map(Acol2Irow(i));
3721 update += ct;
3722
3723 if (final && i + 1 == merge_numrows)
3724 Mrowptr(i + 1) = update;
3725 });
3726
3727 // Allocate nnz
3728 size_t merge_nnz = ::Tpetra::Details::getEntryOnHost(Mrowptr, merge_numrows);
3729 lno_nnz_view_t Mcolind(Kokkos::ViewAllocateWithoutInitializing("Mcolind"), merge_nnz);
3730 scalar_view_t Mvalues(Kokkos::ViewAllocateWithoutInitializing("Mvals"), merge_nnz);
3731
3732 // Use a Kokkos::parallel_for to fill the rowptr/colind arrays
3733 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
3734 Kokkos::parallel_for(
3735 "Tpetra_MatrixMatrix_merg_matrices_buildColindValues", range_type(0, merge_numrows), KOKKOS_LAMBDA(const size_t i) {
3736 if (Acol2Brow(i) != LO_INVALID) {
3737 size_t row = Acol2Brow(i);
3738 size_t start = Bk.graph.row_map(row);
3739 for (size_t j = Mrowptr(i); j < Mrowptr(i + 1); j++) {
3740 Mvalues(j) = Bk.values(j - Mrowptr(i) + start);
3741 Mcolind(j) = Bcol2Ccol(Bk.graph.entries(j - Mrowptr(i) + start));
3742 }
3743 } else {
3744 size_t row = Acol2Irow(i);
3745 size_t start = Iks.graph.row_map(row);
3746 for (size_t j = Mrowptr(i); j < Mrowptr(i + 1); j++) {
3747 Mvalues(j) = Iks.values(j - Mrowptr(i) + start);
3748 Mcolind(j) = Icol2Ccol(Iks.graph.entries(j - Mrowptr(i) + start));
3749 }
3750 }
3751 });
3752
3753 KCRS newmat("CrsMatrix", merge_numrows, mergedNodeNumCols, merge_nnz, Mvalues, Mrowptr, Mcolind);
3754 return newmat;
3755 } else {
3756 // We don't have a Bimport (the easy case)
3757 return Bk;
3758 }
3759} // end merge_matrices
3760
3761/*********************************************************************************************************/
3762// This only merges matrices that look like B & Bimport, aka, they have no overlapping rows
3763template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node, class LocalOrdinalViewType>
3764const typename Tpetra::BlockCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_device_type
3765merge_matrices(BlockCrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Aview,
3766 BlockCrsMatrixStruct<Scalar, LocalOrdinal, GlobalOrdinal, Node>& Bview,
3767 const LocalOrdinalViewType& Acol2Brow,
3768 const LocalOrdinalViewType& Acol2Irow,
3769 const LocalOrdinalViewType& Bcol2Ccol,
3770 const LocalOrdinalViewType& Icol2Ccol,
3771 const size_t mergedNodeNumCols) {
3772 using Teuchos::RCP;
3773 typedef typename Tpetra::BlockCrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_device_type KBCRS;
3774 typedef typename KBCRS::StaticCrsGraphType graph_t;
3775 typedef typename graph_t::row_map_type::non_const_type lno_view_t;
3776 typedef typename graph_t::entries_type::non_const_type lno_nnz_view_t;
3777 typedef typename KBCRS::values_type::non_const_type scalar_view_t;
3778
3779 // Grab the KokkosSparse::BsrMatrix
3780 const KBCRS Ak = Aview.origMatrix->getLocalMatrixDevice();
3781 const KBCRS Bk = Bview.origMatrix->getLocalMatrixDevice();
3782
3783 // We need to do this dance if either (a) We have Bimport or (b) A's colMap is not the same as B's rowMap
3784 if (!Bview.importMatrix.is_null() ||
3785 (Bview.importMatrix.is_null() &&
3786 (&*Aview.origMatrix->getGraph()->getColMap() != &*Bview.origMatrix->getGraph()->getRowMap()))) {
3787 // We do have a Bimport
3788 // NOTE: We're going merge Borig and Bimport into a single matrix and reindex the columns *before* we multiply.
3789 // This option was chosen because we know we don't have any duplicate entries, so we can allocate once.
3790 KBCRS Iks;
3791 if (!Bview.importMatrix.is_null()) Iks = Bview.importMatrix->getLocalMatrixDevice();
3792 size_t merge_numrows = Ak.numCols();
3793
3794 // The last entry of this at least, need to be initialized
3795 lno_view_t Mrowptr("Mrowptr", merge_numrows + 1);
3796
3797 const LocalOrdinal LO_INVALID = Teuchos::OrdinalTraits<LocalOrdinal>::invalid();
3798
3799 // Use a Kokkos::parallel_scan to build the rowptr
3800 typedef typename Node::execution_space execution_space;
3801 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
3802 Kokkos::parallel_scan(
3803 "Tpetra_MatrixMatrix_merge_matrices_buildRowptr", range_type(0, merge_numrows),
3804 KOKKOS_LAMBDA(const size_t i, size_t& update, const bool final) {
3805 if (final) Mrowptr(i) = update;
3806 // Get the row count
3807 size_t ct = 0;
3808 if (Acol2Brow(i) != LO_INVALID)
3809 ct = Bk.graph.row_map(Acol2Brow(i) + 1) - Bk.graph.row_map(Acol2Brow(i));
3810 else
3811 ct = Iks.graph.row_map(Acol2Irow(i) + 1) - Iks.graph.row_map(Acol2Irow(i));
3812 update += ct;
3813
3814 if (final && i + 1 == merge_numrows)
3815 Mrowptr(i + 1) = update;
3816 });
3817
3818 // Allocate nnz
3819 size_t merge_nnz = ::Tpetra::Details::getEntryOnHost(Mrowptr, merge_numrows);
3820 const int blocksize = Ak.blockDim();
3821 lno_nnz_view_t Mcolind(Kokkos::ViewAllocateWithoutInitializing("Mcolind"), merge_nnz);
3822 scalar_view_t Mvalues(Kokkos::ViewAllocateWithoutInitializing("Mvals"), merge_nnz * blocksize * blocksize);
3823
3824 // Use a Kokkos::parallel_for to fill the rowptr/colind arrays
3825 typedef Kokkos::RangePolicy<execution_space, size_t> range_type;
3826 Kokkos::parallel_for(
3827 "Tpetra_MatrixMatrix_merg_matrices_buildColindValues", range_type(0, merge_numrows), KOKKOS_LAMBDA(const size_t i) {
3828 if (Acol2Brow(i) != LO_INVALID) {
3829 size_t row = Acol2Brow(i);
3830 size_t start = Bk.graph.row_map(row);
3831 for (size_t j = Mrowptr(i); j < Mrowptr(i + 1); j++) {
3832 Mcolind(j) = Bcol2Ccol(Bk.graph.entries(j - Mrowptr(i) + start));
3833
3834 for (int b = 0; b < blocksize * blocksize; ++b) {
3835 const int val_indx = j * blocksize * blocksize + b;
3836 const int b_val_indx = (j - Mrowptr(i) + start) * blocksize * blocksize + b;
3837 Mvalues(val_indx) = Bk.values(b_val_indx);
3838 }
3839 }
3840 } else {
3841 size_t row = Acol2Irow(i);
3842 size_t start = Iks.graph.row_map(row);
3843 for (size_t j = Mrowptr(i); j < Mrowptr(i + 1); j++) {
3844 Mcolind(j) = Icol2Ccol(Iks.graph.entries(j - Mrowptr(i) + start));
3845
3846 for (int b = 0; b < blocksize * blocksize; ++b) {
3847 const int val_indx = j * blocksize * blocksize + b;
3848 const int b_val_indx = (j - Mrowptr(i) + start) * blocksize * blocksize + b;
3849 Mvalues(val_indx) = Iks.values(b_val_indx);
3850 }
3851 }
3852 }
3853 });
3854
3855 // Build and return merged KokkosSparse matrix
3856 KBCRS newmat("CrsMatrix", merge_numrows, mergedNodeNumCols, merge_nnz, Mvalues, Mrowptr, Mcolind, blocksize);
3857 return newmat;
3858 } else {
3859 // We don't have a Bimport (the easy case)
3860 return Bk;
3861 }
3862} // end merge_matrices
3863
3864/*********************************************************************************************************/
3865template <typename SC, typename LO, typename GO, typename NO>
3866void AddKernels<SC, LO, GO, NO>::
3867 addSorted(
3868 const typename MMdetails::AddKernels<SC, LO, GO, NO>::values_array& Avals,
3869 const typename MMdetails::AddKernels<SC, LO, GO, NO>::row_ptrs_array_const& Arowptrs,
3870 const typename MMdetails::AddKernels<SC, LO, GO, NO>::col_inds_array& Acolinds,
3871 const typename MMdetails::AddKernels<SC, LO, GO, NO>::impl_scalar_type scalarA,
3872 const typename MMdetails::AddKernels<SC, LO, GO, NO>::values_array& Bvals,
3873 const typename MMdetails::AddKernels<SC, LO, GO, NO>::row_ptrs_array_const& Browptrs,
3874 const typename MMdetails::AddKernels<SC, LO, GO, NO>::col_inds_array& Bcolinds,
3875 const typename MMdetails::AddKernels<SC, LO, GO, NO>::impl_scalar_type scalarB,
3876 GO numGlobalCols,
3877 typename MMdetails::AddKernels<SC, LO, GO, NO>::values_array& Cvals,
3878 typename MMdetails::AddKernels<SC, LO, GO, NO>::row_ptrs_array& Crowptrs,
3879 typename MMdetails::AddKernels<SC, LO, GO, NO>::col_inds_array& Ccolinds) {
3880 using Teuchos::rcp;
3881 using Teuchos::TimeMonitor;
3882 using AddKern = MMdetails::AddKernels<SC, LO, GO, NO>;
3883 TEUCHOS_TEST_FOR_EXCEPTION(Arowptrs.extent(0) != Browptrs.extent(0), std::runtime_error, "Can't add matrices with different numbers of rows.");
3884 auto nrows = Arowptrs.extent(0) - 1;
3885 Crowptrs = row_ptrs_array(Kokkos::ViewAllocateWithoutInitializing("C row ptrs"), nrows + 1);
3886 typename AddKern::KKH handle;
3887 handle.create_spadd_handle(true);
3888 auto addHandle = handle.get_spadd_handle();
3889
3890 auto MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: sorted symbolic"));
3891
3892 KokkosSparse::spadd_symbolic(&handle,
3893 nrows, numGlobalCols,
3894 Arowptrs, Acolinds, Browptrs, Bcolinds, Crowptrs);
3895 // KokkosKernels requires values to be zeroed
3896 Cvals = values_array("C values", addHandle->get_c_nnz());
3897 Ccolinds = col_inds_array(Kokkos::ViewAllocateWithoutInitializing("C colinds"), addHandle->get_c_nnz());
3898
3899 MM = Teuchos::null;
3900 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: sorted numeric"));
3901 KokkosSparse::spadd_numeric(&handle,
3902 nrows, numGlobalCols,
3903 Arowptrs, Acolinds, Avals, scalarA,
3904 Browptrs, Bcolinds, Bvals, scalarB,
3905 Crowptrs, Ccolinds, Cvals);
3906}
3907
3908template <typename SC, typename LO, typename GO, typename NO>
3909void AddKernels<SC, LO, GO, NO>::
3910 addUnsorted(
3911 const typename MMdetails::AddKernels<SC, LO, GO, NO>::values_array& Avals,
3912 const typename MMdetails::AddKernels<SC, LO, GO, NO>::row_ptrs_array_const& Arowptrs,
3913 const typename MMdetails::AddKernels<SC, LO, GO, NO>::col_inds_array& Acolinds,
3914 const typename MMdetails::AddKernels<SC, LO, GO, NO>::impl_scalar_type scalarA,
3915 const typename MMdetails::AddKernels<SC, LO, GO, NO>::values_array& Bvals,
3916 const typename MMdetails::AddKernels<SC, LO, GO, NO>::row_ptrs_array_const& Browptrs,
3917 const typename MMdetails::AddKernels<SC, LO, GO, NO>::col_inds_array& Bcolinds,
3918 const typename MMdetails::AddKernels<SC, LO, GO, NO>::impl_scalar_type scalarB,
3919 GO numGlobalCols,
3920 typename MMdetails::AddKernels<SC, LO, GO, NO>::values_array& Cvals,
3921 typename MMdetails::AddKernels<SC, LO, GO, NO>::row_ptrs_array& Crowptrs,
3922 typename MMdetails::AddKernels<SC, LO, GO, NO>::col_inds_array& Ccolinds) {
3923 using Teuchos::rcp;
3924 using Teuchos::TimeMonitor;
3925 using AddKern = MMdetails::AddKernels<SC, LO, GO, NO>;
3926 TEUCHOS_TEST_FOR_EXCEPTION(Arowptrs.extent(0) != Browptrs.extent(0), std::runtime_error, "Can't add matrices with different numbers of rows.");
3927 auto nrows = Arowptrs.extent(0) - 1;
3928 Crowptrs = row_ptrs_array(Kokkos::ViewAllocateWithoutInitializing("C row ptrs"), nrows + 1);
3929 typedef MMdetails::AddKernels<SC, LO, GO, NO> AddKern;
3930 typename AddKern::KKH handle;
3931 handle.create_spadd_handle(false);
3932 auto addHandle = handle.get_spadd_handle();
3933 auto MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: unsorted symbolic"));
3934
3935 KokkosSparse::spadd_symbolic(&handle,
3936 nrows, numGlobalCols,
3937 Arowptrs, Acolinds, Browptrs, Bcolinds, Crowptrs);
3938 // Cvals must be zeroed out
3939 Cvals = values_array("C values", addHandle->get_c_nnz());
3940 Ccolinds = col_inds_array(Kokkos::ViewAllocateWithoutInitializing("C colinds"), addHandle->get_c_nnz());
3941 MM = Teuchos::null;
3942 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: unsorted numeric"));
3943 KokkosSparse::spadd_numeric(&handle,
3944 nrows, numGlobalCols,
3945 Arowptrs, Acolinds, Avals, scalarA,
3946 Browptrs, Bcolinds, Bvals, scalarB,
3947 Crowptrs, Ccolinds, Cvals);
3948}
3949
3950template <typename GO,
3951 typename LocalIndicesType,
3952 typename GlobalIndicesType,
3953 typename ColMapType>
3954struct ConvertLocalToGlobalFunctor {
3955 ConvertLocalToGlobalFunctor(
3956 const LocalIndicesType& colindsOrig_,
3957 const GlobalIndicesType& colindsConverted_,
3958 const ColMapType& colmap_)
3959 : colindsOrig(colindsOrig_)
3960 , colindsConverted(colindsConverted_)
3961 , colmap(colmap_) {}
3962 KOKKOS_INLINE_FUNCTION void
3963 operator()(const GO i) const {
3964 colindsConverted(i) = colmap.getGlobalElement(colindsOrig(i));
3965 }
3966 LocalIndicesType colindsOrig;
3967 GlobalIndicesType colindsConverted;
3968 ColMapType colmap;
3969};
3970
3971template <typename SC, typename LO, typename GO, typename NO>
3972void MMdetails::AddKernels<SC, LO, GO, NO>::
3973 convertToGlobalAndAdd(
3974 const typename MMdetails::AddKernels<SC, LO, GO, NO>::KCRS A,
3975 const typename MMdetails::AddKernels<SC, LO, GO, NO>::impl_scalar_type scalarA,
3976 const typename MMdetails::AddKernels<SC, LO, GO, NO>::KCRS B,
3977 const typename MMdetails::AddKernels<SC, LO, GO, NO>::impl_scalar_type scalarB,
3978 const typename MMdetails::AddKernels<SC, LO, GO, NO>::local_map_type& AcolMap,
3979 const typename MMdetails::AddKernels<SC, LO, GO, NO>::local_map_type& BcolMap,
3980 typename MMdetails::AddKernels<SC, LO, GO, NO>::values_array& Cvals,
3981 typename MMdetails::AddKernels<SC, LO, GO, NO>::row_ptrs_array& Crowptrs,
3982 typename MMdetails::AddKernels<SC, LO, GO, NO>::global_col_inds_array& Ccolinds) {
3983 using Teuchos::rcp;
3984 using Teuchos::TimeMonitor;
3985 // Need to use a different KokkosKernelsHandle type than other versions,
3986 // since the ordinals are now GO
3987 using KKH_GO = KokkosKernels::Experimental::KokkosKernelsHandle<size_t, GO, impl_scalar_type,
3988 typename NO::execution_space, typename NO::memory_space, typename NO::memory_space>;
3989
3990 const values_array Avals = A.values;
3991 const values_array Bvals = B.values;
3992 const col_inds_array Acolinds = A.graph.entries;
3993 const col_inds_array Bcolinds = B.graph.entries;
3994 auto Arowptrs = A.graph.row_map;
3995 auto Browptrs = B.graph.row_map;
3996 global_col_inds_array AcolindsConverted(Kokkos::ViewAllocateWithoutInitializing("A colinds (converted)"), Acolinds.extent(0));
3997 global_col_inds_array BcolindsConverted(Kokkos::ViewAllocateWithoutInitializing("B colinds (converted)"), Bcolinds.extent(0));
3998
3999 auto MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: diff col map kernel: column map conversion"));
4000
4001 ConvertLocalToGlobalFunctor<GO, col_inds_array, global_col_inds_array, local_map_type> convertA(Acolinds, AcolindsConverted, AcolMap);
4002 Kokkos::parallel_for("Tpetra_MatrixMatrix_convertColIndsA", range_type(0, Acolinds.extent(0)), convertA);
4003 ConvertLocalToGlobalFunctor<GO, col_inds_array, global_col_inds_array, local_map_type> convertB(Bcolinds, BcolindsConverted, BcolMap);
4004 Kokkos::parallel_for("Tpetra_MatrixMatrix_convertColIndsB", range_type(0, Bcolinds.extent(0)), convertB);
4005 KKH_GO handle;
4006 handle.create_spadd_handle(false);
4007 auto addHandle = handle.get_spadd_handle();
4008 MM = Teuchos::null;
4009 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: diff col map kernel: unsorted symbolic"));
4010 auto nrows = Arowptrs.extent(0) - 1;
4011 Crowptrs = row_ptrs_array(Kokkos::ViewAllocateWithoutInitializing("C row ptrs"), nrows + 1);
4012 KokkosSparse::spadd_symbolic(&handle,
4013 nrows, A.numCols(),
4014 Arowptrs, AcolindsConverted, Browptrs, BcolindsConverted, Crowptrs);
4015 Cvals = values_array("C values", addHandle->get_c_nnz());
4016 Ccolinds = global_col_inds_array(Kokkos::ViewAllocateWithoutInitializing("C colinds"), addHandle->get_c_nnz());
4017
4018 MM = Teuchos::null;
4019 MM = rcp(new Tpetra::Details::ProfilingRegion("TpetraExt: add: diff col map kernel: unsorted numeric"));
4020 KokkosSparse::spadd_numeric(&handle,
4021 nrows, A.numCols(),
4022 Arowptrs, AcolindsConverted, Avals, scalarA,
4023 Browptrs, BcolindsConverted, Bvals, scalarB,
4024 Crowptrs, Ccolinds, Cvals);
4025}
4026
4027} // namespace MMdetails
4028
4029} // End namespace Tpetra
4030
4031/*********************************************************************************************************/
4032//
4033// Explicit instantiation macro
4034//
4035// Must be expanded from within the Tpetra namespace!
4036//
4037namespace Tpetra {
4038
4039#define TPETRA_MATRIXMATRIX_INSTANT(SCALAR, LO, GO, NODE) \
4040 template void MatrixMatrix::Multiply( \
4041 const CrsMatrix<SCALAR, LO, GO, NODE>& A, \
4042 bool transposeA, \
4043 const CrsMatrix<SCALAR, LO, GO, NODE>& B, \
4044 bool transposeB, \
4045 CrsMatrix<SCALAR, LO, GO, NODE>& C, \
4046 bool call_FillComplete_on_result, \
4047 const std::string& label, \
4048 const Teuchos::RCP<Teuchos::ParameterList>& params); \
4049 \
4050 template void MatrixMatrix::Multiply( \
4051 const Teuchos::RCP<const BlockCrsMatrix<SCALAR, LO, GO, NODE>>& A, \
4052 bool transposeA, \
4053 const Teuchos::RCP<const BlockCrsMatrix<SCALAR, LO, GO, NODE>>& B, \
4054 bool transposeB, \
4055 Teuchos::RCP<BlockCrsMatrix<SCALAR, LO, GO, NODE>>& C, \
4056 const std::string& label); \
4057 \
4058 template void MatrixMatrix::Jacobi( \
4059 typename Teuchos::ScalarTraits<SCALAR>::magnitudeType omega, \
4060 const Vector<SCALAR, LO, GO, NODE>& Dinv, \
4061 const CrsMatrix<SCALAR, LO, GO, NODE>& A, \
4062 const CrsMatrix<SCALAR, LO, GO, NODE>& B, \
4063 CrsMatrix<SCALAR, LO, GO, NODE>& C, \
4064 bool call_FillComplete_on_result, \
4065 const std::string& label, \
4066 const Teuchos::RCP<Teuchos::ParameterList>& params); \
4067 \
4068 template void MatrixMatrix::Add( \
4069 const CrsMatrix<SCALAR, LO, GO, NODE>& A, \
4070 bool transposeA, \
4071 SCALAR scalarA, \
4072 const CrsMatrix<SCALAR, LO, GO, NODE>& B, \
4073 bool transposeB, \
4074 SCALAR scalarB, \
4075 Teuchos::RCP<CrsMatrix<SCALAR, LO, GO, NODE>>& C); \
4076 \
4077 template void MatrixMatrix::Add( \
4078 const CrsMatrix<SCALAR, LO, GO, NODE>& A, \
4079 bool transposeA, \
4080 SCALAR scalarA, \
4081 const CrsMatrix<SCALAR, LO, GO, NODE>& B, \
4082 bool transposeB, \
4083 SCALAR scalarB, \
4084 const Teuchos::RCP<CrsMatrix<SCALAR, LO, GO, NODE>>& C); \
4085 \
4086 template void MatrixMatrix::Add( \
4087 const CrsMatrix<SCALAR, LO, GO, NODE>& A, \
4088 bool transposeA, \
4089 SCALAR scalarA, \
4090 CrsMatrix<SCALAR, LO, GO, NODE>& B, \
4091 SCALAR scalarB); \
4092 \
4093 template Teuchos::RCP<CrsMatrix<SCALAR, LO, GO, NODE>> \
4094 MatrixMatrix::add<SCALAR, LO, GO, NODE>(const SCALAR& alpha, \
4095 const bool transposeA, \
4096 const CrsMatrix<SCALAR, LO, GO, NODE>& A, \
4097 const SCALAR& beta, \
4098 const bool transposeB, \
4099 const CrsMatrix<SCALAR, LO, GO, NODE>& B, \
4100 const Teuchos::RCP<const Map<LO, GO, NODE>>& domainMap, \
4101 const Teuchos::RCP<const Map<LO, GO, NODE>>& rangeMap, \
4102 const Teuchos::RCP<Teuchos::ParameterList>& params); \
4103 \
4104 template void \
4105 MatrixMatrix::add<SCALAR, LO, GO, NODE>(const SCALAR& alpha, \
4106 const bool transposeA, \
4107 const CrsMatrix<SCALAR, LO, GO, NODE>& A, \
4108 const SCALAR& beta, \
4109 const bool transposeB, \
4110 const CrsMatrix<SCALAR, LO, GO, NODE>& B, \
4111 CrsMatrix<SCALAR, LO, GO, NODE>& C, \
4112 const Teuchos::RCP<const Map<LO, GO, NODE>>& domainMap, \
4113 const Teuchos::RCP<const Map<LO, GO, NODE>>& rangeMap, \
4114 const Teuchos::RCP<Teuchos::ParameterList>& params); \
4115 \
4116 template struct MMdetails::AddKernels<SCALAR, LO, GO, NODE>; \
4117 \
4118 template void MMdetails::import_and_extract_views<SCALAR, LO, GO, NODE>(const CrsMatrix<SCALAR, LO, GO, NODE>& M, \
4119 Teuchos::RCP<const Map<LO, GO, NODE>> targetMap, \
4120 CrsMatrixStruct<SCALAR, LO, GO, NODE>& Mview, \
4121 Teuchos::RCP<const Import<LO, GO, NODE>> prototypeImporter, \
4122 bool userAssertsThereAreNoRemotes, \
4123 const std::string& label, \
4124 const Teuchos::RCP<Teuchos::ParameterList>& params); \
4125 \
4126 template void MMdetails::import_and_extract_views<SCALAR, LO, GO, NODE>(const BlockCrsMatrix<SCALAR, LO, GO, NODE>& M, \
4127 Teuchos::RCP<const Map<LO, GO, NODE>> targetMap, \
4128 BlockCrsMatrixStruct<SCALAR, LO, GO, NODE>& Mview, \
4129 Teuchos::RCP<const Import<LO, GO, NODE>> prototypeImporter, \
4130 bool userAssertsThereAreNoRemotes);
4131} // End namespace Tpetra
4132
4133#endif // TPETRA_MATRIXMATRIX_DEF_HPP
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra's behavior.
Declaration of Tpetra::Details::Profiling, a scope guard for Kokkos Profiling.
Declare and define the functions Tpetra::Details::computeOffsetsFromCounts and Tpetra::computeOffsets...
Declaration and definition of Tpetra::Details::getEntryOnHost.
Utility functions for packing and unpacking sparse matrix entries.
Internal functions and macros designed for use with Tpetra::Import and Tpetra::Export objects.
Stand-alone utility functions and macros.
Forward declaration of some Tpetra Matrix Matrix objects.
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...
Struct that holds views of the contents of a CrsMatrix.
Teuchos::RCP< const map_type > colMap
Col map for the original version of the matrix.
Teuchos::RCP< const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > origMatrix
The original matrix.
static bool debug()
Whether Tpetra is in debug mode.
void start()
Start the deep_copy counter.
void Jacobi(typename Teuchos::ScalarTraits< Scalar >::magnitudeType omega, const Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Dinv, const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &B, CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &C, bool call_FillComplete_on_result=true, const std::string &label=std::string(), const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Teuchos::RCP< CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > add(const Scalar &alpha, const bool transposeA, const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, const Scalar &beta, const bool transposeB, const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &B, const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &domainMap=Teuchos::null, const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &rangeMap=Teuchos::null, const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Compute the sparse matrix sum C = scalarA * Op(A) + scalarB * Op(B), where Op(X) is either X or its t...
void Multiply(const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, bool transposeA, const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &B, bool transposeB, CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &C, bool call_FillComplete_on_result=true, const std::string &label=std::string(), const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Sparse matrix-matrix multiply.
void Add(const CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, bool transposeA, Scalar scalarA, CrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &B, Scalar scalarB)
Namespace Tpetra contains the class and methods constituting the Tpetra library.
void removeCrsMatrixZeros(CrsMatrixType &matrix, typename Teuchos::ScalarTraits< typename CrsMatrixType::scalar_type >::magnitudeType const &threshold=Teuchos::ScalarTraits< typename CrsMatrixType::scalar_type >::magnitude(Teuchos::ScalarTraits< typename CrsMatrixType::scalar_type >::zero()))
Remove zero entries from a matrix.