Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
Ifpack2_Details_Chebyshev_def.hpp
Go to the documentation of this file.
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_DETAILS_CHEBYSHEV_DEF_HPP
11#define IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
12
19
23// #include "Ifpack2_Details_ScaledDampedResidual.hpp"
24#include "Ifpack2_Details_ChebyshevKernel.hpp"
25#if KOKKOS_VERSION >= 40799
26#include "KokkosKernels_ArithTraits.hpp"
27#else
28#include "Kokkos_ArithTraits.hpp"
29#endif
30#include "Teuchos_FancyOStream.hpp"
31#include "Teuchos_oblackholestream.hpp"
32#include "Tpetra_Details_residual.hpp"
33#include "Teuchos_LAPACK.hpp"
34#include "Ifpack2_Details_LapackSupportsScalar.hpp"
35#include <cmath>
36#include <iostream>
37
38namespace Ifpack2 {
39namespace Details {
40
41namespace { // (anonymous)
42
43// We use this text a lot in error messages.
44const char computeBeforeApplyReminder[] =
45 "This means one of the following:\n"
46 " - you have not yet called compute() on this instance, or \n"
47 " - you didn't call compute() after calling setParameters().\n\n"
48 "After creating an Ifpack2::Chebyshev instance,\n"
49 "you must _always_ call compute() at least once before calling apply().\n"
50 "After calling compute() once, you do not need to call it again,\n"
51 "unless the matrix has changed or you have changed parameters\n"
52 "(by calling setParameters()).";
53
54} // namespace
55
56// ReciprocalThreshold stuff below needs to be in a namspace visible outside
57// of this file
58template <class XV, class SizeType = typename XV::size_type>
59struct V_ReciprocalThresholdSelfFunctor {
60 typedef typename XV::execution_space execution_space;
61 typedef typename XV::non_const_value_type value_type;
62 typedef SizeType size_type;
63#if KOKKOS_VERSION >= 40799
64 typedef KokkosKernels::ArithTraits<value_type> KAT;
65#else
66 typedef Kokkos::ArithTraits<value_type> KAT;
67#endif
68 typedef typename KAT::mag_type mag_type;
69
70 XV X_;
71 const value_type minVal_;
72 const mag_type minValMag_;
73
74 V_ReciprocalThresholdSelfFunctor(const XV& X,
75 const value_type& min_val)
76 : X_(X)
77 , minVal_(min_val)
78 , minValMag_(KAT::abs(min_val)) {}
79
80 KOKKOS_INLINE_FUNCTION
81 void operator()(const size_type& i) const {
82 const mag_type X_i_abs = KAT::abs(X_[i]);
83
84 if (X_i_abs < minValMag_) {
85 X_[i] = minVal_;
86 } else {
87 X_[i] = KAT::one() / X_[i];
88 }
89 }
90};
91
92template <class XV, class SizeType = typename XV::size_type>
93struct LocalReciprocalThreshold {
94 static void
95 compute(const XV& X,
96 const typename XV::non_const_value_type& minVal) {
97 typedef typename XV::execution_space execution_space;
98 Kokkos::RangePolicy<execution_space, SizeType> policy(0, X.extent(0));
99 V_ReciprocalThresholdSelfFunctor<XV, SizeType> op(X, minVal);
100 Kokkos::parallel_for(policy, op);
101 }
102};
103
104template <class TpetraVectorType,
105 const bool classic = TpetraVectorType::node_type::classic>
106struct GlobalReciprocalThreshold {};
107
108template <class TpetraVectorType>
109struct GlobalReciprocalThreshold<TpetraVectorType, true> {
110 static void
111 compute(TpetraVectorType& V,
112 const typename TpetraVectorType::scalar_type& min_val) {
113 typedef typename TpetraVectorType::scalar_type scalar_type;
114 typedef typename TpetraVectorType::mag_type mag_type;
115#if KOKKOS_VERSION >= 40799
116 typedef KokkosKernels::ArithTraits<scalar_type> STS;
117#else
118 typedef Kokkos::ArithTraits<scalar_type> STS;
119#endif
120
121 const scalar_type ONE = STS::one();
122 const mag_type min_val_abs = STS::abs(min_val);
123
124 Teuchos::ArrayRCP<scalar_type> V_0 = V.getDataNonConst(0);
125 const size_t lclNumRows = V.getLocalLength();
126
127 for (size_t i = 0; i < lclNumRows; ++i) {
128 const scalar_type V_0i = V_0[i];
129 if (STS::abs(V_0i) < min_val_abs) {
130 V_0[i] = min_val;
131 } else {
132 V_0[i] = ONE / V_0i;
133 }
134 }
135 }
136};
137
138template <class TpetraVectorType>
139struct GlobalReciprocalThreshold<TpetraVectorType, false> {
140 static void
141 compute(TpetraVectorType& X,
142 const typename TpetraVectorType::scalar_type& minVal) {
143 typedef typename TpetraVectorType::impl_scalar_type value_type;
144
145 const value_type minValS = static_cast<value_type>(minVal);
146 auto X_0 = Kokkos::subview(X.getLocalViewDevice(Tpetra::Access::ReadWrite),
147 Kokkos::ALL(), 0);
148 LocalReciprocalThreshold<decltype(X_0)>::compute(X_0, minValS);
149 }
150};
151
152// Utility function for inverting diagonal with threshold.
153template <typename S, typename L, typename G, typename N>
154void reciprocal_threshold(Tpetra::Vector<S, L, G, N>& V, const S& minVal) {
155 GlobalReciprocalThreshold<Tpetra::Vector<S, L, G, N>>::compute(V, minVal);
156}
157
158template <class ScalarType, const bool lapackSupportsScalarType = LapackSupportsScalar<ScalarType>::value>
159struct LapackHelper {
160 static ScalarType
161 tri_diag_spectral_radius(Teuchos::ArrayRCP<typename Teuchos::ScalarTraits<ScalarType>::magnitudeType> diag,
162 Teuchos::ArrayRCP<typename Teuchos::ScalarTraits<ScalarType>::magnitudeType> offdiag) {
163 throw std::runtime_error("LAPACK does not support the scalar type.");
164 }
165};
166
167template <class V>
168void computeInitialGuessForCG(const V& diagonal, V& x) {
169 using device_type = typename V::node_type::device_type;
170 using range_policy = Kokkos::RangePolicy<typename device_type::execution_space>;
171
172 // Initial randomization of the vector
173 x.randomize();
174
175 // Zero the stuff that where the diagonal is equal to one. These are assumed to
176 // correspond to OAZ rows in the matrix.
177 size_t N = x.getLocalLength();
178 auto d_view = diagonal.template getLocalView<device_type>(Tpetra::Access::ReadOnly);
179 auto x_view = x.template getLocalView<device_type>(Tpetra::Access::ReadWrite);
180
181 auto ONE = Teuchos::ScalarTraits<typename V::impl_scalar_type>::one();
182 auto ZERO = Teuchos::ScalarTraits<typename V::impl_scalar_type>::zero();
183
184 Kokkos::parallel_for(
185 "computeInitialGuessforCG::zero_bcs", range_policy(0, N), KOKKOS_LAMBDA(const size_t& i) {
186 if (d_view(i, 0) == ONE)
187 x_view(i, 0) = ZERO;
188 });
189}
190
191template <class ScalarType>
192struct LapackHelper<ScalarType, true> {
193 static ScalarType
194 tri_diag_spectral_radius(Teuchos::ArrayRCP<typename Teuchos::ScalarTraits<ScalarType>::magnitudeType> diag,
195 Teuchos::ArrayRCP<typename Teuchos::ScalarTraits<ScalarType>::magnitudeType> offdiag) {
196 using STS = Teuchos::ScalarTraits<ScalarType>;
197 using MagnitudeType = typename STS::magnitudeType;
198 int info = 0;
199 const int N = diag.size();
200 ScalarType scalar_dummy;
201 std::vector<MagnitudeType> mag_dummy(4 * N);
202 char char_N = 'N';
203
204 // lambdaMin = one;
205 ScalarType lambdaMax = STS::one();
206 if (N > 2) {
207 Teuchos::LAPACK<int, ScalarType> lapack;
208 lapack.PTEQR(char_N, N, diag.getRawPtr(), offdiag.getRawPtr(),
209 &scalar_dummy, 1, &mag_dummy[0], &info);
210 TEUCHOS_TEST_FOR_EXCEPTION(info < 0, std::logic_error,
211 "Ifpack2::Details::LapackHelper::tri_diag_spectral_radius:"
212 "LAPACK's _PTEQR failed with info = "
213 << info << " < 0. This suggests there might be a bug in the way Ifpack2 "
214 "is calling LAPACK. Please report this to the Ifpack2 developers.");
215 // lambdaMin = Teuchos::as<ScalarType> (diag[N-1]);
216 lambdaMax = Teuchos::as<ScalarType>(diag[0]);
217 }
218 return lambdaMax;
219 }
220};
221
222template <class ScalarType, class MV>
223void Chebyshev<ScalarType, MV>::checkInputMatrix() const {
224 TEUCHOS_TEST_FOR_EXCEPTION(
225 !A_.is_null() && A_->getGlobalNumRows() != A_->getGlobalNumCols(),
226 std::invalid_argument,
227 "Ifpack2::Chebyshev: The input matrix A must be square. "
228 "A has "
229 << A_->getGlobalNumRows() << " rows and "
230 << A_->getGlobalNumCols() << " columns.");
231
232 // In debug mode, test that the domain and range Maps of the matrix
233 // are the same.
234 if (debug_ && !A_.is_null()) {
235 Teuchos::RCP<const map_type> domainMap = A_->getDomainMap();
236 Teuchos::RCP<const map_type> rangeMap = A_->getRangeMap();
237
238 // isSameAs is a collective, but if the two pointers are the same,
239 // isSameAs will assume that they are the same on all processes, and
240 // return true without an all-reduce.
242 !domainMap->isSameAs(*rangeMap), std::invalid_argument,
243 "Ifpack2::Chebyshev: The domain Map and range Map of the matrix must be "
244 "the same (in the sense of isSameAs())."
245 << std::endl
246 << "We only check "
247 "for this in debug mode.");
248 }
249}
250
251template <class ScalarType, class MV>
254 // mfh 12 Aug 2016: The if statement avoids an "unreachable
255 // statement" warning for the checkInputMatrix() call, when
256 // STS::isComplex is false.
257 if (STS::isComplex) {
258 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
259 "Ifpack2::Chebyshev: This class' implementation "
260 "of Chebyshev iteration only works for real-valued, symmetric positive "
261 "definite matrices. However, you instantiated this class for ScalarType"
262 " = "
263 << Teuchos::TypeNameTraits<ScalarType>::name() << ", which is a "
264 "complex-valued type. While this may be algorithmically correct if all "
265 "of the complex numbers in the matrix have zero imaginary part, we "
266 "forbid using complex ScalarType altogether in order to remind you of "
267 "the limitations of our implementation (and of the algorithm itself).");
268 } else {
269 checkInputMatrix();
270 }
271}
272
273template <class ScalarType, class MV>
275 Chebyshev(Teuchos::RCP<const row_matrix_type> A)
276 : A_(A)
277 , savedDiagOffsets_(false)
278 , computedLambdaMax_(STS::nan())
279 , computedLambdaMin_(STS::nan())
280 , lambdaMaxForApply_(STS::nan())
281 , lambdaMinForApply_(STS::nan())
282 , eigRatioForApply_(STS::nan())
283 , userLambdaMax_(STS::nan())
284 , userLambdaMin_(STS::nan())
285 , userEigRatio_(Teuchos::as<ST>(30))
286 , minDiagVal_(STS::eps())
287 , numIters_(1)
288 , eigMaxIters_(10)
289 , eigRelTolerance_(Teuchos::ScalarTraits<MT>::zero())
290 , eigKeepVectors_(false)
291 , eigenAnalysisType_("power method")
292 , eigNormalizationFreq_(1)
293 , zeroStartingSolution_(true)
294 , assumeMatrixUnchanged_(false)
295 , chebyshevAlgorithm_("first")
296 , computeMaxResNorm_(false)
297 , computeSpectralRadius_(true)
298 , ckUseNativeSpMV_(MV::node_type::is_gpu)
299 , preAllocateTempVector_(true)
300 , debug_(false) {
301 checkConstructorInput();
302}
303
304template <class ScalarType, class MV>
306 Chebyshev(Teuchos::RCP<const row_matrix_type> A,
307 Teuchos::ParameterList& params)
308 : A_(A)
309 , savedDiagOffsets_(false)
310 , computedLambdaMax_(STS::nan())
311 , computedLambdaMin_(STS::nan())
312 , lambdaMaxForApply_(STS::nan())
313 , boostFactor_(static_cast<MT>(1.1))
314 , lambdaMinForApply_(STS::nan())
315 , eigRatioForApply_(STS::nan())
316 , userLambdaMax_(STS::nan())
317 , userLambdaMin_(STS::nan())
318 , userEigRatio_(Teuchos::as<ST>(30))
319 , minDiagVal_(STS::eps())
320 , numIters_(1)
321 , eigMaxIters_(10)
322 , eigRelTolerance_(Teuchos::ScalarTraits<MT>::zero())
323 , eigKeepVectors_(false)
324 , eigenAnalysisType_("power method")
325 , eigNormalizationFreq_(1)
326 , zeroStartingSolution_(true)
327 , assumeMatrixUnchanged_(false)
328 , chebyshevAlgorithm_("first")
329 , computeMaxResNorm_(false)
330 , computeSpectralRadius_(true)
331 , ckUseNativeSpMV_(MV::node_type::is_gpu)
332 , preAllocateTempVector_(true)
333 , debug_(false) {
334 checkConstructorInput();
335 setParameters(params);
336}
337
338template <class ScalarType, class MV>
340 setParameters(Teuchos::ParameterList& plist) {
341 using Teuchos::RCP;
342 using Teuchos::rcp;
343 using Teuchos::rcp_const_cast;
344
345 // Note to developers: The logic for this method is complicated,
346 // because we want to accept Ifpack and ML parameters whenever
347 // possible, but we don't want to add their default values to the
348 // user's ParameterList. That's why we do all the isParameter()
349 // checks, instead of using the two-argument version of get()
350 // everywhere. The min and max eigenvalue parameters are also a
351 // special case, because we decide whether or not to do eigenvalue
352 // analysis based on whether the user supplied the max eigenvalue.
353
354 // Default values of all the parameters.
355 const ST defaultLambdaMax = STS::nan();
356 const ST defaultLambdaMin = STS::nan();
357 // 30 is Ifpack::Chebyshev's default. ML has a different default
358 // eigRatio for smoothers and the coarse-grid solve (if using
359 // Chebyshev for that). The former uses 20; the latter uses 30.
360 // We're testing smoothers here, so use 20. (However, if you give
361 // ML an Epetra matrix, it will use Ifpack for Chebyshev, in which
362 // case it would defer to Ifpack's default settings.)
363 const ST defaultEigRatio = Teuchos::as<ST>(30);
364 const MT defaultBoostFactor = static_cast<MT>(1.1);
365 const ST defaultMinDiagVal = STS::eps();
366 const int defaultNumIters = 1;
367 const int defaultEigMaxIters = 10;
368 const MT defaultEigRelTolerance = Teuchos::ScalarTraits<MT>::zero();
369 const bool defaultEigKeepVectors = false;
370 const int defaultEigNormalizationFreq = 1;
371 const bool defaultZeroStartingSolution = true; // Ifpack::Chebyshev default
372 const bool defaultAssumeMatrixUnchanged = false;
373 const std::string defaultChebyshevAlgorithm = "first";
374 const bool defaultComputeMaxResNorm = false;
375 const bool defaultComputeSpectralRadius = true;
376 const bool defaultCkUseNativeSpMV = MV::node_type::is_gpu;
377 const bool defaultPreAllocateTempVector = true;
378 const bool defaultDebug = false;
379
380 // We'll set the instance data transactionally, after all reads
381 // from the ParameterList. That way, if any of the ParameterList
382 // reads fail (e.g., due to the wrong parameter type), we will not
383 // have left the instance data in a half-changed state.
384 RCP<const V> userInvDiagCopy; // if nonnull: deep copy of user's Vector
395 bool zeroStartingSolution = defaultZeroStartingSolution;
402 bool debug = defaultDebug;
403
404 // Fetch the parameters from the ParameterList. Defer all
405 // externally visible side effects until we have finished all
406 // ParameterList interaction. This makes the method satisfy the
407 // strong exception guarantee.
408
409 if (plist.isType<bool>("debug")) {
410 debug = plist.get<bool>("debug");
411 } else if (plist.isType<int>("debug")) {
412 const int debugInt = plist.get<bool>("debug");
413 debug = debugInt != 0;
414 }
415
416 // Get the user-supplied inverse diagonal.
417 //
418 // Check for a raw pointer (const V* or V*), for Ifpack
419 // compatibility, as well as for RCP<const V>, RCP<V>, const V, or
420 // V. We'll make a deep copy of the vector at the end of this
421 // method anyway, so its const-ness doesn't matter. We handle the
422 // latter two cases ("const V" or "V") specially (copy them into
423 // userInvDiagCopy first, which is otherwise null at the end of the
424 // long if-then chain) to avoid an extra copy.
425
426 const char opInvDiagLabel[] = "chebyshev: operator inv diagonal";
427 if (plist.isParameter(opInvDiagLabel)) {
428 // Pointer to the user's Vector, if provided.
430
431 if (plist.isType<const V*>(opInvDiagLabel)) {
432 const V* rawUserInvDiag =
433 plist.get<const V*>(opInvDiagLabel);
434 // Nonowning reference (we'll make a deep copy below)
436 } else if (plist.isType<const V*>(opInvDiagLabel)) {
438 // Nonowning reference (we'll make a deep copy below)
439 userInvDiag = rcp(const_cast<const V*>(rawUserInvDiag), false);
440 } else if (plist.isType<RCP<const V>>(opInvDiagLabel)) {
442 } else if (plist.isType<RCP<V>>(opInvDiagLabel)) {
446 } else if (plist.isType<const V>(opInvDiagLabel)) {
447 const V& userInvDiagRef = plist.get<const V>(opInvDiagLabel);
448 userInvDiagCopy = rcp(new V(userInvDiagRef, Teuchos::Copy));
450 } else if (plist.isType<V>(opInvDiagLabel)) {
452 const V& userInvDiagRef = const_cast<const V&>(userInvDiagNonConstRef);
453 userInvDiagCopy = rcp(new V(userInvDiagRef, Teuchos::Copy));
455 }
456
457 // NOTE: If the user's parameter has some strange type that we
458 // didn't test above, userInvDiag might still be null. You may
459 // want to add an error test for this condition. Currently, we
460 // just say in this case that the user didn't give us a Vector.
461
462 // If we have userInvDiag but don't have a deep copy yet, make a
463 // deep copy now.
464 if (!userInvDiag.is_null() && userInvDiagCopy.is_null()) {
465 userInvDiagCopy = rcp(new V(*userInvDiag, Teuchos::Copy));
466 }
467
468 // NOTE: userInvDiag, if provided, is a row Map version of the
469 // Vector. We don't necessarily have a range Map yet. compute()
470 // would be the proper place to compute the range Map version of
471 // userInvDiag.
472 }
473
474 // Load the kernel fuse override from the parameter list
475 if (plist.isParameter("chebyshev: use native spmv"))
476 ckUseNativeSpMV = plist.get("chebyshev: use native spmv", ckUseNativeSpMV);
477
478 // Load the pre-allocate overrride from the parameter list
479 if (plist.isParameter("chebyshev: pre-allocate temp vector"))
480 preAllocateTempVector = plist.get("chebyshev: pre-allocate temp vector", preAllocateTempVector);
481
482 // Don't fill in defaults for the max or min eigenvalue, because
483 // this class uses the existence of those parameters to determine
484 // whether it should do eigenanalysis.
485 if (plist.isParameter("chebyshev: max eigenvalue")) {
486 if (plist.isType<double>("chebyshev: max eigenvalue"))
487 lambdaMax = plist.get<double>("chebyshev: max eigenvalue");
488 else
489 lambdaMax = plist.get<ST>("chebyshev: max eigenvalue");
491 STS::isnaninf(lambdaMax), std::invalid_argument,
492 "Ifpack2::Chebyshev::setParameters: \"chebyshev: max eigenvalue\" "
493 "parameter is NaN or Inf. This parameter is optional, but if you "
494 "choose to supply it, it must have a finite value.");
495 }
496 if (plist.isParameter("chebyshev: min eigenvalue")) {
497 if (plist.isType<double>("chebyshev: min eigenvalue"))
498 lambdaMin = plist.get<double>("chebyshev: min eigenvalue");
499 else
500 lambdaMin = plist.get<ST>("chebyshev: min eigenvalue");
502 STS::isnaninf(lambdaMin), std::invalid_argument,
503 "Ifpack2::Chebyshev::setParameters: \"chebyshev: min eigenvalue\" "
504 "parameter is NaN or Inf. This parameter is optional, but if you "
505 "choose to supply it, it must have a finite value.");
506 }
507
508 // Only fill in Ifpack2's name for the default parameter, not ML's.
509 if (plist.isParameter("smoother: Chebyshev alpha")) { // ML compatibility
510 if (plist.isType<double>("smoother: Chebyshev alpha"))
511 eigRatio = plist.get<double>("smoother: Chebyshev alpha");
512 else
513 eigRatio = plist.get<ST>("smoother: Chebyshev alpha");
514 }
515 // Ifpack2's name overrides ML's name.
516 eigRatio = plist.get("chebyshev: ratio eigenvalue", eigRatio);
518 STS::isnaninf(eigRatio), std::invalid_argument,
519 "Ifpack2::Chebyshev::setParameters: \"chebyshev: ratio eigenvalue\" "
520 "parameter (also called \"smoother: Chebyshev alpha\") is NaN or Inf. "
521 "This parameter is optional, but if you choose to supply it, it must have "
522 "a finite value.");
523 // mfh 11 Feb 2013: This class is currently only correct for real
524 // Scalar types, but we still want it to build for complex Scalar
525 // type so that users of Ifpack2::Factory can build their
526 // executables for real or complex Scalar type. Thus, we take the
527 // real parts here, which are always less-than comparable.
529 STS::real(eigRatio) < STS::real(STS::one()),
530 std::invalid_argument,
531 "Ifpack2::Chebyshev::setParameters: \"chebyshev: ratio eigenvalue\""
532 "parameter (also called \"smoother: Chebyshev alpha\") must be >= 1, "
533 "but you supplied the value "
534 << eigRatio << ".");
535
536 // See Github Issue #234. This parameter may be either MT
537 // (preferred) or double. We check both.
538 {
539 const char paramName[] = "chebyshev: boost factor";
540
541 if (plist.isParameter(paramName)) {
542 if (plist.isType<MT>(paramName)) { // MT preferred
544 } else if (!std::is_same<double, MT>::value &&
545 plist.isType<double>(paramName)) {
546 const double dblBF = plist.get<double>(paramName);
547 boostFactor = static_cast<MT>(dblBF);
548 } else {
549 TEUCHOS_TEST_FOR_EXCEPTION(true, std::invalid_argument,
550 "Ifpack2::Chebyshev::setParameters: \"chebyshev: boost factor\""
551 "parameter must have type magnitude_type (MT) or double.");
552 }
553 } else { // parameter not in the list
554 // mfh 12 Aug 2016: To preserve current behavior (that fills in
555 // any parameters not in the input ParameterList with their
556 // default values), we call set() here. I don't actually like
557 // this behavior; I prefer the Belos model, where the input
558 // ParameterList is a delta from current behavior. However, I
559 // don't want to break things.
561 }
562 TEUCHOS_TEST_FOR_EXCEPTION(boostFactor < Teuchos::ScalarTraits<MT>::one(), std::invalid_argument,
563 "Ifpack2::Chebyshev::setParameters: \"" << paramName << "\" parameter "
564 "must be >= 1, but you supplied the value "
565 << boostFactor << ".");
566 }
567
568 // Same name in Ifpack2 and Ifpack.
569 minDiagVal = plist.get("chebyshev: min diagonal value", minDiagVal);
571 STS::isnaninf(minDiagVal), std::invalid_argument,
572 "Ifpack2::Chebyshev::setParameters: \"chebyshev: min diagonal value\" "
573 "parameter is NaN or Inf. This parameter is optional, but if you choose "
574 "to supply it, it must have a finite value.");
575
576 // Only fill in Ifpack2's name, not ML's or Ifpack's.
577 if (plist.isParameter("smoother: sweeps")) { // ML compatibility
578 numIters = plist.get<int>("smoother: sweeps");
579 } // Ifpack's name overrides ML's name.
580 if (plist.isParameter("relaxation: sweeps")) { // Ifpack compatibility
581 numIters = plist.get<int>("relaxation: sweeps");
582 } // Ifpack2's name overrides Ifpack's name.
583 numIters = plist.get("chebyshev: degree", numIters);
585 numIters < 0, std::invalid_argument,
586 "Ifpack2::Chebyshev::setParameters: \"chebyshev: degree\" parameter (also "
587 "called \"smoother: sweeps\" or \"relaxation: sweeps\") must be a "
588 "nonnegative integer. You gave a value of "
589 << numIters << ".");
590
591 // The last parameter name overrides the first.
592 if (plist.isParameter("eigen-analysis: iterations")) { // ML compatibility
593 eigMaxIters = plist.get<int>("eigen-analysis: iterations");
594 } // Ifpack2's name overrides ML's name.
595 eigMaxIters = plist.get("chebyshev: eigenvalue max iterations", eigMaxIters);
597 eigMaxIters < 0, std::invalid_argument,
598 "Ifpack2::Chebyshev::setParameters: \"chebyshev: eigenvalue max iterations"
599 "\" parameter (also called \"eigen-analysis: iterations\") must be a "
600 "nonnegative integer. You gave a value of "
601 << eigMaxIters << ".");
602
603 if (plist.isType<double>("chebyshev: eigenvalue relative tolerance"))
604 eigRelTolerance = Teuchos::as<MT>(plist.get<double>("chebyshev: eigenvalue relative tolerance"));
605 else if (plist.isType<MT>("chebyshev: eigenvalue relative tolerance"))
606 eigRelTolerance = plist.get<MT>("chebyshev: eigenvalue relative tolerance");
607 else if (plist.isType<ST>("chebyshev: eigenvalue relative tolerance"))
608 eigRelTolerance = Teuchos::ScalarTraits<ST>::magnitude(plist.get<ST>("chebyshev: eigenvalue relative tolerance"));
609
610 eigKeepVectors = plist.get("chebyshev: eigenvalue keep vectors", eigKeepVectors);
611
612 eigNormalizationFreq = plist.get("chebyshev: eigenvalue normalization frequency", eigNormalizationFreq);
614 eigNormalizationFreq < 0, std::invalid_argument,
615 "Ifpack2::Chebyshev::setParameters: \"chebyshev: eigenvalue normalization frequency"
616 "\" parameter must be a "
617 "nonnegative integer. You gave a value of "
618 << eigNormalizationFreq << ".")
619
620 zeroStartingSolution = plist.get("chebyshev: zero starting solution",
621 zeroStartingSolution);
622 assumeMatrixUnchanged = plist.get("chebyshev: assume matrix does not change",
624
625 // We don't want to fill these parameters in, because they shouldn't
626 // be visible to Ifpack2::Chebyshev users.
627 if (plist.isParameter("chebyshev: algorithm")) {
628 chebyshevAlgorithm = plist.get<std::string>("chebyshev: algorithm");
630 chebyshevAlgorithm != "first" &&
631 chebyshevAlgorithm != "textbook" &&
632 chebyshevAlgorithm != "fourth" &&
633 chebyshevAlgorithm != "opt_fourth",
634 std::invalid_argument,
635 "Ifpack2::Chebyshev: Ifpack2 only supports \"first\", \"textbook\", \"fourth\", and \"opt_fourth\", for \"chebyshev: algorithm\".");
636 }
637
638#ifdef IFPACK2_ENABLE_DEPRECATED_CODE
639 // to preserve behavior with previous input decks, only read "chebyshev:textbook algorithm" setting
640 // if a user has not specified "chebyshev: algorithm"
641 if (!plist.isParameter("chebyshev: algorithm")) {
642 if (plist.isParameter("chebyshev: textbook algorithm")) {
643 const bool textbookAlgorithm = plist.get<bool>("chebyshev: textbook algorithm");
644 if (textbookAlgorithm) {
645 chebyshevAlgorithm = "textbook";
646 } else {
647 chebyshevAlgorithm = "first";
648 }
649 }
650 }
651#endif
652
653 if (plist.isParameter("chebyshev: compute max residual norm")) {
654 computeMaxResNorm = plist.get<bool>("chebyshev: compute max residual norm");
655 }
656 if (plist.isParameter("chebyshev: compute spectral radius")) {
657 computeSpectralRadius = plist.get<bool>("chebyshev: compute spectral radius");
658 }
659
660 // Test for Ifpack parameters that we won't ever implement here.
661 // Be careful to use the one-argument version of get(), since the
662 // two-argment version adds the parameter if it's not there.
663 TEUCHOS_TEST_FOR_EXCEPTION(plist.isType<bool>("chebyshev: use block mode") &&
664 !plist.get<bool>("chebyshev: use block mode"),
665 std::invalid_argument,
666 "Ifpack2::Chebyshev requires that if you set \"chebyshev: use "
667 "block mode\" at all, you must set it to false. "
668 "Ifpack2::Chebyshev does not implement Ifpack's block mode.");
669 TEUCHOS_TEST_FOR_EXCEPTION(plist.isType<bool>("chebyshev: solve normal equations") &&
670 !plist.get<bool>("chebyshev: solve normal equations"),
671 std::invalid_argument,
672 "Ifpack2::Chebyshev does not and will never implement the Ifpack "
673 "parameter \"chebyshev: solve normal equations\". If you want to "
674 "solve the normal equations, construct a Tpetra::Operator that "
675 "implements A^* A, and use Chebyshev to solve A^* A x = A^* b.");
676
677 // Test for Ifpack parameters that we haven't implemented yet.
678 //
679 // For now, we only check that this ML parameter, if provided, has
680 // the one value that we support. We consider other values "invalid
681 // arguments" rather than "logic errors," because Ifpack does not
682 // implement eigenanalyses other than the power method.
683 std::string eigenAnalysisType("power-method");
684 if (plist.isParameter("eigen-analysis: type")) {
685 eigenAnalysisType = plist.get<std::string>("eigen-analysis: type");
687 eigenAnalysisType != "power-method" &&
688 eigenAnalysisType != "power method" &&
689 eigenAnalysisType != "cg",
690 std::invalid_argument,
691 "Ifpack2::Chebyshev: Ifpack2 only supports \"power method\" and \"cg\" for \"eigen-analysis: type\".");
692 }
693
694 // We've validated all the parameters, so it's safe now to "commit" them.
695 userInvDiag_ = userInvDiagCopy;
696 userLambdaMax_ = lambdaMax;
697 userLambdaMin_ = lambdaMin;
698 userEigRatio_ = eigRatio;
699 boostFactor_ = static_cast<MT>(boostFactor);
700 minDiagVal_ = minDiagVal;
701 numIters_ = numIters;
702 eigMaxIters_ = eigMaxIters;
703 eigRelTolerance_ = eigRelTolerance;
704 eigKeepVectors_ = eigKeepVectors;
705 eigNormalizationFreq_ = eigNormalizationFreq;
706 eigenAnalysisType_ = eigenAnalysisType;
707 zeroStartingSolution_ = zeroStartingSolution;
708 assumeMatrixUnchanged_ = assumeMatrixUnchanged;
709 chebyshevAlgorithm_ = chebyshevAlgorithm;
710 computeMaxResNorm_ = computeMaxResNorm;
711 computeSpectralRadius_ = computeSpectralRadius;
712 ckUseNativeSpMV_ = ckUseNativeSpMV;
713 preAllocateTempVector_ = preAllocateTempVector;
714 debug_ = debug;
715
716 if (debug_) {
717 // Only print if myRank == 0.
718 int myRank = -1;
719 if (A_.is_null() || A_->getComm().is_null()) {
720 // We don't have a communicator (yet), so we assume that
721 // everybody can print. Revise this expectation in setMatrix().
722 myRank = 0;
723 } else {
724 myRank = A_->getComm()->getRank();
725 }
726
727 if (myRank == 0) {
728 out_ = Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
729 } else {
730 using Teuchos::oblackholestream; // prints nothing
732 out_ = Teuchos::getFancyOStream(blackHole);
733 }
734 } else { // NOT debug
735 // free the "old" output stream, if there was one
736 out_ = Teuchos::null;
737 }
738}
739
740template <class ScalarType, class MV>
742 ck_ = Teuchos::null;
743 D_ = Teuchos::null;
744 diagOffsets_ = offsets_type();
745 savedDiagOffsets_ = false;
746 W_ = Teuchos::null;
747 computedLambdaMax_ = STS::nan();
748 computedLambdaMin_ = STS::nan();
749 eigVector_ = Teuchos::null;
750 eigVector2_ = Teuchos::null;
751}
752
753template <class ScalarType, class MV>
755 setMatrix(const Teuchos::RCP<const row_matrix_type>& A) {
756 if (A.getRawPtr() != A_.getRawPtr()) {
757 if (!assumeMatrixUnchanged_) {
758 reset();
759 }
760 A_ = A;
761 ck_ = Teuchos::null; // constructed on demand
762
763 // The communicator may have changed, or we may not have had a
764 // communicator before. Thus, we may have to reset the debug
765 // output stream.
766 if (debug_) {
767 // Only print if myRank == 0.
768 int myRank = -1;
769 if (A.is_null() || A->getComm().is_null()) {
770 // We don't have a communicator (yet), so we assume that
771 // everybody can print. Revise this expectation in setMatrix().
772 myRank = 0;
773 } else {
774 myRank = A->getComm()->getRank();
775 }
776
777 if (myRank == 0) {
778 out_ = Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
779 } else {
780 Teuchos::RCP<Teuchos::oblackholestream> blackHole(new Teuchos::oblackholestream());
781 out_ = Teuchos::getFancyOStream(blackHole); // print nothing on other processes
782 }
783 } else { // NOT debug
784 // free the "old" output stream, if there was one
785 out_ = Teuchos::null;
786 }
787 }
788}
789
790template <class ScalarType, class MV>
792 using std::endl;
793 // Some of the optimizations below only work if A_ is a
794 // Tpetra::CrsMatrix. We'll make our best guess about its type
795 // here, since we have no way to get back the original fifth
796 // template parameter.
797 typedef Tpetra::CrsMatrix<typename MV::scalar_type,
798 typename MV::local_ordinal_type,
799 typename MV::global_ordinal_type,
800 typename MV::node_type>
801 crs_matrix_type;
802
804 A_.is_null(), std::runtime_error,
805 "Ifpack2::Chebyshev::compute: The input "
806 "matrix A is null. Please call setMatrix() with a nonnull input matrix "
807 "before calling this method.");
808
809 // If A_ is a CrsMatrix and its graph is constant, we presume that
810 // the user plans to reuse the structure of A_, but possibly change
811 // A_'s values before each compute() call. This is the intended use
812 // case for caching the offsets of the diagonal entries of A_, to
813 // speed up extraction of diagonal entries on subsequent compute()
814 // calls.
815
816 // FIXME (mfh 22 Jan 2013, 10 Feb 2013) In all cases when we use
817 // isnaninf() in this method, we really only want to check if the
818 // number is NaN. Inf means something different. However,
819 // Teuchos::ScalarTraits doesn't distinguish the two cases.
820
821 // makeInverseDiagonal() returns a range Map Vector.
822 if (userInvDiag_.is_null()) {
823 Teuchos::RCP<const crs_matrix_type> A_crsMat =
824 Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_);
825 if (D_.is_null()) { // We haven't computed D_ before
826 if (!A_crsMat.is_null() && A_crsMat->isFillComplete()) {
827 // It's a CrsMatrix with a const graph; cache diagonal offsets.
828 const size_t lclNumRows = A_crsMat->getLocalNumRows();
829 if (diagOffsets_.extent(0) < lclNumRows) {
830 diagOffsets_ = offsets_type(); // clear first to save memory
831 diagOffsets_ = offsets_type("offsets", lclNumRows);
832 }
833 A_crsMat->getCrsGraph()->getLocalDiagOffsets(diagOffsets_);
834 savedDiagOffsets_ = true;
835 D_ = makeInverseDiagonal(*A_, true);
836 } else { // either A_ is not a CrsMatrix, or its graph is nonconst
837 D_ = makeInverseDiagonal(*A_);
838 }
839 } else if (!assumeMatrixUnchanged_) { // D_ exists but A_ may have changed
840 if (!A_crsMat.is_null() && A_crsMat->isFillComplete()) {
841 // It's a CrsMatrix with a const graph; cache diagonal offsets
842 // if we haven't already.
843 if (!savedDiagOffsets_) {
844 const size_t lclNumRows = A_crsMat->getLocalNumRows();
845 if (diagOffsets_.extent(0) < lclNumRows) {
846 diagOffsets_ = offsets_type(); // clear first to save memory
847 diagOffsets_ = offsets_type("offsets", lclNumRows);
848 }
849 A_crsMat->getCrsGraph()->getLocalDiagOffsets(diagOffsets_);
850 savedDiagOffsets_ = true;
851 }
852 // Now we're guaranteed to have cached diagonal offsets.
853 D_ = makeInverseDiagonal(*A_, true);
854 } else { // either A_ is not a CrsMatrix, or its graph is nonconst
855 D_ = makeInverseDiagonal(*A_);
856 }
857 }
858 } else { // the user provided an inverse diagonal
859 D_ = makeRangeMapVectorConst(userInvDiag_);
860 }
861
862 // Have we estimated eigenvalues before?
863 const bool computedEigenvalueEstimates =
864 STS::isnaninf(computedLambdaMax_) || STS::isnaninf(computedLambdaMin_);
865
866 // Only recompute the eigenvalue estimates if
867 // - we are supposed to assume that the matrix may have changed, or
868 // - they haven't been computed before, and the user hasn't given
869 // us at least an estimate of the max eigenvalue.
870 //
871 // We at least need an estimate of the max eigenvalue. This is the
872 // most important one if using Chebyshev as a smoother.
873
874 if (!assumeMatrixUnchanged_ ||
875 (!computedEigenvalueEstimates && STS::isnaninf(userLambdaMax_))) {
877 if ((eigenAnalysisType_ == "power method") || (eigenAnalysisType_ == "power-method")) {
878 Teuchos::RCP<V> x;
879 if (eigVector_.is_null()) {
880 x = Teuchos::rcp(new V(A_->getDomainMap()));
881 if (eigKeepVectors_)
882 eigVector_ = x;
883 PowerMethod::computeInitialGuessForPowerMethod(*x, false);
884 } else
885 x = eigVector_;
886
887 Teuchos::RCP<V> y;
888 if (eigVector2_.is_null()) {
889 y = rcp(new V(A_->getRangeMap()));
890 if (eigKeepVectors_)
891 eigVector2_ = y;
892 } else
893 y = eigVector2_;
894
895 Teuchos::RCP<Teuchos::FancyOStream> stream = (debug_ ? out_ : Teuchos::null);
896 computedLambdaMax = PowerMethod::powerMethodWithInitGuess(*A_, *D_, eigMaxIters_, x, y,
897 eigRelTolerance_, eigNormalizationFreq_, stream,
898 computeSpectralRadius_);
899 } else {
900 computedLambdaMax = cgMethod(*A_, *D_, eigMaxIters_);
901 }
903 STS::isnaninf(computedLambdaMax),
904 std::runtime_error,
905 "Ifpack2::Chebyshev::compute: Estimation of the max eigenvalue "
906 "of D^{-1} A failed, by producing Inf or NaN. This probably means that "
907 "the matrix contains Inf or NaN values, or that it is badly scaled.");
909 STS::isnaninf(userEigRatio_),
910 std::logic_error,
911 "Ifpack2::Chebyshev::compute: userEigRatio_ is Inf or NaN."
912 << endl
913 << "This should be impossible." << endl
914 << "Please report this bug to the Ifpack2 developers.");
915
916 // The power method doesn't estimate the min eigenvalue, so we
917 // do our best to provide an estimate. userEigRatio_ has a
918 // reasonable default value, and if the user provided it, we
919 // have already checked that its value is finite and >= 1.
920 const ST computedLambdaMin = computedLambdaMax / userEigRatio_;
921
922 // Defer "committing" results until all computations succeeded.
923 computedLambdaMax_ = computedLambdaMax;
924 computedLambdaMin_ = computedLambdaMin;
925 } else {
927 STS::isnaninf(userLambdaMax_) && STS::isnaninf(computedLambdaMax_),
928 std::logic_error,
929 "Ifpack2::Chebyshev::compute: " << endl
930 << "Both userLambdaMax_ and computedLambdaMax_ are Inf or NaN."
931 << endl
932 << "This should be impossible." << endl
933 << "Please report this bug to the Ifpack2 developers.");
934 }
935
937 // Figure out the eigenvalue estimates that apply() will use.
939
940 // Always favor the user's max eigenvalue estimate, if provided.
941 lambdaMaxForApply_ = STS::isnaninf(userLambdaMax_) ? computedLambdaMax_ : userLambdaMax_;
942
943 // mfh 11 Feb 2013: For now, we imitate Ifpack by ignoring the
944 // user's min eigenvalue estimate, and using the given eigenvalue
945 // ratio to estimate the min eigenvalue. We could instead do this:
946 // favor the user's eigenvalue ratio estimate, but if it's not
947 // provided, use lambdaMax / lambdaMin. However, we want Chebyshev
948 // to have sensible smoother behavior if the user did not provide
949 // eigenvalue estimates. Ifpack's behavior attempts to push down
950 // the error terms associated with the largest eigenvalues, while
951 // expecting that users will only want a small number of iterations,
952 // so that error terms associated with the smallest eigenvalues
953 // won't grow too much. This is sensible behavior for a smoother.
954 lambdaMinForApply_ = lambdaMaxForApply_ / userEigRatio_;
955 eigRatioForApply_ = userEigRatio_;
956
957 if (chebyshevAlgorithm_ == "first") {
958 // Ifpack has a special-case modification of the eigenvalue bounds
959 // for the case where the max eigenvalue estimate is close to one.
960 const ST one = Teuchos::as<ST>(1);
961 // FIXME (mfh 20 Nov 2013) Should scale this 1.0e-6 term
962 // appropriately for MT's machine precision.
963 if (STS::magnitude(lambdaMaxForApply_ - one) < Teuchos::as<MT>(1.0e-6)) {
964 lambdaMinForApply_ = one;
965 lambdaMaxForApply_ = lambdaMinForApply_;
966 eigRatioForApply_ = one; // Ifpack doesn't include this line.
967 }
968 }
969
970 // Allocate temporary vector
971 if (preAllocateTempVector_ && !D_.is_null()) {
972 makeTempMultiVector(*D_);
973 if (chebyshevAlgorithm_ == "fourth" || chebyshevAlgorithm_ == "opt_fourth") {
974 makeSecondTempMultiVector(*D_);
975 }
976 }
977
978 if (chebyshevAlgorithm_ == "textbook") {
979 // no-op
980 } else {
981 if (ck_.is_null()) {
982 ck_ = Teuchos::rcp(new ChebyshevKernel<op_type>(A_, ckUseNativeSpMV_));
983 }
984 if (ckUseNativeSpMV_) {
985 ck_->setAuxiliaryVectors(1);
986 }
987 }
988}
989
990template <class ScalarType, class MV>
993 getLambdaMaxForApply() const {
994 return lambdaMaxForApply_;
995}
996
997template <class ScalarType, class MV>
1000 const char prefix[] = "Ifpack2::Chebyshev::apply: ";
1001
1002 if (debug_) {
1003 *out_ << "apply: " << std::endl;
1004 }
1005 TEUCHOS_TEST_FOR_EXCEPTION(A_.is_null(), std::runtime_error, prefix << "The input matrix A is null. "
1006 " Please call setMatrix() with a nonnull input matrix, and then call "
1007 "compute(), before calling this method.");
1008 TEUCHOS_TEST_FOR_EXCEPTION(STS::isnaninf(lambdaMaxForApply_), std::runtime_error,
1009 prefix << "There is no estimate for the max eigenvalue."
1010 << std::endl
1012 TEUCHOS_TEST_FOR_EXCEPTION(STS::isnaninf(lambdaMinForApply_), std::runtime_error,
1013 prefix << "There is no estimate for the min eigenvalue."
1014 << std::endl
1016 TEUCHOS_TEST_FOR_EXCEPTION(STS::isnaninf(eigRatioForApply_), std::runtime_error,
1017 prefix << "There is no estimate for the ratio of the max "
1018 "eigenvalue to the min eigenvalue."
1019 << std::endl
1021 TEUCHOS_TEST_FOR_EXCEPTION(D_.is_null(), std::runtime_error, prefix << "The vector of inverse "
1022 "diagonal entries of the matrix has not yet been computed."
1023 << std::endl
1025
1026 if (chebyshevAlgorithm_ == "fourth" || chebyshevAlgorithm_ == "opt_fourth") {
1027 fourthKindApplyImpl(*A_, B, X, numIters_, lambdaMaxForApply_, *D_);
1028 } else if (chebyshevAlgorithm_ == "textbook") {
1029 textbookApplyImpl(*A_, B, X, numIters_, lambdaMaxForApply_,
1030 lambdaMinForApply_, eigRatioForApply_, *D_);
1031 } else {
1032 ifpackApplyImpl(*A_, B, X, numIters_, lambdaMaxForApply_,
1033 lambdaMinForApply_, eigRatioForApply_, *D_);
1034 }
1035
1036 if (computeMaxResNorm_ && B.getNumVectors() > 0) {
1037 MV R(B.getMap(), B.getNumVectors());
1038 computeResidual(R, B, *A_, X);
1039 Teuchos::Array<MT> norms(B.getNumVectors());
1040 R.norm2(norms());
1041 return *std::max_element(norms.begin(), norms.end());
1042 } else {
1043 return Teuchos::ScalarTraits<MT>::zero();
1044 }
1045}
1046
1047template <class ScalarType, class MV>
1049 print(std::ostream& out) {
1050 using Teuchos::rcpFromRef;
1051 this->describe(*(Teuchos::getFancyOStream(rcpFromRef(out))),
1052 Teuchos::VERB_MEDIUM);
1053}
1054
1055template <class ScalarType, class MV>
1058 const ScalarType& alpha,
1059 const V& D_inv,
1060 const MV& B,
1061 MV& X) {
1062 solve(W, alpha, D_inv, B); // W = alpha*D_inv*B
1063 Tpetra::deep_copy(X, W); // X = 0 + W
1064}
1065
1066template <class ScalarType, class MV>
1068 computeResidual(MV& R, const MV& B, const op_type& A, const MV& X,
1069 const Teuchos::ETransp mode) {
1070 Tpetra::Details::residual(A, X, B, R);
1071}
1072
1073template <class ScalarType, class MV>
1074void Chebyshev<ScalarType, MV>::
1075 solve(MV& Z, const V& D_inv, const MV& R) {
1076 Z.elementWiseMultiply(STS::one(), D_inv, R, STS::zero());
1077}
1078
1079template <class ScalarType, class MV>
1080void Chebyshev<ScalarType, MV>::
1081 solve(MV& Z, const ST alpha, const V& D_inv, const MV& R) {
1082 Z.elementWiseMultiply(alpha, D_inv, R, STS::zero());
1083}
1084
1085template <class ScalarType, class MV>
1086Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::V>
1087Chebyshev<ScalarType, MV>::
1088 makeInverseDiagonal(const row_matrix_type& A, const bool useDiagOffsets) const {
1089 using Teuchos::RCP;
1090 using Teuchos::rcp_dynamic_cast;
1091 using Teuchos::rcpFromRef;
1092
1093 RCP<V> D_rowMap;
1094 if (!D_.is_null() &&
1095 D_->getMap()->isSameAs(*(A.getRowMap()))) {
1096 if (debug_)
1097 *out_ << "Reusing pre-existing vector for diagonal extraction" << std::endl;
1098 D_rowMap = Teuchos::rcp_const_cast<V>(D_);
1099 } else {
1100 D_rowMap = Teuchos::rcp(new V(A.getRowMap(), /*zeroOut=*/false));
1101 if (debug_)
1102 *out_ << "Allocated new vector for diagonal extraction" << std::endl;
1103 }
1104 if (useDiagOffsets) {
1105 // The optimizations below only work if A_ is a Tpetra::CrsMatrix.
1106 // We'll make our best guess about its type here, since we have no
1107 // way to get back the original fifth template parameter.
1108 typedef Tpetra::CrsMatrix<typename MV::scalar_type,
1109 typename MV::local_ordinal_type,
1110 typename MV::global_ordinal_type,
1111 typename MV::node_type>
1112 crs_matrix_type;
1113 RCP<const crs_matrix_type> A_crsMat =
1114 rcp_dynamic_cast<const crs_matrix_type>(rcpFromRef(A));
1115 if (!A_crsMat.is_null()) {
1116 TEUCHOS_TEST_FOR_EXCEPTION(
1117 !savedDiagOffsets_, std::logic_error,
1118 "Ifpack2::Details::Chebyshev::makeInverseDiagonal: "
1119 "It is not allowed to call this method with useDiagOffsets=true, "
1120 "if you have not previously saved offsets of diagonal entries. "
1121 "This situation should never arise if this class is used properly. "
1122 "Please report this bug to the Ifpack2 developers.");
1123 A_crsMat->getLocalDiagCopy(*D_rowMap, diagOffsets_);
1124 }
1125 } else {
1126 // This always works for a Tpetra::RowMatrix, even if it is not a
1127 // Tpetra::CrsMatrix. We just don't have offsets in this case.
1128 A.getLocalDiagCopy(*D_rowMap);
1129 }
1130 RCP<V> D_rangeMap = makeRangeMapVector(D_rowMap);
1131
1132 if (debug_) {
1133 // In debug mode, make sure that all diagonal entries are
1134 // positive, on all processes. Note that *out_ only prints on
1135 // Process 0 of the matrix's communicator.
1136 bool foundNonpositiveValue = false;
1137 {
1138 auto D_lcl = D_rangeMap->getLocalViewHost(Tpetra::Access::ReadOnly);
1139 auto D_lcl_1d = Kokkos::subview(D_lcl, Kokkos::ALL(), 0);
1140
1141 typedef typename MV::impl_scalar_type IST;
1142 typedef typename MV::local_ordinal_type LO;
1143#if KOKKOS_VERSION >= 40799
1144 typedef KokkosKernels::ArithTraits<IST> ATS;
1145#else
1146 typedef Kokkos::ArithTraits<IST> ATS;
1147#endif
1148#if KOKKOS_VERSION >= 40799
1149 typedef KokkosKernels::ArithTraits<typename ATS::mag_type> STM;
1150#else
1151 typedef Kokkos::ArithTraits<typename ATS::mag_type> STM;
1152#endif
1153
1154 const LO lclNumRows = static_cast<LO>(D_rangeMap->getLocalLength());
1155 for (LO i = 0; i < lclNumRows; ++i) {
1156 if (STS::real(D_lcl_1d(i)) <= STM::zero()) {
1157 foundNonpositiveValue = true;
1158 break;
1159 }
1160 }
1161 }
1162
1163 using Teuchos::outArg;
1164 using Teuchos::REDUCE_MIN;
1165 using Teuchos::reduceAll;
1166
1167 const int lclSuccess = foundNonpositiveValue ? 0 : 1;
1168 int gblSuccess = lclSuccess; // to be overwritten
1169 if (!D_rangeMap->getMap().is_null() && D_rangeMap->getMap()->getComm().is_null()) {
1170 const Teuchos::Comm<int>& comm = *(D_rangeMap->getMap()->getComm());
1171 reduceAll<int, int>(comm, REDUCE_MIN, lclSuccess, outArg(gblSuccess));
1172 }
1173 if (gblSuccess == 1) {
1174 *out_ << "makeInverseDiagonal: The matrix's diagonal entries all have "
1175 "positive real part (this is good for Chebyshev)."
1176 << std::endl;
1177 } else {
1178 *out_ << "makeInverseDiagonal: The matrix's diagonal has at least one "
1179 "entry with nonpositive real part, on at least one process in the "
1180 "matrix's communicator. This is bad for Chebyshev."
1181 << std::endl;
1182 }
1183 }
1184
1185 // Invert the diagonal entries, replacing entries less (in
1186 // magnitude) than the user-specified value with that value.
1187 reciprocal_threshold(*D_rangeMap, minDiagVal_);
1188 return Teuchos::rcp_const_cast<const V>(D_rangeMap);
1189}
1190
1191template <class ScalarType, class MV>
1192Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::V>
1193Chebyshev<ScalarType, MV>::
1194 makeRangeMapVectorConst(const Teuchos::RCP<const V>& D) const {
1195 using Teuchos::RCP;
1196 using Teuchos::rcp;
1197 typedef Tpetra::Export<typename MV::local_ordinal_type,
1198 typename MV::global_ordinal_type,
1199 typename MV::node_type>
1200 export_type;
1201 // This throws logic_error instead of runtime_error, because the
1202 // methods that call makeRangeMapVector should all have checked
1203 // whether A_ is null before calling this method.
1204 TEUCHOS_TEST_FOR_EXCEPTION(
1205 A_.is_null(), std::logic_error,
1206 "Ifpack2::Details::Chebyshev::"
1207 "makeRangeMapVector: The input matrix A is null. Please call setMatrix() "
1208 "with a nonnull input matrix before calling this method. This is probably "
1209 "a bug in Ifpack2; please report this bug to the Ifpack2 developers.");
1210 TEUCHOS_TEST_FOR_EXCEPTION(
1211 D.is_null(), std::logic_error,
1212 "Ifpack2::Details::Chebyshev::"
1213 "makeRangeMapVector: The input Vector D is null. This is probably "
1214 "a bug in Ifpack2; please report this bug to the Ifpack2 developers.");
1215
1216 RCP<const map_type> sourceMap = D->getMap();
1217 RCP<const map_type> rangeMap = A_->getRangeMap();
1218 RCP<const map_type> rowMap = A_->getRowMap();
1219
1220 if (rangeMap->isSameAs(*sourceMap)) {
1221 // The given vector's Map is the same as the matrix's range Map.
1222 // That means we don't need to Export. This is the fast path.
1223 return D;
1224 } else { // We need to Export.
1225 RCP<const export_type> exporter;
1226 // Making an Export object from scratch is expensive enough that
1227 // it's worth the O(1) global reductions to call isSameAs(), to
1228 // see if we can avoid that cost.
1229 if (sourceMap->isSameAs(*rowMap)) {
1230 // We can reuse the matrix's Export object, if there is one.
1231 exporter = A_->getGraph()->getExporter();
1232 } else { // We have to make a new Export object.
1233 exporter = rcp(new export_type(sourceMap, rangeMap));
1234 }
1235
1236 if (exporter.is_null()) {
1237 return D; // Row Map and range Map are the same; no need to Export.
1238 } else { // Row Map and range Map are _not_ the same; must Export.
1239 RCP<V> D_out = rcp(new V(*D, Teuchos::Copy));
1240 D_out->doExport(*D, *exporter, Tpetra::ADD);
1241 return Teuchos::rcp_const_cast<const V>(D_out);
1242 }
1243 }
1244}
1245
1246template <class ScalarType, class MV>
1247Teuchos::RCP<typename Chebyshev<ScalarType, MV>::V>
1248Chebyshev<ScalarType, MV>::
1249 makeRangeMapVector(const Teuchos::RCP<V>& D) const {
1250 using Teuchos::rcp_const_cast;
1251 return rcp_const_cast<V>(makeRangeMapVectorConst(rcp_const_cast<V>(D)));
1252}
1253
1254template <class ScalarType, class MV>
1255void Chebyshev<ScalarType, MV>::
1256 textbookApplyImpl(const op_type& A,
1257 const MV& B,
1258 MV& X,
1259 const int numIters,
1260 const ST lambdaMax,
1261 const ST lambdaMin,
1262 const ST eigRatio,
1263 const V& D_inv) const {
1264 (void)lambdaMin; // Forestall compiler warning.
1265 const ST myLambdaMin = lambdaMax / eigRatio;
1266
1267 const ST zero = Teuchos::as<ST>(0);
1268 const ST one = Teuchos::as<ST>(1);
1269 const ST two = Teuchos::as<ST>(2);
1270 const ST d = (lambdaMax + myLambdaMin) / two; // Ifpack2 calls this theta
1271 const ST c = (lambdaMax - myLambdaMin) / two; // Ifpack2 calls this 1/delta
1272
1273 if (zeroStartingSolution_ && numIters > 0) {
1274 // If zero iterations, then input X is output X.
1275 X.putScalar(zero);
1276 }
1277 MV R(B.getMap(), B.getNumVectors(), false);
1278 MV P(B.getMap(), B.getNumVectors(), false);
1279 MV Z(B.getMap(), B.getNumVectors(), false);
1280 ST alpha, beta;
1281 for (int i = 0; i < numIters; ++i) {
1282 computeResidual(R, B, A, X); // R = B - A*X
1283 solve(Z, D_inv, R); // z = D_inv * R, that is, D \ R.
1284 if (i == 0) {
1285 P = Z;
1286 alpha = two / d;
1287 } else {
1288 // beta = (c * alpha / two)^2;
1289 // const ST sqrtBeta = c * alpha / two;
1290 // beta = sqrtBeta * sqrtBeta;
1291 beta = alpha * (c / two) * (c / two);
1292 alpha = one / (d - beta);
1293 P.update(one, Z, beta); // P = Z + beta*P
1294 }
1295 X.update(alpha, P, one); // X = X + alpha*P
1296 // If we compute the residual here, we could either do R = B -
1297 // A*X, or R = R - alpha*A*P. Since we choose the former, we
1298 // can move the computeResidual call to the top of the loop.
1299 }
1300}
1301
1302template <class ScalarType, class MV>
1303void Chebyshev<ScalarType, MV>::
1304 fourthKindApplyImpl(const op_type& A,
1305 const MV& B,
1306 MV& X,
1307 const int numIters,
1308 const ST lambdaMax,
1309 const V& D_inv) {
1310 // standard 4th kind Chebyshev smoother has \beta_i := 1
1311 std::vector<ScalarType> betas(numIters, 1.0);
1312 if (chebyshevAlgorithm_ == "opt_fourth") {
1313 betas = optimalWeightsImpl<ScalarType>(numIters);
1314 }
1315
1316 const ST invEig = MT(1) / (lambdaMax * boostFactor_);
1317
1318 // Fetch cached temporary (multi)vector.
1319 Teuchos::RCP<MV> Z_ptr = makeTempMultiVector(B);
1320 MV& Z = *Z_ptr;
1321
1322 // Store 4th-kind result (needed as temporary for bootstrapping opt. 4th-kind Chebyshev)
1323 // Fetch the second cached temporary (multi)vector.
1324 Teuchos::RCP<MV> X4_ptr = makeSecondTempMultiVector(B);
1325 MV& X4 = *X4_ptr;
1326
1327 // Special case for the first iteration.
1328 if (!zeroStartingSolution_) {
1329 // X4 = X
1330 Tpetra::deep_copy(X4, X);
1331
1332 if (ck_.is_null()) {
1333 Teuchos::RCP<const op_type> A_op = A_;
1334 ck_ = Teuchos::rcp(new ChebyshevKernel<op_type>(A_op, ckUseNativeSpMV_));
1335 }
1336 // Z := (4/3 * invEig)*D_inv*(B-A*X4)
1337 // X4 := X4 + Z
1338 ck_->compute(Z, MT(4.0 / 3.0) * invEig, const_cast<V&>(D_inv),
1339 const_cast<MV&>(B), X4, STS::zero());
1340
1341 // X := X + beta[0] * Z
1342 X.update(betas[0], Z, STS::one());
1343 } else {
1344 // Z := (4/3 * invEig)*D_inv*B and X := 0 + Z.
1345 firstIterationWithZeroStartingSolution(Z, MT(4.0 / 3.0) * invEig, D_inv, B, X4);
1346
1347 // X := 0 + beta * Z
1348 X.update(betas[0], Z, STS::zero());
1349 }
1350
1351 if (numIters > 1 && ck_.is_null()) {
1352 Teuchos::RCP<const op_type> A_op = A_;
1353 ck_ = Teuchos::rcp(new ChebyshevKernel<op_type>(A_op, ckUseNativeSpMV_));
1354 }
1355
1356 for (int i = 1; i < numIters; ++i) {
1357 const ST zScale = (2.0 * i - 1.0) / (2.0 * i + 3.0);
1358 const ST rScale = MT((8.0 * i + 4.0) / (2.0 * i + 3.0)) * invEig;
1359
1360 // Z := rScale*D_inv*(B - A*X4) + zScale*Z.
1361 // X4 := X4 + Z
1362 ck_->compute(Z, rScale, const_cast<V&>(D_inv),
1363 const_cast<MV&>(B), (X4), zScale);
1364
1365 // X := X + beta[i] * Z
1366 X.update(betas[i], Z, STS::one());
1367 }
1368}
1369
1370template <class ScalarType, class MV>
1372Chebyshev<ScalarType, MV>::maxNormInf(const MV& X) {
1373 Teuchos::Array<MT> norms(X.getNumVectors());
1374 X.normInf(norms());
1375 return *std::max_element(norms.begin(), norms.end());
1376}
1377
1378template <class ScalarType, class MV>
1379void Chebyshev<ScalarType, MV>::
1380 ifpackApplyImpl(const op_type& A,
1381 const MV& B,
1382 MV& X,
1383 const int numIters,
1384 const ST lambdaMax,
1385 const ST lambdaMin,
1386 const ST eigRatio,
1387 const V& D_inv) {
1388 using std::endl;
1389#ifdef HAVE_IFPACK2_DEBUG
1390 const bool debug = debug_;
1391#else
1392 const bool debug = false;
1393#endif
1394
1395 if (debug) {
1396 *out_ << " \\|B\\|_{\\infty} = " << maxNormInf(B) << endl;
1397 *out_ << " \\|X\\|_{\\infty} = " << maxNormInf(X) << endl;
1398 }
1399
1400 if (numIters <= 0) {
1401 return;
1402 }
1403 const ST zero = static_cast<ST>(0.0);
1404 const ST one = static_cast<ST>(1.0);
1405 const ST two = static_cast<ST>(2.0);
1406
1407 // Quick solve when the matrix A is the identity.
1408 if (lambdaMin == one && lambdaMax == lambdaMin) {
1409 solve(X, D_inv, B);
1410 return;
1411 }
1412
1413 // Initialize coefficients
1414 const ST alpha = lambdaMax / eigRatio;
1415 const ST beta = boostFactor_ * lambdaMax;
1416 const ST delta = two / (beta - alpha);
1417 const ST theta = (beta + alpha) / two;
1418 const ST s1 = theta * delta;
1419
1420 if (debug) {
1421 *out_ << " alpha = " << alpha << endl
1422 << " beta = " << beta << endl
1423 << " delta = " << delta << endl
1424 << " theta = " << theta << endl
1425 << " s1 = " << s1 << endl;
1426 }
1427
1428 // Fetch cached temporary (multi)vector.
1429 Teuchos::RCP<MV> W_ptr = makeTempMultiVector(B);
1430 MV& W = *W_ptr;
1431
1432 if (debug) {
1433 *out_ << " Iteration " << 1 << ":" << endl
1434 << " - \\|D\\|_{\\infty} = " << D_->normInf() << endl;
1435 }
1436
1437 // Special case for the first iteration.
1438 if (!zeroStartingSolution_) {
1439 // mfh 22 May 2019: Tests don't actually exercise this path.
1440
1441 if (ck_.is_null()) {
1442 Teuchos::RCP<const op_type> A_op = A_;
1443 ck_ = Teuchos::rcp(new ChebyshevKernel<op_type>(A_op, ckUseNativeSpMV_));
1444 }
1445 // W := (1/theta)*D_inv*(B-A*X) and X := X + W.
1446 // X := X + W
1447 ck_->compute(W, one / theta, const_cast<V&>(D_inv),
1448 const_cast<MV&>(B), X, zero);
1449 } else {
1450 // W := (1/theta)*D_inv*B and X := 0 + W.
1451 firstIterationWithZeroStartingSolution(W, one / theta, D_inv, B, X);
1452 }
1453
1454 if (debug) {
1455 *out_ << " - \\|W\\|_{\\infty} = " << maxNormInf(W) << endl
1456 << " - \\|X\\|_{\\infty} = " << maxNormInf(X) << endl;
1457 }
1458
1459 if (numIters > 1 && ck_.is_null()) {
1460 Teuchos::RCP<const op_type> A_op = A_;
1461 ck_ = Teuchos::rcp(new ChebyshevKernel<op_type>(A_op, ckUseNativeSpMV_));
1462 }
1463
1464 // The rest of the iterations.
1465 ST rhok = one / s1;
1466 ST rhokp1, dtemp1, dtemp2;
1467 for (int deg = 1; deg < numIters; ++deg) {
1468 if (debug) {
1469 *out_ << " Iteration " << deg + 1 << ":" << endl
1470 << " - \\|D\\|_{\\infty} = " << D_->normInf() << endl
1471 << " - \\|B\\|_{\\infty} = " << maxNormInf(B) << endl
1472 << " - \\|A\\|_{\\text{frob}} = " << A_->getFrobeniusNorm()
1473 << endl
1474 << " - rhok = " << rhok << endl;
1475 }
1476
1477 rhokp1 = one / (two * s1 - rhok);
1478 dtemp1 = rhokp1 * rhok;
1479 dtemp2 = two * rhokp1 * delta;
1480 rhok = rhokp1;
1481
1482 if (debug) {
1483 *out_ << " - dtemp1 = " << dtemp1 << endl
1484 << " - dtemp2 = " << dtemp2 << endl;
1485 }
1486
1487 // W := dtemp2*D_inv*(B - A*X) + dtemp1*W.
1488 // X := X + W
1489 ck_->compute(W, dtemp2, const_cast<V&>(D_inv),
1490 const_cast<MV&>(B), (X), dtemp1);
1491
1492 if (debug) {
1493 *out_ << " - \\|W\\|_{\\infty} = " << maxNormInf(W) << endl
1494 << " - \\|X\\|_{\\infty} = " << maxNormInf(X) << endl;
1495 }
1496 }
1497}
1498
1499template <class ScalarType, class MV>
1501Chebyshev<ScalarType, MV>::
1502 cgMethodWithInitGuess(const op_type& A,
1503 const V& D_inv,
1504 const int numIters,
1505 V& r) {
1506 using std::endl;
1507 using MagnitudeType = typename STS::magnitudeType;
1508 if (debug_) {
1509 *out_ << " cgMethodWithInitGuess:" << endl;
1510 }
1511
1512 const ST one = STS::one();
1513 ST beta, betaOld = one, pAp, pApOld = one, alpha, rHz, rHzOld, rHzOld2 = one, lambdaMax;
1514 // ST lambdaMin;
1515 Teuchos::ArrayRCP<MagnitudeType> diag, offdiag;
1516 Teuchos::RCP<V> p, z, Ap;
1517 diag.resize(numIters);
1518 offdiag.resize(numIters - 1);
1519
1520 p = rcp(new V(A.getRangeMap()));
1521 z = rcp(new V(A.getRangeMap()));
1522 Ap = rcp(new V(A.getRangeMap()));
1523
1524 // Tpetra::Details::residual (A, x, *b, *r);
1525 solve(*p, D_inv, r);
1526 rHz = r.dot(*p);
1527
1528 for (int iter = 0; iter < numIters; ++iter) {
1529 if (debug_) {
1530 *out_ << " Iteration " << iter << endl;
1531 }
1532 A.apply(*p, *Ap);
1533 pAp = p->dot(*Ap);
1534 alpha = rHz / pAp;
1535 r.update(-alpha, *Ap, one);
1536 rHzOld = rHz;
1537 solve(*z, D_inv, r);
1538 rHz = r.dot(*z);
1539 beta = rHz / rHzOld;
1540 p->update(one, *z, beta);
1541 if (iter > 0) {
1542 diag[iter] = STS::real((betaOld * betaOld * pApOld + pAp) / rHzOld);
1543 offdiag[iter - 1] = -STS::real((betaOld * pApOld / (sqrt(rHzOld * rHzOld2))));
1544 if (debug_) {
1545 *out_ << " diag[" << iter << "] = " << diag[iter] << endl;
1546 *out_ << " offdiag[" << iter - 1 << "] = " << offdiag[iter - 1] << endl;
1547 *out_ << " rHz = " << rHz << endl;
1548 *out_ << " alpha = " << alpha << endl;
1549 *out_ << " beta = " << beta << endl;
1550 }
1551 } else {
1552 diag[iter] = STS::real(pAp / rHzOld);
1553 if (debug_) {
1554 *out_ << " diag[" << iter << "] = " << diag[iter] << endl;
1555 *out_ << " rHz = " << rHz << endl;
1556 *out_ << " alpha = " << alpha << endl;
1557 *out_ << " beta = " << beta << endl;
1558 }
1559 }
1560 rHzOld2 = rHzOld;
1561 betaOld = beta;
1562 pApOld = pAp;
1563 }
1564
1565 lambdaMax = LapackHelper<ST>::tri_diag_spectral_radius(diag, offdiag);
1566
1567 return lambdaMax;
1568}
1569
1570template <class ScalarType, class MV>
1572Chebyshev<ScalarType, MV>::
1573 cgMethod(const op_type& A, const V& D_inv, const int numIters) {
1574 using std::endl;
1575
1576 if (debug_) {
1577 *out_ << "cgMethod:" << endl;
1578 }
1579
1580 Teuchos::RCP<V> r;
1581 if (eigVector_.is_null()) {
1582 r = rcp(new V(A.getDomainMap()));
1583 if (eigKeepVectors_)
1584 eigVector_ = r;
1585 // For CG, we need to get the BCs right and we'll use D_inv to get that
1586 Details::computeInitialGuessForCG(D_inv, *r);
1587 } else
1588 r = eigVector_;
1589
1590 ST lambdaMax = cgMethodWithInitGuess(A, D_inv, numIters, *r);
1591
1592 return lambdaMax;
1593}
1594
1595template <class ScalarType, class MV>
1596Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::row_matrix_type>
1598 return A_;
1599}
1600
1601template <class ScalarType, class MV>
1603 hasTransposeApply() const {
1604 // Technically, this is true, because the matrix must be symmetric.
1605 return true;
1606}
1607
1608template <class ScalarType, class MV>
1609Teuchos::RCP<MV>
1611 makeTempMultiVector(const MV& B) {
1612 // ETP 02/08/17: We must check not only if the temporary vectors are
1613 // null, but also if the number of columns match, since some multi-RHS
1614 // solvers (e.g., Belos) may call apply() with different numbers of columns.
1615
1616 const size_t B_numVecs = B.getNumVectors();
1617 if (W_.is_null() || W_->getNumVectors() != B_numVecs) {
1618 W_ = Teuchos::rcp(new MV(B.getMap(), B_numVecs, false));
1619 }
1620 return W_;
1621}
1622
1623template <class ScalarType, class MV>
1624Teuchos::RCP<MV>
1625Chebyshev<ScalarType, MV>::
1626 makeSecondTempMultiVector(const MV& B) {
1627 // ETP 02/08/17: We must check not only if the temporary vectors are
1628 // null, but also if the number of columns match, since some multi-RHS
1629 // solvers (e.g., Belos) may call apply() with different numbers of columns.
1630
1631 const size_t B_numVecs = B.getNumVectors();
1632 if (W2_.is_null() || W2_->getNumVectors() != B_numVecs) {
1633 W2_ = Teuchos::rcp(new MV(B.getMap(), B_numVecs, false));
1634 }
1635 return W2_;
1636}
1637
1638template <class ScalarType, class MV>
1639std::string
1641 description() const {
1642 std::ostringstream oss;
1643 // YAML requires quoting the key in this case, to distinguish
1644 // key's colons from the colon that separates key from value.
1645 oss << "\"Ifpack2::Details::Chebyshev\":"
1646 << "{"
1647 << "degree: " << numIters_
1648 << ", lambdaMax: " << lambdaMaxForApply_
1649 << ", alpha: " << eigRatioForApply_
1650 << ", lambdaMin: " << lambdaMinForApply_
1651 << ", boost factor: " << boostFactor_
1652 << ", algorithm: " << chebyshevAlgorithm_;
1653 if (!userInvDiag_.is_null())
1654 oss << ", diagonal: user-supplied";
1655 oss << "}";
1656 return oss.str();
1657}
1658
1659template <class ScalarType, class MV>
1661 describe(Teuchos::FancyOStream& out,
1662 const Teuchos::EVerbosityLevel verbLevel) const {
1663 using std::endl;
1664 using Teuchos::TypeNameTraits;
1665
1666 const Teuchos::EVerbosityLevel vl =
1667 (verbLevel == Teuchos::VERB_DEFAULT) ? Teuchos::VERB_LOW : verbLevel;
1668 if (vl == Teuchos::VERB_NONE) {
1669 return; // print NOTHING
1670 }
1671
1672 // By convention, describe() starts with a tab.
1673 //
1674 // This does affect all processes on which it's valid to print to
1675 // 'out'. However, it does not actually print spaces to 'out'
1676 // unless operator<< gets called, so it's safe to use on all
1677 // processes.
1678 Teuchos::OSTab tab0(out);
1679
1680 // We only print on Process 0 of the matrix's communicator. If
1681 // the matrix isn't set, we don't have a communicator, so we have
1682 // to assume that every process can print.
1683 int myRank = -1;
1684 if (A_.is_null() || A_->getComm().is_null()) {
1685 myRank = 0;
1686 } else {
1687 myRank = A_->getComm()->getRank();
1688 }
1689 if (myRank == 0) {
1690 // YAML requires quoting the key in this case, to distinguish
1691 // key's colons from the colon that separates key from value.
1692 out << "\"Ifpack2::Details::Chebyshev\":" << endl;
1693 }
1694 Teuchos::OSTab tab1(out);
1695
1696 if (vl == Teuchos::VERB_LOW) {
1697 if (myRank == 0) {
1698 out << "degree: " << numIters_ << endl
1699 << "lambdaMax: " << lambdaMaxForApply_ << endl
1700 << "alpha: " << eigRatioForApply_ << endl
1701 << "lambdaMin: " << lambdaMinForApply_ << endl
1702 << "boost factor: " << boostFactor_ << endl;
1703 }
1704 return;
1705 }
1706
1707 // vl > Teuchos::VERB_LOW
1708
1709 if (myRank == 0) {
1710 out << "Template parameters:" << endl;
1711 {
1712 Teuchos::OSTab tab2(out);
1713 out << "ScalarType: " << TypeNameTraits<ScalarType>::name() << endl
1714 << "MV: " << TypeNameTraits<MV>::name() << endl;
1715 }
1716
1717 // "Computed parameters" literally means "parameters whose
1718 // values were computed by compute()."
1719 if (myRank == 0) {
1720 out << "Computed parameters:" << endl;
1721 }
1722 }
1723
1724 // Print computed parameters
1725 {
1726 Teuchos::OSTab tab2(out);
1727 // Users might want to see the values in the computed inverse
1728 // diagonal, so we print them out at the highest verbosity.
1729 if (myRank == 0) {
1730 out << "D_: ";
1731 }
1732 if (D_.is_null()) {
1733 if (myRank == 0) {
1734 out << "unset" << endl;
1735 }
1736 } else if (vl <= Teuchos::VERB_HIGH) {
1737 if (myRank == 0) {
1738 out << "set" << endl;
1739 }
1740 } else { // D_ not null and vl > Teuchos::VERB_HIGH
1741 if (myRank == 0) {
1742 out << endl;
1743 }
1744 // By convention, describe() first indents, then prints.
1745 // We can rely on other describe() implementations to do that.
1746 D_->describe(out, vl);
1747 }
1748 if (myRank == 0) {
1749 // W_ is scratch space; its values are irrelevant.
1750 // All that matters is whether or not they have been set.
1751 out << "W_: " << (W_.is_null() ? "unset" : "set") << endl
1752 << "computedLambdaMax_: " << computedLambdaMax_ << endl
1753 << "computedLambdaMin_: " << computedLambdaMin_ << endl
1754 << "lambdaMaxForApply_: " << lambdaMaxForApply_ << endl
1755 << "lambdaMinForApply_: " << lambdaMinForApply_ << endl
1756 << "eigRatioForApply_: " << eigRatioForApply_ << endl;
1757 }
1758 } // print computed parameters
1759
1760 if (myRank == 0) {
1761 out << "User parameters:" << endl;
1762 }
1763
1764 // Print user parameters
1765 {
1766 Teuchos::OSTab tab2(out);
1767 out << "userInvDiag_: ";
1768 if (userInvDiag_.is_null()) {
1769 out << "unset" << endl;
1770 } else if (vl <= Teuchos::VERB_HIGH) {
1771 out << "set" << endl;
1772 } else { // userInvDiag_ not null and vl > Teuchos::VERB_HIGH
1773 if (myRank == 0) {
1774 out << endl;
1775 }
1776 userInvDiag_->describe(out, vl);
1777 }
1778 if (myRank == 0) {
1779 out << "userLambdaMax_: " << userLambdaMax_ << endl
1780 << "userLambdaMin_: " << userLambdaMin_ << endl
1781 << "userEigRatio_: " << userEigRatio_ << endl
1782 << "numIters_: " << numIters_ << endl
1783 << "eigMaxIters_: " << eigMaxIters_ << endl
1784 << "eigRelTolerance_: " << eigRelTolerance_ << endl
1785 << "eigNormalizationFreq_: " << eigNormalizationFreq_ << endl
1786 << "zeroStartingSolution_: " << zeroStartingSolution_ << endl
1787 << "assumeMatrixUnchanged_: " << assumeMatrixUnchanged_ << endl
1788 << "chebyshevAlgorithm_: " << chebyshevAlgorithm_ << endl
1789 << "computeMaxResNorm_: " << computeMaxResNorm_ << endl;
1790 }
1791 } // print user parameters
1792}
1793
1794} // namespace Details
1795} // namespace Ifpack2
1796
1797#define IFPACK2_DETAILS_CHEBYSHEV_INSTANT(S, LO, GO, N) \
1798 template class Ifpack2::Details::Chebyshev<S, Tpetra::MultiVector<S, LO, GO, N>>;
1799
1800#endif // IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
Definition of Chebyshev implementation.
Declaration of Chebyshev implementation.
Definition of power methods.
Diagonally scaled Chebyshev iteration for Tpetra sparse matrices.
Definition Ifpack2_Chebyshev_decl.hpp:172
Left-scaled Chebyshev iteration.
Definition Ifpack2_Details_Chebyshev_decl.hpp:75
Tpetra::Vector< typename MV::scalar_type, typename MV::local_ordinal_type, typename MV::global_ordinal_type, typename MV::node_type > V
Specialization of Tpetra::Vector.
Definition Ifpack2_Details_Chebyshev_decl.hpp:103
Teuchos::ScalarTraits< scalar_type > STS
Traits class for ST.
Definition Ifpack2_Details_Chebyshev_decl.hpp:83
void compute()
(Re)compute the left scaling D_inv, and estimate min and max eigenvalues of D_inv * A.
Definition Ifpack2_Details_Chebyshev_def.hpp:791
void setParameters(Teuchos::ParameterList &plist)
Set (or reset) parameters.
Definition Ifpack2_Details_Chebyshev_def.hpp:340
void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Set the matrix.
Definition Ifpack2_Details_Chebyshev_def.hpp:755
std::string description() const
A single-line description of the Chebyshev solver.
Definition Ifpack2_Details_Chebyshev_def.hpp:1641
Chebyshev(Teuchos::RCP< const row_matrix_type > A)
Definition Ifpack2_Details_Chebyshev_def.hpp:275
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Print a description of the Chebyshev solver to out.
Definition Ifpack2_Details_Chebyshev_def.hpp:1661
void print(std::ostream &out)
Print instance data to the given output stream.
Definition Ifpack2_Details_Chebyshev_def.hpp:1049
scalar_type ST
The type of entries in the matrix and vectors.
Definition Ifpack2_Details_Chebyshev_decl.hpp:81
STS::magnitudeType MT
The type of the absolute value of a ScalarType.
Definition Ifpack2_Details_Chebyshev_decl.hpp:85
MT apply(const MV &B, MV &X)
Solve Ax=b for x with Chebyshev iteration with left diagonal scaling.
Definition Ifpack2_Details_Chebyshev_def.hpp:999
bool hasTransposeApply() const
Whether it's possible to apply the transpose of this operator.
Definition Ifpack2_Details_Chebyshev_def.hpp:1603
Teuchos::RCP< const row_matrix_type > getMatrix() const
Get the matrix given to the constructor.
Definition Ifpack2_Details_Chebyshev_def.hpp:1597
Ifpack2's implementation of Trilinos::Details::LinearSolver interface.
Definition Ifpack2_Details_LinearSolver_decl.hpp:75
void solve(MV &X, const MV &B)
Solve the linear system AX=B for X.
Definition Ifpack2_Details_LinearSolver_def.hpp:122
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Implementation of Teuchos::Describable::describe.
Definition Ifpack2_Details_LinearSolver_def.hpp:178
Ifpack2 implementation details.
Preconditioners and smoothers for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:40