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