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