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)
334 checkConstructorInput();
338template <
class ScalarType,
class MV>
343 using Teuchos::rcp_const_cast;
355 const ST defaultLambdaMax = STS::nan();
356 const ST defaultLambdaMin = STS::nan();
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;
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;
384 RCP<const V> userInvDiagCopy;
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;
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;
426 const char opInvDiagLabel[] =
"chebyshev: operator inv diagonal";
427 if (plist.isParameter(opInvDiagLabel)) {
429 RCP<const V> userInvDiag;
431 if (plist.isType<
const V*>(opInvDiagLabel)) {
432 const V* rawUserInvDiag =
433 plist.get<
const V*>(opInvDiagLabel);
435 userInvDiag = rcp(rawUserInvDiag,
false);
436 }
else if (plist.isType<
const V*>(opInvDiagLabel)) {
437 V* rawUserInvDiag = plist.get<
V*>(opInvDiagLabel);
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;
464 if (!userInvDiag.is_null() && userInvDiagCopy.is_null()) {
465 userInvDiagCopy = rcp(
new V(*userInvDiag, Teuchos::Copy));
475 if (plist.isParameter(
"chebyshev: use native spmv"))
476 ckUseNativeSpMV = plist.get(
"chebyshev: use native spmv", ckUseNativeSpMV);
479 if (plist.isParameter(
"chebyshev: pre-allocate temp vector"))
480 preAllocateTempVector = plist.get(
"chebyshev: pre-allocate temp vector", preAllocateTempVector);
485 if (plist.isParameter(
"chebyshev: max eigenvalue")) {
486 if (plist.isType<
double>(
"chebyshev: max eigenvalue"))
487 lambdaMax = plist.get<
double>(
"chebyshev: max eigenvalue");
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.");
496 if (plist.isParameter(
"chebyshev: min eigenvalue")) {
497 if (plist.isType<
double>(
"chebyshev: min eigenvalue"))
498 lambdaMin = plist.get<
double>(
"chebyshev: min eigenvalue");
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.");
509 if (plist.isParameter(
"smoother: Chebyshev alpha")) {
510 if (plist.isType<
double>(
"smoother: Chebyshev alpha"))
511 eigRatio = plist.get<
double>(
"smoother: Chebyshev alpha");
513 eigRatio = plist.get<
ST>(
"smoother: Chebyshev alpha");
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 "
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 "
539 const char paramName[] =
"chebyshev: boost factor";
541 if (plist.isParameter(paramName)) {
542 if (plist.isType<
MT>(paramName)) {
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);
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.");
560 plist.set(paramName, defaultBoostFactor);
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 <<
".");
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.");
577 if (plist.isParameter(
"smoother: sweeps")) {
578 numIters = plist.get<
int>(
"smoother: sweeps");
580 if (plist.isParameter(
"relaxation: sweeps")) {
581 numIters = plist.get<
int>(
"relaxation: sweeps");
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 "
592 if (plist.isParameter(
"eigen-analysis: iterations")) {
593 eigMaxIters = plist.get<
int>(
"eigen-analysis: iterations");
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 <<
".");
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"));
610 eigKeepVectors = plist.get(
"chebyshev: eigenvalue keep vectors", eigKeepVectors);
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 <<
".")
620 zeroStartingSolution = plist.get(
"chebyshev: zero starting solution",
621 zeroStartingSolution);
622 assumeMatrixUnchanged = plist.get(
"chebyshev: assume matrix does not change",
623 assumeMatrixUnchanged);
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\".");
638 if (plist.isParameter(
"chebyshev: compute max residual norm")) {
639 computeMaxResNorm = plist.get<
bool>(
"chebyshev: compute max residual norm");
641 if (plist.isParameter(
"chebyshev: compute spectral radius")) {
642 computeSpectralRadius = plist.get<
bool>(
"chebyshev: compute spectral radius");
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.");
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\".");
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;
704 if (A_.is_null() || A_->getComm().is_null()) {
709 myRank = A_->getComm()->getRank();
713 out_ = Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
715 using Teuchos::oblackholestream;
716 RCP<oblackholestream> blackHole(
new oblackholestream());
717 out_ = Teuchos::getFancyOStream(blackHole);
721 out_ = Teuchos::null;
725template <
class ScalarType,
class MV>
726void Chebyshev<ScalarType, MV>::reset() {
729 diagOffsets_ = offsets_type();
730 savedDiagOffsets_ =
false;
732 computedLambdaMax_ = STS::nan();
733 computedLambdaMin_ = STS::nan();
734 eigVector_ = Teuchos::null;
735 eigVector2_ = Teuchos::null;
738template <
class ScalarType,
class MV>
740 setMatrix(
const Teuchos::RCP<const row_matrix_type>& A) {
741 if (A.getRawPtr() != A_.getRawPtr()) {
742 if (!assumeMatrixUnchanged_) {
754 if (A.is_null() || A->getComm().is_null()) {
759 myRank = A->getComm()->getRank();
763 out_ = Teuchos::getFancyOStream(Teuchos::rcpFromRef(std::cerr));
765 Teuchos::RCP<Teuchos::oblackholestream> blackHole(
new Teuchos::oblackholestream());
766 out_ = Teuchos::getFancyOStream(blackHole);
770 out_ = Teuchos::null;
775template <
class ScalarType,
class MV>
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>
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.");
807 if (userInvDiag_.is_null()) {
808 Teuchos::RCP<const crs_matrix_type> A_crsMat =
809 Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_);
811 if (!A_crsMat.is_null() && A_crsMat->isFillComplete()) {
813 const size_t lclNumRows = A_crsMat->getLocalNumRows();
814 if (diagOffsets_.extent(0) < lclNumRows) {
815 diagOffsets_ = offsets_type();
816 diagOffsets_ = offsets_type(
"offsets", lclNumRows);
818 A_crsMat->getCrsGraph()->getLocalDiagOffsets(diagOffsets_);
819 savedDiagOffsets_ =
true;
820 D_ = makeInverseDiagonal(*A_,
true);
822 D_ = makeInverseDiagonal(*A_);
824 }
else if (!assumeMatrixUnchanged_) {
825 if (!A_crsMat.is_null() && A_crsMat->isFillComplete()) {
828 if (!savedDiagOffsets_) {
829 const size_t lclNumRows = A_crsMat->getLocalNumRows();
830 if (diagOffsets_.extent(0) < lclNumRows) {
831 diagOffsets_ = offsets_type();
832 diagOffsets_ = offsets_type(
"offsets", lclNumRows);
834 A_crsMat->getCrsGraph()->getLocalDiagOffsets(diagOffsets_);
835 savedDiagOffsets_ =
true;
838 D_ = makeInverseDiagonal(*A_,
true);
840 D_ = makeInverseDiagonal(*A_);
844 D_ = makeRangeMapVectorConst(userInvDiag_);
848 const bool computedEigenvalueEstimates =
849 STS::isnaninf(computedLambdaMax_) || STS::isnaninf(computedLambdaMin_);
859 if (!assumeMatrixUnchanged_ ||
860 (!computedEigenvalueEstimates && STS::isnaninf(userLambdaMax_))) {
861 ST computedLambdaMax;
862 if ((eigenAnalysisType_ ==
"power method") || (eigenAnalysisType_ ==
"power-method")) {
864 if (eigVector_.is_null()) {
865 x = Teuchos::rcp(
new V(A_->getDomainMap()));
873 if (eigVector2_.is_null()) {
874 y = rcp(
new V(A_->getRangeMap()));
880 Teuchos::RCP<Teuchos::FancyOStream> stream = (debug_ ? out_ : Teuchos::null);
882 eigRelTolerance_, eigNormalizationFreq_, stream,
883 computeSpectralRadius_);
885 computedLambdaMax = cgMethod(*A_, *D_, eigMaxIters_);
887 TEUCHOS_TEST_FOR_EXCEPTION(
888 STS::isnaninf(computedLambdaMax),
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_),
896 "Ifpack2::Chebyshev::compute: userEigRatio_ is Inf or NaN."
898 <<
"This should be impossible." << endl
899 <<
"Please report this bug to the Ifpack2 developers.");
905 const ST computedLambdaMin = computedLambdaMax / userEigRatio_;
908 computedLambdaMax_ = computedLambdaMax;
909 computedLambdaMin_ = computedLambdaMin;
911 TEUCHOS_TEST_FOR_EXCEPTION(
912 STS::isnaninf(userLambdaMax_) && STS::isnaninf(computedLambdaMax_),
914 "Ifpack2::Chebyshev::compute: " << endl
915 <<
"Both userLambdaMax_ and computedLambdaMax_ are Inf or NaN."
917 <<
"This should be impossible." << endl
918 <<
"Please report this bug to the Ifpack2 developers.");
926 lambdaMaxForApply_ = STS::isnaninf(userLambdaMax_) ? computedLambdaMax_ : userLambdaMax_;
939 lambdaMinForApply_ = lambdaMaxForApply_ / userEigRatio_;
940 eigRatioForApply_ = userEigRatio_;
942 if (chebyshevAlgorithm_ ==
"first") {
945 const ST one = Teuchos::as<ST>(1);
948 if (STS::magnitude(lambdaMaxForApply_ - one) < Teuchos::as<MT>(1.0e-6)) {
949 lambdaMinForApply_ = one;
950 lambdaMaxForApply_ = lambdaMinForApply_;
951 eigRatioForApply_ = one;
956 if (preAllocateTempVector_ && !D_.is_null()) {
957 makeTempMultiVector(*D_);
958 if (chebyshevAlgorithm_ ==
"fourth" || chebyshevAlgorithm_ ==
"opt_fourth") {
959 makeSecondTempMultiVector(*D_);
963 if (chebyshevAlgorithm_ ==
"textbook") {
969 if (ckUseNativeSpMV_) {
970 ck_->setAuxiliaryVectors(1);
975template <
class ScalarType,
class MV>
979 return lambdaMaxForApply_;
982template <
class ScalarType,
class MV>
985 const char prefix[] =
"Ifpack2::Chebyshev::apply: ";
988 *out_ <<
"apply: " << std::endl;
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."
996 << computeBeforeApplyReminder);
997 TEUCHOS_TEST_FOR_EXCEPTION(STS::isnaninf(lambdaMinForApply_), std::runtime_error,
998 prefix <<
"There is no estimate for the min eigenvalue."
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."
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."
1009 << computeBeforeApplyReminder);
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_);
1017 ifpackApplyImpl(*A_, B, X, numIters_, lambdaMaxForApply_,
1018 lambdaMinForApply_, eigRatioForApply_, *D_);
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());
1026 return *std::max_element(norms.begin(), norms.end());
1028 return Teuchos::ScalarTraits<MT>::zero();
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);
1040template <
class ScalarType,
class MV>
1043 const ScalarType& alpha,
1047 solve(W, alpha, D_inv, B);
1048 Tpetra::deep_copy(X, W);
1051template <
class ScalarType,
class MV>
1054 const Teuchos::ETransp mode) {
1055 Tpetra::Details::residual(A, X, B, R);
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());
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());
1070template <
class ScalarType,
class MV>
1071Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::V>
1075 using Teuchos::rcp_dynamic_cast;
1076 using Teuchos::rcpFromRef;
1079 if (!D_.is_null() &&
1080 D_->getMap()->isSameAs(*(A.getRowMap()))) {
1082 *out_ <<
"Reusing pre-existing vector for diagonal extraction" << std::endl;
1083 D_rowMap = Teuchos::rcp_const_cast<V>(D_);
1085 D_rowMap = Teuchos::rcp(
new V(A.getRowMap(),
false));
1087 *out_ <<
"Allocated new vector for diagonal extraction" << std::endl;
1089 if (useDiagOffsets) {
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>
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_);
1113 A.getLocalDiagCopy(*D_rowMap);
1115 RCP<V> D_rangeMap = makeRangeMapVector(D_rowMap);
1121 bool foundNonpositiveValue =
false;
1123 auto D_lcl = D_rangeMap->getLocalViewHost(Tpetra::Access::ReadOnly);
1124 auto D_lcl_1d = Kokkos::subview(D_lcl, Kokkos::ALL(), 0);
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;
1131 typedef Kokkos::ArithTraits<IST> ATS;
1133#if KOKKOS_VERSION >= 40799
1134 typedef KokkosKernels::ArithTraits<typename ATS::mag_type> STM;
1136 typedef Kokkos::ArithTraits<typename ATS::mag_type> STM;
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;
1148 using Teuchos::outArg;
1149 using Teuchos::REDUCE_MIN;
1150 using Teuchos::reduceAll;
1152 const int lclSuccess = foundNonpositiveValue ? 0 : 1;
1153 int gblSuccess = lclSuccess;
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));
1158 if (gblSuccess == 1) {
1159 *out_ <<
"makeInverseDiagonal: The matrix's diagonal entries all have "
1160 "positive real part (this is good for Chebyshev)."
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."
1172 reciprocal_threshold(*D_rangeMap, minDiagVal_);
1173 return Teuchos::rcp_const_cast<const V>(D_rangeMap);
1176template <
class ScalarType,
class MV>
1177Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::V>
1182 typedef Tpetra::Export<
typename MV::local_ordinal_type,
1183 typename MV::global_ordinal_type,
1184 typename MV::node_type>
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.");
1201 RCP<const map_type> sourceMap = D->getMap();
1202 RCP<const map_type> rangeMap = A_->getRangeMap();
1203 RCP<const map_type> rowMap = A_->getRowMap();
1205 if (rangeMap->isSameAs(*sourceMap)) {
1210 RCP<const export_type> exporter;
1214 if (sourceMap->isSameAs(*rowMap)) {
1216 exporter = A_->getGraph()->getExporter();
1218 exporter = rcp(
new export_type(sourceMap, rangeMap));
1221 if (exporter.is_null()) {
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);
1231template <
class ScalarType,
class MV>
1232Teuchos::RCP<typename Chebyshev<ScalarType, MV>::V>
1235 using Teuchos::rcp_const_cast;
1236 return rcp_const_cast<V>(makeRangeMapVectorConst(rcp_const_cast<V>(D)));
1239template <
class ScalarType,
class MV>
1248 const V& D_inv)
const {
1250 const ST myLambdaMin = lambdaMax / eigRatio;
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;
1256 const ST c = (lambdaMax - myLambdaMin) / two;
1258 if (zeroStartingSolution_ && numIters > 0) {
1262 MV R(B.getMap(), B.getNumVectors(),
false);
1263 MV P(B.getMap(), B.getNumVectors(),
false);
1264 MV Z(B.getMap(), B.getNumVectors(),
false);
1266 for (
int i = 0; i < numIters; ++i) {
1267 computeResidual(R, B, A, X);
1276 beta = alpha * (c / two) * (c / two);
1277 alpha = one / (d - beta);
1278 P.update(one, Z, beta);
1280 X.update(alpha, P, one);
1287template <
class ScalarType,
class MV>
1296 std::vector<ScalarType> betas(numIters, 1.0);
1297 if (chebyshevAlgorithm_ ==
"opt_fourth") {
1301 const ST invEig = MT(1) / (lambdaMax * boostFactor_);
1304 Teuchos::RCP<MV> Z_ptr = makeTempMultiVector(B);
1309 Teuchos::RCP<MV> X4_ptr = makeSecondTempMultiVector(B);
1313 if (!zeroStartingSolution_) {
1315 Tpetra::deep_copy(X4, X);
1317 if (ck_.is_null()) {
1318 Teuchos::RCP<const op_type> A_op = A_;
1323 ck_->compute(Z, MT(4.0 / 3.0) * invEig,
const_cast<V&
>(D_inv),
1324 const_cast<MV&
>(B), X4, STS::zero());
1327 X.update(betas[0], Z, STS::one());
1330 firstIterationWithZeroStartingSolution(Z, MT(4.0 / 3.0) * invEig, D_inv, B, X4);
1333 X.update(betas[0], Z, STS::zero());
1336 if (numIters > 1 && ck_.is_null()) {
1337 Teuchos::RCP<const op_type> A_op = A_;
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;
1347 ck_->compute(Z, rScale,
const_cast<V&
>(D_inv),
1348 const_cast<MV&
>(B), (X4), zScale);
1351 X.update(betas[i], Z, STS::one());
1355template <
class ScalarType,
class MV>
1357Chebyshev<ScalarType, MV>::maxNormInf(
const MV& X) {
1358 Teuchos::Array<MT> norms(X.getNumVectors());
1360 return *std::max_element(norms.begin(), norms.end());
1363template <
class ScalarType,
class MV>
1374#ifdef HAVE_IFPACK2_DEBUG
1375 const bool debug = debug_;
1377 const bool debug =
false;
1381 *out_ <<
" \\|B\\|_{\\infty} = " << maxNormInf(B) << endl;
1382 *out_ <<
" \\|X\\|_{\\infty} = " << maxNormInf(X) << endl;
1385 if (numIters <= 0) {
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);
1393 if (lambdaMin == one && lambdaMax == lambdaMin) {
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;
1406 *out_ <<
" alpha = " << alpha << endl
1407 <<
" beta = " << beta << endl
1408 <<
" delta = " << delta << endl
1409 <<
" theta = " << theta << endl
1410 <<
" s1 = " << s1 << endl;
1414 Teuchos::RCP<MV> W_ptr = makeTempMultiVector(B);
1418 *out_ <<
" Iteration " << 1 <<
":" << endl
1419 <<
" - \\|D\\|_{\\infty} = " << D_->normInf() << endl;
1423 if (!zeroStartingSolution_) {
1426 if (ck_.is_null()) {
1427 Teuchos::RCP<const op_type> A_op = A_;
1432 ck_->compute(W, one / theta,
const_cast<V&
>(D_inv),
1433 const_cast<MV&
>(B), X, zero);
1436 firstIterationWithZeroStartingSolution(W, one / theta, D_inv, B, X);
1440 *out_ <<
" - \\|W\\|_{\\infty} = " << maxNormInf(W) << endl
1441 <<
" - \\|X\\|_{\\infty} = " << maxNormInf(X) << endl;
1444 if (numIters > 1 && ck_.is_null()) {
1445 Teuchos::RCP<const op_type> A_op = A_;
1451 ST rhokp1, dtemp1, dtemp2;
1452 for (
int deg = 1; deg < numIters; ++deg) {
1454 *out_ <<
" Iteration " << deg + 1 <<
":" << endl
1455 <<
" - \\|D\\|_{\\infty} = " << D_->normInf() << endl
1456 <<
" - \\|B\\|_{\\infty} = " << maxNormInf(B) << endl
1457 <<
" - \\|A\\|_{\\text{frob}} = " << A_->getFrobeniusNorm()
1459 <<
" - rhok = " << rhok << endl;
1462 rhokp1 = one / (two * s1 - rhok);
1463 dtemp1 = rhokp1 * rhok;
1464 dtemp2 = two * rhokp1 * delta;
1468 *out_ <<
" - dtemp1 = " << dtemp1 << endl
1469 <<
" - dtemp2 = " << dtemp2 << endl;
1474 ck_->compute(W, dtemp2,
const_cast<V&
>(D_inv),
1475 const_cast<MV&
>(B), (X), dtemp1);
1478 *out_ <<
" - \\|W\\|_{\\infty} = " << maxNormInf(W) << endl
1479 <<
" - \\|X\\|_{\\infty} = " << maxNormInf(X) << endl;
1484template <
class ScalarType,
class MV>
1492 using MagnitudeType =
typename STS::magnitudeType;
1494 *out_ <<
" cgMethodWithInitGuess:" << endl;
1497 const ST one = STS::one();
1498 ST beta, betaOld = one, pAp, pApOld = one, alpha, rHz, rHzOld, rHzOld2 = one, lambdaMax;
1500 Teuchos::ArrayRCP<MagnitudeType> diag, offdiag;
1501 Teuchos::RCP<V> p, z, Ap;
1502 diag.resize(numIters);
1503 offdiag.resize(numIters - 1);
1505 p = rcp(
new V(A.getRangeMap()));
1506 z = rcp(
new V(A.getRangeMap()));
1507 Ap = rcp(
new V(A.getRangeMap()));
1510 solve(*p, D_inv, r);
1513 for (
int iter = 0; iter < numIters; ++iter) {
1515 *out_ <<
" Iteration " << iter << endl;
1520 r.update(-alpha, *Ap, one);
1522 solve(*z, D_inv, r);
1524 beta = rHz / rHzOld;
1525 p->update(one, *z, beta);
1527 diag[iter] = STS::real((betaOld * betaOld * pApOld + pAp) / rHzOld);
1528 offdiag[iter - 1] = -STS::real((betaOld * pApOld / (sqrt(rHzOld * rHzOld2))));
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;
1537 diag[iter] = STS::real(pAp / rHzOld);
1539 *out_ <<
" diag[" << iter <<
"] = " << diag[iter] << endl;
1540 *out_ <<
" rHz = " << rHz << endl;
1541 *out_ <<
" alpha = " << alpha << endl;
1542 *out_ <<
" beta = " << beta << endl;
1550 lambdaMax = LapackHelper<ST>::tri_diag_spectral_radius(diag, offdiag);
1555template <
class ScalarType,
class MV>
1558 cgMethod(
const op_type& A,
const V& D_inv,
const int numIters) {
1562 *out_ <<
"cgMethod:" << endl;
1566 if (eigVector_.is_null()) {
1567 r = rcp(
new V(A.getDomainMap()));
1568 if (eigKeepVectors_)
1571 Details::computeInitialGuessForCG(D_inv, *r);
1575 ST lambdaMax = cgMethodWithInitGuess(A, D_inv, numIters, *r);
1580template <
class ScalarType,
class MV>
1581Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::row_matrix_type>
1586template <
class ScalarType,
class MV>
1593template <
class ScalarType,
class MV>
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));
1608template <
class ScalarType,
class MV>
1610Chebyshev<ScalarType, MV>::
1611 makeSecondTempMultiVector(
const MV& B) {
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));
1623template <
class ScalarType,
class MV>
1627 std::ostringstream oss;
1630 oss <<
"\"Ifpack2::Details::Chebyshev\":"
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";
1644template <
class ScalarType,
class MV>
1646 describe(Teuchos::FancyOStream& out,
1647 const Teuchos::EVerbosityLevel verbLevel)
const {
1649 using Teuchos::TypeNameTraits;
1651 const Teuchos::EVerbosityLevel vl =
1652 (verbLevel == Teuchos::VERB_DEFAULT) ? Teuchos::VERB_LOW : verbLevel;
1653 if (vl == Teuchos::VERB_NONE) {
1663 Teuchos::OSTab tab0(out);
1669 if (A_.is_null() || A_->getComm().is_null()) {
1672 myRank = A_->getComm()->getRank();
1677 out <<
"\"Ifpack2::Details::Chebyshev\":" << endl;
1679 Teuchos::OSTab tab1(out);
1681 if (vl == Teuchos::VERB_LOW) {
1683 out <<
"degree: " << numIters_ << endl
1684 <<
"lambdaMax: " << lambdaMaxForApply_ << endl
1685 <<
"alpha: " << eigRatioForApply_ << endl
1686 <<
"lambdaMin: " << lambdaMinForApply_ << endl
1687 <<
"boost factor: " << boostFactor_ << endl;
1695 out <<
"Template parameters:" << endl;
1697 Teuchos::OSTab tab2(out);
1698 out <<
"ScalarType: " << TypeNameTraits<ScalarType>::name() << endl
1699 <<
"MV: " << TypeNameTraits<MV>::name() << endl;
1705 out <<
"Computed parameters:" << endl;
1711 Teuchos::OSTab tab2(out);
1719 out <<
"unset" << endl;
1721 }
else if (vl <= Teuchos::VERB_HIGH) {
1723 out <<
"set" << endl;
1731 D_->describe(out, vl);
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;
1746 out <<
"User parameters:" << endl;
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;
1761 userInvDiag_->describe(out, vl);
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;