Loading...
Searching...
No Matches
pbicgstab.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "identity_solver.hpp"
9#include "util/table.hpp"
10#include "util/timer.hpp"
11
12namespace terra::linalg::solvers {
13
14/// @brief BiCGStab(l) iterative solver for general (possibly unsymmetric) linear systems.
15///
16/// See
17/// @code
18/// Sleijpen, G. L., & Fokkema, D. R. (1993).
19/// BiCGstab (ell) for linear equations involving unsymmetric matrices with complex spectrum.
20/// Electronic Transactions on Numerical Analysis., 1, 11-32.
21/// @endcode
22/// for details.
23///
24/// Satisfies the SolverLike concept (see solver.hpp).
25/// Supports optional preconditioning.
26/// @tparam OperatorT Operator type (must satisfy OperatorLike).
27/// @tparam PreconditionerT Preconditioner type (must satisfy SolverLike, defaults to IdentitySolver).
28template < OperatorLike OperatorT, SolverLike PreconditionerT = IdentitySolver< OperatorT > >
30{
31 public:
32 /// @brief Operator type to be solved.
33 using OperatorType = OperatorT;
34 /// @brief Solution vector type.
36 /// @brief Right-hand side vector type.
38 /// @brief Scalar type for computations.
39 using ScalarType = typename SolutionVectorType::ScalarType;
40
41 /// @brief Construct a PBiCGStab solver.
42 /// @param l Number of BiCG iterations per "minimal residual" (MR) step.
43 /// @param params Iterative solver parameters.
44 /// @param statistics Shared pointer to statistics table.
45 /// @param tmp Temporary vectors for workspace. (At least 2 * (l + 1) + 2 vectors are required.)
46 /// @param preconditioner Preconditioner solver (optional).
48 const int l,
49 const IterativeSolverParameters& params,
50 const std::shared_ptr< util::Table >& statistics,
51 const std::vector< SolutionVectorType >& tmp,
52 const PreconditionerT& preconditioner = IdentitySolver< OperatorT >() )
53 : l_( l )
54 , params_( params )
55 , statistics_( statistics )
56 , tmp_( tmp )
57 , tag_( "pbicgstab_solver" )
58 , preconditioner_( preconditioner )
59 , conv_rate_tolerance_( std::numeric_limits< ScalarType >::max() )
60 {
61 const int num_required_tmp_vectors = 2 * ( l + 1 ) + 2;
62 if ( tmp.size() < num_required_tmp_vectors )
63 {
64 throw std::runtime_error(
65 "PBiCGStab: tmp.size() < 2 * (l+1) + 2 = " + std::to_string( num_required_tmp_vectors ) );
66 }
67
68 if ( tmp.size() > num_required_tmp_vectors )
69 {
70 std::cout << "Note: You are using more tmp vectors that required in PBiCGStab. Required: "
71 << num_required_tmp_vectors << ", passed: " << tmp.size() << std::endl;
72 }
73 }
74
75 /// @brief Set a tag string for statistics output.
76 /// @param tag Tag string.
77 void set_tag( const std::string& tag ) { tag_ = tag; }
78
79 /// @brief Sets the convergence rate tolerance. Iteration exits when that tolerance is surpassed.
80 void set_convergence_rate_tolerance( const ScalarType& tolerance ) { conv_rate_tolerance_ = tolerance; }
81
82 /// @brief Solve the linear system \‍( Ax = b \‍) using PBiCGStab.
83 /// Calls the iterative solver and updates statistics.
84 /// @param A Operator (matrix).
85 /// @param x Solution vector (output).
86 /// @param b Right-hand side vector (input).
88 {
89 util::Timer timer( "pbicgstab" );
90
91 linalg::randomize( r_shadow() );
92
93 for ( int j = 0; j < l_ + 1; ++j )
94 {
95 assign( us( j ), 0 );
96 }
97
98 apply( A, x, residual() );
99 lincomb( residual(), { 1.0, -1.0 }, { b, residual() } );
100
101 if constexpr ( !std::is_same_v< PreconditionerT, IdentitySolver< OperatorT > > )
102 {
103 assign( tmp_prec(), residual() );
104 solve( preconditioner_, A, tmp_prec(), residual() );
105 assign( residual(), tmp_prec() );
106 }
107
108 const ScalarType initial_residual = std::sqrt( dot( residual(), residual() ) );
109
110 ScalarType absolute_residual = initial_residual;
111 ScalarType relative_residual = 1.0;
112 int iteration = 0;
113
114 /// @brief Lambda to add a row to the statistics table.
115 auto add_table_row = [&]( bool final_iteration ) {
116 if ( statistics_ )
117 {
118 if ( final_iteration )
119 {
120 statistics_->add_row(
121 { { "tag", tag_ },
122 { "final_iteration", iteration },
123 { "relative_residual", relative_residual },
124 { "absolute_residual", absolute_residual } } );
125 }
126 else
127 {
128 statistics_->add_row(
129 { { "tag", tag_ },
130 { "iteration", iteration },
131 { "relative_residual", relative_residual },
132 { "absolute_residual", absolute_residual } } );
133 }
134 }
135 };
136
137 add_table_row( false );
138
139 if ( initial_residual < params_.absolute_residual_tolerance() )
140 {
141 add_table_row( true );
142 return;
143 }
144
145 ScalarType omega = 1;
146 ScalarType sigma = 1;
147
148 // This has to be double regardless of the template parameter for robustness.
149 Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > M( l_, l_ );
150 Eigen::Matrix< double, Eigen::Dynamic, 1 > gamma( l_ );
151
152 iteration++;
153
154 for ( ; iteration < params_.max_iterations(); ++iteration )
155 {
156 sigma = -omega * sigma;
157
158 // BiCG part
159
160 for ( int j = 0; j < l_; ++j )
161 {
162 auto rho = dot( r_shadow(), rs( j ) );
163 auto beta = rho / sigma;
164
165 for ( int i = 0; i <= j; ++i )
166 {
167 lincomb( us( i ), { 1.0, -beta }, { rs( i ), us( i ) } );
168 }
169
170 apply( A, us( j ), us( j + 1 ) );
171
172 if constexpr ( !std::is_same_v< PreconditionerT, IdentitySolver< OperatorT > > )
173 {
174 assign( tmp_prec(), us( j + 1 ) );
175 solve( preconditioner_, A, tmp_prec(), us( j + 1 ) );
176 assign( us( j + 1 ), tmp_prec() );
177 }
178
179 sigma = dot( r_shadow(), us( j + 1 ) );
180 auto alpha = rho / sigma;
181
182 for ( int i = 0; i <= j; ++i )
183 {
184 lincomb( rs( i ), { 1.0, -alpha }, { rs( i ), us( i + 1 ) } );
185 }
186
187 apply( A, rs( j ), rs( j + 1 ) );
188
189 if constexpr ( !std::is_same_v< PreconditionerT, IdentitySolver< OperatorT > > )
190 {
191 assign( tmp_prec(), rs( j + 1 ) );
192 solve( preconditioner_, A, tmp_prec(), rs( j + 1 ) );
193 assign( rs( j + 1 ), tmp_prec() );
194 }
195
196 lincomb( x, { 1.0, alpha }, { x, us( 0 ) } );
197 }
198
199 // MR part
200
201 // This has to be double regardless of the template parameter for robustness.
202 Eigen::Matrix< double, Eigen::Dynamic, 1 > M0( l_ );
203
204 for ( int j = 1; j < l_ + 1; j++ )
205 {
206 M0( j - 1 ) = dot( rs( 0 ), rs( j ) );
207 }
208
209 for ( int i = 0; i < l_; i++ )
210 {
211 for ( int j = 0; j < l_; j++ )
212 {
213 M( i, j ) = dot( rs( i + 1 ), rs( j + 1 ) );
214 }
215 }
216
217 gamma = M.fullPivLu().solve( M0 );
218
219 for ( int j = 1; j < l_ + 1; ++j )
220 {
221 lincomb( us( 0 ), { 1.0, ScalarType( -gamma( j - 1 ) ) }, { us( 0 ), us( j ) } );
222 }
223
224 for ( int j = 0; j < l_; ++j )
225 {
226 lincomb( x, { 1.0, ScalarType( gamma( j ) ) }, { x, rs( j ) } );
227 }
228
229 for ( int j = 1; j < l_ + 1; ++j )
230 {
231 lincomb( rs( 0 ), { 1.0, ScalarType( -gamma( j - 1 ) ) }, { rs( 0 ), rs( j ) } );
232 }
233
234 omega = gamma( l_ - 1 );
235
236 const auto prev_abs_residual = absolute_residual;
237 absolute_residual = std::sqrt( dot( residual(), residual() ) );
238 relative_residual = absolute_residual / initial_residual;
239
240 add_table_row( false );
241
242 if ( relative_residual <= params_.relative_residual_tolerance() )
243 {
244 add_table_row( true );
245 return;
246 }
247
248 if ( absolute_residual < params_.absolute_residual_tolerance() )
249 {
250 add_table_row( true );
251 return;
252 }
253
254 if ( absolute_residual / prev_abs_residual > conv_rate_tolerance_ )
255 {
256 add_table_row( true );
257 return;
258 }
259 }
260
261 add_table_row( true );
262 }
263
264 private:
265 /// @brief Accessor for the shadow residual vector.
266 SolutionVectorType& r_shadow() { return tmp_[0]; }
267 /// @brief Accessor for the j-th residual vector.
268 SolutionVectorType& rs( int index ) { return tmp_[1 + index]; }
269 /// @brief Accessor for the j-th search direction vector.
270 SolutionVectorType& us( int index ) { return tmp_[1 + l_ + 1 + index]; }
271 /// @brief Accessor for the temporary preconditioner vector.
272 SolutionVectorType& tmp_prec() { return tmp_[1 + 2 * ( l_ + 1 )]; }
273 /// @brief Accessor for the main residual vector.
274 SolutionVectorType& residual() { return rs( 0 ); }
275
276 int l_; ///< Number of BiCG iterations per MR step.
277
278 IterativeSolverParameters params_; ///< Solver parameters.
279
280 std::shared_ptr< util::Table > statistics_; ///< Statistics table.
281
282 std::vector< SolutionVectorType > tmp_; ///< Temporary workspace vectors.
283
284 std::string tag_; ///< Tag for statistics output.
285
286 PreconditionerT preconditioner_; ///< Preconditioner solver.
287
288 ScalarType conv_rate_tolerance_; ///< Exits when the convergence rate surpasses that value.
289};
290
291/// @brief Static assertion: PBiCGStab satisfies SolverLike concept.
292static_assert( SolverLike< PBiCGStab< linalg::detail::DummyOperator<
293 linalg::detail::DummyVector< double >,
294 linalg::detail::DummyVector< double > > > > );
295
296} // namespace terra::linalg::solvers
"Identity solver" for linear systems.
Definition identity_solver.hpp:21
Definition iterative_solver_info.hpp:7
double absolute_residual_tolerance() const
Definition iterative_solver_info.hpp:20
int max_iterations() const
Definition iterative_solver_info.hpp:18
double relative_residual_tolerance() const
Definition iterative_solver_info.hpp:19
BiCGStab(l) iterative solver for general (possibly unsymmetric) linear systems.
Definition pbicgstab.hpp:30
void set_tag(const std::string &tag)
Set a tag string for statistics output.
Definition pbicgstab.hpp:77
void set_convergence_rate_tolerance(const ScalarType &tolerance)
Sets the convergence rate tolerance. Iteration exits when that tolerance is surpassed.
Definition pbicgstab.hpp:80
typename SolutionVectorType::ScalarType ScalarType
Scalar type for computations.
Definition pbicgstab.hpp:39
SrcOf< OperatorType > SolutionVectorType
Solution vector type.
Definition pbicgstab.hpp:35
DstOf< OperatorType > RHSVectorType
Right-hand side vector type.
Definition pbicgstab.hpp:37
PBiCGStab(const int l, const IterativeSolverParameters &params, const std::shared_ptr< util::Table > &statistics, const std::vector< SolutionVectorType > &tmp, const PreconditionerT &preconditioner=IdentitySolver< OperatorT >())
Construct a PBiCGStab solver.
Definition pbicgstab.hpp:47
void solve_impl(OperatorType &A, SolutionVectorType &x, const RHSVectorType &b)
Solve the linear system ( Ax = b ) using PBiCGStab. Calls the iterative solver and updates statistics...
Definition pbicgstab.hpp:87
OperatorT OperatorType
Operator type to be solved.
Definition pbicgstab.hpp:33
Timer supporting RAII scope or manual stop.
Definition timer.hpp:270
Definition block_preconditioner_2x2.hpp:7
void solve(Solver &solver, Operator &A, SolutionVector &x, const RHSVector &b)
Solve a linear system using the given solver and operator. Calls the solver's solve_impl method.
Definition solver.hpp:51
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
void randomize(Vector &y)
Randomize the entries of a vector. Sets each entry of to a random value.
Definition vector.hpp:146
Operator::SrcVectorType SrcOf
Alias for the source vector type of an operator.
Definition operator.hpp:145
ScalarOf< Vector > dot(const Vector &y, const Vector &x)
Compute the dot product of two vectors. Implements: .
Definition vector.hpp:118
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 assign(Vector &y, const ScalarOf< Vector > &c0)
Assign a scalar value to a vector. Implements: .
Definition vector.hpp:97