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