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