Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
Ifpack2_PowerMethod.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Ifpack2: Templated Object-Oriented Algebraic Preconditioner Package
4//
5// Copyright 2009 NTESS and the Ifpack2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef IFPACK2_POWERMETHOD_HPP
11#define IFPACK2_POWERMETHOD_HPP
12
19
20#include "Kokkos_ArithTraits.hpp"
21#include "Teuchos_FancyOStream.hpp"
22#include "Teuchos_oblackholestream.hpp"
23#include "Tpetra_Details_residual.hpp"
24#include <cmath>
25#include <iostream>
26
27namespace Ifpack2 {
28namespace PowerMethod {
29
30namespace { // (anonymous)
31
32// Functor for making sure the real parts of all entries of a vector
33// are nonnegative. We use this in computeInitialGuessForPowerMethod
34// below.
35template<class OneDViewType,
36 class LocalOrdinal = typename OneDViewType::size_type>
37class PositivizeVector {
38 static_assert (Kokkos::is_view<OneDViewType>::value,
39 "OneDViewType must be a 1-D Kokkos::View.");
40 static_assert (static_cast<int> (OneDViewType::rank) == 1,
41 "This functor only works with a 1-D View.");
42 static_assert (std::is_integral<LocalOrdinal>::value,
43 "The second template parameter, LocalOrdinal, "
44 "must be an integer type.");
45public:
46 PositivizeVector (const OneDViewType& x) : x_ (x) {}
47
48 KOKKOS_INLINE_FUNCTION void
49 operator () (const LocalOrdinal& i) const
50 {
51 typedef typename OneDViewType::non_const_value_type IST;
52 typedef Kokkos::ArithTraits<IST> STS;
53 typedef Kokkos::ArithTraits<typename STS::mag_type> STM;
54
55 if(STS::real (x_(i)) < STM::zero ()) {
56 x_(i) = -x_(i);
57 }
58 }
59
60private:
61 OneDViewType x_;
62};
63
64} // namespace (anonymous)
65
66
67
87template<class OperatorType, class V>
88typename V::scalar_type
89powerMethodWithInitGuess(const OperatorType& A,
90 const V& D_inv,
91 const int numIters,
92 Teuchos::RCP<V> x,
93 Teuchos::RCP<V> y,
94 const typename Teuchos::ScalarTraits<typename V::scalar_type>::magnitudeType tolerance = 1e-7,
95 const int eigNormalizationFreq = 1,
96 Teuchos::RCP<Teuchos::FancyOStream> out = Teuchos::null,
97 const bool computeSpectralRadius = true)
98{
99 typedef typename V::scalar_type ST;
100 typedef Teuchos::ScalarTraits<typename V::scalar_type> STS;
101 typedef typename Teuchos::ScalarTraits<typename V::scalar_type>::magnitudeType MT;
102
103 bool verbose = (out != Teuchos::null);
104
105 using std::endl;
106 if(verbose) {
107 *out << " powerMethodWithInitGuess:" << endl;
108 }
109
110 const ST zero = static_cast<ST> (0.0);
111 const ST one = static_cast<ST> (1.0);
112 ST lambdaMax = zero;
113 ST lambdaMaxOld = one;
114 ST norm;
115
116 norm = x->norm2();
117 TEUCHOS_TEST_FOR_EXCEPTION
118 (norm == zero, std::runtime_error,
119 "Ifpack2::PowerMethod::powerMethodWithInitGuess: The initial guess "
120 "has zero norm. This could be either because Tpetra::Vector::"
121 "randomize filled the vector with zeros (if that was used to "
122 "compute the initial guess), or because the norm2 method has a "
123 "bug. The first is not impossible, but unlikely.");
124
125 if(verbose) {
126 *out << " Original norm1(x): " << x->norm1()
127 << ", norm2(x): " << norm << endl;
128 }
129
130 x->scale(one / norm);
131
132 if(verbose) {
133 *out << " norm1(x.scale(one/norm)): " << x->norm1() << endl;
134 }
135
136 if(y.is_null())
137 y = Teuchos::rcp(new V(A.getRangeMap()));
138
139 // GH 04 Nov 2022: The previous implementation of the power method was inconsistent with itself.
140 // It computed x->norm2() inside the loop, but returned x^T*Ax.
141 // This returned horribly incorrect estimates for Chebyshev spectral radius in certain
142 // cases, such as the Cheby_belos test, which has two dominant eigenvalues of 12.5839, -10.6639.
143 // In such case, the power method iteration history would appear to converge to something
144 // between 10 and 12, but the resulting spectral radius estimate was sometimes negative.
145 // These have now been split into two routes which behave consistently with themselves.
146 if(computeSpectralRadius) {
147 // In this route, the update is as follows:
148 // y=A*x, x = Dinv*y, lambda = norm(x), x = x/lambda
149 if(verbose) {
150 *out << " PowerMethod using spectral radius route" << endl;
151 }
152 for(int iter = 0; iter < numIters-1; ++iter) {
153 if(verbose) {
154 *out << " Iteration " << iter << endl;
155 }
156 A.apply(*x, *y);
157 x->elementWiseMultiply(STS::one(), D_inv, *y, STS::zero());
158
159 if(((iter+1) % eigNormalizationFreq == 0) && (iter < numIters-2)) {
160 norm = x->norm2();
161 if(norm == zero) { // Return something reasonable.
162 if(verbose) {
163 *out << " norm is zero; returning zero" << endl;
164 *out << " Power method terminated after "<< iter << " iterations." << endl;
165 }
166 return zero;
167 } else {
168 lambdaMaxOld = lambdaMax;
169 lambdaMax = pow(norm, Teuchos::ScalarTraits<MT>::one() / eigNormalizationFreq);
170 if(Teuchos::ScalarTraits<ST>::magnitude(lambdaMax-lambdaMaxOld) < tolerance * Teuchos::ScalarTraits<ST>::magnitude(lambdaMax)) {
171 if(verbose) {
172 *out << " lambdaMax: " << lambdaMax << endl;
173 *out << " Power method terminated after "<< iter << " iterations." << endl;
174 }
175 return lambdaMax;
176 } else if(verbose) {
177 *out << " lambdaMaxOld: " << lambdaMaxOld << endl;
178 *out << " lambdaMax: " << lambdaMax << endl;
179 *out << " |lambdaMax-lambdaMaxOld|/|lambdaMax|: " << Teuchos::ScalarTraits<ST>::magnitude(lambdaMax-lambdaMaxOld)/Teuchos::ScalarTraits<ST>::magnitude(lambdaMax) << endl;
180 }
181 }
182 x->scale(one / norm);
183 }
184 }
185 if(verbose) {
186 *out << " lambdaMax: " << lambdaMax << endl;
187 }
188
189 norm = x->norm2();
190 if(norm == zero) { // Return something reasonable.
191 if(verbose) {
192 *out << " norm is zero; returning zero" << endl;
193 *out << " Power method terminated after "<< numIters << " iterations." << endl;
194 }
195 return zero;
196 }
197 x->scale(one / norm);
198 A.apply(*x, *y);
199 y->elementWiseMultiply(STS::one(), D_inv, *y, STS::zero());
200 lambdaMax = y->norm2();
201 } else {
202 // In this route, the update is as follows:
203 // y=A*x, y = Dinv*y, lambda = x->dot(y), x = y/lambda
204 ST xDinvAx = norm;
205 if(verbose) {
206 *out << " PowerMethod using largest eigenvalue route" << endl;
207 }
208 for (int iter = 0; iter < numIters-1; ++iter) {
209 if(verbose) {
210 *out << " Iteration " << iter << endl;
211 }
212 A.apply(*x, *y);
213 y->elementWiseMultiply(STS::one(), D_inv, *y, STS::zero());
214
215 if(((iter+1) % eigNormalizationFreq == 0) && (iter < numIters-2)) {
216 xDinvAx = x->dot(*y);
217 if(xDinvAx == zero) { // Return something reasonable.
218 if(verbose) {
219 *out << " xDinvAx is zero; returning zero" << endl;
220 *out << " Power method terminated after "<< iter << " iterations." << endl;
221 }
222 return zero;
223 } else {
224 lambdaMaxOld = lambdaMax;
225 lambdaMax = pow(xDinvAx, Teuchos::ScalarTraits<MT>::one() / eigNormalizationFreq);
226 if(Teuchos::ScalarTraits<ST>::magnitude(lambdaMax-lambdaMaxOld) < tolerance * Teuchos::ScalarTraits<ST>::magnitude(lambdaMax)) {
227 if(verbose) {
228 *out << " lambdaMax: " << lambdaMax << endl;
229 *out << " Power method terminated after "<< iter << " iterations." << endl;
230 }
231 return lambdaMax;
232 } else if(verbose) {
233 *out << " lambdaMaxOld: " << lambdaMaxOld << endl;
234 *out << " lambdaMax: " << lambdaMax << endl;
235 *out << " |lambdaMax-lambdaMaxOld|/|lambdaMax|: " << Teuchos::ScalarTraits<ST>::magnitude(lambdaMax-lambdaMaxOld)/Teuchos::ScalarTraits<ST>::magnitude(lambdaMax) << endl;
236 }
237 }
238 x->swap(*y);
239 norm = x->norm2();
240 x->scale(one / norm);
241 }
242 }
243 if(verbose) {
244 *out << " lambdaMax: " << lambdaMax << endl;
245 }
246
247 norm = x->norm2();
248 if(norm == zero) { // Return something reasonable.
249 if(verbose) {
250 *out << " norm is zero; returning zero" << endl;
251 *out << " Power method terminated after "<< numIters << " iterations." << endl;
252 }
253 return zero;
254 }
255 x->scale(one / norm);
256 A.apply(*x, *y);
257 y->elementWiseMultiply(STS::one(), D_inv, *y, STS::zero());
258 lambdaMax = y->dot(*x);
259 }
260
261 if(verbose) {
262 *out << " lambdaMax: " << lambdaMax << endl;
263 *out << " Power method terminated after "<< numIters << " iterations." << endl;
264 }
265
266 return lambdaMax;
267}
268
269
281template<class V>
282void
283computeInitialGuessForPowerMethod (V& x, const bool nonnegativeRealParts)
284{
285 typedef typename V::device_type::execution_space dev_execution_space;
286 typedef typename V::local_ordinal_type LO;
287
288 x.randomize ();
289
290 if(nonnegativeRealParts) {
291 auto x_lcl = x.getLocalViewDevice (Tpetra::Access::ReadWrite);
292 auto x_lcl_1d = Kokkos::subview (x_lcl, Kokkos::ALL (), 0);
293
294 const LO lclNumRows = static_cast<LO> (x.getLocalLength ());
295 Kokkos::RangePolicy<dev_execution_space, LO> range (0, lclNumRows);
296 PositivizeVector<decltype (x_lcl_1d), LO> functor (x_lcl_1d);
297 Kokkos::parallel_for (range, functor);
298 }
299}
300
301
318template<class OperatorType, class V>
319typename V::scalar_type
320powerMethod(const OperatorType& A,
321 const V& D_inv,
322 const int numIters,
323 Teuchos::RCP<V> y,
324 const typename Teuchos::ScalarTraits<typename V::scalar_type>::magnitudeType tolerance = 1e-7,
325 const int eigNormalizationFreq = 1,
326 Teuchos::RCP<Teuchos::FancyOStream> out = Teuchos::null,
327 const bool computeSpectralRadius = true)
328{
329 typedef typename V::scalar_type ST;
330 typedef Teuchos::ScalarTraits<typename V::scalar_type> STS;
331
332 bool verbose = (out != Teuchos::null);
333
334 if(verbose) {
335 *out << "powerMethod:" << std::endl;
336 }
337
338 const ST zero = static_cast<ST> (0.0);
339
340 Teuchos::RCP<V> x = Teuchos::rcp(new V(A.getDomainMap ()));
341 // For the first pass, just let the pseudorandom number generator
342 // fill x with whatever values it wants; don't try to make its
343 // entries nonnegative.
345
346 ST lambdaMax = powerMethodWithInitGuess (A, D_inv, numIters, x, y, tolerance, eigNormalizationFreq, out, computeSpectralRadius);
347
348 // mfh 07 Jan 2015: Taking the real part here is only a concession
349 // to the compiler, so that this can build with ScalarType =
350 // std::complex<T>. Our Chebyshev implementation only works with
351 // real, symmetric positive definite matrices. The right thing to
352 // do would be what Belos does, which is provide a partial
353 // specialization for ScalarType = std::complex<T> with a stub
354 // implementation (that builds, but whose constructor throws).
355 if(STS::real (lambdaMax) < STS::real (zero)) {
356 if(verbose) {
357 *out << "real(lambdaMax) = " << STS::real (lambdaMax) << " < 0; "
358 "try again with a different random initial guess" << std::endl;
359 }
360 // Max eigenvalue estimate was negative. Perhaps we got unlucky
361 // with the random initial guess. Try again with a different (but
362 // still random) initial guess. Only try again once, so that the
363 // run time is bounded.
364
365 // For the second pass, make all the entries of the initial guess
366 // vector have nonnegative real parts.
368 lambdaMax = powerMethodWithInitGuess (A, D_inv, numIters, x, y, tolerance, eigNormalizationFreq, out, computeSpectralRadius);
369 }
370 return lambdaMax;
371}
372
373} // namespace PowerMethod
374} // namespace Ifpack2
375
376#endif // IFPACK2_POWERMETHOD_HPP
V::scalar_type powerMethodWithInitGuess(const OperatorType &A, const V &D_inv, const int numIters, Teuchos::RCP< V > x, Teuchos::RCP< V > y, const typename Teuchos::ScalarTraits< typename V::scalar_type >::magnitudeType tolerance=1e-7, const int eigNormalizationFreq=1, Teuchos::RCP< Teuchos::FancyOStream > out=Teuchos::null, const bool computeSpectralRadius=true)
Use the power method to estimate the maximum eigenvalue of A*D_inv, given an initial guess vector x.
Definition Ifpack2_PowerMethod.hpp:89
V::scalar_type powerMethod(const OperatorType &A, const V &D_inv, const int numIters, Teuchos::RCP< V > y, const typename Teuchos::ScalarTraits< typename V::scalar_type >::magnitudeType tolerance=1e-7, const int eigNormalizationFreq=1, Teuchos::RCP< Teuchos::FancyOStream > out=Teuchos::null, const bool computeSpectralRadius=true)
Use the power method to estimate the maximum eigenvalue of A*D_inv.
Definition Ifpack2_PowerMethod.hpp:320
void computeInitialGuessForPowerMethod(V &x, const bool nonnegativeRealParts)
Fill x with random initial guess for power method.
Definition Ifpack2_PowerMethod.hpp:283
Preconditioners and smoothers for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:41