Loading...
Searching...
No Matches
chebyshev.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "linalg/operator.hpp"
5#include "power_iteration.hpp"
6#include "solver.hpp"
7
8#include <cmath>
9
10namespace terra::linalg::solvers {
11
12/// @brief Chebyshev accelerated Jacobi iterative solver for linear systems.
13///
14/// Computes (given an order \f$p \geq 1, \ p \in \mathbb{N}\f$)
15/// \f[
16/// x^{k+1} = x^k + \alpha_k \, D^{-1}(b - A x^k) + \beta_k \, d^k, \quad
17/// d^0 = 0
18/// \f]
19/// for \f$k = 0\f$ up to \f$k+1 = p\f$, where \f$D = \mathrm{diag}(A)\f$, and the coefficients \f$ \alpha_k \f$ and
20/// \f$ \beta_k \f$ are computed recursively from
21/// \f[
22/// \begin{aligned}
23/// \rho_0 &= \frac{\delta}{\theta}, \quad \\
24/// \rho_{k+1} &= (2(\theta/\delta) - \rho_k)^{-1}, \quad \\
25/// \alpha_0 &= \theta^{-1}, \\
26/// \alpha_k &= \frac{2 \, \rho_{k+1}}{\delta}, \quad \\
27/// \beta_0 &= 0, \\
28/// \beta_k &= \rho_{k+1} \rho_k, \\
29/// d_0 &= 0, \\
30/// d_k &= x^{k+1} - x^{k} = \alpha_k D^{-1}(b - A x^k) + \beta_k d_{k-1}
31/// \end{aligned}
32/// \f]
33/// with \f$ \theta = (\lambda_{\max} + \lambda_{\min})/2 \f$ and \f$ \delta = (\lambda_{\max} - \lambda_{\min})/2\f$ ,
34/// and \f$ \lambda_{\min}, \lambda_{\max} \f$ the eigenvalue bounds of \f$ D^{-1}A \f$.
35///
36/// The max eigenvalue \f$\lambda_{\max}\f$ is estimated before the first solve (or if requested via
37/// `refresh_max_eigenvalue_estimate_in_next_solve()`) applying a specified number of power iterations. A safety margin
38/// is added to hopefully ensure that the estimate is not too far off. The minimum eigenvalue is the obtained by scaling
39/// the maximum eigenvalue.
40///
41/// Each iteration executes \f$p\f$ matrix-vector products (plus some vector-vector operations).
42/// Setting the order \f$p = 1\f$ recovers the standard weighted Jacobi relaxation.
43/// If unsure, try low-order settings with \f$p = 2\f$ or \f$p = 3\f$.
44///
45/// The number of iterations repeats the entire process. For instance, setting the iterations \f$\text{it} = 10\f$ will
46/// perform \f$\text{it} \times p\f$ updates.
47///
48/// Satisfies the SolverLike concept (see solver.hpp).
49///
50/// @tparam OperatorT Operator type (must satisfy OperatorLike).
51template < OperatorLike OperatorT >
53{
54 public:
55 /// @brief Operator type to be solved.
56 using OperatorType = OperatorT;
57 /// @brief Solution vector type.
59 /// @brief Right-hand side vector type.
61
62 /// @brief Scalar type for computations.
63 using ScalarType = SolutionVectorType::ScalarType;
64
65 /// @brief Construct a Chebyshev accelerated Jacobi solver.
66 /// @param order Chebyshev order \f$p >= 1\f$. Equivalent to weighted Jacobi for \f$p = 1\f$.
67 /// @param inverse_diagonal Inverse of the diagonal of the operator (D^-1).
68 /// @param tmps Temporary vectors for workspace. We need 2 here.
69 /// @param iterations Number of Chebyshev iterations to perform.
70 /// @param max_ev_power_iterations Number of power iterations executed to estimate the max eigenvalue of ((D^-1)A).
71 /// An additional safety margin is added internally during the setup of the Chebyshev
72 /// solver.
74 const int order,
75 const SolutionVectorType& inverse_diagonal,
76 const std::vector< SolutionVectorType >& tmps,
77 const int iterations = 1,
78 const int max_ev_power_iterations = 50 )
79 : order_( order )
80 , inverse_diagonal_( inverse_diagonal )
81 , iterations_( iterations )
82 , tmps_( tmps )
83 , max_ev_power_iterations_( max_ev_power_iterations )
84 , need_max_ev_estimation_( true )
85 {
86 if ( order_ < 1 )
87 {
88 Kokkos::abort( "Chebyshev order must be at least 1." );
89 }
90
91 if ( tmps_.size() < 2 )
92 {
93 Kokkos::abort( "Chebyshev requires at least 2 tmp vectors." );
94 }
95 }
96
97 /// @brief Solve the linear system using the Chebyshev iteration.
98 /// Applies the update rule for the specified number of iterations.
99 /// @param A Operator (matrix).
100 /// @param x Solution vector (output).
101 /// @param b Right-hand side vector (input).
103 {
104 if ( need_max_ev_estimation_ )
105 {
106 estimate_max_eigenvalues( A );
107 need_max_ev_estimation_ = false;
108 }
109
110 auto& d = tmps_[0];
111 auto& z = tmps_[1];
112
113 // Guard against degenerate eigenvalue estimates (e.g. from near-singular operators).
114 if ( !std::isfinite( max_ev_estimate_ ) || max_ev_estimate_ <= ScalarType( 0 ) )
115 {
116 max_ev_estimate_ = ScalarType( 1 );
117 }
118
119 const auto lambda_max = 1.5 * max_ev_estimate_;
120 const auto lambda_min = 0.1 * max_ev_estimate_;
121
122 const auto theta = 0.5 * ( lambda_max + lambda_min );
123 const auto delta = 0.5 * ( lambda_max - lambda_min );
124
125 const auto sigma = theta / delta;
126 auto rho = 1.0 / sigma;
127
128 for ( int iteration = 0; iteration < iterations_; ++iteration )
129 {
130 // r = b - A(x);
131 // z = M_inv(r);
132
133 apply( A, x, z );
134 lincomb( z, { 1.0, -1.0 }, { b, z } );
135 scale_in_place( z, inverse_diagonal_ );
136
137 // d = z / theta; // first Chebyshev direction
138 // x = x + d;
139
140 lincomb( d, { 1.0 / theta }, { z } );
141 lincomb( x, { 1.0, 1.0 }, { x, d } );
142
143 // cheby recurrence
144
145 for ( int i = 2; i <= order_; ++i )
146 {
147 const auto rho_new = 1.0 / ( 2.0 * sigma - rho );
148
149 const auto beta = rho_new * rho;
150 const auto alpha = 2.0 * rho_new / delta;
151
152 apply( A, x, z );
153 lincomb( z, { 1.0, -1.0 }, { b, z } );
154 scale_in_place( z, inverse_diagonal_ );
155
156 // d = alpha * z + beta * d;
157 // x = x + d;
158
159 lincomb( d, { alpha, beta }, { z, d } );
160 lincomb( x, { 1.0, 1.0 }, { x, d } );
161
162 rho = rho_new;
163 }
164 }
165 }
166
167 SolutionVectorType& get_inverse_diagonal() { return inverse_diagonal_; }
168
169 void refresh_max_eigenvalue_estimate_in_next_solve() { need_max_ev_estimation_ = true; }
170
171 private:
172 void estimate_max_eigenvalues( OperatorType& A )
173 {
174 DiagonallyScaledOperator< OperatorType > inv_diag_A( A, inverse_diagonal_ );
175 max_ev_estimate_ = solvers::power_iteration( inv_diag_A, tmps_[0], tmps_[1], max_ev_power_iterations_ );
176 }
177
178 int order_; ///< Order of the Chebyshev smoother.
179 SolutionVectorType inverse_diagonal_; ///< Inverse diagonal vector.
180 int iterations_; ///< Number of iterations.
181 std::vector< SolutionVectorType > tmps_; ///< Temporary workspace vectors.
182 int max_ev_power_iterations_; ///< Number of power iterations to estimate the max eigenvalue.
183 bool need_max_ev_estimation_; ///< If true, max eigenvalue is estimated in next solve call.
184 ScalarType max_ev_estimate_; ///< Estimate of the max eigenvalue of ((D^-1)A).
185};
186
187/// @brief Static assertion: Chebyshev satisfies SolverLike concept.
188static_assert( SolverLike< Chebyshev< linalg::detail::DummyConcreteOperator > > );
189
190} // namespace terra::linalg::solvers
Given some operator and a vector , this operator is equivalent to an operator defined as.
Definition diagonally_scaled_operator.hpp:16
Chebyshev accelerated Jacobi iterative solver for linear systems.
Definition chebyshev.hpp:53
void refresh_max_eigenvalue_estimate_in_next_solve()
Definition chebyshev.hpp:169
SrcOf< OperatorType > SolutionVectorType
Solution vector type.
Definition chebyshev.hpp:58
SolutionVectorType::ScalarType ScalarType
Scalar type for computations.
Definition chebyshev.hpp:63
Chebyshev(const int order, const SolutionVectorType &inverse_diagonal, const std::vector< SolutionVectorType > &tmps, const int iterations=1, const int max_ev_power_iterations=50)
Construct a Chebyshev accelerated Jacobi solver.
Definition chebyshev.hpp:73
void solve_impl(OperatorType &A, SolutionVectorType &x, const RHSVectorType &b)
Solve the linear system using the Chebyshev iteration. Applies the update rule for the specified numb...
Definition chebyshev.hpp:102
DstOf< OperatorType > RHSVectorType
Right-hand side vector type.
Definition chebyshev.hpp:60
OperatorT OperatorType
Operator type to be solved.
Definition chebyshev.hpp:56
SolutionVectorType & get_inverse_diagonal()
Definition chebyshev.hpp:167
Definition block_preconditioner_2x2.hpp:7
double power_iteration(OperatorT &op, SrcOf< OperatorT > &tmpIt, SrcOf< OperatorT > &tmpAux, const int iterations)
Power iteration to estimate the largest eigenvalue of a row-normalized operator D^{-1}A,...
Definition power_iteration.hpp:15
void lincomb(Vector &y, const std::vector< ScalarOf< Vector > > &c, const std::vector< Vector > &x, const ScalarOf< Vector > &c0)
Compute a linear combination of vectors. Implements: .
Definition vector.hpp:72
Operator::SrcVectorType SrcOf
Alias for the source vector type of an operator.
Definition operator.hpp:145
void apply(LinearForm &L, typename LinearForm::DstVectorType &dst)
Apply a linear form and write to a destination vector.
Definition linear_form.hpp:37
Operator::DstVectorType DstOf
Alias for the destination vector type of an operator.
Definition operator.hpp:149
void scale_in_place(Vector &y, const Vector &x)
Scale a vector in place with another vector. For each entry , computes .
Definition vector.hpp:137