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