44 #ifndef IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
45 #define IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
56 #include "Ifpack2_Details_ChebyshevKernel.hpp"
57 #include "Kokkos_ArithTraits.hpp"
58 #include "Teuchos_FancyOStream.hpp"
59 #include "Teuchos_oblackholestream.hpp"
60 #include "Tpetra_Details_residual.hpp"
70 const char computeBeforeApplyReminder[] =
71 "This means one of the following:\n"
72 " - you have not yet called compute() on this instance, or \n"
73 " - you didn't call compute() after calling setParameters().\n\n"
74 "After creating an Ifpack2::Chebyshev instance,\n"
75 "you must _always_ call compute() at least once before calling apply().\n"
76 "After calling compute() once, you do not need to call it again,\n"
77 "unless the matrix has changed or you have changed parameters\n"
78 "(by calling setParameters()).";
84 template<
class XV,
class SizeType =
typename XV::
size_type>
85 struct V_ReciprocalThresholdSelfFunctor
87 typedef typename XV::execution_space execution_space;
88 typedef typename XV::non_const_value_type value_type;
89 typedef SizeType size_type;
90 typedef Kokkos::Details::ArithTraits<value_type> KAT;
91 typedef typename KAT::mag_type mag_type;
94 const value_type minVal_;
95 const mag_type minValMag_;
97 V_ReciprocalThresholdSelfFunctor (
const XV& X,
98 const value_type& min_val) :
101 minValMag_ (KAT::abs (min_val))
104 KOKKOS_INLINE_FUNCTION
105 void operator() (
const size_type& i)
const
107 const mag_type X_i_abs = KAT::abs (X_[i]);
109 if (X_i_abs < minValMag_) {
113 X_[i] = KAT::one () / X_[i];
118 template<
class XV,
class SizeType =
typename XV::
size_type>
119 struct LocalReciprocalThreshold {
121 compute (
const XV& X,
122 const typename XV::non_const_value_type& minVal)
124 typedef typename XV::execution_space execution_space;
125 Kokkos::RangePolicy<execution_space, SizeType> policy (0, X.extent (0));
126 V_ReciprocalThresholdSelfFunctor<XV, SizeType> op (X, minVal);
127 Kokkos::parallel_for (policy, op);
131 template <
class TpetraVectorType,
132 const bool classic = TpetraVectorType::node_type::classic>
133 struct GlobalReciprocalThreshold {};
135 template <
class TpetraVectorType>
136 struct GlobalReciprocalThreshold<TpetraVectorType, true> {
138 compute (TpetraVectorType& V,
139 const typename TpetraVectorType::scalar_type& min_val)
141 typedef typename TpetraVectorType::scalar_type scalar_type;
142 typedef typename TpetraVectorType::mag_type mag_type;
143 typedef Kokkos::Details::ArithTraits<scalar_type> STS;
145 const scalar_type ONE = STS::one ();
146 const mag_type min_val_abs = STS::abs (min_val);
149 const size_t lclNumRows = V.getLocalLength ();
151 for (
size_t i = 0; i < lclNumRows; ++i) {
152 const scalar_type V_0i = V_0[i];
153 if (STS::abs (V_0i) < min_val_abs) {
162 template <
class TpetraVectorType>
163 struct GlobalReciprocalThreshold<TpetraVectorType, false> {
165 compute (TpetraVectorType& X,
166 const typename TpetraVectorType::scalar_type& minVal)
168 typedef typename TpetraVectorType::impl_scalar_type value_type;
169 typedef typename TpetraVectorType::device_type::memory_space memory_space;
171 X.template sync<memory_space> ();
172 X.template modify<memory_space> ();
174 const value_type minValS =
static_cast<value_type
> (minVal);
175 auto X_0 = Kokkos::subview (X.template getLocalView<memory_space> (),
177 LocalReciprocalThreshold<decltype (X_0) >::compute (X_0, minValS);
182 template <
typename S,
typename L,
typename G,
typename N>
184 reciprocal_threshold (Tpetra::Vector<S,L,G,N>& V,
const S& minVal)
186 GlobalReciprocalThreshold<Tpetra::Vector<S,L,G,N> >::compute (V, minVal);
194 template<
class OneDViewType,
195 class LocalOrdinal =
typename OneDViewType::size_type>
196 class PositivizeVector {
197 static_assert (Kokkos::Impl::is_view<OneDViewType>::value,
198 "OneDViewType must be a 1-D Kokkos::View.");
199 static_assert (static_cast<int> (OneDViewType::rank) == 1,
200 "This functor only works with a 1-D View.");
201 static_assert (std::is_integral<LocalOrdinal>::value,
202 "The second template parameter, LocalOrdinal, "
203 "must be an integer type.");
205 PositivizeVector (
const OneDViewType& x) : x_ (x) {}
207 KOKKOS_INLINE_FUNCTION
void
208 operator () (
const LocalOrdinal& i)
const
210 typedef typename OneDViewType::non_const_value_type IST;
211 typedef Kokkos::Details::ArithTraits<IST> STS;
212 typedef Kokkos::Details::ArithTraits<typename STS::mag_type> STM;
214 if (STS::real (x_(i)) < STM::zero ()) {
225 template<
class ScalarType,
class MV>
226 void Chebyshev<ScalarType, MV>::checkInputMatrix ()
const
229 ! A_.is_null () && A_->getGlobalNumRows () != A_->getGlobalNumCols (),
230 std::invalid_argument,
231 "Ifpack2::Chebyshev: The input matrix A must be square. "
232 "A has " << A_->getGlobalNumRows () <<
" rows and "
233 << A_->getGlobalNumCols () <<
" columns.");
237 if (debug_ && ! A_.is_null ()) {
245 ! domainMap->isSameAs (*rangeMap), std::invalid_argument,
246 "Ifpack2::Chebyshev: The domain Map and range Map of the matrix must be "
247 "the same (in the sense of isSameAs())." << std::endl <<
"We only check "
248 "for this in debug mode.");
253 template<
class ScalarType,
class MV>
255 Chebyshev<ScalarType, MV>::
256 checkConstructorInput ()
const
261 if (STS::isComplex) {
263 (
true, std::logic_error,
"Ifpack2::Chebyshev: This class' implementation "
264 "of Chebyshev iteration only works for real-valued, symmetric positive "
265 "definite matrices. However, you instantiated this class for ScalarType"
267 "complex-valued type. While this may be algorithmically correct if all "
268 "of the complex numbers in the matrix have zero imaginary part, we "
269 "forbid using complex ScalarType altogether in order to remind you of "
270 "the limitations of our implementation (and of the algorithm itself).");
277 template<
class ScalarType,
class MV>
281 savedDiagOffsets_ (false),
282 computedLambdaMax_ (STS::nan ()),
283 computedLambdaMin_ (STS::nan ()),
284 lambdaMaxForApply_ (STS::nan ()),
285 lambdaMinForApply_ (STS::nan ()),
286 eigRatioForApply_ (STS::nan ()),
287 userLambdaMax_ (STS::nan ()),
288 userLambdaMin_ (STS::nan ()),
289 userEigRatio_ (Teuchos::
as<
ST> (30)),
290 minDiagVal_ (STS::eps ()),
293 zeroStartingSolution_ (true),
294 assumeMatrixUnchanged_ (false),
295 textbookAlgorithm_ (false),
296 computeMaxResNorm_ (false),
299 checkConstructorInput ();
302 template<
class ScalarType,
class MV>
307 savedDiagOffsets_ (false),
308 computedLambdaMax_ (STS::nan ()),
309 computedLambdaMin_ (STS::nan ()),
310 lambdaMaxForApply_ (STS::nan ()),
311 boostFactor_ (static_cast<
MT> (1.1)),
312 lambdaMinForApply_ (STS::nan ()),
313 eigRatioForApply_ (STS::nan ()),
314 userLambdaMax_ (STS::nan ()),
315 userLambdaMin_ (STS::nan ()),
316 userEigRatio_ (Teuchos::
as<
ST> (30)),
317 minDiagVal_ (STS::eps ()),
320 zeroStartingSolution_ (true),
321 assumeMatrixUnchanged_ (false),
322 textbookAlgorithm_ (false),
323 computeMaxResNorm_ (false),
326 checkConstructorInput ();
330 template<
class ScalarType,
class MV>
337 using Teuchos::rcp_const_cast;
349 const ST defaultLambdaMax = STS::nan ();
350 const ST defaultLambdaMin = STS::nan ();
357 const ST defaultEigRatio = Teuchos::as<ST> (30);
358 const MT defaultBoostFactor =
static_cast<MT> (1.1);
359 const ST defaultMinDiagVal = STS::eps ();
360 const int defaultNumIters = 1;
361 const int defaultEigMaxIters = 10;
362 const bool defaultZeroStartingSolution =
true;
363 const bool defaultAssumeMatrixUnchanged =
false;
364 const bool defaultTextbookAlgorithm =
false;
365 const bool defaultComputeMaxResNorm =
false;
366 const bool defaultDebug =
false;
372 RCP<const V> userInvDiagCopy;
373 ST lambdaMax = defaultLambdaMax;
374 ST lambdaMin = defaultLambdaMin;
375 ST eigRatio = defaultEigRatio;
376 MT boostFactor = defaultBoostFactor;
377 ST minDiagVal = defaultMinDiagVal;
378 int numIters = defaultNumIters;
379 int eigMaxIters = defaultEigMaxIters;
380 bool zeroStartingSolution = defaultZeroStartingSolution;
381 bool assumeMatrixUnchanged = defaultAssumeMatrixUnchanged;
382 bool textbookAlgorithm = defaultTextbookAlgorithm;
383 bool computeMaxResNorm = defaultComputeMaxResNorm;
384 bool debug = defaultDebug;
391 if (plist.
isType<
bool> (
"debug")) {
392 debug = plist.
get<
bool> (
"debug");
394 else if (plist.
isType<
int> (
"debug")) {
395 const int debugInt = plist.
get<
bool> (
"debug");
396 debug = debugInt != 0;
409 const char opInvDiagLabel[] =
"chebyshev: operator inv diagonal";
412 RCP<const V> userInvDiag;
414 if (plist.
isType<
const V*> (opInvDiagLabel)) {
415 const V* rawUserInvDiag =
416 plist.
get<
const V*> (opInvDiagLabel);
418 userInvDiag =
rcp (rawUserInvDiag,
false);
420 else if (plist.
isType<
const V*> (opInvDiagLabel)) {
421 V* rawUserInvDiag = plist.
get<V*> (opInvDiagLabel);
423 userInvDiag =
rcp (const_cast<const V*> (rawUserInvDiag),
false);
425 else if (plist.
isType<RCP<const V>> (opInvDiagLabel)) {
426 userInvDiag = plist.
get<RCP<const V> > (opInvDiagLabel);
428 else if (plist.
isType<RCP<V>> (opInvDiagLabel)) {
429 RCP<V> userInvDiagNonConst =
430 plist.
get<RCP<V> > (opInvDiagLabel);
431 userInvDiag = rcp_const_cast<
const V> (userInvDiagNonConst);
433 else if (plist.
isType<
const V> (opInvDiagLabel)) {
434 const V& userInvDiagRef = plist.
get<
const V> (opInvDiagLabel);
436 userInvDiag = userInvDiagCopy;
438 else if (plist.
isType<V> (opInvDiagLabel)) {
439 V& userInvDiagNonConstRef = plist.
get<V> (opInvDiagLabel);
440 const V& userInvDiagRef =
const_cast<const V&
> (userInvDiagNonConstRef);
442 userInvDiag = userInvDiagCopy;
452 if (! userInvDiag.is_null () && userInvDiagCopy.is_null ()) {
465 if (plist.
isParameter (
"chebyshev: max eigenvalue")) {
466 if (plist.
isType<
double>(
"chebyshev: max eigenvalue"))
467 lambdaMax = plist.
get<
double> (
"chebyshev: max eigenvalue");
469 lambdaMax = plist.
get<
ST> (
"chebyshev: max eigenvalue");
471 STS::isnaninf (lambdaMax), std::invalid_argument,
472 "Ifpack2::Chebyshev::setParameters: \"chebyshev: max eigenvalue\" "
473 "parameter is NaN or Inf. This parameter is optional, but if you "
474 "choose to supply it, it must have a finite value.");
476 if (plist.
isParameter (
"chebyshev: min eigenvalue")) {
477 if (plist.
isType<
double>(
"chebyshev: min eigenvalue"))
478 lambdaMin = plist.
get<
double> (
"chebyshev: min eigenvalue");
480 lambdaMin = plist.
get<
ST> (
"chebyshev: min eigenvalue");
482 STS::isnaninf (lambdaMin), std::invalid_argument,
483 "Ifpack2::Chebyshev::setParameters: \"chebyshev: min eigenvalue\" "
484 "parameter is NaN or Inf. This parameter is optional, but if you "
485 "choose to supply it, it must have a finite value.");
489 if (plist.
isParameter (
"smoother: Chebyshev alpha")) {
490 if (plist.
isType<
double>(
"smoother: Chebyshev alpha"))
491 eigRatio = plist.
get<
double> (
"smoother: Chebyshev alpha");
493 eigRatio = plist.
get<
ST> (
"smoother: Chebyshev alpha");
496 eigRatio = plist.
get (
"chebyshev: ratio eigenvalue", eigRatio);
498 STS::isnaninf (eigRatio), std::invalid_argument,
499 "Ifpack2::Chebyshev::setParameters: \"chebyshev: ratio eigenvalue\" "
500 "parameter (also called \"smoother: Chebyshev alpha\") is NaN or Inf. "
501 "This parameter is optional, but if you choose to supply it, it must have "
509 STS::real (eigRatio) < STS::real (STS::one ()),
510 std::invalid_argument,
511 "Ifpack2::Chebyshev::setParameters: \"chebyshev: ratio eigenvalue\""
512 "parameter (also called \"smoother: Chebyshev alpha\") must be >= 1, "
513 "but you supplied the value " << eigRatio <<
".");
518 const char paramName[] =
"chebyshev: boost factor";
522 boostFactor = plist.
get<
MT> (paramName);
524 else if (! std::is_same<double, MT>::value &&
525 plist.
isType<
double> (paramName)) {
526 const double dblBF = plist.
get<
double> (paramName);
527 boostFactor =
static_cast<MT> (dblBF);
531 (
true, std::invalid_argument,
532 "Ifpack2::Chebyshev::setParameters: \"chebyshev: boost factor\""
533 "parameter must have type magnitude_type (MT) or double.");
543 plist.
set (paramName, defaultBoostFactor);
547 "Ifpack2::Chebyshev::setParameters: \"" << paramName <<
"\" parameter "
548 "must be >= 1, but you supplied the value " << boostFactor <<
".");
552 minDiagVal = plist.
get (
"chebyshev: min diagonal value", minDiagVal);
554 STS::isnaninf (minDiagVal), std::invalid_argument,
555 "Ifpack2::Chebyshev::setParameters: \"chebyshev: min diagonal value\" "
556 "parameter is NaN or Inf. This parameter is optional, but if you choose "
557 "to supply it, it must have a finite value.");
561 numIters = plist.
get<
int> (
"smoother: sweeps");
564 numIters = plist.
get<
int> (
"relaxation: sweeps");
566 numIters = plist.
get (
"chebyshev: degree", numIters);
568 numIters < 0, std::invalid_argument,
569 "Ifpack2::Chebyshev::setParameters: \"chebyshev: degree\" parameter (also "
570 "called \"smoother: sweeps\" or \"relaxation: sweeps\") must be a "
571 "nonnegative integer. You gave a value of " << numIters <<
".");
574 if (plist.
isParameter (
"eigen-analysis: iterations")) {
575 eigMaxIters = plist.
get<
int> (
"eigen-analysis: iterations");
577 eigMaxIters = plist.
get (
"chebyshev: eigenvalue max iterations", eigMaxIters);
579 eigMaxIters < 0, std::invalid_argument,
580 "Ifpack2::Chebyshev::setParameters: \"chebyshev: eigenvalue max iterations"
581 "\" parameter (also called \"eigen-analysis: iterations\") must be a "
582 "nonnegative integer. You gave a value of " << eigMaxIters <<
".");
584 zeroStartingSolution = plist.
get (
"chebyshev: zero starting solution",
585 zeroStartingSolution);
586 assumeMatrixUnchanged = plist.
get (
"chebyshev: assume matrix does not change",
587 assumeMatrixUnchanged);
591 if (plist.
isParameter (
"chebyshev: textbook algorithm")) {
592 textbookAlgorithm = plist.
get<
bool> (
"chebyshev: textbook algorithm");
594 if (plist.
isParameter (
"chebyshev: compute max residual norm")) {
595 computeMaxResNorm = plist.
get<
bool> (
"chebyshev: compute max residual norm");
602 (plist.
isType<
bool> (
"chebyshev: use block mode") &&
603 ! plist.
get<
bool> (
"chebyshev: use block mode"),
604 std::invalid_argument,
605 "Ifpack2::Chebyshev requires that if you set \"chebyshev: use "
606 "block mode\" at all, you must set it to false. "
607 "Ifpack2::Chebyshev does not implement Ifpack's block mode.");
609 (plist.
isType<
bool> (
"chebyshev: solve normal equations") &&
610 ! plist.
get<
bool> (
"chebyshev: solve normal equations"),
611 std::invalid_argument,
612 "Ifpack2::Chebyshev does not and will never implement the Ifpack "
613 "parameter \"chebyshev: solve normal equations\". If you want to "
614 "solve the normal equations, construct a Tpetra::Operator that "
615 "implements A^* A, and use Chebyshev to solve A^* A x = A^* b.");
623 std::string eigenAnalysisType (
"power-method");
625 eigenAnalysisType = plist.
get<std::string> (
"eigen-analysis: type");
627 eigenAnalysisType !=
"power-method" &&
628 eigenAnalysisType !=
"power method",
629 std::invalid_argument,
630 "Ifpack2::Chebyshev: This class supports the ML parameter \"eigen-"
631 "analysis: type\" for backwards compatibility. However, Ifpack2 "
632 "currently only supports the \"power-method\" option for this "
633 "parameter. This imitates Ifpack, which only implements the power "
634 "method for eigenanalysis.");
638 userInvDiag_ = userInvDiagCopy;
639 userLambdaMax_ = lambdaMax;
640 userLambdaMin_ = lambdaMin;
641 userEigRatio_ = eigRatio;
642 boostFactor_ =
static_cast<MT> (boostFactor);
643 minDiagVal_ = minDiagVal;
644 numIters_ = numIters;
645 eigMaxIters_ = eigMaxIters;
646 zeroStartingSolution_ = zeroStartingSolution;
647 assumeMatrixUnchanged_ = assumeMatrixUnchanged;
648 textbookAlgorithm_ = textbookAlgorithm;
649 computeMaxResNorm_ = computeMaxResNorm;
655 if (A_.is_null () || A_->getComm ().is_null ()) {
661 myRank = A_->getComm ()->getRank ();
665 out_ = Teuchos::getFancyOStream (Teuchos::rcpFromRef (std::cerr));
670 out_ = Teuchos::getFancyOStream (blackHole);
675 out_ = Teuchos::null;
680 template<
class ScalarType,
class MV>
686 diagOffsets_ = offsets_type ();
687 savedDiagOffsets_ =
false;
689 computedLambdaMax_ = STS::nan ();
690 computedLambdaMin_ = STS::nan ();
694 template<
class ScalarType,
class MV>
700 if (! assumeMatrixUnchanged_) {
718 myRank = A->getComm ()->getRank ();
722 out_ = Teuchos::getFancyOStream (Teuchos::rcpFromRef (std::cerr));
726 out_ = Teuchos::getFancyOStream (blackHole);
731 out_ = Teuchos::null;
736 template<
class ScalarType,
class MV>
745 typedef Tpetra::CrsMatrix<
typename MV::scalar_type,
746 typename MV::local_ordinal_type,
747 typename MV::global_ordinal_type,
748 typename MV::node_type> crs_matrix_type;
751 A_.is_null (), std::runtime_error,
"Ifpack2::Chebyshev::compute: The input "
752 "matrix A is null. Please call setMatrix() with a nonnull input matrix "
753 "before calling this method.");
768 if (userInvDiag_.is_null ()) {
770 Teuchos::rcp_dynamic_cast<
const crs_matrix_type> (A_);
773 if (! A_crsMat.
is_null () && A_crsMat->isStaticGraph ()) {
775 const size_t lclNumRows = A_crsMat->getNodeNumRows ();
776 if (diagOffsets_.extent (0) < lclNumRows) {
777 diagOffsets_ = offsets_type ();
778 diagOffsets_ = offsets_type (
"offsets", lclNumRows);
780 A_crsMat->getCrsGraph ()->getLocalDiagOffsets (diagOffsets_);
781 savedDiagOffsets_ =
true;
782 D_ = makeInverseDiagonal (*A_,
true);
785 D_ = makeInverseDiagonal (*A_);
788 else if (! assumeMatrixUnchanged_) {
789 if (! A_crsMat.
is_null () && A_crsMat->isStaticGraph ()) {
792 if (! savedDiagOffsets_) {
793 const size_t lclNumRows = A_crsMat->getNodeNumRows ();
794 if (diagOffsets_.extent (0) < lclNumRows) {
795 diagOffsets_ = offsets_type ();
796 diagOffsets_ = offsets_type (
"offsets", lclNumRows);
798 A_crsMat->getCrsGraph ()->getLocalDiagOffsets (diagOffsets_);
799 savedDiagOffsets_ =
true;
802 D_ = makeInverseDiagonal (*A_,
true);
805 D_ = makeInverseDiagonal (*A_);
810 D_ = makeRangeMapVectorConst (userInvDiag_);
814 const bool computedEigenvalueEstimates =
815 STS::isnaninf (computedLambdaMax_) || STS::isnaninf (computedLambdaMin_);
824 if (! assumeMatrixUnchanged_ ||
825 (! computedEigenvalueEstimates && STS::isnaninf (userLambdaMax_))) {
826 const ST computedLambdaMax = powerMethod (*A_, *D_, eigMaxIters_);
828 STS::isnaninf (computedLambdaMax),
830 "Ifpack2::Chebyshev::compute: Estimation of the max eigenvalue "
831 "of D^{-1} A failed, by producing Inf or NaN. This probably means that "
832 "the matrix contains Inf or NaN values, or that it is badly scaled.");
834 STS::isnaninf (userEigRatio_),
836 "Ifpack2::Chebyshev::compute: userEigRatio_ is Inf or NaN."
837 << endl <<
"This should be impossible." << endl <<
838 "Please report this bug to the Ifpack2 developers.");
844 const ST computedLambdaMin = computedLambdaMax / userEigRatio_;
847 computedLambdaMax_ = computedLambdaMax;
848 computedLambdaMin_ = computedLambdaMin;
851 STS::isnaninf (userLambdaMax_) && STS::isnaninf (computedLambdaMax_),
853 "Ifpack2::Chebyshev::compute: " << endl <<
854 "Both userLambdaMax_ and computedLambdaMax_ are Inf or NaN."
855 << endl <<
"This should be impossible." << endl <<
856 "Please report this bug to the Ifpack2 developers.");
864 lambdaMaxForApply_ = STS::isnaninf (userLambdaMax_) ? computedLambdaMax_ : userLambdaMax_;
877 lambdaMinForApply_ = lambdaMaxForApply_ / userEigRatio_;
878 eigRatioForApply_ = userEigRatio_;
880 if (! textbookAlgorithm_) {
883 const ST one = Teuchos::as<ST> (1);
886 if (STS::magnitude (lambdaMaxForApply_ - one) < Teuchos::as<MT> (1.0e-6)) {
887 lambdaMinForApply_ = one;
888 lambdaMaxForApply_ = lambdaMinForApply_;
889 eigRatioForApply_ = one;
895 template<
class ScalarType,
class MV>
899 return lambdaMaxForApply_;
903 template<
class ScalarType,
class MV>
907 const char prefix[] =
"Ifpack2::Chebyshev::apply: ";
910 *out_ <<
"apply: " << std::endl;
913 (A_.is_null (), std::runtime_error, prefix <<
"The input matrix A is null. "
914 " Please call setMatrix() with a nonnull input matrix, and then call "
915 "compute(), before calling this method.");
917 (STS::isnaninf (lambdaMaxForApply_), std::runtime_error,
918 prefix <<
"There is no estimate for the max eigenvalue."
919 << std::endl << computeBeforeApplyReminder);
920 TEUCHOS_TEST_FOR_EXCEPTION
921 (STS::isnaninf (lambdaMinForApply_), std::runtime_error,
922 prefix <<
"There is no estimate for the min eigenvalue."
923 << std::endl << computeBeforeApplyReminder);
924 TEUCHOS_TEST_FOR_EXCEPTION
925 (STS::isnaninf (eigRatioForApply_), std::runtime_error,
926 prefix <<
"There is no estimate for the ratio of the max "
927 "eigenvalue to the min eigenvalue."
928 << std::endl << computeBeforeApplyReminder);
929 TEUCHOS_TEST_FOR_EXCEPTION
930 (D_.is_null (), std::runtime_error, prefix <<
"The vector of inverse "
931 "diagonal entries of the matrix has not yet been computed."
932 << std::endl << computeBeforeApplyReminder);
934 if (textbookAlgorithm_) {
935 textbookApplyImpl (*A_, B, X, numIters_, lambdaMaxForApply_,
936 lambdaMinForApply_, eigRatioForApply_, *D_);
939 ifpackApplyImpl (*A_, B, X, numIters_, lambdaMaxForApply_,
940 lambdaMinForApply_, eigRatioForApply_, *D_);
943 if (computeMaxResNorm_ && B.getNumVectors () > 0) {
944 MV R (B.getMap (), B.getNumVectors ());
945 computeResidual (R, B, *A_, X);
948 return *std::max_element (norms.begin (), norms.end ());
955 template<
class ScalarType,
class MV>
960 using Teuchos::rcpFromRef;
961 this->describe (* (Teuchos::getFancyOStream (rcpFromRef (out))),
965 template<
class ScalarType,
class MV>
969 const ScalarType& alpha,
974 solve (W, alpha, D_inv, B);
975 Tpetra::deep_copy (X, W);
978 template<
class ScalarType,
class MV>
984 Tpetra::Details::residual(A,X,B,R);
987 template <
class ScalarType,
class MV>
989 Chebyshev<ScalarType, MV>::
990 solve (MV& Z,
const V& D_inv,
const MV& R) {
991 Z.elementWiseMultiply (STS::one(), D_inv, R, STS::zero());
994 template<
class ScalarType,
class MV>
996 Chebyshev<ScalarType, MV>::
997 solve (MV& Z,
const ST alpha,
const V& D_inv,
const MV& R) {
998 Z.elementWiseMultiply (alpha, D_inv, R, STS::zero());
1001 template<
class ScalarType,
class MV>
1003 Chebyshev<ScalarType, MV>::
1004 makeInverseDiagonal (
const row_matrix_type& A,
const bool useDiagOffsets)
const
1007 using Teuchos::rcpFromRef;
1008 using Teuchos::rcp_dynamic_cast;
1010 RCP<V> D_rowMap (
new V (A.getGraph ()->getRowMap ()));
1011 if (useDiagOffsets) {
1015 typedef Tpetra::CrsMatrix<
typename MV::scalar_type,
1016 typename MV::local_ordinal_type,
1017 typename MV::global_ordinal_type,
1018 typename MV::node_type> crs_matrix_type;
1019 RCP<const crs_matrix_type> A_crsMat =
1020 rcp_dynamic_cast<
const crs_matrix_type> (rcpFromRef (A));
1021 if (! A_crsMat.is_null ()) {
1023 ! savedDiagOffsets_, std::logic_error,
1024 "Ifpack2::Details::Chebyshev::makeInverseDiagonal: "
1025 "It is not allowed to call this method with useDiagOffsets=true, "
1026 "if you have not previously saved offsets of diagonal entries. "
1027 "This situation should never arise if this class is used properly. "
1028 "Please report this bug to the Ifpack2 developers.");
1029 A_crsMat->getLocalDiagCopy (*D_rowMap, diagOffsets_);
1035 A.getLocalDiagCopy (*D_rowMap);
1037 RCP<V> D_rangeMap = makeRangeMapVector (D_rowMap);
1043 D_rangeMap->sync_host ();
1044 auto D_lcl = D_rangeMap->getLocalViewHost ();
1045 auto D_lcl_1d = Kokkos::subview (D_lcl, Kokkos::ALL (), 0);
1047 typedef typename MV::impl_scalar_type IST;
1048 typedef typename MV::local_ordinal_type LO;
1049 typedef Kokkos::Details::ArithTraits<IST> STS;
1050 typedef Kokkos::Details::ArithTraits<typename STS::mag_type> STM;
1052 const LO lclNumRows =
static_cast<LO
> (D_rangeMap->getLocalLength ());
1053 bool foundNonpositiveValue =
false;
1054 for (LO i = 0; i < lclNumRows; ++i) {
1055 if (STS::real (D_lcl_1d(i)) <= STM::zero ()) {
1056 foundNonpositiveValue =
true;
1061 using Teuchos::outArg;
1063 using Teuchos::reduceAll;
1065 const int lclSuccess = foundNonpositiveValue ? 0 : 1;
1066 int gblSuccess = lclSuccess;
1067 if (! D_rangeMap->getMap ().is_null () && D_rangeMap->getMap ()->getComm ().is_null ()) {
1069 reduceAll<int, int> (comm,
REDUCE_MIN, lclSuccess, outArg (gblSuccess));
1071 if (gblSuccess == 1) {
1072 *out_ <<
"makeInverseDiagonal: The matrix's diagonal entries all have "
1073 "positive real part (this is good for Chebyshev)." << std::endl;
1076 *out_ <<
"makeInverseDiagonal: The matrix's diagonal has at least one "
1077 "entry with nonpositive real part, on at least one process in the "
1078 "matrix's communicator. This is bad for Chebyshev." << std::endl;
1084 reciprocal_threshold (*D_rangeMap, minDiagVal_);
1085 return Teuchos::rcp_const_cast<
const V> (D_rangeMap);
1089 template<
class ScalarType,
class MV>
1091 Chebyshev<ScalarType, MV>::
1096 typedef Tpetra::Export<
typename MV::local_ordinal_type,
1097 typename MV::global_ordinal_type,
1098 typename MV::node_type> export_type;
1103 A_.is_null (), std::logic_error,
"Ifpack2::Details::Chebyshev::"
1104 "makeRangeMapVector: The input matrix A is null. Please call setMatrix() "
1105 "with a nonnull input matrix before calling this method. This is probably "
1106 "a bug in Ifpack2; please report this bug to the Ifpack2 developers.");
1108 D.
is_null (), std::logic_error,
"Ifpack2::Details::Chebyshev::"
1109 "makeRangeMapVector: The input Vector D is null. This is probably "
1110 "a bug in Ifpack2; please report this bug to the Ifpack2 developers.");
1112 RCP<const map_type> sourceMap = D->getMap ();
1113 RCP<const map_type> rangeMap = A_->getRangeMap ();
1114 RCP<const map_type> rowMap = A_->getRowMap ();
1116 if (rangeMap->isSameAs (*sourceMap)) {
1122 RCP<const export_type> exporter;
1126 if (sourceMap->isSameAs (*rowMap)) {
1128 exporter = A_->getGraph ()->getExporter ();
1131 exporter =
rcp (
new export_type (sourceMap, rangeMap));
1134 if (exporter.is_null ()) {
1139 D_out->doExport (*D, *exporter, Tpetra::ADD);
1140 return Teuchos::rcp_const_cast<
const V> (D_out);
1146 template<
class ScalarType,
class MV>
1148 Chebyshev<ScalarType, MV>::
1151 using Teuchos::rcp_const_cast;
1152 return rcp_const_cast<V> (makeRangeMapVectorConst (rcp_const_cast<V> (D)));
1156 template<
class ScalarType,
class MV>
1158 Chebyshev<ScalarType, MV>::
1159 textbookApplyImpl (
const op_type& A,
1166 const V& D_inv)
const
1169 const ST myLambdaMin = lambdaMax / eigRatio;
1171 const ST zero = Teuchos::as<ST> (0);
1172 const ST one = Teuchos::as<ST> (1);
1173 const ST two = Teuchos::as<ST> (2);
1174 const ST d = (lambdaMax + myLambdaMin) / two;
1175 const ST c = (lambdaMax - myLambdaMin) / two;
1177 if (zeroStartingSolution_ && numIters > 0) {
1181 MV R (B.getMap (), B.getNumVectors (),
false);
1182 MV P (B.getMap (), B.getNumVectors (),
false);
1183 MV Z (B.getMap (), B.getNumVectors (),
false);
1185 for (
int i = 0; i < numIters; ++i) {
1186 computeResidual (R, B, A, X);
1187 solve (Z, D_inv, R);
1195 beta = alpha * (c/two) * (c/two);
1196 alpha = one / (d - beta);
1197 P.update (one, Z, beta);
1199 X.update (alpha, P, one);
1206 template<
class ScalarType,
class MV>
1207 typename Chebyshev<ScalarType, MV>::MT
1208 Chebyshev<ScalarType, MV>::maxNormInf (
const MV& X) {
1210 X.normInf (norms());
1211 return *std::max_element (norms.begin (), norms.end ());
1214 template<
class ScalarType,
class MV>
1216 Chebyshev<ScalarType, MV>::
1217 ifpackApplyImpl (
const op_type& A,
1227 #ifdef HAVE_IFPACK2_DEBUG
1228 const bool debug = debug_;
1230 const bool debug =
false;
1234 *out_ <<
" \\|B\\|_{\\infty} = " << maxNormInf (B) << endl;
1235 *out_ <<
" \\|X\\|_{\\infty} = " << maxNormInf (X) << endl;
1238 if (numIters <= 0) {
1241 const ST zero =
static_cast<ST
> (0.0);
1242 const ST one =
static_cast<ST
> (1.0);
1243 const ST two =
static_cast<ST
> (2.0);
1246 if (lambdaMin == one && lambdaMax == lambdaMin) {
1247 solve (X, D_inv, B);
1252 const ST alpha = lambdaMax / eigRatio;
1253 const ST beta = boostFactor_ * lambdaMax;
1254 const ST delta = two / (beta - alpha);
1255 const ST theta = (beta + alpha) / two;
1256 const ST s1 = theta * delta;
1259 *out_ <<
" alpha = " << alpha << endl
1260 <<
" beta = " << beta << endl
1261 <<
" delta = " << delta << endl
1262 <<
" theta = " << theta << endl
1263 <<
" s1 = " << s1 << endl;
1271 *out_ <<
" Iteration " << 1 <<
":" << endl
1272 <<
" - \\|D\\|_{\\infty} = " << D_->normInf () << endl;
1276 if (! zeroStartingSolution_) {
1279 if (ck_.is_null ()) {
1281 ck_ =
Teuchos::rcp (
new ChebyshevKernel<op_type> (A_op));
1285 ck_->compute (W, one/theta, const_cast<V&> (D_inv),
1286 const_cast<MV&> (B), X, zero);
1290 firstIterationWithZeroStartingSolution (W, one/theta, D_inv, B, X);
1294 *out_ <<
" - \\|W\\|_{\\infty} = " << maxNormInf (W) << endl
1295 <<
" - \\|X\\|_{\\infty} = " << maxNormInf (X) << endl;
1298 if (numIters > 1 && ck_.is_null ()) {
1300 ck_ =
Teuchos::rcp (
new ChebyshevKernel<op_type> (A_op));
1305 ST rhokp1, dtemp1, dtemp2;
1306 for (
int deg = 1; deg < numIters; ++deg) {
1308 *out_ <<
" Iteration " << deg+1 <<
":" << endl
1309 <<
" - \\|D\\|_{\\infty} = " << D_->normInf () << endl
1310 <<
" - \\|B\\|_{\\infty} = " << maxNormInf (B) << endl
1311 <<
" - \\|A\\|_{\\text{frob}} = " << A_->getFrobeniusNorm ()
1312 << endl <<
" - rhok = " << rhok << endl;
1315 rhokp1 = one / (two * s1 - rhok);
1316 dtemp1 = rhokp1 * rhok;
1317 dtemp2 = two * rhokp1 * delta;
1321 *out_ <<
" - dtemp1 = " << dtemp1 << endl
1322 <<
" - dtemp2 = " << dtemp2 << endl;
1327 ck_->compute (W, dtemp2, const_cast<V&> (D_inv),
1328 const_cast<MV&> (B), (X), dtemp1);
1331 *out_ <<
" - \\|W\\|_{\\infty} = " << maxNormInf (W) << endl
1332 <<
" - \\|X\\|_{\\infty} = " << maxNormInf (X) << endl;
1337 template<
class ScalarType,
class MV>
1338 typename Chebyshev<ScalarType, MV>::ST
1339 Chebyshev<ScalarType, MV>::
1340 powerMethodWithInitGuess (
const op_type& A,
1347 *out_ <<
" powerMethodWithInitGuess:" << endl;
1350 const ST zero =
static_cast<ST
> (0.0);
1351 const ST one =
static_cast<ST
> (1.0);
1352 ST lambdaMax = zero;
1353 ST RQ_top, RQ_bottom, norm;
1355 V y (A.getRangeMap ());
1358 (norm == zero, std::runtime_error,
1359 "Ifpack2::Chebyshev::powerMethodWithInitGuess: The initial guess "
1360 "has zero norm. This could be either because Tpetra::Vector::"
1361 "randomize filled the vector with zeros (if that was used to "
1362 "compute the initial guess), or because the norm2 method has a "
1363 "bug. The first is not impossible, but unlikely.");
1366 *out_ <<
" Original norm1(x): " << x.norm1 ()
1367 <<
", norm2(x): " << norm << endl;
1370 x.scale (one / norm);
1373 *out_ <<
" norm1(x.scale(one/norm)): " << x.norm1 () << endl;
1376 for (
int iter = 0; iter < numIters; ++iter) {
1378 *out_ <<
" Iteration " << iter << endl;
1381 solve (y, D_inv, y);
1383 RQ_bottom = x.dot (x);
1385 *out_ <<
" RQ_top: " << RQ_top
1386 <<
", RQ_bottom: " << RQ_bottom << endl;
1388 lambdaMax = RQ_top / RQ_bottom;
1392 *out_ <<
" norm is zero; returning zero" << endl;
1396 x.update (one / norm, y, zero);
1399 *out_ <<
" lambdaMax: " << lambdaMax << endl;
1404 template<
class ScalarType,
class MV>
1406 Chebyshev<ScalarType, MV>::
1407 computeInitialGuessForPowerMethod (V& x,
const bool nonnegativeRealParts)
const
1409 typedef typename MV::device_type::execution_space dev_execution_space;
1410 typedef typename MV::device_type::memory_space dev_memory_space;
1411 typedef typename MV::local_ordinal_type LO;
1415 if (nonnegativeRealParts) {
1416 x.template modify<dev_memory_space> ();
1417 auto x_lcl = x.template getLocalView<dev_memory_space> ();
1418 auto x_lcl_1d = Kokkos::subview (x_lcl, Kokkos::ALL (), 0);
1420 const LO lclNumRows =
static_cast<LO
> (x.getLocalLength ());
1421 Kokkos::RangePolicy<dev_execution_space, LO> range (0, lclNumRows);
1422 PositivizeVector<decltype (x_lcl_1d), LO> functor (x_lcl_1d);
1423 Kokkos::parallel_for (range, functor);
1427 template<
class ScalarType,
class MV>
1428 typename Chebyshev<ScalarType, MV>::ST
1429 Chebyshev<ScalarType, MV>::
1430 powerMethod (
const op_type& A,
const V& D_inv,
const int numIters)
1434 *out_ <<
"powerMethod:" << endl;
1437 const ST zero =
static_cast<ST
> (0.0);
1438 V x (A.getDomainMap ());
1442 computeInitialGuessForPowerMethod (x,
false);
1444 ST lambdaMax = powerMethodWithInitGuess (A, D_inv, numIters, x);
1453 if (STS::real (lambdaMax) < STS::real (zero)) {
1455 *out_ <<
"real(lambdaMax) = " << STS::real (lambdaMax) <<
" < 0; "
1456 "try again with a different random initial guess" << endl;
1465 computeInitialGuessForPowerMethod (x,
true);
1466 lambdaMax = powerMethodWithInitGuess (A, D_inv, numIters, x);
1471 template<
class ScalarType,
class MV>
1477 template<
class ScalarType,
class MV>
1485 template<
class ScalarType,
class MV>
1495 const size_t B_numVecs = B.getNumVectors ();
1496 if (W_.is_null () || W_->getNumVectors () != B_numVecs) {
1497 W_ =
Teuchos::rcp (
new MV (B.getMap (), B_numVecs,
true));
1502 template<
class ScalarType,
class MV>
1506 std::ostringstream oss;
1509 oss <<
"\"Ifpack2::Details::Chebyshev\":"
1511 <<
"degree: " << numIters_
1512 <<
", lambdaMax: " << lambdaMaxForApply_
1513 <<
", alpha: " << eigRatioForApply_
1514 <<
", lambdaMin: " << lambdaMinForApply_
1515 <<
", boost factor: " << boostFactor_
1520 template<
class ScalarType,
class MV>
1547 if (A_.is_null () || A_->getComm ().is_null ()) {
1551 myRank = A_->getComm ()->getRank ();
1556 out <<
"\"Ifpack2::Details::Chebyshev\":" << endl;
1562 out <<
"degree: " << numIters_ << endl
1563 <<
"lambdaMax: " << lambdaMaxForApply_ << endl
1564 <<
"alpha: " << eigRatioForApply_ << endl
1565 <<
"lambdaMin: " << lambdaMinForApply_ << endl
1566 <<
"boost factor: " << boostFactor_ << endl;
1574 out <<
"Template parameters:" << endl;
1577 out <<
"ScalarType: " << TypeNameTraits<ScalarType>::name () << endl
1578 <<
"MV: " << TypeNameTraits<MV>::name () << endl;
1584 out <<
"Computed parameters:" << endl;
1596 if (D_.is_null ()) {
1598 out <<
"unset" << endl;
1603 out <<
"set" << endl;
1612 D_->describe (out, vl);
1617 out <<
"W_: " << (W_.is_null () ?
"unset" :
"set") << endl
1618 <<
"computedLambdaMax_: " << computedLambdaMax_ << endl
1619 <<
"computedLambdaMin_: " << computedLambdaMin_ << endl
1620 <<
"lambdaMaxForApply_: " << lambdaMaxForApply_ << endl
1621 <<
"lambdaMinForApply_: " << lambdaMinForApply_ << endl
1622 <<
"eigRatioForApply_: " << eigRatioForApply_ << endl;
1627 out <<
"User parameters:" << endl;
1633 out <<
"userInvDiag_: ";
1634 if (userInvDiag_.is_null ()) {
1635 out <<
"unset" << endl;
1638 out <<
"set" << endl;
1644 userInvDiag_->describe (out, vl);
1647 out <<
"userLambdaMax_: " << userLambdaMax_ << endl
1648 <<
"userLambdaMin_: " << userLambdaMin_ << endl
1649 <<
"userEigRatio_: " << userEigRatio_ << endl
1650 <<
"numIters_: " << numIters_ << endl
1651 <<
"eigMaxIters_: " << eigMaxIters_ << endl
1652 <<
"zeroStartingSolution_: " << zeroStartingSolution_ << endl
1653 <<
"assumeMatrixUnchanged_: " << assumeMatrixUnchanged_ << endl
1654 <<
"textbookAlgorithm_: " << textbookAlgorithm_ << endl
1655 <<
"computeMaxResNorm_: " << computeMaxResNorm_ << endl;
1663 #define IFPACK2_DETAILS_CHEBYSHEV_INSTANT(S,LO,GO,N) \
1664 template class Ifpack2::Details::Chebyshev< S, Tpetra::MultiVector<S, LO, GO, N> >;
1666 #endif // IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
void setParameters(Teuchos::ParameterList &plist)
Set (or reset) parameters.
Definition: Ifpack2_Details_Chebyshev_def.hpp:333
MT apply(const MV &B, MV &X)
Solve Ax=b for x with Chebyshev iteration with left diagonal scaling.
Definition: Ifpack2_Details_Chebyshev_def.hpp:905
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Print a description of the Chebyshev solver to out.
Definition: Ifpack2_Details_Chebyshev_def.hpp:1523
T & get(const std::string &name, T def_value)
void setMatrix(const Teuchos::RCP< const row_matrix_type > &A)
Set the matrix.
Definition: Ifpack2_Details_Chebyshev_def.hpp:697
ParameterList & set(std::string const &name, T const &value, std::string const &docString="", RCP< const ParameterEntryValidator > const &validator=null)
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Teuchos::RCP< const row_matrix_type > getMatrix() const
Get the matrix given to the constructor.
Definition: Ifpack2_Details_Chebyshev_def.hpp:1473
Chebyshev(Teuchos::RCP< const row_matrix_type > A)
Definition: Ifpack2_Details_Chebyshev_def.hpp:279
bool isParameter(const std::string &name) const
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Diagonally scaled Chebyshev iteration for Tpetra sparse matrices.
Definition: Ifpack2_Chebyshev_decl.hpp:199
void print(std::ostream &out)
Print instance data to the given output stream.
Definition: Ifpack2_Details_Chebyshev_def.hpp:958
STS::magnitudeType MT
The type of the absolute value of a ScalarType.
Definition: Ifpack2_Details_Chebyshev_decl.hpp:116
ScalarType ST
The type of entries in the matrix and vectors.
Definition: Ifpack2_Details_Chebyshev_decl.hpp:112
Left-scaled Chebyshev iteration.
Definition: Ifpack2_Details_Chebyshev_decl.hpp:106
Declaration of Chebyshev implementation.
std::string description() const
A single-line description of the Chebyshev solver.
Definition: Ifpack2_Details_Chebyshev_def.hpp:1505
TypeTo as(const TypeFrom &t)
bool isType(const std::string &name) const
bool hasTransposeApply() const
Whether it's possible to apply the transpose of this operator.
Definition: Ifpack2_Details_Chebyshev_def.hpp:1480
void compute()
(Re)compute the left scaling D_inv, and estimate min and max eigenvalues of D_inv * A...
Definition: Ifpack2_Details_Chebyshev_def.hpp:738
basic_oblackholestream< char, std::char_traits< char > > oblackholestream