|
Ifpack2 Templated Preconditioning Package
Version 1.0
|
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
1.7.6.1