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