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