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