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