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