Ifpack2 Templated Preconditioning Package  Version 1.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends
Ifpack2_Details_Chebyshev_def.hpp
Go to the documentation of this file.
00001 /*
00002 //@HEADER
00003 // ***********************************************************************
00004 //
00005 //       Ifpack2: Tempated Object-Oriented Algebraic Preconditioner Package
00006 //                 Copyright (2009) Sandia Corporation
00007 //
00008 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00009 // license for use of this work by or on behalf of the U.S. Government.
00010 //
00011 // Redistribution and use in source and binary forms, with or without
00012 // modification, are permitted provided that the following conditions are
00013 // met:
00014 //
00015 // 1. Redistributions of source code must retain the above copyright
00016 // notice, this list of conditions and the following disclaimer.
00017 //
00018 // 2. Redistributions in binary form must reproduce the above copyright
00019 // notice, this list of conditions and the following disclaimer in the
00020 // documentation and/or other materials provided with the distribution.
00021 //
00022 // 3. Neither the name of the Corporation nor the names of the
00023 // contributors may be used to endorse or promote products derived from
00024 // this software without specific prior written permission.
00025 //
00026 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00027 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00028 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00029 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00030 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00031 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00032 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00033 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00034 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00035 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00036 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00037 //
00038 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
00039 //
00040 // ***********************************************************************
00041 //@HEADER
00042 */
00043 
00044 // ***********************************************************************
00045 //
00046 //      Ifpack2: Templated Object-Oriented Algebraic Preconditioner Package
00047 //                 Copyright (2004) Sandia Corporation
00048 //
00049 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00050 // license for use of this work by or on behalf of the U.S. Government.
00051 //
00052 // This library is free software; you can redistribute it and/or modify
00053 // it under the terms of the GNU Lesser General Public License as
00054 // published by the Free Software Foundation; either version 2.1 of the
00055 // License, or (at your option) any later version.
00056 //
00057 // This library is distributed in the hope that it will be useful, but
00058 // WITHOUT ANY WARRANTY; without even the implied warranty of
00059 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00060 // Lesser General Public License for more details.
00061 //
00062 // You should have received a copy of the GNU Lesser General Public
00063 // License along with this library; if not, write to the Free Software
00064 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
00065 // USA
00066 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
00067 //
00068 // ***********************************************************************
00069 
00070 #ifndef IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
00071 #define IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
00072 
00079 
00080 #include "Ifpack2_Details_Chebyshev_decl.hpp"
00081 #include <cmath>
00082 
00083 // Uncommit the #define line below if you want Chebyshev to do extra
00084 // debug checking and generate a lot of debugging output to stderr (on
00085 // all processes in the communicator).  Even if you uncomment this
00086 // line, the debugging code will only be enabled if the CMake option
00087 // Teuchos_ENABLE_DEBUG was set to ON when configuring Trilinos.
00088 //#define IFPACK_DETAILS_CHEBYSHEV_DEBUG 1
00089 
00090 namespace Ifpack2 {
00091 namespace Details {
00092 
00093 namespace {
00094   // We use this text a lot in error messages.
00095   const char computeBeforeApplyReminder[] =
00096     "This means one of the following:\n"
00097     "  - you have not yet called compute() on this instance, or \n"
00098     "  - you didn't call compute() after calling setParameters().\n\n"
00099     "After creating an Ifpack2::Chebyshev instance,\n"
00100     "you must _always_ call compute() at least once before calling apply().\n"
00101     "After calling compute() once, you do not need to call it again,\n"
00102     "unless the matrix has changed or you have changed parameters\n"
00103     "(by calling setParameters()).";
00104 
00105   // Utility function for inverting diagonal
00106   //
00107   // FIXME (mfh 06 Oct 2014) Ifpack2's CMake isn't defining
00108   // HAVE_IFPACK2_KOKKOSCLASSIC for some reason, even though
00109   // KokkosClassic is listed as a dependency of Ifpack2.  We'll need
00110   // to protect use of KokkosClassic with some kind of macro, at some
00111   // point, once we deprecate the KokkosClassic subpackage.
00112   template <typename S, typename L, typename G, typename N>
00113   void
00114   reciprocal_threshold (const Tpetra::Vector<S,L,G,N>& v,
00115                         const S& min_val)
00116   {
00117     typedef KokkosClassic::MultiVector<S,N> KMV;
00118     typedef KokkosClassic::DefaultArithmetic<KMV> KMVT;
00119     KMV local_v = v.getLocalMV ();
00120     KMVT::ReciprocalThreshold (local_v, min_val);
00121   }
00122 
00123   // mfh 06 Oct 2014: This specialization is still valid, whether or
00124   // not we allow the above default implementation that uses
00125   // KokkosClassic.
00126 #ifdef TPETRA_HAVE_KOKKOS_REFACTOR
00127   template <typename S, typename L, typename G, typename D>
00128   void reciprocal_threshold(
00129     const Tpetra::Vector<S,L,G,Kokkos::Compat::KokkosDeviceWrapperNode<D> >& v,
00130     const S& min_val ) {
00131     Kokkos::MV_ReciprocalThreshold( v.template getLocalView<D>(),
00132                                     min_val );
00133   }
00134 #endif // TPETRA_HAVE_KOKKOS_REFACTOR
00135 
00136 } // namespace (anonymous)
00137 
00138 template<class ScalarType, class MV>
00139 void Chebyshev<ScalarType, MV>::checkInputMatrix () const
00140 {
00141   TEUCHOS_TEST_FOR_EXCEPTION(
00142     ! A_.is_null () && A_->getGlobalNumRows () != A_->getGlobalNumCols (),
00143     std::invalid_argument,
00144     "Ifpack2::Chebyshev: The input matrix A must be square.  "
00145     "A has " << A_->getGlobalNumRows () << " rows and "
00146     << A_->getGlobalNumCols () << " columns.");
00147 
00148   // In a debug build, test that the domain and range Maps of the
00149   // matrix are the same.
00150 #ifdef HAVE_TEUCHOS_DEBUG
00151   if (! A_.is_null ()) {
00152     Teuchos::RCP<const map_type> domainMap = A_->getDomainMap ();
00153     Teuchos::RCP<const map_type> rangeMap = A_->getRangeMap ();
00154 
00155     // isSameAs is a collective, but if the two pointers are the same,
00156     // isSameAs will assume that they are the same on all processes, and
00157     // return true without an all-reduce.
00158     TEUCHOS_TEST_FOR_EXCEPTION(
00159       ! domainMap->isSameAs (*rangeMap), std::invalid_argument,
00160       "Ifpack2::Chebyshev: The domain Map and range Map of the matrix must be "
00161       "the same (in the sense of isSameAs())." << std::endl << "We only check "
00162       "for this if Trilinos was built with the CMake configuration option "
00163       "Teuchos_ENABLE_DEBUG set to ON.");
00164   }
00165 #endif // HAVE_TEUCHOS_DEBUG
00166 }
00167 
00168 
00169 template<class ScalarType, class MV>
00170 void
00171 Chebyshev<ScalarType, MV>::
00172 checkConstructorInput () const
00173 {
00174   TEUCHOS_TEST_FOR_EXCEPTION(STS::isComplex, std::logic_error,
00175     "Ifpack2::Chebyshev: This class' implementation of Chebyshev iteration "
00176     "only works for real-valued, symmetric positive definite matrices.  "
00177     "However, you instantiated this class for ScalarType = "
00178     << Teuchos::TypeNameTraits<ScalarType>::name () << ", which is a complex-"
00179     "valued type.  While this may be algorithmically correct if all of the "
00180     "complex numbers in the matrix have zero imaginary part, we forbid using "
00181     "complex ScalarType altogether in order to remind you of the limitations "
00182     "of our implementation (and of the algorithm itself).");
00183 
00184   checkInputMatrix ();
00185 }
00186 
00187 template<class ScalarType, class MV>
00188 Chebyshev<ScalarType, MV>::
00189 Chebyshev (Teuchos::RCP<const row_matrix_type> A) :
00190   A_ (A),
00191   savedDiagOffsets_ (false),
00192   computedLambdaMax_ (STS::nan ()),
00193   computedLambdaMin_ (STS::nan ()),
00194   lambdaMaxForApply_ (STS::nan ()),
00195   lambdaMinForApply_ (STS::nan ()),
00196   eigRatioForApply_ (STS::nan ()),
00197   userLambdaMax_ (STS::nan ()),
00198   userLambdaMin_ (STS::nan ()),
00199   userEigRatio_ (Teuchos::as<ST> (30)),
00200   minDiagVal_ (STS::eps ()),
00201   numIters_ (1),
00202   eigMaxIters_ (10),
00203   zeroStartingSolution_ (true),
00204   assumeMatrixUnchanged_ (false),
00205   textbookAlgorithm_ (false),
00206   computeMaxResNorm_ (false)
00207 {
00208   checkConstructorInput ();
00209 }
00210 
00211 template<class ScalarType, class MV>
00212 Chebyshev<ScalarType, MV>::
00213 Chebyshev (Teuchos::RCP<const row_matrix_type> A, Teuchos::ParameterList& params) :
00214   A_ (A),
00215   savedDiagOffsets_ (false),
00216   computedLambdaMax_ (STS::nan ()),
00217   computedLambdaMin_ (STS::nan ()),
00218   lambdaMaxForApply_ (STS::nan ()),
00219   lambdaMinForApply_ (STS::nan ()),
00220   eigRatioForApply_ (STS::nan ()),
00221   userLambdaMax_ (STS::nan ()),
00222   userLambdaMin_ (STS::nan ()),
00223   userEigRatio_ (Teuchos::as<ST> (30)),
00224   minDiagVal_ (STS::eps ()),
00225   numIters_ (1),
00226   eigMaxIters_ (10),
00227   zeroStartingSolution_ (true),
00228   assumeMatrixUnchanged_ (false),
00229   textbookAlgorithm_ (false),
00230   computeMaxResNorm_ (false)
00231 {
00232   checkConstructorInput ();
00233   setParameters (params);
00234 }
00235 
00236 template<class ScalarType, class MV>
00237 void
00238 Chebyshev<ScalarType, MV>::
00239 setParameters (Teuchos::ParameterList& plist)
00240 {
00241   using Teuchos::RCP;
00242   using Teuchos::rcp;
00243   using Teuchos::rcp_const_cast;
00244 
00245   // Note to developers: The logic for this method is complicated,
00246   // because we want to accept Ifpack and ML parameters whenever
00247   // possible, but we don't want to add their default values to the
00248   // user's ParameterList.  That's why we do all the isParameter()
00249   // checks, instead of using the two-argument version of get()
00250   // everywhere.  The min and max eigenvalue parameters are also a
00251   // special case, because we decide whether or not to do eigenvalue
00252   // analysis based on whether the user supplied the max eigenvalue.
00253 
00254   // Default values of all the parameters.
00255   const ST defaultLambdaMax = STS::nan ();
00256   const ST defaultLambdaMin = STS::nan ();
00257   // 30 is Ifpack::Chebyshev's default.  ML has a different default
00258   // eigRatio for smoothers and the coarse-grid solve (if using
00259   // Chebyshev for that).  The former uses 20; the latter uses 30.
00260   // We're testing smoothers here, so use 20.  (However, if you give
00261   // ML an Epetra matrix, it will use Ifpack for Chebyshev, in which
00262   // case it would defer to Ifpack's default settings.)
00263   const ST defaultEigRatio = Teuchos::as<ST> (30);
00264   const ST defaultMinDiagVal = STS::eps ();
00265   const int defaultNumIters = 1;
00266   const int defaultEigMaxIters = 10;
00267   const bool defaultZeroStartingSolution = true; // Ifpack::Chebyshev default
00268   const bool defaultAssumeMatrixUnchanged = false;
00269   const bool defaultTextbookAlgorithm = false;
00270   const bool defaultComputeMaxResNorm = false;
00271 
00272   // We'll set the instance data transactionally, after all reads
00273   // from the ParameterList.  That way, if any of the ParameterList
00274   // reads fail (e.g., due to the wrong parameter type), we will not
00275   // have left the instance data in a half-changed state.
00276   RCP<const V> userInvDiagCopy; // if nonnull: deep copy of user's Vector
00277   ST lambdaMax = defaultLambdaMax;
00278   ST lambdaMin = defaultLambdaMin;
00279   ST eigRatio = defaultEigRatio;
00280   ST minDiagVal = defaultMinDiagVal;
00281   int numIters = defaultNumIters;
00282   int eigMaxIters = defaultEigMaxIters;
00283   bool zeroStartingSolution = defaultZeroStartingSolution;
00284   bool assumeMatrixUnchanged = defaultAssumeMatrixUnchanged;
00285   bool textbookAlgorithm = defaultTextbookAlgorithm;
00286   bool computeMaxResNorm = defaultComputeMaxResNorm;
00287 
00288   // Fetch the parameters from the ParameterList.  Defer all
00289   // externally visible side effects until we have finished all
00290   // ParameterList interaction.  This makes the method satisfy the
00291   // strong exception guarantee.
00292 
00293   // Get the user-supplied inverse diagonal.
00294   //
00295   // Check for a raw pointer (const V* or V*), for Ifpack
00296   // compatibility, as well as for RCP<const V>, RCP<V>, const V, or
00297   // V.  We'll make a deep copy of the vector at the end of this
00298   // method anyway, so its const-ness doesn't matter.  We handle the
00299   // latter two cases ("const V" or "V") specially (copy them into
00300   // userInvDiagCopy first, which is otherwise null at the end of the
00301   // long if-then chain) to avoid an extra copy.
00302   if (plist.isParameter ("chebyshev: operator inv diagonal")) {
00303     // Pointer to the user's Vector, if provided.
00304     RCP<const V> userInvDiag;
00305 
00306     try { // Could the type be const V*?
00307       const V* rawUserInvDiag =
00308         plist.get<const V*> ("chebyshev: operator inv diagonal");
00309       // Nonowning reference (we'll make a deep copy below)
00310       userInvDiag = rcp (rawUserInvDiag, false);
00311     } catch (Teuchos::Exceptions::InvalidParameterType&) {
00312     }
00313     if (userInvDiag.is_null ()) {
00314       try { // Could the type be V*?
00315         V* rawUserInvDiag = plist.get<V*> ("chebyshev: operator inv diagonal");
00316         // Nonowning reference (we'll make a deep copy below)
00317         userInvDiag = rcp (const_cast<const V*> (rawUserInvDiag), false);
00318       } catch (Teuchos::Exceptions::InvalidParameterType&) {
00319       }
00320     }
00321     if (userInvDiag.is_null ()) {
00322       try { // Could the type be RCP<const V>?
00323         userInvDiag =
00324           plist.get<RCP<const V> > ("chebyshev: operator inv diagonal");
00325       } catch (Teuchos::Exceptions::InvalidParameterType&) {
00326       }
00327     }
00328     if (userInvDiag.is_null ()) {
00329       try { // Could the type be RCP<V>?
00330         RCP<V> userInvDiagNonConst =
00331           plist.get<RCP<V> > ("chebyshev: operator inv diagonal");
00332         userInvDiag = rcp_const_cast<const V> (userInvDiagNonConst);
00333       } catch (Teuchos::Exceptions::InvalidParameterType&) {
00334       }
00335     }
00336     if (userInvDiag.is_null ()) {
00337 #ifndef _MSC_VER
00338       try { // Could the type be const V?
00339         // ParameterList::get() returns by reference.  Thus, we don't
00340         // have to invoke Vector's copy constructor here.  It's good
00341         // practice not to make an RCP to this reference, even though
00342         // it should be valid as long as the ParameterList that holds
00343         // it is valid.  Thus, we make our deep copy here, rather than
00344         // waiting to do it below.
00345         const V& userInvDiagRef =
00346           plist.get<const V> ("chebyshev: operator inv diagonal");
00347         userInvDiagCopy = rcp (new V (userInvDiagRef, Teuchos::Copy));
00348         // Tell the if-chain below not to keep trying.
00349         userInvDiag = userInvDiagCopy;
00350       } catch (Teuchos::Exceptions::InvalidParameterType&) {
00351       }
00352 #else
00353     TEUCHOS_TEST_FOR_EXCEPTION(
00354       true, std::runtime_error,
00355       "Ifpack2::Chebyshev::setParameters: \"chebyshev: operator inv diagonal\" "
00356       "plist.get<const V> does not compile around return held == other_held "
00357       "in Teuchos::any in Visual Studio.  Can't fix it now, so throwing "
00358       "in case someone builds there.");
00359 #endif
00360     }
00361     if (userInvDiag.is_null ()) {
00362 #ifndef _MSC_VER
00363       try { // Could the type be V?
00364         // ParameterList::get() returns by reference.  Thus, we don't
00365         // have to invoke Vector's copy constructor here.  It's good
00366         // practice not to make an RCP to this reference, even though
00367         // it should be valid as long as the ParameterList that holds
00368         // it is valid.  Thus, we make our deep copy here, rather than
00369         // waiting to do it below.
00370         V& userInvDiagNonConstRef =
00371           plist.get<V> ("chebyshev: operator inv diagonal");
00372         const V& userInvDiagRef = const_cast<const V&> (userInvDiagNonConstRef);
00373         userInvDiagCopy = rcp (new V (userInvDiagRef, Teuchos::Copy));
00374         // Tell the if-chain below not to keep trying.
00375         userInvDiag = userInvDiagCopy;
00376       } catch (Teuchos::Exceptions::InvalidParameterType&) {
00377       }
00378 #else
00379     TEUCHOS_TEST_FOR_EXCEPTION(
00380       true, std::runtime_error,
00381       "Ifpack2::Chebyshev::setParameters: \"chebyshev: operator inv diagonal\" "
00382       "plist.get<V> does not compile around return held == other_held "
00383       "in Teuchos::any in Visual Studio.  Can't fix it now, so throwing "
00384       "in case someone builds there.");
00385 #endif
00386     }
00387 
00388     // NOTE: If the user's parameter has some strange type that we
00389     // didn't test above, userInvDiag might still be null.  You may
00390     // want to add an error test for this condition.  Currently, we
00391     // just say in this case that the user didn't give us a Vector.
00392 
00393     // If we have userInvDiag but don't have a deep copy yet, make a
00394     // deep copy now.
00395     if (! userInvDiag.is_null () && userInvDiagCopy.is_null ()) {
00396       userInvDiagCopy = rcp (new V (*userInvDiag, Teuchos::Copy));
00397     }
00398 
00399     // NOTE: userInvDiag, if provided, is a row Map version of the
00400     // Vector.  We don't necessarily have a range Map yet.  compute()
00401     // would be the proper place to compute the range Map version of
00402     // userInvDiag.
00403   }
00404 
00405   // Don't fill in defaults for the max or min eigenvalue, because
00406   // this class uses the existence of those parameters to determine
00407   // whether it should do eigenanalysis.
00408   if (plist.isParameter ("chebyshev: max eigenvalue")) {
00409     if (plist.isType<double>("chebyshev: max eigenvalue"))
00410       lambdaMax = plist.get<double> ("chebyshev: max eigenvalue");
00411     else
00412       lambdaMax = plist.get<ST> ("chebyshev: max eigenvalue");
00413     TEUCHOS_TEST_FOR_EXCEPTION(
00414       STS::isnaninf (lambdaMax), std::invalid_argument,
00415       "Ifpack2::Chebyshev::setParameters: \"chebyshev: max eigenvalue\" "
00416       "parameter is NaN or Inf.  This parameter is optional, but if you "
00417       "choose to supply it, it must have a finite value.");
00418   }
00419   if (plist.isParameter ("chebyshev: min eigenvalue")) {
00420     if (plist.isType<double>("chebyshev: min eigenvalue"))
00421       lambdaMin = plist.get<double> ("chebyshev: min eigenvalue");
00422     else
00423       lambdaMin = plist.get<ST> ("chebyshev: min eigenvalue");
00424     TEUCHOS_TEST_FOR_EXCEPTION(
00425       STS::isnaninf (lambdaMin), std::invalid_argument,
00426       "Ifpack2::Chebyshev::setParameters: \"chebyshev: min eigenvalue\" "
00427       "parameter is NaN or Inf.  This parameter is optional, but if you "
00428       "choose to supply it, it must have a finite value.");
00429   }
00430 
00431   // Only fill in Ifpack2's name for the default parameter, not ML's.
00432   if (plist.isParameter ("smoother: Chebyshev alpha")) { // ML compatibility
00433     if (plist.isType<double>("smoother: Chebyshev alpha"))
00434       eigRatio = plist.get<double> ("smoother: Chebyshev alpha");
00435     else
00436       eigRatio = plist.get<ST> ("smoother: Chebyshev alpha");
00437   }
00438   // Ifpack2's name overrides ML's name.
00439   eigRatio = plist.get ("chebyshev: ratio eigenvalue", eigRatio);
00440   TEUCHOS_TEST_FOR_EXCEPTION(
00441     STS::isnaninf (eigRatio), std::invalid_argument,
00442     "Ifpack2::Chebyshev::setParameters: \"chebyshev: ratio eigenvalue\" "
00443     "parameter (also called \"smoother: Chebyshev alpha\") is NaN or Inf.  "
00444     "This parameter is optional, but if you choose to supply it, it must have "
00445     "a finite value.");
00446   // mfh 11 Feb 2013: This class is currently only correct for real
00447   // Scalar types, but we still want it to build for complex Scalar
00448   // type so that users of Ifpack2::Factory can build their
00449   // executables for real or complex Scalar type.  Thus, we take the
00450   // real parts here, which are always less-than comparable.
00451   TEUCHOS_TEST_FOR_EXCEPTION(
00452     STS::real (eigRatio) < STS::real (STS::one ()),
00453     std::invalid_argument,
00454     "Ifpack2::Chebyshev::setParameters: \"chebyshev: ratio eigenvalue\""
00455     "parameter (also called \"smoother: Chebyshev alpha\") must be >= 1, "
00456     "but you supplied the value " << eigRatio << ".");
00457 
00458   // Same name in Ifpack2 and Ifpack.
00459   minDiagVal = plist.get ("chebyshev: min diagonal value", minDiagVal);
00460   TEUCHOS_TEST_FOR_EXCEPTION(
00461     STS::isnaninf (minDiagVal), std::invalid_argument,
00462     "Ifpack2::Chebyshev::setParameters: \"chebyshev: min diagonal value\" "
00463     "parameter is NaN or Inf.  This parameter is optional, but if you choose "
00464     "to supply it, it must have a finite value.");
00465 
00466   // Only fill in Ifpack2's name, not ML's or Ifpack's.
00467   if (plist.isParameter ("smoother: sweeps")) { // ML compatibility
00468     numIters = plist.get<int> ("smoother: sweeps");
00469   } // Ifpack's name overrides ML's name.
00470   if (plist.isParameter ("relaxation: sweeps")) { // Ifpack compatibility
00471     numIters = plist.get<int> ("relaxation: sweeps");
00472   } // Ifpack2's name overrides Ifpack's name.
00473   numIters = plist.get ("chebyshev: degree", numIters);
00474   TEUCHOS_TEST_FOR_EXCEPTION(
00475     numIters < 0, std::invalid_argument,
00476     "Ifpack2::Chebyshev::setParameters: \"chebyshev: degree\" parameter (also "
00477     "called \"smoother: sweeps\" or \"relaxation: sweeps\") must be a "
00478     "nonnegative integer.  You gave a value of " << numIters << ".");
00479 
00480   // The last parameter name overrides the first.
00481   if (plist.isParameter ("eigen-analysis: iterations")) { // ML compatibility
00482     eigMaxIters = plist.get<int> ("eigen-analysis: iterations");
00483   } // Ifpack2's name overrides ML's name.
00484   eigMaxIters = plist.get ("chebyshev: eigenvalue max iterations", eigMaxIters);
00485   TEUCHOS_TEST_FOR_EXCEPTION(
00486     eigMaxIters < 0, std::invalid_argument,
00487     "Ifpack2::Chebyshev::setParameters: \"chebyshev: eigenvalue max iterations"
00488     "\" parameter (also called \"eigen-analysis: iterations\") must be a "
00489     "nonnegative integer.  You gave a value of " << eigMaxIters << ".");
00490 
00491   zeroStartingSolution = plist.get ("chebyshev: zero starting solution",
00492                                     zeroStartingSolution);
00493   assumeMatrixUnchanged = plist.get ("chebyshev: assume matrix does not change",
00494                                      assumeMatrixUnchanged);
00495 
00496   // We don't want to fill these parameters in, because they shouldn't
00497   // be visible to Ifpack2::Chebyshev users.
00498   if (plist.isParameter ("chebyshev: textbook algorithm")) {
00499     textbookAlgorithm = plist.get<bool> ("chebyshev: textbook algorithm");
00500   }
00501   if (plist.isParameter ("chebyshev: compute max residual norm")) {
00502     computeMaxResNorm = plist.get<bool> ("chebyshev: compute max residual norm");
00503   }
00504 
00505   // Test for Ifpack parameters that we won't ever implement here.
00506   // Be careful to use the one-argument version of get(), since the
00507   // two-argment version adds the parameter if it's not there.
00508   TEUCHOS_TEST_FOR_EXCEPTION(
00509     plist.isParameter ("chebyshev: use block mode") &&
00510     ! plist.get<bool> ("chebyshev: use block mode"),
00511     std::invalid_argument,
00512     "Ifpack2::Chebyshev requires that if you supply the Ifpack parameter "
00513     "\"chebyshev: use block mode\", it must be set to false.  Ifpack2's "
00514     "Chebyshev does not implement Ifpack's block mode.");
00515   TEUCHOS_TEST_FOR_EXCEPTION(
00516     plist.isParameter ("chebyshev: solve normal equations") &&
00517     ! plist.get<bool> ("chebyshev: solve normal equations"),
00518     std::invalid_argument,
00519     "Ifpack2::Chebyshev does not and will never implement the Ifpack "
00520     "parameter \"chebyshev: solve normal equations\".  If you want to solve "
00521     "the normal equations, construct a Tpetra::Operator that implements "
00522     "A^* A, and use Chebyshev to solve A^* A x = A^* b.");
00523 
00524   // Test for Ifpack parameters that we haven't implemented yet.
00525   //
00526   // For now, we only check that this ML parameter, if provided, has
00527   // the one value that we support.  We consider other values "invalid
00528   // arguments" rather than "logic errors," because Ifpack does not
00529   // implement eigenanalyses other than the power method.
00530   std::string eigenAnalysisType ("power-method");
00531   if (plist.isParameter ("eigen-analysis: type")) {
00532     eigenAnalysisType = plist.get<std::string> ("eigen-analysis: type");
00533     TEUCHOS_TEST_FOR_EXCEPTION(
00534       eigenAnalysisType != "power-method" &&
00535       eigenAnalysisType != "power method",
00536       std::invalid_argument,
00537       "Ifpack2::Chebyshev: This class supports the ML parameter \"eigen-"
00538       "analysis: type\" for backwards compatibility.  However, Ifpack2 "
00539       "currently only supports the \"power-method\" option for this "
00540       "parameter.  This imitates Ifpack, which only implements the power "
00541       "method for eigenanalysis.");
00542   }
00543 
00544   // We've validated all the parameters, so it's safe now to "commit" them.
00545   userInvDiag_ = userInvDiagCopy;
00546   userLambdaMax_ = lambdaMax;
00547   userLambdaMin_ = lambdaMin;
00548   userEigRatio_ = eigRatio;
00549   minDiagVal_ = minDiagVal;
00550   numIters_ = numIters;
00551   eigMaxIters_ = eigMaxIters;
00552   zeroStartingSolution_ = zeroStartingSolution;
00553   assumeMatrixUnchanged_ = assumeMatrixUnchanged;
00554   textbookAlgorithm_ = textbookAlgorithm;
00555   computeMaxResNorm_ = computeMaxResNorm;
00556 }
00557 
00558 
00559 template<class ScalarType, class MV>
00560 void
00561 Chebyshev<ScalarType, MV>::reset ()
00562 {
00563   D_ = Teuchos::null;
00564   diagOffsets_ = Teuchos::null;
00565   savedDiagOffsets_ = false;
00566   V_ = Teuchos::null;
00567   W_ = Teuchos::null;
00568   computedLambdaMax_ = STS::nan ();
00569   computedLambdaMin_ = STS::nan ();
00570 }
00571 
00572 
00573 template<class ScalarType, class MV>
00574 void
00575 Chebyshev<ScalarType, MV>::setMatrix (const Teuchos::RCP<const row_matrix_type>& A)
00576 {
00577   if (A.getRawPtr () != A_.getRawPtr ()) {
00578     if (! assumeMatrixUnchanged_) {
00579       reset ();
00580     }
00581     A_ = A;
00582   }
00583 }
00584 
00585 
00586 template<class ScalarType, class MV>
00587 void
00588 Chebyshev<ScalarType, MV>::compute ()
00589 {
00590   using std::endl;
00591   // Some of the optimizations below only work if A_ is a
00592   // Tpetra::CrsMatrix.  We'll make our best guess about its type
00593   // here, since we have no way to get back the original fifth
00594   // template parameter.
00595   typedef Tpetra::CrsMatrix<typename MV::scalar_type,
00596     typename MV::local_ordinal_type,
00597     typename MV::global_ordinal_type,
00598     typename MV::node_type> crs_matrix_type;
00599 
00600   TEUCHOS_TEST_FOR_EXCEPTION(
00601     A_.is_null (), std::runtime_error, "Ifpack2::Chebyshev::compute: The input "
00602     "matrix A is null.  Please call setMatrix() with a nonnull input matrix "
00603     "before calling this method.");
00604 
00605   // If A_ is a CrsMatrix and its graph is constant, we presume that
00606   // the user plans to reuse the structure of A_, but possibly change
00607   // A_'s values before each compute() call.  This is the intended use
00608   // case for caching the offsets of the diagonal entries of A_, to
00609   // speed up extraction of diagonal entries on subsequent compute()
00610   // calls.
00611 
00612   // FIXME (mfh 22 Jan 2013, 10 Feb 2013) In all cases when we use
00613   // isnaninf() in this method, we really only want to check if the
00614   // number is NaN.  Inf means something different.  However,
00615   // Teuchos::ScalarTraits doesn't distinguish the two cases.
00616 
00617   // makeInverseDiagonal() returns a range Map Vector.
00618   if (userInvDiag_.is_null ()) {
00619     Teuchos::RCP<const crs_matrix_type> A_crsMat =
00620       Teuchos::rcp_dynamic_cast<const crs_matrix_type> (A_);
00621 
00622     if (D_.is_null ()) { // We haven't computed D_ before
00623       if (! A_crsMat.is_null () && A_crsMat->isStaticGraph ()) {
00624         // It's a CrsMatrix with a const graph; cache diagonal offsets.
00625         A_crsMat->getLocalDiagOffsets (diagOffsets_);
00626         savedDiagOffsets_ = true;
00627         D_ = makeInverseDiagonal (*A_, true);
00628       }
00629       else { // either A_ is not a CrsMatrix, or its graph is nonconst
00630         D_ = makeInverseDiagonal (*A_);
00631       }
00632     }
00633     else if (! assumeMatrixUnchanged_) { // D_ exists but A_ may have changed
00634       if (! A_crsMat.is_null () && A_crsMat->isStaticGraph ()) {
00635         // It's a CrsMatrix with a const graph; cache diagonal offsets
00636         // if we haven't already.
00637         if (! savedDiagOffsets_) {
00638           A_crsMat->getLocalDiagOffsets (diagOffsets_);
00639           savedDiagOffsets_ = true;
00640         }
00641         // Now we're guaranteed to have cached diagonal offsets.
00642         D_ = makeInverseDiagonal (*A_, true);
00643       }
00644       else { // either A_ is not a CrsMatrix, or its graph is nonconst
00645         D_ = makeInverseDiagonal (*A_);
00646       }
00647     }
00648   }
00649   else { // the user provided an inverse diagonal
00650     D_ = makeRangeMapVectorConst (userInvDiag_);
00651   }
00652 
00653   // Have we estimated eigenvalues before?
00654   const bool computedEigenvalueEstimates =
00655     STS::isnaninf (computedLambdaMax_) || STS::isnaninf (computedLambdaMin_);
00656 
00657   // Only recompute the eigenvalue estimates if
00658   // - we are supposed to assume that the matrix may have changed, or
00659   // - they haven't been computed before, and the user hasn't given
00660   //   us at least an estimate of the max eigenvalue.
00661   //
00662   // We at least need an estimate of the max eigenvalue.  This is the
00663   // most important one if using Chebyshev as a smoother.
00664   if (! assumeMatrixUnchanged_ ||
00665       (! computedEigenvalueEstimates && STS::isnaninf (userLambdaMax_))) {
00666     const ST computedLambdaMax = powerMethod (*A_, *D_, eigMaxIters_);
00667     TEUCHOS_TEST_FOR_EXCEPTION(
00668       STS::isnaninf (computedLambdaMax),
00669       std::runtime_error,
00670       "Ifpack2::Chebyshev::compute: Estimation of the max eigenvalue "
00671       "of D^{-1} A failed, by producing Inf or NaN.  This probably means that "
00672       "the matrix contains Inf or NaN values, or that it is badly scaled.");
00673     TEUCHOS_TEST_FOR_EXCEPTION(
00674       STS::isnaninf (userEigRatio_),
00675       std::logic_error,
00676       "Ifpack2::Chebyshev::compute: userEigRatio_ is Inf or NaN."
00677       << endl << "This should be impossible." << endl <<
00678       "Please report this bug to the Ifpack2 developers.");
00679 
00680     // The power method doesn't estimate the min eigenvalue, so we
00681     // do our best to provide an estimate.  userEigRatio_ has a
00682     // reasonable default value, and if the user provided it, we
00683     // have already checked that its value is finite and >= 1.
00684     const ST computedLambdaMin = computedLambdaMax / userEigRatio_;
00685 
00686     // Defer "committing" results until all computations succeeded.
00687     computedLambdaMax_ = computedLambdaMax;
00688     computedLambdaMin_ = computedLambdaMin;
00689   } else {
00690     TEUCHOS_TEST_FOR_EXCEPTION(
00691       STS::isnaninf (userLambdaMax_) && STS::isnaninf (computedLambdaMax_),
00692       std::logic_error,
00693       "Ifpack2::Chebyshev::compute: " << endl <<
00694       "Both userLambdaMax_ and computedLambdaMax_ are Inf or NaN."
00695       << endl << "This should be impossible." << endl <<
00696       "Please report this bug to the Ifpack2 developers.");
00697   }
00698 
00700   // Figure out the eigenvalue estimates that apply() will use.
00702 
00703   // Always favor the user's max eigenvalue estimate, if provided.
00704   if (STS::isnaninf (userLambdaMax_)) {
00705     lambdaMaxForApply_ = computedLambdaMax_;
00706   } else {
00707     lambdaMaxForApply_ = userLambdaMax_;
00708   }
00709   // mfh 11 Feb 2013: For now, we imitate Ifpack by ignoring the
00710   // user's min eigenvalue estimate, and using the given eigenvalue
00711   // ratio to estimate the min eigenvalue.  We could instead do this:
00712   // favor the user's eigenvalue ratio estimate, but if it's not
00713   // provided, use lambdaMax / lambdaMin.  However, we want Chebyshev
00714   // to have sensible smoother behavior if the user did not provide
00715   // eigenvalue estimates.  Ifpack's behavior attempts to push down
00716   // the error terms associated with the largest eigenvalues, while
00717   // expecting that users will only want a small number of iterations,
00718   // so that error terms associated with the smallest eigenvalues
00719   // won't grow too much.  This is sensible behavior for a smoother.
00720   lambdaMinForApply_ = lambdaMaxForApply_ / userEigRatio_;
00721   eigRatioForApply_ = userEigRatio_;
00722 
00723   if (! textbookAlgorithm_) {
00724     // Ifpack has a special-case modification of the eigenvalue bounds
00725     // for the case where the max eigenvalue estimate is close to one.
00726     const ST one = Teuchos::as<ST> (1);
00727     // FIXME (mfh 20 Nov 2013) Should scale this 1.0e-6 term
00728     // appropriately for MT's machine precision.
00729     if (STS::magnitude (lambdaMaxForApply_ - one) < Teuchos::as<MT> (1.0e-6)) {
00730       lambdaMinForApply_ = one;
00731       lambdaMaxForApply_ = lambdaMinForApply_;
00732       eigRatioForApply_ = one; // Ifpack doesn't include this line.
00733     }
00734   }
00735 }
00736 
00737 
00738 template<class ScalarType, class MV>
00739 ScalarType
00740 Chebyshev<ScalarType, MV>::
00741 getLambdaMaxForApply() const {
00742   return lambdaMaxForApply_;
00743 }
00744 
00745 
00746 template<class ScalarType, class MV>
00747 typename Chebyshev<ScalarType, MV>::MT
00748 Chebyshev<ScalarType, MV>::apply (const MV& B, MV& X)
00749 {
00750   TEUCHOS_TEST_FOR_EXCEPTION(
00751     A_.is_null (), std::runtime_error, "Ifpack2::Chebyshev::apply: The input "
00752     "matrix A is null.  Please call setMatrix() with a nonnull input matrix, "
00753     "and then call compute(), before calling this method.");
00754   TEUCHOS_TEST_FOR_EXCEPTION(
00755     STS::isnaninf (lambdaMaxForApply_), std::runtime_error,
00756     "Ifpack2::Chebyshev::apply: There is no estimate for the max eigenvalue."
00757     << std::endl << computeBeforeApplyReminder);
00758   TEUCHOS_TEST_FOR_EXCEPTION(
00759     STS::isnaninf (lambdaMinForApply_), std::runtime_error,
00760     "Ifpack2::Chebyshev::apply: There is no estimate for the min eigenvalue."
00761     << std::endl << computeBeforeApplyReminder);
00762   TEUCHOS_TEST_FOR_EXCEPTION(
00763     STS::isnaninf (eigRatioForApply_), std::runtime_error,
00764     "Ifpack2::Chebyshev::apply: There is no estimate for the ratio of the max "
00765     "eigenvalue to the min eigenvalue."
00766     << std::endl << computeBeforeApplyReminder);
00767   TEUCHOS_TEST_FOR_EXCEPTION(
00768     D_.is_null (), std::runtime_error,
00769     "Ifpack2::Chebyshev::apply: "
00770     "The vector of inverse diagonal entries of the matrix has not yet been "
00771     "computed." << std::endl << computeBeforeApplyReminder);
00772 
00773   if (textbookAlgorithm_) {
00774     textbookApplyImpl (*A_, B, X, numIters_, lambdaMaxForApply_,
00775                        lambdaMinForApply_, eigRatioForApply_, *D_);
00776   } else {
00777     ifpackApplyImpl (*A_, B, X, numIters_, lambdaMaxForApply_,
00778                      lambdaMinForApply_, eigRatioForApply_, *D_);
00779   }
00780 
00781   if (computeMaxResNorm_ && B.getNumVectors () > 0) {
00782     MV R (B.getMap (), B.getNumVectors ());
00783     computeResidual (R, B, *A_, X);
00784     Teuchos::Array<MT> norms (B.getNumVectors ());
00785     R.norm2 (norms ());
00786     return *std::max_element (norms.begin (), norms.end ());
00787   } else {
00788     return Teuchos::ScalarTraits<MT>::zero ();
00789   }
00790 }
00791 
00792 template<class ScalarType, class MV>
00793 void
00794 Chebyshev<ScalarType, MV>::
00795 print (std::ostream& out) {
00796   this->describe (* (Teuchos::getFancyOStream (Teuchos::rcpFromRef (out))),
00797                   Teuchos::VERB_MEDIUM);
00798 }
00799 
00800 template<class ScalarType, class MV>
00801 void
00802 Chebyshev<ScalarType, MV>::
00803 computeResidual (MV& R, const MV& B, const op_type& A, const MV& X,
00804                  const Teuchos::ETransp mode)
00805 {
00806   Tpetra::deep_copy(R, B);
00807   A.apply (X, R, mode, -STS::one(), STS::one());
00808 }
00809 
00810 template<class ScalarType, class MV>
00811 void
00812 Chebyshev<ScalarType, MV>::
00813 solve (MV& Z, const V& D_inv, const MV& R) {
00814   Z.elementWiseMultiply (STS::one(), D_inv, R, STS::zero());
00815 }
00816 
00817 template<class ScalarType, class MV>
00818 void
00819 Chebyshev<ScalarType, MV>::
00820 solve (MV& Z, const ST alpha, const V& D_inv, const MV& R) {
00821   Z.elementWiseMultiply (alpha, D_inv, R, STS::zero());
00822 }
00823 
00824 template<class ScalarType, class MV>
00825 Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::V>
00826 Chebyshev<ScalarType, MV>::
00827 makeInverseDiagonal (const row_matrix_type& A, const bool useDiagOffsets) const
00828 {
00829   using Teuchos::RCP;
00830   using Teuchos::rcpFromRef;
00831   using Teuchos::rcp_dynamic_cast;
00832 
00833   RCP<V> D_rowMap (new V (A.getGraph ()->getRowMap ()));
00834   if (useDiagOffsets) {
00835     // The optimizations below only work if A_ is a Tpetra::CrsMatrix.
00836     // We'll make our best guess about its type here, since we have no
00837     // way to get back the original fifth template parameter.
00838     typedef Tpetra::CrsMatrix<typename MV::scalar_type,
00839       typename MV::local_ordinal_type,
00840       typename MV::global_ordinal_type,
00841       typename MV::node_type> crs_matrix_type;
00842     RCP<const crs_matrix_type> A_crsMat =
00843       rcp_dynamic_cast<const crs_matrix_type> (rcpFromRef (A));
00844     if (! A_crsMat.is_null ()) {
00845       TEUCHOS_TEST_FOR_EXCEPTION(
00846         ! savedDiagOffsets_, std::logic_error,
00847         "Ifpack2::Details::Chebyshev::makeInverseDiagonal: "
00848         "It is not allowed to call this method with useDiagOffsets=true, "
00849         "if you have not previously saved offsets of diagonal entries.  "
00850         "This situation should never arise if this class is used properly.  "
00851         "Please report this bug to the Ifpack2 developers.");
00852       A_crsMat->getLocalDiagCopy (*D_rowMap, diagOffsets_ ());
00853     }
00854   }
00855   else {
00856     // This always works for a Tpetra::RowMatrix, even if it is not a
00857     // Tpetra::CrsMatrix.  We just don't have offsets in this case.
00858     A.getLocalDiagCopy (*D_rowMap);
00859   }
00860   RCP<V> D_rangeMap = makeRangeMapVector (D_rowMap);
00861 
00862   // Invert the diagonal entries, replacing entries less (in
00863   // magnitude) than the user-specified value with that value.
00864   reciprocal_threshold (*D_rangeMap, minDiagVal_);
00865   return Teuchos::rcp_const_cast<const V> (D_rangeMap);
00866 }
00867 
00868 
00869 template<class ScalarType, class MV>
00870 Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::V>
00871 Chebyshev<ScalarType, MV>::
00872 makeRangeMapVectorConst (const Teuchos::RCP<const V>& D) const
00873 {
00874   using Teuchos::RCP;
00875   using Teuchos::rcp;
00876   typedef Tpetra::Export<typename MV::local_ordinal_type,
00877                          typename MV::global_ordinal_type,
00878                          typename MV::node_type> export_type;
00879   // This throws logic_error instead of runtime_error, because the
00880   // methods that call makeRangeMapVector should all have checked
00881   // whether A_ is null before calling this method.
00882   TEUCHOS_TEST_FOR_EXCEPTION(
00883     A_.is_null (), std::logic_error, "Ifpack2::Details::Chebyshev::"
00884     "makeRangeMapVector: The input matrix A is null.  Please call setMatrix() "
00885     "with a nonnull input matrix before calling this method.  This is probably "
00886     "a bug in Ifpack2; please report this bug to the Ifpack2 developers.");
00887   TEUCHOS_TEST_FOR_EXCEPTION(
00888     D.is_null (), std::logic_error, "Ifpack2::Details::Chebyshev::"
00889     "makeRangeMapVector: The input Vector D is null.  This is probably "
00890     "a bug in Ifpack2; please report this bug to the Ifpack2 developers.");
00891 
00892   RCP<const map_type> sourceMap = D->getMap ();
00893   RCP<const map_type> rangeMap = A_->getRangeMap ();
00894   RCP<const map_type> rowMap = A_->getRowMap ();
00895 
00896   if (rangeMap->isSameAs (*sourceMap)) {
00897     // The given vector's Map is the same as the matrix's range Map.
00898     // That means we don't need to Export.  This is the fast path.
00899     return D;
00900   }
00901   else { // We need to Export.
00902     RCP<const export_type> exporter;
00903     // Making an Export object from scratch is expensive enough that
00904     // it's worth the O(1) global reductions to call isSameAs(), to
00905     // see if we can avoid that cost.
00906     if (sourceMap->isSameAs (*rowMap)) {
00907       // We can reuse the matrix's Export object, if there is one.
00908       exporter = A_->getGraph ()->getExporter ();
00909     }
00910     else { // We have to make a new Export object.
00911       exporter = rcp (new export_type (sourceMap, rangeMap));
00912     }
00913 
00914     if (exporter.is_null ()) {
00915       return D; // Row Map and range Map are the same; no need to Export.
00916     }
00917     else { // Row Map and range Map are _not_ the same; must Export.
00918       RCP<V> D_out = rcp (new V (*D, Teuchos::Copy));
00919       D_out->doExport (*D, *exporter, Tpetra::ADD);
00920       return Teuchos::rcp_const_cast<const V> (D_out);
00921     }
00922   }
00923 }
00924 
00925 
00926 template<class ScalarType, class MV>
00927 Teuchos::RCP<typename Chebyshev<ScalarType, MV>::V>
00928 Chebyshev<ScalarType, MV>::
00929 makeRangeMapVector (const Teuchos::RCP<V>& D) const
00930 {
00931   using Teuchos::rcp_const_cast;
00932   return rcp_const_cast<V> (makeRangeMapVectorConst (rcp_const_cast<V> (D)));
00933 }
00934 
00935 
00936 template<class ScalarType, class MV>
00937 void
00938 Chebyshev<ScalarType, MV>::
00939 textbookApplyImpl (const op_type& A,
00940                    const MV& B,
00941                    MV& X,
00942                    const int numIters,
00943                    const ST lambdaMax,
00944                    const ST lambdaMin,
00945                    const ST eigRatio,
00946                    const V& D_inv) const
00947 {
00948   (void) lambdaMin; // Forestall compiler warning.
00949   const ST myLambdaMin = lambdaMax / eigRatio;
00950 
00951   const ST zero = Teuchos::as<ST> (0);
00952   const ST one = Teuchos::as<ST> (1);
00953   const ST two = Teuchos::as<ST> (2);
00954   const ST d = (lambdaMax + myLambdaMin) / two; // Ifpack2 calls this theta
00955   const ST c = (lambdaMax - myLambdaMin) / two; // Ifpack2 calls this 1/delta
00956 
00957   if (zeroStartingSolution_ && numIters > 0) {
00958     // If zero iterations, then input X is output X.
00959     X.putScalar (zero);
00960   }
00961   MV R (B.getMap (), B.getNumVectors (), false);
00962   MV P (B.getMap (), B.getNumVectors (), false);
00963   MV Z (B.getMap (), B.getNumVectors (), false);
00964   ST alpha, beta;
00965   for (int i = 0; i < numIters; ++i) {
00966     computeResidual (R, B, A, X); // R = B - A*X
00967     solve (Z, D_inv, R); // z = D_inv * R, that is, D \ R.
00968     if (i == 0) {
00969       P = Z;
00970       alpha = two / d;
00971     } else {
00972       //beta = (c * alpha / two)^2;
00973       //const ST sqrtBeta = c * alpha / two;
00974       //beta = sqrtBeta * sqrtBeta;
00975       beta = alpha * (c/two) * (c/two);
00976       alpha = one / (d - beta);
00977       P.update (one, Z, beta); // P = Z + beta*P
00978     }
00979     X.update (alpha, P, one); // X = X + alpha*P
00980     // If we compute the residual here, we could either do R = B -
00981     // A*X, or R = R - alpha*A*P.  Since we choose the former, we
00982     // can move the computeResidual call to the top of the loop.
00983   }
00984 }
00985 
00986 template<class ScalarType, class MV>
00987 typename Chebyshev<ScalarType, MV>::MT
00988 Chebyshev<ScalarType, MV>::maxNormInf (const MV& X) {
00989   Teuchos::Array<MT> norms (X.getNumVectors ());
00990   X.normInf (norms());
00991   return *std::max_element (norms.begin (), norms.end ());
00992 }
00993 
00994 template<class ScalarType, class MV>
00995 void
00996 Chebyshev<ScalarType, MV>::
00997 ifpackApplyImpl (const op_type& A,
00998                  const MV& B,
00999                  MV& X,
01000                  const int numIters,
01001                  const ST lambdaMax,
01002                  const ST lambdaMin,
01003                  const ST eigRatio,
01004                  const V& D_inv)
01005 {
01006 #ifdef HAVE_TEUCHOS_DEBUG
01007 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01008   using std::cerr;
01009   using std::endl;
01010   cerr << "\\|B\\|_{\\infty} = " << maxNormInf (B) << endl;
01011   cerr << "\\|X\\|_{\\infty} = " << maxNormInf (X) << endl;
01012 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01013 #endif // HAVE_TEUCHOS_DEBUG
01014 
01015   if (numIters <= 0) {
01016     return;
01017   }
01018   const ST one = Teuchos::as<ST> (1);
01019   const ST two = Teuchos::as<ST> (2);
01020 
01021   // Quick solve when the matrix A is the identity.
01022   if (lambdaMin == one && lambdaMax == lambdaMin) {
01023     solve (X, D_inv, B);
01024     return;
01025   }
01026 
01027   // Initialize coefficients
01028   const ST alpha = lambdaMax / eigRatio;
01029   const ST beta = Teuchos::as<ST> (1.1) * lambdaMax;
01030   const ST delta = two / (beta - alpha);
01031   const ST theta = (beta + alpha) / two;
01032   const ST s1 = theta * delta;
01033 
01034 #ifdef HAVE_TEUCHOS_DEBUG
01035 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01036   cerr << "alpha = " << alpha << endl
01037        << "beta = " << beta << endl
01038        << "delta = " << delta << endl
01039        << "theta = " << theta << endl
01040        << "s1 = " << s1 << endl;
01041 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01042 #endif // HAVE_TEUCHOS_DEBUG
01043 
01044   // Fetch cached temporary vectors.
01045   Teuchos::RCP<MV> V_ptr, W_ptr;
01046   makeTempMultiVectors (V_ptr, W_ptr, B);
01047 
01048   // mfh 28 Jan 2013: We write V1 instead of V, so as not to confuse
01049   // the multivector V with the typedef V (for Tpetra::Vector).
01050   //MV V1 (B.getMap (), B.getNumVectors (), false);
01051   //MV W (B.getMap (), B.getNumVectors (), false);
01052   MV& V1 = *V_ptr;
01053   MV& W = *W_ptr;
01054 
01055 #ifdef HAVE_TEUCHOS_DEBUG
01056 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01057   cerr << "Iteration " << 1 << ":" << endl
01058        << "- \\|D\\|_{\\infty} = " << D_->normInf () << endl;
01059 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01060 #endif // HAVE_TEUCHOS_DEBUG
01061 
01062   // Special case for the first iteration.
01063   if (! zeroStartingSolution_) {
01064     computeResidual (V1, B, A, X); // V1 = B - A*X
01065 
01066 #ifdef HAVE_TEUCHOS_DEBUG
01067 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01068     cerr << "- \\|B - A*X\\|_{\\infty} = " << maxNormInf (V1) << endl;
01069 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01070 #endif // HAVE_TEUCHOS_DEBUG
01071 
01072     solve (W, one/theta, D_inv, V1); // W = (1/theta)*D_inv*(B-A*X)
01073 
01074 #ifdef HAVE_TEUCHOS_DEBUG
01075 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01076     cerr << "- \\|W\\|_{\\infty} = " << maxNormInf (W) << endl;
01077 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01078 #endif // HAVE_TEUCHOS_DEBUG
01079 
01080     X.update (one, W, one); // X = X + W
01081   } else {
01082     solve (W, one/theta, D_inv, B); // W = (1/theta)*D_inv*B
01083 
01084 #ifdef HAVE_TEUCHOS_DEBUG
01085 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01086     cerr << "- \\|W\\|_{\\infty} = " << maxNormInf (W) << endl;
01087 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01088 #endif // HAVE_TEUCHOS_DEBUG
01089 
01090     Tpetra::deep_copy(X, W); // X = 0 + W
01091   }
01092 #ifdef HAVE_TEUCHOS_DEBUG
01093 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01094   cerr << "- \\|X\\|_{\\infty} = " << maxNormInf (X) << endl;
01095 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01096 #endif // HAVE_TEUCHOS_DEBUG
01097 
01098   // The rest of the iterations.
01099   ST rhok = one / s1;
01100   ST rhokp1, dtemp1, dtemp2;
01101   for (int deg = 1; deg < numIters; ++deg) {
01102 
01103 #ifdef HAVE_TEUCHOS_DEBUG
01104 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01105     cerr << "Iteration " << deg+1 << ":" << endl;
01106     cerr << "- \\|D\\|_{\\infty} = " << D_->normInf () << endl;
01107     cerr << "- \\|B\\|_{\\infty} = " << maxNormInf (B) << endl;
01108     cerr << "- \\|A\\|_{\\text{frob}} = " << A_->getFrobeniusNorm () << endl;
01109     cerr << "- rhok = " << rhok << endl;
01110     V1.putScalar (STS::zero ());
01111 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01112 #endif // HAVE_TEUCHOS_DEBUG
01113 
01114     computeResidual (V1, B, A, X); // V1 = B - A*X
01115 
01116 #ifdef HAVE_TEUCHOS_DEBUG
01117 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01118     cerr << "- \\|B - A*X\\|_{\\infty} = " << maxNormInf (V1) << endl;
01119 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01120 #endif // HAVE_TEUCHOS_DEBUG
01121 
01122     rhokp1 = one / (two * s1 - rhok);
01123     dtemp1 = rhokp1 * rhok;
01124     dtemp2 = two * rhokp1 * delta;
01125     rhok = rhokp1;
01126 
01127 #ifdef HAVE_TEUCHOS_DEBUG
01128 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01129     cerr << "- dtemp1 = " << dtemp1 << endl
01130          << "- dtemp2 = " << dtemp2 << endl;
01131 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01132 #endif // HAVE_TEUCHOS_DEBUG
01133 
01134     W.scale (dtemp1);
01135     W.elementWiseMultiply (dtemp2, D_inv, V1, one);
01136     X.update (one, W, one);
01137 
01138 #ifdef HAVE_TEUCHOS_DEBUG
01139 #ifdef IFPACK_DETAILS_CHEBYSHEV_DEBUG
01140     cerr << "- \\|W\\|_{\\infty} = " << maxNormInf (W) << endl;
01141     cerr << "- \\|X\\|_{\\infty} = " << maxNormInf (X) << endl;
01142 #endif // IFPACK_DETAILS_CHEBYSHEV_DEBUG
01143 #endif // HAVE_TEUCHOS_DEBUG
01144   }
01145 }
01146 
01147 template<class ScalarType, class MV>
01148 typename Chebyshev<ScalarType, MV>::ST
01149 Chebyshev<ScalarType, MV>::
01150 powerMethod (const op_type& A, const V& D_inv, const int numIters)
01151 {
01152   const ST zero = Teuchos::as<ST> (0);
01153   const ST one = Teuchos::as<ST> (1);
01154   ST lambdaMax = zero;
01155   ST RQ_top, RQ_bottom, norm;
01156 
01157   V x (A.getDomainMap ());
01158   V y (A.getRangeMap ());
01159   x.randomize ();
01160   norm = x.norm2 ();
01161   TEUCHOS_TEST_FOR_EXCEPTION(norm == zero, std::runtime_error,
01162     "Ifpack2::Chebyshev::powerMethod: "
01163     "Tpetra::Vector's randomize() method filled the vector "
01164     "with zeros.  This is not impossible, but is unlikely.  "
01165     "It's far more likely that there is a bug in Tpetra.");
01166 
01167   x.scale (one / norm);
01168   for (int iter = 0; iter < numIters; ++iter) {
01169     A.apply (x, y);
01170     solve (y, D_inv, y);
01171     RQ_top = y.dot (x);
01172     RQ_bottom = x.dot (x);
01173     lambdaMax = RQ_top / RQ_bottom;
01174     norm = y.norm2 ();
01175     if (norm == zero) { // Return something reasonable.
01176       return zero;
01177     }
01178     x.update (one / norm, y, zero);
01179   }
01180   return lambdaMax;
01181 }
01182 
01183 template<class ScalarType, class MV>
01184 Teuchos::RCP<const typename Chebyshev<ScalarType, MV>::row_matrix_type>
01185 Chebyshev<ScalarType, MV>::getMatrix () const {
01186   return A_;
01187 }
01188 
01189 template<class ScalarType, class MV>
01190 bool
01191 Chebyshev<ScalarType, MV>::
01192 hasTransposeApply () const {
01193   // Technically, this is true, because the matrix must be symmetric.
01194   return true;
01195 }
01196 
01197 template<class ScalarType, class MV>
01198 void
01199 Chebyshev<ScalarType, MV>::
01200 makeTempMultiVectors (Teuchos::RCP<MV>& V1,
01201                       Teuchos::RCP<MV>& W,
01202                       const MV& X)
01203 {
01204   if (V_.is_null ()) {
01205     V_ = Teuchos::rcp (new MV (X.getMap (), X.getNumVectors (), false));
01206   }
01207   //W must be initialized to zero when it is used as a multigrid smoother.
01208   if (W_.is_null ()) {
01209     W_ = Teuchos::rcp (new MV (X.getMap (), X.getNumVectors (), true));
01210   }
01211   V1 = V_;
01212   W = W_;
01213 }
01214 
01215 template<class ScalarType, class MV>
01216 std::string
01217 Chebyshev<ScalarType, MV>::
01218 description () const {
01219   std::ostringstream oss;
01220   // YAML requires quoting the key in this case, to distinguish
01221   // key's colons from the colon that separates key from value.
01222   oss << "\"Ifpack2::Details::Chebyshev\":"
01223       << "{"
01224       << "degree: " << numIters_
01225       << ", lambdaMax: " << lambdaMaxForApply_
01226       << ", alpha: " << eigRatioForApply_
01227       << ", lambdaMin: " << lambdaMinForApply_
01228       << "}";
01229   return oss.str();
01230 }
01231 
01232 template<class ScalarType, class MV>
01233 void
01234 Chebyshev<ScalarType, MV>::
01235 describe (Teuchos::FancyOStream& out,
01236           const Teuchos::EVerbosityLevel verbLevel) const
01237 {
01238   using Teuchos::TypeNameTraits;
01239   using std::endl;
01240 
01241   const Teuchos::EVerbosityLevel vl =
01242     (verbLevel == Teuchos::VERB_DEFAULT) ? Teuchos::VERB_LOW : verbLevel;
01243   if (vl > Teuchos::VERB_NONE) {
01244     if (vl == Teuchos::VERB_LOW) {
01245       out << description () << endl;
01246     } else { // vl > Teuchos::VERB_LOW
01247       // YAML requires quoting the key in this case, to distinguish
01248       // key's colons from the colon that separates key from value.
01249       out << "\"Ifpack2::Details::Chebyshev\":" << endl;
01250       Teuchos::OSTab tab1 (Teuchos::rcpFromRef (out));
01251       out << "Template parameters:" << endl;
01252       {
01253         Teuchos::OSTab tab2 (Teuchos::rcpFromRef (out));
01254         out << "ScalarType: \"" << TypeNameTraits<ScalarType>::name () << "\"" << endl
01255             << "MV: \"" << TypeNameTraits<MV>::name () << "\"" << endl;
01256       }
01257       // "Computed parameters" literally means "parameters whose
01258       // values were computed by compute()."
01259       out << endl << "Computed parameters:" << endl;
01260       {
01261         Teuchos::OSTab tab2 (Teuchos::rcpFromRef (out));
01262         // Users might want to see the values in the computed inverse
01263         // diagonal, so we print them out at the highest verbosity.
01264         out << "D_: ";
01265         if (D_.is_null ()) {
01266           out << "unset" << endl;
01267         } else if (vl <= Teuchos::VERB_HIGH) {
01268           out << "set" << endl;
01269         } else { // D_ not null and vl > Teuchos::VERB_HIGH
01270           out << endl;
01271           {
01272             Teuchos::OSTab tab3 (Teuchos::rcpFromRef (out));
01273             D_->describe (out, vl);
01274           }
01275           out << endl;
01276         }
01277         // V_ and W_ are scratch space; their values are irrelevant.
01278         // All that matters is whether or not they have been set.
01279         out << "V_: " << (V_.is_null () ? "unset" : "set") << endl
01280             << "W_: " << (W_.is_null () ? "unset" : "set") << endl
01281             << "computedLambdaMax_: " << computedLambdaMax_ << endl
01282             << "computedLambdaMin_: " << computedLambdaMin_ << endl
01283             << "lambdaMaxForApply_: " << lambdaMaxForApply_ << endl
01284             << "lambdaMinForApply_: " << lambdaMinForApply_ << endl
01285             << "eigRatioForApply_: " << eigRatioForApply_ << endl;
01286       }
01287       out << "User parameters:" << endl;
01288       {
01289         Teuchos::OSTab tab2 (Teuchos::rcpFromRef (out));
01290         out << "userInvDiag_: ";
01291         if (userInvDiag_.is_null ()) {
01292           out << "unset" << endl;
01293         } else if (vl <= Teuchos::VERB_HIGH) {
01294           out << "set" << endl;
01295         } else { // userInvDiag_ not null and vl > Teuchos::VERB_HIGH
01296           out << endl;
01297           {
01298             Teuchos::OSTab tab3 (Teuchos::rcpFromRef (out));
01299             userInvDiag_->describe (out, vl);
01300           }
01301           out << endl;
01302         }
01303         out << "userLambdaMax_: " << userLambdaMax_ << endl
01304             << "userLambdaMin_: " << userLambdaMin_ << endl
01305             << "userEigRatio_: " << userEigRatio_ << endl
01306             << "numIters_: " << numIters_ << endl
01307             << "eigMaxIters_: " << eigMaxIters_ << endl
01308             << "zeroStartingSolution_: " << zeroStartingSolution_ << endl
01309             << "assumeMatrixUnchanged_: " << assumeMatrixUnchanged_ << endl
01310             << "textbookAlgorithm_: " << textbookAlgorithm_ << endl
01311             << "computeMaxResNorm_: " << computeMaxResNorm_ << endl;
01312       }
01313       out << endl;
01314     }
01315   }
01316 }
01317 
01318 } // namespace Details
01319 } // namespace Ifpack2
01320 
01321 #define IFPACK2_DETAILS_CHEBYSHEV_INSTANT(S,LO,GO,N) \
01322   template class Ifpack2::Details::Chebyshev< S, Tpetra::MultiVector<S, LO, GO, N> >;
01323 
01324 #endif // IFPACK2_DETAILS_CHEBYSHEV_DEF_HPP
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends