Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
Ifpack2_LocalSparseTriangularSolver_def.hpp
1// @HEADER
2// *****************************************************************************
3// Ifpack2: Templated Object-Oriented Algebraic Preconditioner Package
4//
5// Copyright 2009 NTESS and the Ifpack2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef IFPACK2_LOCALSPARSETRIANGULARSOLVER_DEF_HPP
11#define IFPACK2_LOCALSPARSETRIANGULARSOLVER_DEF_HPP
12
13#include "Kokkos_Macros.hpp"
14#include <sstream> // ostringstream
15#include <stdexcept> // runtime_error
16
17#include "Ifpack2_LocalSparseTriangularSolver_decl.hpp"
18#include "Tpetra_CrsMatrix.hpp"
19#include "Tpetra_Core.hpp"
20#include "Teuchos_StandardParameterEntryValidators.hpp"
21#include "Tpetra_Details_determineLocalTriangularStructure.hpp"
22#include "KokkosSparse_sptrsv.hpp"
23#include "KokkosSparse_trsv.hpp"
24
25#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
26#include "shylu_hts.hpp"
27#endif
28
29namespace Ifpack2 {
30
31namespace Details {
32
33#if defined(KOKKOSKERNELS_ENABLE_TPL_CUSPARSE) && defined(KOKKOS_ENABLE_CUDA)
34
35inline void cusparse_error_throw(cusparseStatus_t cusparseStatus, const char* name,
36 const char* file, const int line) {
37 std::ostringstream out;
38#if defined(CUSPARSE_VERSION) && (10300 <= CUSPARSE_VERSION)
39 out << name << " error( " << cusparseGetErrorName(cusparseStatus) << "): " << cusparseGetErrorString(cusparseStatus);
40#else
41 out << name << " error( ";
42 switch (cusparseStatus) {
43 case CUSPARSE_STATUS_NOT_INITIALIZED:
44 out << "CUSPARSE_STATUS_NOT_INITIALIZED): cusparse handle was not "
45 "created correctly.";
46 break;
47 case CUSPARSE_STATUS_ALLOC_FAILED:
48 out << "CUSPARSE_STATUS_ALLOC_FAILED): you might tried to allocate too "
49 "much memory";
50 break;
51 case CUSPARSE_STATUS_INVALID_VALUE: out << "CUSPARSE_STATUS_INVALID_VALUE)"; break;
52 case CUSPARSE_STATUS_ARCH_MISMATCH: out << "CUSPARSE_STATUS_ARCH_MISMATCH)"; break;
53 case CUSPARSE_STATUS_MAPPING_ERROR: out << "CUSPARSE_STATUS_MAPPING_ERROR)"; break;
54 case CUSPARSE_STATUS_EXECUTION_FAILED: out << "CUSPARSE_STATUS_EXECUTION_FAILED)"; break;
55 case CUSPARSE_STATUS_INTERNAL_ERROR: out << "CUSPARSE_STATUS_INTERNAL_ERROR)"; break;
56 case CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED: out << "CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED)"; break;
57 case CUSPARSE_STATUS_ZERO_PIVOT: out << "CUSPARSE_STATUS_ZERO_PIVOT)"; break;
58 default: out << "unrecognized error code): this is bad!"; break;
59 }
60#endif // CUSPARSE_VERSION
61 if (file) {
62 out << " " << file << ":" << line;
63 }
64 throw std::runtime_error(out.str());
65}
66
67inline void cusparse_safe_call(cusparseStatus_t cusparseStatus, const char* name, const char* file = nullptr,
68 const int line = 0) {
69 if (CUSPARSE_STATUS_SUCCESS != cusparseStatus) {
70 cusparse_error_throw(cusparseStatus, name, file, line);
71 }
72}
73
74#define IFPACK2_DETAILS_CUSPARSE_SAFE_CALL(call) \
75 Ifpack2::Details::cusparse_safe_call(call, #call, __FILE__, __LINE__)
76
77#endif // defined(KOKKOSKERNELS_ENABLE_TPL_CUSPARSE) && defined(KOKKOS_ENABLE_CUDA)
78
79struct TrisolverType {
80 enum Enum {
81 Internal,
82 HTS,
83 KSPTRSV
84 };
85
86 static void loadPLTypeOption(Teuchos::Array<std::string>& type_strs, Teuchos::Array<Enum>& type_enums) {
87 type_strs.resize(3);
88 type_strs[0] = "Internal";
89 type_strs[1] = "HTS";
90 type_strs[2] = "KSPTRSV";
91 type_enums.resize(3);
92 type_enums[0] = Internal;
93 type_enums[1] = HTS;
94 type_enums[2] = KSPTRSV;
95 }
96};
97} // namespace Details
98
99template <class MatrixType>
100class LocalSparseTriangularSolver<MatrixType>::HtsImpl {
101 public:
102 typedef Tpetra::CrsMatrix<scalar_type, local_ordinal_type, global_ordinal_type, node_type> crs_matrix_type;
103
104 void reset() {
105#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
106 Timpl_ = Teuchos::null;
107 levelset_block_size_ = 1;
108#endif
109 }
110
111 void setParameters(const Teuchos::ParameterList& pl) {
112 (void)pl;
113#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
114 const char* block_size_s = "trisolver: block size";
115 if (pl.isParameter(block_size_s)) {
116 TEUCHOS_TEST_FOR_EXCEPT_MSG(!pl.isType<int>(block_size_s),
117 "The parameter \"" << block_size_s << "\" must be of type int.");
118 levelset_block_size_ = pl.get<int>(block_size_s);
119 }
120 if (levelset_block_size_ < 1)
121 levelset_block_size_ = 1;
122#endif
123 }
124
125 // HTS has the phases symbolic+numeric, numeric, and apply. Hence the first
126 // call to compute() will trigger the symbolic+numeric phase, and subsequent
127 // calls (with the same Timpl_) will trigger the numeric phase. In the call to
128 // initialize(), essentially nothing happens.
129 void initialize(const crs_matrix_type& /* unused */) {
130#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
131 reset();
132 transpose_ = conjugate_ = false;
133#endif
134 }
135
136 void compute(const crs_matrix_type& T_in, const Teuchos::RCP<Teuchos::FancyOStream>& out) {
137 (void)T_in;
138 (void)out;
139#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
140 using Teuchos::ArrayRCP;
141
142 auto rowptr = T_in.getLocalRowPtrsHost();
143 auto colidx = T_in.getLocalIndicesHost();
144 auto val = T_in.getLocalValuesHost(Tpetra::Access::ReadOnly);
145 Kokkos::fence();
146
147 Teuchos::RCP<HtsCrsMatrix> T_hts = Teuchos::rcpWithDealloc(
148 HTST::make_CrsMatrix(rowptr.size() - 1,
149 rowptr.data(), colidx.data(),
150 // For std/Kokkos::complex.
151 reinterpret_cast<const scalar_type*>(val.data()),
152 transpose_, conjugate_),
153 HtsCrsMatrixDeleter());
154
155 if (Teuchos::nonnull(Timpl_)) {
156 // Reuse the nonzero pattern.
157 HTST::reprocess_numeric(Timpl_.get(), T_hts.get());
158 } else {
159 // Build from scratch.
160 if (T_in.getCrsGraph().is_null()) {
161 if (Teuchos::nonnull(out))
162 *out << "HTS compute failed because T_in.getCrsGraph().is_null().\n";
163 return;
164 }
165 if (!T_in.getCrsGraph()->isSorted()) {
166 if (Teuchos::nonnull(out))
167 *out << "HTS compute failed because ! T_in.getCrsGraph().isSorted().\n";
168 return;
169 }
170 if (!T_in.isStorageOptimized()) {
171 if (Teuchos::nonnull(out))
172 *out << "HTS compute failed because ! T_in.isStorageOptimized().\n";
173 return;
174 }
175
176 typename HTST::PreprocessArgs args;
177 args.T = T_hts.get();
178 args.max_nrhs = 1;
179#ifdef _OPENMP
180 args.nthreads = omp_get_max_threads();
181#else
182 args.nthreads = 1;
183#endif
184 args.save_for_reprocess = true;
185 typename HTST::Options opts;
186 opts.levelset_block_size = levelset_block_size_;
187 args.options = &opts;
188
189 try {
190 Timpl_ = Teuchos::rcpWithDealloc(HTST::preprocess(args), TImplDeleter());
191 } catch (const std::exception& e) {
192 if (Teuchos::nonnull(out))
193 *out << "HTS preprocess threw: " << e.what() << "\n";
194 }
195 }
196#endif
197 }
198
199 // HTS may not be able to handle a matrix, so query whether compute()
200 // succeeded.
201 bool isComputed() {
202#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
203 return Teuchos::nonnull(Timpl_);
204#else
205 return false;
206#endif
207 }
208
209 // Y := beta * Y + alpha * (M * X)
210 void localApply(const MV& X, MV& Y,
211 const Teuchos::ETransp /* mode */,
212 const scalar_type& alpha, const scalar_type& beta) const {
213 (void)X;
214 (void)Y;
215 (void)alpha;
216 (void)beta;
217#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
218 const auto& X_view = X.getLocalViewHost(Tpetra::Access::ReadOnly);
219 const auto& Y_view = Y.getLocalViewHost(Tpetra::Access::ReadWrite);
220
221 // Only does something if #rhs > current capacity.
222 HTST::reset_max_nrhs(Timpl_.get(), X_view.extent(1));
223 // Switch alpha and beta because of HTS's opposite convention.
224 HTST::solve_omp(Timpl_.get(),
225 // For std/Kokkos::complex.
226 reinterpret_cast<const scalar_type*>(X_view.data()),
227 X_view.extent(1),
228 // For std/Kokkos::complex.
229 reinterpret_cast<scalar_type*>(Y_view.data()),
230 beta, alpha);
231#endif
232 }
233
234 private:
235#ifdef HAVE_IFPACK2_SHYLU_NODEHTS
236 typedef ::Experimental::HTS<local_ordinal_type, size_t, scalar_type> HTST;
237 typedef typename HTST::Impl TImpl;
238 typedef typename HTST::CrsMatrix HtsCrsMatrix;
239
240 struct TImplDeleter {
241 void free(TImpl* impl) {
242 HTST::delete_Impl(impl);
243 }
244 };
245
246 struct HtsCrsMatrixDeleter {
247 void free(HtsCrsMatrix* T) {
248 HTST::delete_CrsMatrix(T);
249 }
250 };
251
252 Teuchos::RCP<TImpl> Timpl_;
253 bool transpose_, conjugate_;
254 int levelset_block_size_;
255#endif
256};
257
258template <class MatrixType>
260 LocalSparseTriangularSolver(const Teuchos::RCP<const row_matrix_type>& A)
261 : A_(A) {
262 initializeState();
263
264 if (!A.is_null()) {
265 Teuchos::RCP<const crs_matrix_type> A_crs =
266 Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A);
267 TEUCHOS_TEST_FOR_EXCEPTION(A_crs.is_null(), std::invalid_argument,
268 "Ifpack2::LocalSparseTriangularSolver constructor: "
269 "The input matrix A is not a Tpetra::CrsMatrix.");
270 A_crs_ = A_crs;
271 }
272}
273
274template <class MatrixType>
276 LocalSparseTriangularSolver(const Teuchos::RCP<const row_matrix_type>& A,
277 const Teuchos::RCP<Teuchos::FancyOStream>& out)
278 : A_(A)
279 , out_(out) {
280 initializeState();
281 if (!out_.is_null()) {
282 *out_ << ">>> DEBUG Ifpack2::LocalSparseTriangularSolver constructor"
283 << std::endl;
284 }
285
286 if (!A.is_null()) {
287 Teuchos::RCP<const crs_matrix_type> A_crs =
288 Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A);
289 TEUCHOS_TEST_FOR_EXCEPTION(A_crs.is_null(), std::invalid_argument,
290 "Ifpack2::LocalSparseTriangularSolver constructor: "
291 "The input matrix A is not a Tpetra::CrsMatrix.");
292 A_crs_ = A_crs;
293 }
294}
295
296template <class MatrixType>
301
302template <class MatrixType>
304 LocalSparseTriangularSolver(const bool /* unused */, const Teuchos::RCP<Teuchos::FancyOStream>& out)
305 : out_(out) {
306 initializeState();
307 if (!out_.is_null()) {
308 *out_ << ">>> DEBUG Ifpack2::LocalSparseTriangularSolver constructor"
309 << std::endl;
310 }
311}
312
313template <class MatrixType>
315 isInitialized_ = false;
316 isComputed_ = false;
317 reverseStorage_ = false;
318 isInternallyChanged_ = false;
319 numInitialize_ = 0;
320 numCompute_ = 0;
321 numApply_ = 0;
322 initializeTime_ = 0.0;
323 computeTime_ = 0.0;
324 applyTime_ = 0.0;
325 isKokkosKernelsSptrsv_ = false;
326 isKokkosKernelsStream_ = false;
327 num_streams_ = 0;
328 uplo_ = "N";
329 diag_ = "N";
330}
331
332template <class MatrixType>
335 if (!isKokkosKernelsStream_) {
336 if (Teuchos::nonnull(kh_)) {
337 kh_->destroy_sptrsv_handle();
338 }
339 } else {
340 for (size_t i = 0; i < kh_v_.size(); i++) {
341 if (Teuchos::nonnull(kh_v_[i])) {
342 kh_v_[i]->destroy_sptrsv_handle();
343 }
344 }
345 }
346}
347
348namespace {
349template <typename MatrixType>
350constexpr bool is_host_type() {
351 using node_type = typename MatrixType::node_type;
352 return std::is_same_v<typename node_type::execution_space, Kokkos::Serial>
353#ifdef KOKKOS_ENABLE_THREADS
354 || std::is_same_v<typename node_type::execution_space, Kokkos::Threads>
355#endif
356#ifdef KOKKOS_ENABLE_OPENMP
357 || std::is_same_v<typename node_type::execution_space, Kokkos::OpenMP>
358#endif
359 ;
360}
361} // namespace
362
363template <class MatrixType>
365 setParameters(const Teuchos::ParameterList& pl) {
366 using Teuchos::Array;
367 using Teuchos::ParameterList;
368 using Teuchos::RCP;
369
370 Details::TrisolverType::Enum trisolverType = is_host_type<MatrixType>() ? Details::TrisolverType::Internal : Details::TrisolverType::KSPTRSV;
371 do {
372 static const char typeName[] = "trisolver: type";
373
374 if (!pl.isType<std::string>(typeName)) break;
375
376 // Map std::string <-> TrisolverType::Enum.
377 Array<std::string> trisolverTypeStrs;
378 Array<Details::TrisolverType::Enum> trisolverTypeEnums;
379 Details::TrisolverType::loadPLTypeOption(trisolverTypeStrs, trisolverTypeEnums);
380 Teuchos::StringToIntegralParameterEntryValidator<Details::TrisolverType::Enum>
381 s2i(trisolverTypeStrs(), trisolverTypeEnums(), typeName, false);
382
383 trisolverType = s2i.getIntegralValue(pl.get<std::string>(typeName));
384 } while (0);
385
386 if (trisolverType == Details::TrisolverType::HTS) {
387 htsImpl_ = Teuchos::rcp(new HtsImpl());
388 htsImpl_->setParameters(pl);
389 }
390
391 if (trisolverType == Details::TrisolverType::KSPTRSV) {
392 this->isKokkosKernelsSptrsv_ = true;
393 } else {
394 this->isKokkosKernelsSptrsv_ = false;
395 }
396
397 if (pl.isParameter("trisolver: reverse U"))
398 reverseStorage_ = pl.get<bool>("trisolver: reverse U");
399
400 TEUCHOS_TEST_FOR_EXCEPTION(reverseStorage_ && (trisolverType == Details::TrisolverType::HTS || trisolverType == Details::TrisolverType::KSPTRSV),
401 std::logic_error,
402 "Ifpack2::LocalSparseTriangularSolver::setParameters: "
403 "You are not allowed to enable both HTS or KSPTRSV and the \"trisolver: reverse U\" "
404 "options. See GitHub issue #2647.");
405}
406
407template <class MatrixType>
409 initialize() {
410 using Tpetra::Details::determineLocalTriangularStructure;
411
412 using local_matrix_type = typename crs_matrix_type::local_matrix_device_type;
413 using LO = local_ordinal_type;
414
415 const char prefix[] = "Ifpack2::LocalSparseTriangularSolver::initialize: ";
416 if (!out_.is_null()) {
417 *out_ << ">>> DEBUG " << prefix << std::endl;
418 }
419
420 if (!isKokkosKernelsStream_) {
421 TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::runtime_error, prefix << "You must call "
422 "setMatrix() with a nonnull input matrix before you may call "
423 "initialize() or compute().");
424 if (A_crs_.is_null()) {
425 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_);
426 TEUCHOS_TEST_FOR_EXCEPTION(A_crs.get() == nullptr, std::invalid_argument,
427 prefix << "The input matrix A is not a Tpetra::CrsMatrix.");
428 A_crs_ = A_crs;
429 }
430 auto G = A_crs_->getGraph();
431 TEUCHOS_TEST_FOR_EXCEPTION(G.is_null(), std::logic_error, prefix << "A_ and A_crs_ are nonnull, "
432 "but A_crs_'s RowGraph G is null. "
433 "Please report this bug to the Ifpack2 developers.");
434 // At this point, the graph MUST be fillComplete. The "initialize"
435 // (symbolic) part of setup only depends on the graph structure, so
436 // the matrix itself need not be fill complete.
437 TEUCHOS_TEST_FOR_EXCEPTION(!G->isFillComplete(), std::runtime_error,
438 "If you call this method, "
439 "the matrix's graph must be fill complete. It is not.");
440
441 // mfh 30 Apr 2018: See GitHub Issue #2658.
442 constexpr bool ignoreMapsForTriStructure = true;
443 auto lclTriStructure = [&] {
444 auto lclMatrix = A_crs_->getLocalMatrixDevice();
445 auto lclRowMap = A_crs_->getRowMap()->getLocalMap();
446 auto lclColMap = A_crs_->getColMap()->getLocalMap();
447 auto lclTriStruct =
448 determineLocalTriangularStructure(lclMatrix.graph,
449 lclRowMap,
450 lclColMap,
451 ignoreMapsForTriStructure);
452 const LO lclNumRows = lclRowMap.getLocalNumElements();
453 this->diag_ = (lclTriStruct.diagCount < lclNumRows) ? "U" : "N";
454 this->uplo_ = lclTriStruct.couldBeLowerTriangular ? "L" : (lclTriStruct.couldBeUpperTriangular ? "U" : "N");
455 return lclTriStruct;
456 }();
457
458 if (reverseStorage_ && lclTriStructure.couldBeUpperTriangular &&
459 htsImpl_.is_null()) {
460 // Reverse the storage for an upper triangular matrix
461 auto Alocal = A_crs_->getLocalMatrixDevice();
462 auto ptr = Alocal.graph.row_map;
463 auto ind = Alocal.graph.entries;
464 auto val = Alocal.values;
465
466 auto numRows = Alocal.numRows();
467 auto numCols = Alocal.numCols();
468 auto numNnz = Alocal.nnz();
469
470 typename decltype(ptr)::non_const_type newptr("ptr", ptr.extent(0));
471 typename decltype(ind)::non_const_type newind("ind", ind.extent(0));
472 decltype(val) newval("val", val.extent(0));
473
474 // FIXME: The code below assumes UVM
475 typename crs_matrix_type::execution_space().fence();
476 newptr(0) = 0;
477 for (local_ordinal_type row = 0, rowStart = 0; row < numRows; ++row) {
478 auto A_r = Alocal.row(numRows - 1 - row);
479
480 auto numEnt = A_r.length;
481 for (local_ordinal_type k = 0; k < numEnt; ++k) {
482 newind(rowStart + k) = numCols - 1 - A_r.colidx(numEnt - 1 - k);
483 newval(rowStart + k) = A_r.value(numEnt - 1 - k);
484 }
485 rowStart += numEnt;
486 newptr(row + 1) = rowStart;
487 }
488 typename crs_matrix_type::execution_space().fence();
489
490 // Reverse maps
491 Teuchos::RCP<map_type> newRowMap, newColMap;
492 {
493 // Reverse row map
494 auto rowMap = A_->getRowMap();
495 auto numElems = rowMap->getLocalNumElements();
496 auto rowElems = rowMap->getLocalElementList();
497
498 Teuchos::Array<global_ordinal_type> newRowElems(rowElems.size());
499 for (size_t i = 0; i < numElems; i++)
500 newRowElems[i] = rowElems[numElems - 1 - i];
501
502 newRowMap = Teuchos::rcp(new map_type(rowMap->getGlobalNumElements(), newRowElems, rowMap->getIndexBase(), rowMap->getComm()));
503 }
504 {
505 // Reverse column map
506 auto colMap = A_->getColMap();
507 auto numElems = colMap->getLocalNumElements();
508 auto colElems = colMap->getLocalElementList();
509
510 Teuchos::Array<global_ordinal_type> newColElems(colElems.size());
511 for (size_t i = 0; i < numElems; i++)
512 newColElems[i] = colElems[numElems - 1 - i];
513
514 newColMap = Teuchos::rcp(new map_type(colMap->getGlobalNumElements(), newColElems, colMap->getIndexBase(), colMap->getComm()));
515 }
516
517 // Construct new matrix
518 local_matrix_type newLocalMatrix("Upermuted", numRows, numCols, numNnz, newval, newptr, newind);
519
520 A_crs_ = Teuchos::rcp(new crs_matrix_type(newLocalMatrix, newRowMap, newColMap, A_crs_->getDomainMap(), A_crs_->getRangeMap()));
521
522 isInternallyChanged_ = true;
523
524 // FIXME (mfh 18 Apr 2019) Recomputing this is unnecessary, but I
525 // didn't want to break any invariants, especially considering
526 // that this branch is likely poorly tested.
527 auto newLclTriStructure =
528 determineLocalTriangularStructure(newLocalMatrix.graph,
529 newRowMap->getLocalMap(),
530 newColMap->getLocalMap(),
531 ignoreMapsForTriStructure);
532 const LO newLclNumRows = newRowMap->getLocalNumElements();
533 this->diag_ = (newLclTriStructure.diagCount < newLclNumRows) ? "U" : "N";
534 this->uplo_ = newLclTriStructure.couldBeLowerTriangular ? "L" : (newLclTriStructure.couldBeUpperTriangular ? "U" : "N");
535 }
536 } else {
537 bool prev_ambiguous = false;
538 bool all_ambiguous = true;
539 for (int i = 0; i < num_streams_; i++) {
540 TEUCHOS_TEST_FOR_EXCEPTION(A_crs_v_[i].is_null(), std::runtime_error, prefix << "You must call "
541 "setMatrix() with a nonnull input matrix before you may call "
542 "initialize() or compute().");
543 auto G = A_crs_v_[i]->getGraph();
544 TEUCHOS_TEST_FOR_EXCEPTION(G.is_null(), std::logic_error, prefix << "A_crs_ are nonnull, "
545 "but A_crs_'s RowGraph G is null. "
546 "Please report this bug to the Ifpack2 developers.");
547 // At this point, the graph MUST be fillComplete. The "initialize"
548 // (symbolic) part of setup only depends on the graph structure, so
549 // the matrix itself need not be fill complete.
550 TEUCHOS_TEST_FOR_EXCEPTION(!G->isFillComplete(), std::runtime_error,
551 "If you call this method, "
552 "the matrix's graph must be fill complete. It is not.");
553
554 // mfh 30 Apr 2018: See GitHub Issue #2658.
555 constexpr bool ignoreMapsForTriStructure = true;
556 std::string prev_uplo = this->uplo_;
557 std::string prev_diag = this->diag_;
558 auto lclMatrix = A_crs_v_[i]->getLocalMatrixDevice();
559 auto lclRowMap = A_crs_v_[i]->getRowMap()->getLocalMap();
560 auto lclColMap = A_crs_v_[i]->getColMap()->getLocalMap();
561 auto lclTriStruct =
562 determineLocalTriangularStructure(lclMatrix.graph,
563 lclRowMap,
564 lclColMap,
565 ignoreMapsForTriStructure);
566 const LO lclNumRows = lclRowMap.getLocalNumElements();
567 this->diag_ = (lclTriStruct.diagCount < lclNumRows) ? "U" : "N";
568 const bool could_be_lower = lclTriStruct.couldBeLowerTriangular;
569 const bool could_be_upper = lclTriStruct.couldBeUpperTriangular;
570 if (could_be_lower && could_be_upper) {
571 // Ambiguous, but that's OK if at least one stream is unabiguous
572 this->uplo_ = prev_uplo;
573 prev_ambiguous = true;
574 } else {
575 this->uplo_ = could_be_lower ? "L" : (could_be_upper ? "U" : "N");
576 if (this->uplo_ != "N" && prev_uplo == "N" && prev_ambiguous) {
577 prev_uplo = this->uplo_;
578 }
579 prev_ambiguous = false;
580 }
581 all_ambiguous &= prev_ambiguous;
582 if (i > 0) {
583 TEUCHOS_TEST_FOR_EXCEPTION((this->diag_ != prev_diag) || (this->uplo_ != prev_uplo),
584 std::logic_error, prefix << "A_crs_'s structures in streams "
585 "are different. Please report this bug to the Ifpack2 developers.");
586 }
587 }
588 // If all streams were ambiguous, just call it "L"
589 if (all_ambiguous) {
590 this->uplo_ = "L";
591 }
592 }
593
594 if (Teuchos::nonnull(htsImpl_)) {
595 htsImpl_->initialize(*A_crs_);
596 isInternallyChanged_ = true;
597 }
598
599 const bool ksptrsv_valid_uplo = (this->uplo_ != "N");
600 kh_v_nonnull_ = false;
601 if (this->isKokkosKernelsSptrsv_ && ksptrsv_valid_uplo && this->diag_ != "U") {
602 if (!isKokkosKernelsStream_) {
603 kh_ = Teuchos::rcp(new k_handle());
604 const bool is_lower_tri = (this->uplo_ == "L") ? true : false;
605
606 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_, true);
607 auto Alocal = A_crs->getLocalMatrixDevice();
608 auto ptr = Alocal.graph.row_map;
609 auto ind = Alocal.graph.entries;
610 auto val = Alocal.values;
611
612 auto numRows = Alocal.numRows();
613 kh_->create_sptrsv_handle(kokkosKernelsAlgorithm(), numRows, is_lower_tri);
614 KokkosSparse::sptrsv_symbolic(kh_.getRawPtr(), ptr, ind, val);
615 } else {
616 kh_v_ = std::vector<Teuchos::RCP<k_handle>>(num_streams_);
617 for (int i = 0; i < num_streams_; i++) {
618 kh_v_[i] = Teuchos::rcp(new k_handle());
619 auto A_crs_i = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_crs_v_[i], true);
620 auto Alocal_i = A_crs_i->getLocalMatrixDevice();
621 auto ptr_i = Alocal_i.graph.row_map;
622 auto ind_i = Alocal_i.graph.entries;
623 auto val_i = Alocal_i.values;
624
625 auto numRows_i = Alocal_i.numRows();
626
627 const bool is_lower_tri = (this->uplo_ == "L") ? true : false;
628 kh_v_[i]->create_sptrsv_handle(kokkosKernelsAlgorithm(), numRows_i, is_lower_tri);
629 KokkosSparse::sptrsv_symbolic(kh_v_[i].getRawPtr(), ptr_i, ind_i, val_i);
630 }
631 kh_v_nonnull_ = true;
632 }
633 }
634
635 isInitialized_ = true;
636 ++numInitialize_;
637}
638
639template <class MatrixType>
640KokkosSparse::Experimental::SPTRSVAlgorithm
642#if defined(KOKKOSKERNELS_ENABLE_TPL_CUSPARSE) && defined(KOKKOS_ENABLE_CUDA)
643 // CuSparse only supports int type ordinals
644 // and scalar types of float, double, float complex and double complex
645 if constexpr (std::is_same<Kokkos::Cuda, HandleExecSpace>::value &&
646 std::is_same<int, local_ordinal_type>::value &&
647 (std::is_same<scalar_type, float>::value ||
648 std::is_same<scalar_type, double>::value ||
649 std::is_same<impl_scalar_type, Kokkos::complex<float>>::value ||
650 std::is_same<impl_scalar_type, Kokkos::complex<double>>::value)) {
651 return KokkosSparse::Experimental::SPTRSVAlgorithm::SPTRSV_CUSPARSE;
652 }
653#endif
654 return KokkosSparse::Experimental::SPTRSVAlgorithm::SEQLVLSCHD_TP1;
655}
656
657template <class MatrixType>
659 compute() {
660 const char prefix[] = "Ifpack2::LocalSparseTriangularSolver::compute: ";
661 if (!out_.is_null()) {
662 *out_ << ">>> DEBUG " << prefix << std::endl;
663 }
664
665 if (!isKokkosKernelsStream_) {
666 TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::runtime_error, prefix << "You must call "
667 "setMatrix() with a nonnull input matrix before you may call "
668 "initialize() or compute().");
669 TEUCHOS_TEST_FOR_EXCEPTION(A_crs_.is_null(), std::logic_error, prefix << "A_ is nonnull, but "
670 "A_crs_ is null. Please report this bug to the Ifpack2 developers.");
671 // At this point, the matrix MUST be fillComplete.
672 TEUCHOS_TEST_FOR_EXCEPTION(!A_crs_->isFillComplete(), std::runtime_error,
673 "If you call this "
674 "method, the matrix must be fill complete. It is not.");
675 } else {
676 for (int i = 0; i < num_streams_; i++) {
677 TEUCHOS_TEST_FOR_EXCEPTION(A_crs_v_[i].is_null(), std::runtime_error, prefix << "You must call "
678 "setMatrices() with a nonnull input matrix before you may call "
679 "initialize() or compute().");
680 // At this point, the matrix MUST be fillComplete.
681 TEUCHOS_TEST_FOR_EXCEPTION(!A_crs_v_[i]->isFillComplete(), std::runtime_error,
682 "If you call this "
683 "method, the matrix must be fill complete. It is not.");
684 }
685 }
686
687 if (!isInitialized_) {
688 initialize();
689 }
690 TEUCHOS_TEST_FOR_EXCEPTION(!isInitialized_, std::logic_error, prefix << "initialize() should have "
691 "been called by this point, but isInitialized_ is false. "
692 "Please report this bug to the Ifpack2 developers.");
693
694// NOTE (Nov-09-2022):
695// For Cuda >= 11.3 (using cusparseSpSV), always call symbolic during compute
696// even when matrix values are changed with the same sparsity pattern.
697// For Cuda >= 12.1 has a new cusparseSpSV_updateMatrix function just for updating the
698// values that is substantially faster.
699// This would all be much better handled via a KokkosSparse::sptrsv_numeric(...)
700// that could hide the Cuda implementation details.
701#if defined(KOKKOS_ENABLE_CUDA) && defined(KOKKOSKERNELS_ENABLE_TPL_CUSPARSE) && (CUDA_VERSION >= 11030)
702 if constexpr (std::is_same_v<typename crs_matrix_type::execution_space, Kokkos::Cuda>) {
703 if (this->isKokkosKernelsSptrsv_) {
704 if (Teuchos::nonnull(kh_) && !isKokkosKernelsStream_) {
705 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_crs_, true);
706 auto Alocal = A_crs->getLocalMatrixDevice();
707 auto val = Alocal.values;
708 auto ptr = Alocal.graph.row_map;
709 auto ind = Alocal.graph.entries;
710 KokkosSparse::sptrsv_symbolic(kh_.getRawPtr(), ptr, ind, val);
711 } else if (kh_v_nonnull_) {
712 for (int i = 0; i < num_streams_; i++) {
713 auto A_crs_i = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_crs_v_[i], true);
714 auto Alocal_i = A_crs_i->getLocalMatrixDevice();
715 auto val_i = Alocal_i.values;
716 auto ptr_i = Alocal_i.graph.row_map;
717 auto ind_i = Alocal_i.graph.entries;
718 KokkosSparse::sptrsv_symbolic(exec_space_instances_[i], kh_v_[i].getRawPtr(), ptr_i, ind_i, val_i);
719 }
720 }
721 }
722 }
723#endif
724
725 if (!isComputed_) { // Only compute if not computed before
726 if (Teuchos::nonnull(htsImpl_))
727 htsImpl_->compute(*A_crs_, out_);
728
729 isComputed_ = true;
730 ++numCompute_;
731 }
732}
733
734template <class MatrixType>
736 apply(const Tpetra::MultiVector<scalar_type, local_ordinal_type,
738 Tpetra::MultiVector<scalar_type, local_ordinal_type,
740 Teuchos::ETransp mode,
741 scalar_type alpha,
742 scalar_type beta) const {
743 using Teuchos::RCP;
744 using Teuchos::rcp;
745 using Teuchos::rcpFromRef;
746 typedef scalar_type ST;
747 typedef Teuchos::ScalarTraits<ST> STS;
748 const char prefix[] = "Ifpack2::LocalSparseTriangularSolver::apply: ";
749
750 if (!out_.is_null()) {
751 *out_ << ">>> DEBUG " << prefix;
752 if (!isKokkosKernelsStream_) {
753 if (A_crs_.is_null()) {
754 *out_ << "A_crs_ is null!" << std::endl;
755 } else {
756 Teuchos::RCP<const crs_matrix_type> A_crs =
757 Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_);
758 const std::string uplo = this->uplo_;
759 const std::string trans = (mode == Teuchos::CONJ_TRANS) ? "C" : (mode == Teuchos::TRANS ? "T" : "N");
760 const std::string diag = this->diag_;
761 *out_ << "uplo=\"" << uplo
762 << "\", trans=\"" << trans
763 << "\", diag=\"" << diag << "\"" << std::endl;
764 }
765 } else {
766 for (int i = 0; i < num_streams_; i++) {
767 if (A_crs_v_[i].is_null()) {
768 *out_ << "A_crs_v_[" << i << "]"
769 << " is null!" << std::endl;
770 } else {
771 const std::string uplo = this->uplo_;
772 const std::string trans = (mode == Teuchos::CONJ_TRANS) ? "C" : (mode == Teuchos::TRANS ? "T" : "N");
773 const std::string diag = this->diag_;
774 *out_ << "A_crs_v_[" << i << "]: "
775 << "uplo=\"" << uplo
776 << "\", trans=\"" << trans
777 << "\", diag=\"" << diag << "\"" << std::endl;
778 }
779 }
780 }
781 }
782
783 TEUCHOS_TEST_FOR_EXCEPTION(!isComputed(), std::runtime_error, prefix << "If compute() has not yet "
784 "been called, or if you have changed the matrix via setMatrix(), you must "
785 "call compute() before you may call this method.");
786 // If isComputed() is true, it's impossible for the matrix to be
787 // null, or for it not to be a Tpetra::CrsMatrix.
788 if (!isKokkosKernelsStream_) {
789 TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::logic_error, prefix << "A_ is null. "
790 "Please report this bug to the Ifpack2 developers.");
791 TEUCHOS_TEST_FOR_EXCEPTION(A_crs_.is_null(), std::logic_error, prefix << "A_crs_ is null. "
792 "Please report this bug to the Ifpack2 developers.");
793 // However, it _is_ possible that the user called resumeFill() on
794 // the matrix, after calling compute(). This is NOT allowed.
795 TEUCHOS_TEST_FOR_EXCEPTION(!A_crs_->isFillComplete(), std::runtime_error,
796 "If you call this "
797 "method, the matrix must be fill complete. It is not. This means that "
798 " you must have called resumeFill() on the matrix before calling apply(). "
799 "This is NOT allowed. Note that this class may use the matrix's data in "
800 "place without copying it. Thus, you cannot change the matrix and expect "
801 "the solver to stay the same. If you have changed the matrix, first call "
802 "fillComplete() on it, then call compute() on this object, before you call"
803 " apply(). You do NOT need to call setMatrix, as long as the matrix "
804 "itself (that is, its address in memory) is the same.");
805 } else {
806 for (int i = 0; i < num_streams_; i++) {
807 TEUCHOS_TEST_FOR_EXCEPTION(A_crs_v_[i].is_null(), std::logic_error, prefix << "A_crs_ is null. "
808 "Please report this bug to the Ifpack2 developers.");
809 // However, it _is_ possible that the user called resumeFill() on
810 // the matrix, after calling compute(). This is NOT allowed.
811 TEUCHOS_TEST_FOR_EXCEPTION(!A_crs_v_[i]->isFillComplete(), std::runtime_error,
812 "If you call this "
813 "method, the matrix must be fill complete. It is not. This means that "
814 " you must have called resumeFill() on the matrix before calling apply(). "
815 "This is NOT allowed. Note that this class may use the matrix's data in "
816 "place without copying it. Thus, you cannot change the matrix and expect "
817 "the solver to stay the same. If you have changed the matrix, first call "
818 "fillComplete() on it, then call compute() on this object, before you call"
819 " apply(). You do NOT need to call setMatrix, as long as the matrix "
820 "itself (that is, its address in memory) is the same.");
821 }
822 }
823
824 RCP<const MV> X_cur;
825 RCP<MV> Y_cur;
826
827 if (!isKokkosKernelsStream_) {
828 auto G = A_crs_->getGraph();
829 TEUCHOS_TEST_FOR_EXCEPTION(G.is_null(), std::logic_error, prefix << "A_ and A_crs_ are nonnull, "
830 "but A_crs_'s RowGraph G is null. "
831 "Please report this bug to the Ifpack2 developers.");
832 auto importer = G->getImporter();
833 auto exporter = G->getExporter();
834
835 if (!importer.is_null()) {
836 if (X_colMap_.is_null() || X_colMap_->getNumVectors() != X.getNumVectors()) {
837 X_colMap_ = rcp(new MV(importer->getTargetMap(), X.getNumVectors()));
838 } else {
839 X_colMap_->putScalar(STS::zero());
840 }
841 // See discussion of Github Issue #672 for why the Import needs to
842 // use the ZERO CombineMode. The case where the Export is
843 // nontrivial is likely never exercised.
844 X_colMap_->doImport(X, *importer, Tpetra::ZERO);
845 }
846 X_cur = importer.is_null() ? rcpFromRef(X) : Teuchos::rcp_const_cast<const MV>(X_colMap_);
847
848 if (!exporter.is_null()) {
849 if (Y_rowMap_.is_null() || Y_rowMap_->getNumVectors() != Y.getNumVectors()) {
850 Y_rowMap_ = rcp(new MV(exporter->getSourceMap(), Y.getNumVectors()));
851 } else {
852 Y_rowMap_->putScalar(STS::zero());
853 }
854 Y_rowMap_->doExport(Y, *importer, Tpetra::ADD);
855 }
856 Y_cur = exporter.is_null() ? rcpFromRef(Y) : Y_rowMap_;
857 } else {
858 // Currently assume X and Y are local vectors (same sizes as A_crs_).
859 // Should do a better job here!!!
860 X_cur = rcpFromRef(X);
861 Y_cur = rcpFromRef(Y);
862 }
863
864 localApply(*X_cur, *Y_cur, mode, alpha, beta);
865
866 if (!isKokkosKernelsStream_) {
867 auto G = A_crs_->getGraph();
868 auto exporter = G->getExporter();
869 if (!exporter.is_null()) {
870 Y.putScalar(STS::zero());
871 Y.doExport(*Y_cur, *exporter, Tpetra::ADD);
872 }
873 }
874 ++numApply_;
875}
876
877template <class MatrixType>
879 localTriangularSolve(const MV& Y,
880 MV& X,
881 const Teuchos::ETransp mode) const {
882 using Teuchos::CONJ_TRANS;
883 using Teuchos::NO_TRANS;
884 using Teuchos::TRANS;
885 const char tfecfFuncName[] = "localTriangularSolve: ";
886
887 if (!isKokkosKernelsStream_) {
888 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!A_crs_->isFillComplete(), std::runtime_error,
889 "The matrix is not fill complete.");
890 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(A_crs_->getLocalNumRows() > 0 && this->uplo_ == "N", std::runtime_error,
891 "The matrix is neither upper triangular or lower triangular. "
892 "You may only call this method if the matrix is triangular. "
893 "Remember that this is a local (per MPI process) property, and that "
894 "Tpetra only knows how to do a local (per process) triangular solve.");
895 } else {
896 for (int i = 0; i < num_streams_; i++) {
897 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!A_crs_v_[i]->isFillComplete(), std::runtime_error,
898 "The matrix is not fill complete.");
899 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(A_crs_v_[i]->getLocalNumRows() > 0 && this->uplo_ == "N", std::runtime_error,
900 "The matrix is neither upper triangular or lower triangular. "
901 "You may only call this method if the matrix is triangular. "
902 "Remember that this is a local (per MPI process) property, and that "
903 "Tpetra only knows how to do a local (per process) triangular solve.");
904 }
905 }
906 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!X.isConstantStride() || !Y.isConstantStride(), std::invalid_argument,
907 "X and Y must be constant stride.");
908 using STS = Teuchos::ScalarTraits<scalar_type>;
909 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(STS::isComplex && mode == TRANS, std::logic_error,
910 "This method does "
911 "not currently support non-conjugated transposed solve (mode == "
912 "Teuchos::TRANS) for complex scalar types.");
913
914 const std::string uplo = this->uplo_;
915 const std::string trans = (mode == Teuchos::CONJ_TRANS) ? "C" : (mode == Teuchos::TRANS ? "T" : "N");
916 const size_t numVecs = std::min(X.getNumVectors(), Y.getNumVectors());
917
918 if (Teuchos::nonnull(kh_) && this->isKokkosKernelsSptrsv_ && trans == "N") {
919 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(this->A_);
920 auto A_lclk = A_crs->getLocalMatrixDevice();
921 auto ptr = A_lclk.graph.row_map;
922 auto ind = A_lclk.graph.entries;
923 auto val = A_lclk.values;
924
925 for (size_t j = 0; j < numVecs; ++j) {
926 auto X_j = X.getVectorNonConst(j);
927 auto Y_j = Y.getVector(j);
928 auto X_lcl = X_j->getLocalViewDevice(Tpetra::Access::ReadWrite);
929 auto Y_lcl = Y_j->getLocalViewDevice(Tpetra::Access::ReadOnly);
930 auto X_lcl_1d = Kokkos::subview(X_lcl, Kokkos::ALL(), 0);
931 auto Y_lcl_1d = Kokkos::subview(Y_lcl, Kokkos::ALL(), 0);
932 KokkosSparse::sptrsv_solve(kh_.getRawPtr(), ptr, ind, val, Y_lcl_1d, X_lcl_1d);
933 // TODO is this fence needed...
934 typename k_handle::HandleExecSpace().fence();
935 }
936 } // End using regular interface of Kokkos Kernels Sptrsv
937 else if (kh_v_nonnull_ && this->isKokkosKernelsSptrsv_ && trans == "N") {
938 std::vector<lno_row_view_t> ptr_v(num_streams_);
939 std::vector<lno_nonzero_view_t> ind_v(num_streams_);
940 std::vector<scalar_nonzero_view_t> val_v(num_streams_);
941 std::vector<k_handle*> KernelHandle_rawptr_v_(num_streams_);
942 for (size_t j = 0; j < numVecs; ++j) {
943 auto X_j = X.getVectorNonConst(j);
944 auto Y_j = Y.getVector(j);
945 auto X_lcl = X_j->getLocalViewDevice(Tpetra::Access::ReadWrite);
946 auto Y_lcl = Y_j->getLocalViewDevice(Tpetra::Access::ReadOnly);
947 auto X_lcl_1d = Kokkos::subview(X_lcl, Kokkos::ALL(), 0);
948 auto Y_lcl_1d = Kokkos::subview(Y_lcl, Kokkos::ALL(), 0);
949 std::vector<decltype(X_lcl_1d)> x_v(num_streams_);
950 std::vector<decltype(Y_lcl_1d)> y_v(num_streams_);
951 local_ordinal_type stream_begin = 0;
952 local_ordinal_type stream_end;
953 for (int i = 0; i < num_streams_; i++) {
954 auto A_crs_i = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_crs_v_[i]);
955 auto Alocal_i = A_crs_i->getLocalMatrixDevice();
956 ptr_v[i] = Alocal_i.graph.row_map;
957 ind_v[i] = Alocal_i.graph.entries;
958 val_v[i] = Alocal_i.values;
959 stream_end = stream_begin + Alocal_i.numRows();
960 x_v[i] = Kokkos::subview(X_lcl, Kokkos::make_pair(stream_begin, stream_end), 0);
961 y_v[i] = Kokkos::subview(Y_lcl, Kokkos::make_pair(stream_begin, stream_end), 0);
962 KernelHandle_rawptr_v_[i] = kh_v_[i].getRawPtr();
963 stream_begin = stream_end;
964 }
965 Kokkos::fence();
966 KokkosSparse::Experimental::sptrsv_solve_streams(exec_space_instances_, KernelHandle_rawptr_v_,
967 ptr_v, ind_v, val_v, y_v, x_v);
968 for (int i = 0; i < num_streams_; i++) exec_space_instances_[i].fence();
969 // Kokkos::fence();
970 }
971 } // End using stream interface of Kokkos Kernels Sptrsv
972 else {
973 const std::string diag = this->diag_;
974 // NOTE (mfh 20 Aug 2017): KokkosSparse::trsv currently is a
975 // sequential, host-only code. See
976 // https://github.com/kokkos/kokkos-kernels/issues/48.
977
978 auto A_lcl = this->A_crs_->getLocalMatrixHost();
979
980 if (X.isConstantStride() && Y.isConstantStride()) {
981 auto X_lcl = X.getLocalViewHost(Tpetra::Access::ReadWrite);
982 auto Y_lcl = Y.getLocalViewHost(Tpetra::Access::ReadOnly);
983 KokkosSparse::trsv(uplo.c_str(), trans.c_str(), diag.c_str(),
984 A_lcl, Y_lcl, X_lcl);
985 } else {
986 for (size_t j = 0; j < numVecs; ++j) {
987 auto X_j = X.getVectorNonConst(j);
988 auto Y_j = Y.getVector(j);
989 auto X_lcl = X_j->getLocalViewHost(Tpetra::Access::ReadWrite);
990 auto Y_lcl = Y_j->getLocalViewHost(Tpetra::Access::ReadOnly);
991 KokkosSparse::trsv(uplo.c_str(), trans.c_str(),
992 diag.c_str(), A_lcl, Y_lcl, X_lcl);
993 }
994 }
995 }
996}
997
998template <class MatrixType>
999void LocalSparseTriangularSolver<MatrixType>::
1000 localApply(const MV& X,
1001 MV& Y,
1002 const Teuchos::ETransp mode,
1003 const scalar_type& alpha,
1004 const scalar_type& beta) const {
1005 if (mode == Teuchos::NO_TRANS && Teuchos::nonnull(htsImpl_) &&
1006 htsImpl_->isComputed()) {
1007 htsImpl_->localApply(X, Y, mode, alpha, beta);
1008 return;
1009 }
1010
1011 using Teuchos::RCP;
1012 typedef scalar_type ST;
1013 typedef Teuchos::ScalarTraits<ST> STS;
1014
1015 if (beta == STS::zero()) {
1016 if (alpha == STS::zero()) {
1017 Y.putScalar(STS::zero()); // Y := 0 * Y (ignore contents of Y)
1018 } else { // alpha != 0
1019 this->localTriangularSolve(X, Y, mode);
1020 if (alpha != STS::one()) {
1021 Y.scale(alpha);
1022 }
1023 }
1024 } else { // beta != 0
1025 if (alpha == STS::zero()) {
1026 Y.scale(beta); // Y := beta * Y
1027 } else { // alpha != 0
1028 MV Y_tmp(Y, Teuchos::Copy);
1029 this->localTriangularSolve(X, Y_tmp, mode); // Y_tmp := M * X
1030 Y.update(alpha, Y_tmp, beta); // Y := beta * Y + alpha * Y_tmp
1031 }
1032 }
1033}
1034
1035template <class MatrixType>
1037 getNumInitialize() const {
1038 return numInitialize_;
1039}
1040
1041template <class MatrixType>
1043 getNumCompute() const {
1044 return numCompute_;
1045}
1046
1047template <class MatrixType>
1049 getNumApply() const {
1050 return numApply_;
1051}
1052
1053template <class MatrixType>
1054double
1056 getInitializeTime() const {
1057 return initializeTime_;
1058}
1059
1060template <class MatrixType>
1061double
1063 getComputeTime() const {
1064 return computeTime_;
1065}
1066
1067template <class MatrixType>
1068double
1070 getApplyTime() const {
1071 return applyTime_;
1072}
1073
1074template <class MatrixType>
1075std::string
1077 description() const {
1078 std::ostringstream os;
1079
1080 // Output is a valid YAML dictionary in flow style. If you don't
1081 // like everything on a single line, you should call describe()
1082 // instead.
1083 os << "\"Ifpack2::LocalSparseTriangularSolver\": {";
1084 if (this->getObjectLabel() != "") {
1085 os << "Label: \"" << this->getObjectLabel() << "\", ";
1086 }
1087 os << "Initialized: " << (isInitialized() ? "true" : "false") << ", "
1088 << "Computed: " << (isComputed() ? "true" : "false") << ", ";
1089
1090 if (isKokkosKernelsSptrsv_) os << "KK-SPTRSV, ";
1091 if (isKokkosKernelsStream_) os << "KK-SolveStream, ";
1092
1093 if (A_.is_null()) {
1094 os << "Matrix: null";
1095 } else {
1096 os << "Matrix dimensions: ["
1097 << A_->getGlobalNumRows() << ", "
1098 << A_->getGlobalNumCols() << "]"
1099 << ", Number of nonzeros: " << A_->getGlobalNumEntries();
1100 }
1101
1102 if (Teuchos::nonnull(htsImpl_))
1103 os << ", HTS computed: " << (htsImpl_->isComputed() ? "true" : "false");
1104 os << "}";
1105 return os.str();
1106}
1107
1108template <class MatrixType>
1110 describe(Teuchos::FancyOStream& out,
1111 const Teuchos::EVerbosityLevel verbLevel) const {
1112 using std::endl;
1113 // Default verbosity level is VERB_LOW, which prints only on Process
1114 // 0 of the matrix's communicator.
1115 const Teuchos::EVerbosityLevel vl = (verbLevel == Teuchos::VERB_DEFAULT) ? Teuchos::VERB_LOW : verbLevel;
1116
1117 if (vl != Teuchos::VERB_NONE) {
1118 // Print only on Process 0 in the matrix's communicator. If the
1119 // matrix is null, though, we have to get the communicator from
1120 // somewhere, so we ask Tpetra for its default communicator. If
1121 // MPI is enabled, this wraps MPI_COMM_WORLD or a clone thereof.
1122 auto comm = A_.is_null() ? Tpetra::getDefaultComm() : A_->getComm();
1123
1124 // Users aren't supposed to do anything with the matrix on
1125 // processes where its communicator is null.
1126 if (!comm.is_null() && comm->getRank() == 0) {
1127 // By convention, describe() should always begin with a tab.
1128 Teuchos::OSTab tab0(out);
1129 // Output is in YAML format. We have to escape the class name,
1130 // because it has a colon.
1131 out << "\"Ifpack2::LocalSparseTriangularSolver\":" << endl;
1132 Teuchos::OSTab tab1(out);
1133 out << "Scalar: " << Teuchos::TypeNameTraits<scalar_type>::name() << endl
1134 << "LocalOrdinal: " << Teuchos::TypeNameTraits<local_ordinal_type>::name() << endl
1135 << "GlobalOrdinal: " << Teuchos::TypeNameTraits<global_ordinal_type>::name() << endl
1136 << "Node: " << Teuchos::TypeNameTraits<node_type>::name() << endl;
1137 }
1138 }
1139}
1140
1141template <class MatrixType>
1142Teuchos::RCP<const typename LocalSparseTriangularSolver<MatrixType>::map_type>
1144 getDomainMap() const {
1145 TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::runtime_error,
1146 "Ifpack2::LocalSparseTriangularSolver::getDomainMap: "
1147 "The matrix is null. Please call setMatrix() with a nonnull input "
1148 "before calling this method.");
1149 return A_->getDomainMap();
1150}
1151
1152template <class MatrixType>
1153Teuchos::RCP<const typename LocalSparseTriangularSolver<MatrixType>::map_type>
1155 getRangeMap() const {
1156 TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::runtime_error,
1157 "Ifpack2::LocalSparseTriangularSolver::getRangeMap: "
1158 "The matrix is null. Please call setMatrix() with a nonnull input "
1159 "before calling this method.");
1160 return A_->getRangeMap();
1161}
1162
1163template <class MatrixType>
1165 setMatrix(const Teuchos::RCP<const row_matrix_type>& A) {
1166 const char prefix[] = "Ifpack2::LocalSparseTriangularSolver::setMatrix: ";
1167
1168 // If the pointer didn't change, do nothing. This is reasonable
1169 // because users are supposed to call this method with the same
1170 // object over all participating processes, and pointer identity
1171 // implies object identity.
1172 if (A.getRawPtr() != A_.getRawPtr() || isInternallyChanged_) {
1173 // Check in serial or one-process mode if the matrix is square.
1174 TEUCHOS_TEST_FOR_EXCEPTION(!A.is_null() && A->getComm()->getSize() == 1 &&
1175 A->getLocalNumRows() != A->getLocalNumCols(),
1176 std::runtime_error, prefix << "If A's communicator only contains one "
1177 "process, then A must be square. Instead, you provided a matrix A with "
1178 << A->getLocalNumRows() << " rows and " << A->getLocalNumCols() << " columns.");
1179
1180 // It's legal for A to be null; in that case, you may not call
1181 // initialize() until calling setMatrix() with a nonnull input.
1182 // Regardless, setting the matrix invalidates the preconditioner.
1183 isInitialized_ = false;
1184 isComputed_ = false;
1185
1186 if (A.is_null()) {
1187 A_crs_ = Teuchos::null;
1188 A_ = Teuchos::null;
1189 } else { // A is not null
1190 Teuchos::RCP<const crs_matrix_type> A_crs =
1191 Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A);
1192 TEUCHOS_TEST_FOR_EXCEPTION(A_crs.is_null(), std::invalid_argument, prefix << "The input matrix A is not a Tpetra::CrsMatrix.");
1193 A_crs_ = A_crs;
1194 A_ = A;
1195 }
1196
1197 if (Teuchos::nonnull(htsImpl_))
1198 htsImpl_->reset();
1199 } // pointers are not the same
1200}
1201
1202template <class MatrixType>
1204 setStreamInfo(const bool& isKokkosKernelsStream, const int& num_streams,
1205 const std::vector<HandleExecSpace>& exec_space_instances) {
1206 isKokkosKernelsStream_ = isKokkosKernelsStream;
1207 num_streams_ = num_streams;
1208 exec_space_instances_ = exec_space_instances;
1209 A_crs_v_ = std::vector<Teuchos::RCP<crs_matrix_type>>(num_streams_);
1210}
1211
1212template <class MatrixType>
1214 setMatrices(const std::vector<Teuchos::RCP<crs_matrix_type>>& A_crs_v) {
1215 const char prefix[] = "Ifpack2::LocalSparseTriangularSolver::setMatrixWithStreams: ";
1216
1217 for (int i = 0; i < num_streams_; i++) {
1218 // If the pointer didn't change, do nothing. This is reasonable
1219 // because users are supposed to call this method with the same
1220 // object over all participating processes, and pointer identity
1221 // implies object identity.
1222 if (A_crs_v[i].getRawPtr() != A_crs_v_[i].getRawPtr() || isInternallyChanged_) {
1223 // Check in serial or one-process mode if the matrix is square.
1224 TEUCHOS_TEST_FOR_EXCEPTION(!A_crs_v[i].is_null() && A_crs_v[i]->getComm()->getSize() == 1 &&
1225 A_crs_v[i]->getLocalNumRows() != A_crs_v[i]->getLocalNumCols(),
1226 std::runtime_error, prefix << "If A's communicator only contains one "
1227 "process, then A must be square. Instead, you provided a matrix A with "
1228 << A_crs_v[i]->getLocalNumRows() << " rows and " << A_crs_v[i]->getLocalNumCols() << " columns.");
1229
1230 // It's legal for A to be null; in that case, you may not call
1231 // initialize() until calling setMatrix() with a nonnull input.
1232 // Regardless, setting the matrix invalidates the preconditioner.
1233 isInitialized_ = false;
1234 isComputed_ = false;
1235
1236 if (A_crs_v[i].is_null()) {
1237 A_crs_v_[i] = Teuchos::null;
1238 } else { // A is not null
1239 Teuchos::RCP<crs_matrix_type> A_crs =
1240 Teuchos::rcp_dynamic_cast<crs_matrix_type>(A_crs_v[i]);
1241 TEUCHOS_TEST_FOR_EXCEPTION(A_crs.is_null(), std::invalid_argument, prefix << "The input matrix A is not a Tpetra::CrsMatrix.");
1242 A_crs_v_[i] = A_crs;
1243 }
1244 } // pointers are not the same
1245 }
1246}
1247
1248} // namespace Ifpack2
1249
1250#define IFPACK2_LOCALSPARSETRIANGULARSOLVER_INSTANT(S, LO, GO, N) \
1251 template class Ifpack2::LocalSparseTriangularSolver<Tpetra::RowMatrix<S, LO, GO, N>>;
1252
1253#endif // IFPACK2_LOCALSPARSETRIANGULARSOLVER_DEF_HPP
"Preconditioner" that solves local sparse triangular systems.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:54
bool isComputed() const
Return true if compute() has been called.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:192
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Print this object with given verbosity to the given output stream.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1110
std::string description() const
A one-line description of this object.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1077
Tpetra::Map< local_ordinal_type, global_ordinal_type, node_type > map_type
Specialization of Tpetra::Map used by this class.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:70
LocalSparseTriangularSolver()
Constructor that takes no input matrix.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:298
MatrixType::local_ordinal_type local_ordinal_type
Type of the local indices of the input matrix.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:61
void setParameters(const Teuchos::ParameterList &params)
Set this object's parameters.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:365
double getComputeTime() const
Return the time spent in compute().
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1063
void initialize()
"Symbolic" phase of setup
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:409
MatrixType::global_ordinal_type global_ordinal_type
Type of the global indices of the input matrix.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:63
int getNumCompute() const
Return the number of calls to compute().
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1043
double getInitializeTime() const
Return the time spent in initialize().
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1056
Teuchos::RCP< const map_type > getDomainMap() const
The domain of this operator.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1144
MatrixType::scalar_type scalar_type
Scalar type of the entries of the input matrix.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:57
MatrixType::node_type node_type
Node type of the input matrix.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:65
int getNumInitialize() const
Return the number of calls to initialize().
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1037
void setStreamInfo(const bool &isKokkosKernelsStream, const int &num_streams, const std::vector< HandleExecSpace > &exec_space_instances)
Set this triangular solver's stream information.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1204
Tpetra::CrsMatrix< scalar_type, local_ordinal_type, global_ordinal_type, node_type > crs_matrix_type
Specialization of Tpetra::CrsMatrix used by this class.
Definition Ifpack2_LocalSparseTriangularSolver_decl.hpp:78
Teuchos::RCP< const map_type > getRangeMap() const
The range of this operator.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1155
virtual void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Set this preconditioner's matrix.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1165
void setMatrices(const std::vector< Teuchos::RCP< crs_matrix_type > > &A_crs_v)
Set this preconditioner's matrices (used by stream interface of triangular solve).
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1214
virtual ~LocalSparseTriangularSolver()
Destructor (virtual for memory safety).
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:334
double getApplyTime() const
Return the time spent in apply().
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1070
void apply(const Tpetra::MultiVector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > &X, Tpetra::MultiVector< scalar_type, local_ordinal_type, global_ordinal_type, node_type > &Y, Teuchos::ETransp mode=Teuchos::NO_TRANS, scalar_type alpha=Teuchos::ScalarTraits< scalar_type >::one(), scalar_type beta=Teuchos::ScalarTraits< scalar_type >::zero()) const
Apply the preconditioner to X, and put the result in Y.
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:736
int getNumApply() const
Return the number of calls to apply().
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:1049
void compute()
"Numeric" phase of setup
Definition Ifpack2_LocalSparseTriangularSolver_def.hpp:659
TRANS
Ifpack2 implementation details.
Preconditioners and smoothers for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:40