1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1998 - 2020 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15
16 #ifndef dealii_solver_gmres_h
17 #define dealii_solver_gmres_h
18
19
20
21 #include <deal.II/base/config.h>
22
23 #include <deal.II/base/logstream.h>
24 #include <deal.II/base/subscriptor.h>
25
26 #include <deal.II/lac/full_matrix.h>
27 #include <deal.II/lac/householder.h>
28 #include <deal.II/lac/lapack_full_matrix.h>
29 #include <deal.II/lac/solver.h>
30 #include <deal.II/lac/solver_control.h>
31 #include <deal.II/lac/vector.h>
32
33 #include <algorithm>
34 #include <cmath>
35 #include <memory>
36 #include <vector>
37
38 DEAL_II_NAMESPACE_OPEN
39
40 /*!@addtogroup Solvers */
41 /*@{*/
42
43 namespace internal
44 {
45 /**
46 * A namespace for a helper class to the GMRES solver.
47 */
48 namespace SolverGMRESImplementation
49 {
50 /**
51 * Class to hold temporary vectors. This class automatically allocates a
52 * new vector, once it is needed.
53 *
54 * A future version should also be able to shift through vectors
55 * automatically, avoiding restart.
56 */
57
58 template <typename VectorType>
59 class TmpVectors
60 {
61 public:
62 /**
63 * Constructor. Prepares an array of @p VectorType of length @p
64 * max_size.
65 */
66 TmpVectors(const unsigned int max_size, VectorMemory<VectorType> &vmem);
67
68 /**
69 * Destructor. Delete all allocated vectors.
70 */
71 ~TmpVectors() = default;
72
73 /**
74 * Get vector number @p i. If this vector was unused before, an error
75 * occurs.
76 */
77 VectorType &operator[](const unsigned int i) const;
78
79 /**
80 * Get vector number @p i. Allocate it if necessary.
81 *
82 * If a vector must be allocated, @p temp is used to reinit it to the
83 * proper dimensions.
84 */
85 VectorType &
86 operator()(const unsigned int i, const VectorType &temp);
87
88 /**
89 * Return size of data vector. It is used in the solver to store
90 * the Arnoldi vectors.
91 */
92 unsigned int
93 size() const;
94
95
96 private:
97 /**
98 * Pool where vectors are obtained from.
99 */
100 VectorMemory<VectorType> &mem;
101
102 /**
103 * Field for storing the vectors.
104 */
105 std::vector<typename VectorMemory<VectorType>::Pointer> data;
106 };
107 } // namespace SolverGMRESImplementation
108 } // namespace internal
109
110 /**
111 * Implementation of the Restarted Preconditioned Direct Generalized Minimal
112 * Residual Method. The stopping criterion is the norm of the residual.
113 *
114 * The AdditionalData structure contains the number of temporary vectors used.
115 * The size of the Arnoldi basis is this number minus three. Additionally, it
116 * allows you to choose between right or left preconditioning. The default is
117 * left preconditioning. Finally it includes a flag indicating whether or not
118 * the default residual is used as stopping criterion.
119 *
120 *
121 * <h3>Left versus right preconditioning</h3>
122 *
123 * @p AdditionalData allows you to choose between left and right
124 * preconditioning. As expected, this switches between solving for the systems
125 * <i>P<sup>-1</sup>A</i> and <i>AP<sup>-1</sup></i>, respectively.
126 *
127 * A second consequence is the type of residual which is used to measure
128 * convergence. With left preconditioning, this is the <b>preconditioned</b>
129 * residual, while with right preconditioning, it is the residual of the
130 * unpreconditioned system.
131 *
132 * Optionally, this behavior can be overridden by using the flag
133 * AdditionalData::use_default_residual. A <tt>true</tt> value refers to the
134 * behavior described in the previous paragraph, while <tt>false</tt> reverts
135 * it. Be aware though that additional residuals have to be computed in this
136 * case, impeding the overall performance of the solver.
137 *
138 *
139 * <h3>The size of the Arnoldi basis</h3>
140 *
141 * The maximal basis size is controlled by AdditionalData::max_n_tmp_vectors,
142 * and it is this number minus 2. If the number of iteration steps exceeds
143 * this number, all basis vectors are discarded and the iteration starts anew
144 * from the approximation obtained so far.
145 *
146 * Note that the minimizing property of GMRes only pertains to the Krylov
147 * space spanned by the Arnoldi basis. Therefore, restarted GMRes is
148 * <b>not</b> minimizing anymore. The choice of the basis length is a trade-
149 * off between memory consumption and convergence speed, since a longer basis
150 * means minimization over a larger space.
151 *
152 * For the requirements on matrices and vectors in order to work with this
153 * class, see the documentation of the Solver base class.
154 *
155 *
156 * <h3>Observing the progress of linear solver iterations</h3>
157 *
158 * The solve() function of this class uses the mechanism described in the
159 * Solver base class to determine convergence. This mechanism can also be used
160 * to observe the progress of the iteration.
161 *
162 *
163 * <h3>Eigenvalue and condition number estimates</h3>
164 *
165 * This class can estimate eigenvalues and condition number during the
166 * solution process. This is done by creating the Hessenberg matrix during the
167 * inner iterations. The eigenvalues are estimated as the eigenvalues of the
168 * Hessenberg matrix and the condition number is estimated as the ratio of the
169 * largest and smallest singular value of the Hessenberg matrix. The estimates
170 * can be obtained by connecting a function as a slot using @p
171 * connect_condition_number_slot and @p connect_eigenvalues_slot. These slots
172 * will then be called from the solver with the estimates as argument.
173 */
174 template <class VectorType = Vector<double>>
175 class SolverGMRES : public SolverBase<VectorType>
176 {
177 public:
178 /**
179 * Standardized data struct to pipe additional data to the solver.
180 */
181 struct AdditionalData
182 {
183 /**
184 * Constructor. By default, set the number of temporary vectors to 30,
185 * i.e. do a restart every 28 iterations. Also set preconditioning from
186 * left, the residual of the stopping criterion to the default residual,
187 * and re-orthogonalization only if necessary.
188 */
189 explicit AdditionalData(const unsigned int max_n_tmp_vectors = 30,
190 const bool right_preconditioning = false,
191 const bool use_default_residual = true,
192 const bool force_re_orthogonalization = false);
193
194 /**
195 * Maximum number of temporary vectors. This parameter controls the size
196 * of the Arnoldi basis, which for historical reasons is
197 * #max_n_tmp_vectors-2. SolverGMRES assumes that there are at least three
198 * temporary vectors, so this value must be greater than or equal to three.
199 */
200 unsigned int max_n_tmp_vectors;
201
202 /**
203 * Flag for right preconditioning.
204 *
205 * @note Change between left and right preconditioning will also change
206 * the way residuals are evaluated. See the corresponding section in the
207 * SolverGMRES.
208 */
209 bool right_preconditioning;
210
211 /**
212 * Flag for the default residual that is used to measure convergence.
213 */
214 bool use_default_residual;
215
216 /**
217 * Flag to force re-orthogonalization of orthonormal basis in every step.
218 * If set to false, the solver automatically checks for loss of
219 * orthogonality every 5 iterations and enables re-orthogonalization only
220 * if necessary.
221 */
222 bool force_re_orthogonalization;
223 };
224
225 /**
226 * Constructor.
227 */
228 SolverGMRES(SolverControl & cn,
229 VectorMemory<VectorType> &mem,
230 const AdditionalData & data = AdditionalData());
231
232 /**
233 * Constructor. Use an object of type GrowingVectorMemory as a default to
234 * allocate memory.
235 */
236 SolverGMRES(SolverControl &cn, const AdditionalData &data = AdditionalData());
237
238 /**
239 * The copy constructor is deleted.
240 */
241 SolverGMRES(const SolverGMRES<VectorType> &) = delete;
242
243 /**
244 * Solve the linear system $Ax=b$ for x.
245 */
246 template <typename MatrixType, typename PreconditionerType>
247 void
248 solve(const MatrixType & A,
249 VectorType & x,
250 const VectorType & b,
251 const PreconditionerType &preconditioner);
252
253 /**
254 * Connect a slot to retrieve the estimated condition number. Called on each
255 * outer iteration if every_iteration=true, otherwise called once when
256 * iterations are ended (i.e., either because convergence has been achieved,
257 * or because divergence has been detected).
258 */
259 boost::signals2::connection
260 connect_condition_number_slot(const std::function<void(double)> &slot,
261 const bool every_iteration = false);
262
263 /**
264 * Connect a slot to retrieve the estimated eigenvalues. Called on each
265 * outer iteration if every_iteration=true, otherwise called once when
266 * iterations are ended (i.e., either because convergence has been achieved,
267 * or because divergence has been detected).
268 */
269 boost::signals2::connection
270 connect_eigenvalues_slot(
271 const std::function<void(const std::vector<std::complex<double>> &)> &slot,
272 const bool every_iteration = false);
273
274 /**
275 * Connect a slot to retrieve the Hessenberg matrix obtained by the
276 * projection of the initial matrix on the Krylov basis. Called on each
277 * outer iteration if every_iteration=true, otherwise called once when
278 * iterations are ended (i.e., either because convergence has been achieved,
279 * or because divergence has been detected).
280 */
281 boost::signals2::connection
282 connect_hessenberg_slot(
283 const std::function<void(const FullMatrix<double> &)> &slot,
284 const bool every_iteration = true);
285
286 /**
287 * Connect a slot to retrieve the basis vectors of the Krylov space
288 * generated by the Arnoldi algorithm. Called at once when iterations
289 * are completed (i.e., either because convergence has been achieved,
290 * or because divergence has been detected).
291 */
292 boost::signals2::connection
293 connect_krylov_space_slot(
294 const std::function<
295 void(const internal::SolverGMRESImplementation::TmpVectors<VectorType> &)>
296 &slot);
297
298
299 /**
300 * Connect a slot to retrieve a notification when the vectors are
301 * re-orthogonalized.
302 */
303 boost::signals2::connection
304 connect_re_orthogonalization_slot(const std::function<void(int)> &slot);
305
306
307 DeclException1(ExcTooFewTmpVectors,
308 int,
309 << "The number of temporary vectors you gave (" << arg1
310 << ") is too small. It should be at least 10 for "
311 << "any results, and much more for reasonable ones.");
312
313 protected:
314 /**
315 * Includes the maximum number of tmp vectors.
316 */
317 AdditionalData additional_data;
318
319 /**
320 * Signal used to retrieve the estimated condition number. Called once when
321 * all iterations are ended.
322 */
323 boost::signals2::signal<void(double)> condition_number_signal;
324
325 /**
326 * Signal used to retrieve the estimated condition numbers. Called on each
327 * outer iteration.
328 */
329 boost::signals2::signal<void(double)> all_condition_numbers_signal;
330
331 /**
332 * Signal used to retrieve the estimated eigenvalues. Called once when all
333 * iterations are ended.
334 */
335 boost::signals2::signal<void(const std::vector<std::complex<double>> &)>
336 eigenvalues_signal;
337
338 /**
339 * Signal used to retrieve the estimated eigenvalues. Called on each outer
340 * iteration.
341 */
342 boost::signals2::signal<void(const std::vector<std::complex<double>> &)>
343 all_eigenvalues_signal;
344
345 /**
346 * Signal used to retrieve the Hessenberg matrix. Called once when
347 * all iterations are ended.
348 */
349 boost::signals2::signal<void(const FullMatrix<double> &)> hessenberg_signal;
350
351 /**
352 * Signal used to retrieve the Hessenberg matrix. Called on each outer
353 * iteration.
354 */
355 boost::signals2::signal<void(const FullMatrix<double> &)>
356 all_hessenberg_signal;
357
358 /**
359 * Signal used to retrieve the Krylov space basis vectors. Called once
360 * when all iterations are ended.
361 */
362 boost::signals2::signal<void(
363 const internal::SolverGMRESImplementation::TmpVectors<VectorType> &)>
364 krylov_space_signal;
365
366 /**
367 * Signal used to retrieve a notification
368 * when the vectors are re-orthogonalized.
369 */
370 boost::signals2::signal<void(int)> re_orthogonalize_signal;
371
372 /**
373 * Implementation of the computation of the norm of the residual.
374 */
375 virtual double
376 criterion();
377
378 /**
379 * Transformation of an upper Hessenberg matrix into tridiagonal structure
380 * by givens rotation of the last column
381 */
382 void
383 givens_rotation(Vector<double> &h,
384 Vector<double> &b,
385 Vector<double> &ci,
386 Vector<double> &si,
387 int col) const;
388
389 /**
390 * Orthogonalize the vector @p vv against the @p dim (orthogonal) vectors
391 * given by the first argument using the modified Gram-Schmidt algorithm.
392 * The factors used for orthogonalization are stored in @p h. The boolean @p
393 * re_orthogonalize specifies whether the modified Gram-Schmidt algorithm
394 * should be applied twice. The algorithm checks loss of orthogonality in
395 * the procedure every fifth step and sets the flag to true in that case.
396 * All subsequent iterations use re-orthogonalization.
397 * Calls the signal re_orthogonalize_signal if it is connected.
398 */
399 static double
400 modified_gram_schmidt(
401 const internal::SolverGMRESImplementation::TmpVectors<VectorType>
402 & orthogonal_vectors,
403 const unsigned int dim,
404 const unsigned int accumulated_iterations,
405 VectorType & vv,
406 Vector<double> & h,
407 bool & re_orthogonalize,
408 const boost::signals2::signal<void(int)> &re_orthogonalize_signal =
409 boost::signals2::signal<void(int)>());
410
411 /**
412 * Estimates the eigenvalues from the Hessenberg matrix, H_orig, generated
413 * during the inner iterations. Uses these estimate to compute the condition
414 * number. Calls the signals eigenvalues_signal and cond_signal with these
415 * estimates as arguments.
416 */
417 static void
418 compute_eigs_and_cond(
419 const FullMatrix<double> &H_orig,
420 const unsigned int dim,
421 const boost::signals2::signal<
422 void(const std::vector<std::complex<double>> &)> &eigenvalues_signal,
423 const boost::signals2::signal<void(const FullMatrix<double> &)>
424 & hessenberg_signal,
425 const boost::signals2::signal<void(double)> &cond_signal);
426
427 /**
428 * Projected system matrix
429 */
430 FullMatrix<double> H;
431
432 /**
433 * Auxiliary matrix for inverting @p H
434 */
435 FullMatrix<double> H1;
436 };
437
438 /**
439 * Implementation of the Generalized minimal residual method with flexible
440 * preconditioning (flexible GMRES or FGMRES).
441 *
442 * This flexible version of the GMRES method allows for the use of a different
443 * preconditioner in each iteration step. Therefore, it is also more robust
444 * with respect to inaccurate evaluation of the preconditioner. An important
445 * application is the use of a Krylov space method inside the
446 * preconditioner. As opposed to SolverGMRES which allows one to choose
447 * between left and right preconditioning, this solver always applies the
448 * preconditioner from the right.
449 *
450 * FGMRES needs two vectors in each iteration steps yielding a total of
451 * <tt>2*SolverFGMRES::%AdditionalData::%max_basis_size+1</tt> auxiliary
452 * vectors. Otherwise, FGMRES requires roughly the same number of operations
453 * per iteration compared to GMRES, except one application of the
454 * preconditioner less at each restart and at the end of solve().
455 *
456 * For more details see @cite Saad1991.
457 */
458 template <class VectorType = Vector<double>>
459 class SolverFGMRES : public SolverBase<VectorType>
460 {
461 public:
462 /**
463 * Standardized data struct to pipe additional data to the solver.
464 */
465 struct AdditionalData
466 {
467 /**
468 * Constructor. By default, set the maximum basis size to 30.
469 */
470 explicit AdditionalData(const unsigned int max_basis_size = 30)
max_basis_sizeAdditionalData471 : max_basis_size(max_basis_size)
472 {}
473
474 /**
475 * Maximum basis size.
476 */
477 unsigned int max_basis_size;
478 };
479
480 /**
481 * Constructor.
482 */
483 SolverFGMRES(SolverControl & cn,
484 VectorMemory<VectorType> &mem,
485 const AdditionalData & data = AdditionalData());
486
487 /**
488 * Constructor. Use an object of type GrowingVectorMemory as a default to
489 * allocate memory.
490 */
491 SolverFGMRES(SolverControl & cn,
492 const AdditionalData &data = AdditionalData());
493
494 /**
495 * Solve the linear system $Ax=b$ for x.
496 */
497 template <typename MatrixType, typename PreconditionerType>
498 void
499 solve(const MatrixType & A,
500 VectorType & x,
501 const VectorType & b,
502 const PreconditionerType &preconditioner);
503
504 private:
505 /**
506 * Additional flags.
507 */
508 AdditionalData additional_data;
509
510 /**
511 * Projected system matrix
512 */
513 FullMatrix<double> H;
514
515 /**
516 * Auxiliary matrix for inverting @p H
517 */
518 FullMatrix<double> H1;
519 };
520
521 /*@}*/
522 /* --------------------- Inline and template functions ------------------- */
523
524
525 #ifndef DOXYGEN
526 namespace internal
527 {
528 namespace SolverGMRESImplementation
529 {
530 template <class VectorType>
TmpVectors(const unsigned int max_size,VectorMemory<VectorType> & vmem)531 inline TmpVectors<VectorType>::TmpVectors(const unsigned int max_size,
532 VectorMemory<VectorType> &vmem)
533 : mem(vmem)
534 , data(max_size)
535 {}
536
537
538
539 template <class VectorType>
540 inline VectorType &TmpVectors<VectorType>::
541 operator[](const unsigned int i) const
542 {
543 AssertIndexRange(i, data.size());
544
545 Assert(data[i] != nullptr, ExcNotInitialized());
546 return *data[i];
547 }
548
549
550
551 template <class VectorType>
552 inline VectorType &
operator()553 TmpVectors<VectorType>::operator()(const unsigned int i,
554 const VectorType & temp)
555 {
556 AssertIndexRange(i, data.size());
557 if (data[i] == nullptr)
558 {
559 data[i] = std::move(typename VectorMemory<VectorType>::Pointer(mem));
560 data[i]->reinit(temp);
561 }
562 return *data[i];
563 }
564
565
566
567 template <class VectorType>
568 unsigned int
size()569 TmpVectors<VectorType>::size() const
570 {
571 return (data.size() > 0 ? data.size() - 1 : 0);
572 }
573
574
575
576 // A comparator for better printing eigenvalues
577 inline bool
complex_less_pred(const std::complex<double> & x,const std::complex<double> & y)578 complex_less_pred(const std::complex<double> &x,
579 const std::complex<double> &y)
580 {
581 return x.real() < y.real() ||
582 (x.real() == y.real() && x.imag() < y.imag());
583 }
584 } // namespace SolverGMRESImplementation
585 } // namespace internal
586
587
588
589 template <class VectorType>
AdditionalData(const unsigned int max_n_tmp_vectors,const bool right_preconditioning,const bool use_default_residual,const bool force_re_orthogonalization)590 inline SolverGMRES<VectorType>::AdditionalData::AdditionalData(
591 const unsigned int max_n_tmp_vectors,
592 const bool right_preconditioning,
593 const bool use_default_residual,
594 const bool force_re_orthogonalization)
595 : max_n_tmp_vectors(max_n_tmp_vectors)
596 , right_preconditioning(right_preconditioning)
597 , use_default_residual(use_default_residual)
598 , force_re_orthogonalization(force_re_orthogonalization)
599 {
600 Assert(3 <= max_n_tmp_vectors,
601 ExcMessage("SolverGMRES needs at least three "
602 "temporary vectors."));
603 }
604
605
606
607 template <class VectorType>
SolverGMRES(SolverControl & cn,VectorMemory<VectorType> & mem,const AdditionalData & data)608 SolverGMRES<VectorType>::SolverGMRES(SolverControl & cn,
609 VectorMemory<VectorType> &mem,
610 const AdditionalData & data)
611 : SolverBase<VectorType>(cn, mem)
612 , additional_data(data)
613 {}
614
615
616
617 template <class VectorType>
SolverGMRES(SolverControl & cn,const AdditionalData & data)618 SolverGMRES<VectorType>::SolverGMRES(SolverControl & cn,
619 const AdditionalData &data)
620 : SolverBase<VectorType>(cn)
621 , additional_data(data)
622 {}
623
624
625
626 template <class VectorType>
627 inline void
givens_rotation(Vector<double> & h,Vector<double> & b,Vector<double> & ci,Vector<double> & si,int col)628 SolverGMRES<VectorType>::givens_rotation(Vector<double> &h,
629 Vector<double> &b,
630 Vector<double> &ci,
631 Vector<double> &si,
632 int col) const
633 {
634 for (int i = 0; i < col; i++)
635 {
636 const double s = si(i);
637 const double c = ci(i);
638 const double dummy = h(i);
639 h(i) = c * dummy + s * h(i + 1);
640 h(i + 1) = -s * dummy + c * h(i + 1);
641 };
642
643 const double r = 1. / std::sqrt(h(col) * h(col) + h(col + 1) * h(col + 1));
644 si(col) = h(col + 1) * r;
645 ci(col) = h(col) * r;
646 h(col) = ci(col) * h(col) + si(col) * h(col + 1);
647 b(col + 1) = -si(col) * b(col);
648 b(col) *= ci(col);
649 }
650
651
652
653 template <class VectorType>
654 inline double
modified_gram_schmidt(const internal::SolverGMRESImplementation::TmpVectors<VectorType> & orthogonal_vectors,const unsigned int dim,const unsigned int accumulated_iterations,VectorType & vv,Vector<double> & h,bool & reorthogonalize,const boost::signals2::signal<void (int)> & reorthogonalize_signal)655 SolverGMRES<VectorType>::modified_gram_schmidt(
656 const internal::SolverGMRESImplementation::TmpVectors<VectorType>
657 & orthogonal_vectors,
658 const unsigned int dim,
659 const unsigned int accumulated_iterations,
660 VectorType & vv,
661 Vector<double> & h,
662 bool & reorthogonalize,
663 const boost::signals2::signal<void(int)> &reorthogonalize_signal)
664 {
665 Assert(dim > 0, ExcInternalError());
666 const unsigned int inner_iteration = dim - 1;
667
668 // need initial norm for detection of re-orthogonalization, see below
669 double norm_vv_start = 0;
670 const bool consider_reorthogonalize =
671 (reorthogonalize == false) && (inner_iteration % 5 == 4);
672 if (consider_reorthogonalize)
673 norm_vv_start = vv.l2_norm();
674
675 // Orthogonalization
676 h(0) = vv * orthogonal_vectors[0];
677 for (unsigned int i = 1; i < dim; ++i)
678 h(i) = vv.add_and_dot(-h(i - 1),
679 orthogonal_vectors[i - 1],
680 orthogonal_vectors[i]);
681 double norm_vv =
682 std::sqrt(vv.add_and_dot(-h(dim - 1), orthogonal_vectors[dim - 1], vv));
683
684 // Re-orthogonalization if loss of orthogonality detected. For the test, use
685 // a strategy discussed in C. T. Kelley, Iterative Methods for Linear and
686 // Nonlinear Equations, SIAM, Philadelphia, 1995: Compare the norm of vv
687 // after orthogonalization with its norm when starting the
688 // orthogonalization. If vv became very small (here: less than the square
689 // root of the machine precision times 10), it is almost in the span of the
690 // previous vectors, which indicates loss of precision.
691 if (consider_reorthogonalize)
692 {
693 if (norm_vv >
694 10. * norm_vv_start *
695 std::sqrt(
696 std::numeric_limits<typename VectorType::value_type>::epsilon()))
697 return norm_vv;
698
699 else
700 {
701 reorthogonalize = true;
702 if (!reorthogonalize_signal.empty())
703 reorthogonalize_signal(accumulated_iterations);
704 }
705 }
706
707 if (reorthogonalize == true)
708 {
709 double htmp = vv * orthogonal_vectors[0];
710 h(0) += htmp;
711 for (unsigned int i = 1; i < dim; ++i)
712 {
713 htmp = vv.add_and_dot(-htmp,
714 orthogonal_vectors[i - 1],
715 orthogonal_vectors[i]);
716 h(i) += htmp;
717 }
718 norm_vv =
719 std::sqrt(vv.add_and_dot(-htmp, orthogonal_vectors[dim - 1], vv));
720 }
721
722 return norm_vv;
723 }
724
725
726
727 template <class VectorType>
728 inline void
compute_eigs_and_cond(const FullMatrix<double> & H_orig,const unsigned int dim,const boost::signals2::signal<void (const std::vector<std::complex<double>> &)> & eigenvalues_signal,const boost::signals2::signal<void (const FullMatrix<double> &)> & hessenberg_signal,const boost::signals2::signal<void (double)> & cond_signal)729 SolverGMRES<VectorType>::compute_eigs_and_cond(
730 const FullMatrix<double> &H_orig,
731 const unsigned int dim,
732 const boost::signals2::signal<void(const std::vector<std::complex<double>> &)>
733 &eigenvalues_signal,
734 const boost::signals2::signal<void(const FullMatrix<double> &)>
735 & hessenberg_signal,
736 const boost::signals2::signal<void(double)> &cond_signal)
737 {
738 // Avoid copying the Hessenberg matrix if it isn't needed.
739 if ((!eigenvalues_signal.empty() || !hessenberg_signal.empty() ||
740 !cond_signal.empty()) &&
741 dim > 0)
742 {
743 LAPACKFullMatrix<double> mat(dim, dim);
744 for (unsigned int i = 0; i < dim; ++i)
745 for (unsigned int j = 0; j < dim; ++j)
746 mat(i, j) = H_orig(i, j);
747 hessenberg_signal(H_orig);
748 // Avoid computing eigenvalues if they are not needed.
749 if (!eigenvalues_signal.empty())
750 {
751 // Copy mat so that we can compute svd below. Necessary since
752 // compute_eigenvalues will leave mat in state
753 // LAPACKSupport::unusable.
754 LAPACKFullMatrix<double> mat_eig(mat);
755 mat_eig.compute_eigenvalues();
756 std::vector<std::complex<double>> eigenvalues(dim);
757 for (unsigned int i = 0; i < mat_eig.n(); ++i)
758 eigenvalues[i] = mat_eig.eigenvalue(i);
759 // Sort eigenvalues for nicer output.
760 std::sort(eigenvalues.begin(),
761 eigenvalues.end(),
762 internal::SolverGMRESImplementation::complex_less_pred);
763 eigenvalues_signal(eigenvalues);
764 }
765 // Calculate condition number, avoid calculating the svd if a slot
766 // isn't connected. Need at least a 2-by-2 matrix to do the estimate.
767 if (!cond_signal.empty() && (mat.n() > 1))
768 {
769 mat.compute_svd();
770 double condition_number =
771 mat.singular_value(0) / mat.singular_value(mat.n() - 1);
772 cond_signal(condition_number);
773 }
774 }
775 }
776
777
778
779 template <class VectorType>
780 template <typename MatrixType, typename PreconditionerType>
781 void
solve(const MatrixType & A,VectorType & x,const VectorType & b,const PreconditionerType & preconditioner)782 SolverGMRES<VectorType>::solve(const MatrixType & A,
783 VectorType & x,
784 const VectorType & b,
785 const PreconditionerType &preconditioner)
786 {
787 // TODO:[?] Check, why there are two different start residuals.
788 // TODO:[GK] Make sure the parameter in the constructor means maximum basis
789 // size
790
791 LogStream::Prefix prefix("GMRES");
792
793 // extra call to std::max to placate static analyzers: coverity rightfully
794 // complains that data.max_n_tmp_vectors - 2 may overflow
795 const unsigned int n_tmp_vectors =
796 std::max(additional_data.max_n_tmp_vectors, 3u);
797
798 // Generate an object where basis vectors are stored.
799 internal::SolverGMRESImplementation::TmpVectors<VectorType> tmp_vectors(
800 n_tmp_vectors, this->memory);
801
802 // number of the present iteration; this
803 // number is not reset to zero upon a
804 // restart
805 unsigned int accumulated_iterations = 0;
806
807 const bool do_eigenvalues =
808 !condition_number_signal.empty() || !all_condition_numbers_signal.empty() ||
809 !eigenvalues_signal.empty() || !all_eigenvalues_signal.empty() ||
810 !hessenberg_signal.empty() || !all_hessenberg_signal.empty();
811 // for eigenvalue computation, need to collect the Hessenberg matrix (before
812 // applying Givens rotations)
813 FullMatrix<double> H_orig;
814 if (do_eigenvalues)
815 H_orig.reinit(n_tmp_vectors, n_tmp_vectors - 1);
816
817 // matrix used for the orthogonalization process later
818 H.reinit(n_tmp_vectors, n_tmp_vectors - 1);
819
820 // some additional vectors, also used in the orthogonalization
821 dealii::Vector<double> gamma(n_tmp_vectors), ci(n_tmp_vectors - 1),
822 si(n_tmp_vectors - 1), h(n_tmp_vectors - 1);
823
824
825 unsigned int dim = 0;
826
827 SolverControl::State iteration_state = SolverControl::iterate;
828 double last_res = -std::numeric_limits<double>::max();
829
830 // switch to determine whether we want a left or a right preconditioner. at
831 // present, left is default, but both ways are implemented
832 const bool left_precondition = !additional_data.right_preconditioning;
833
834 // Per default the left preconditioned GMRes uses the preconditioned
835 // residual and the right preconditioned GMRes uses the unpreconditioned
836 // residual as stopping criterion.
837 const bool use_default_residual = additional_data.use_default_residual;
838
839 // define two aliases
840 VectorType &v = tmp_vectors(0, x);
841 VectorType &p = tmp_vectors(n_tmp_vectors - 1, x);
842
843 // Following vectors are needed when we are not using the default residuals
844 // as stopping criterion
845 typename VectorMemory<VectorType>::Pointer r;
846 typename VectorMemory<VectorType>::Pointer x_;
847 std::unique_ptr<dealii::Vector<double>> gamma_;
848 if (!use_default_residual)
849 {
850 r = std::move(typename VectorMemory<VectorType>::Pointer(this->memory));
851 x_ = std::move(typename VectorMemory<VectorType>::Pointer(this->memory));
852 r->reinit(x);
853 x_->reinit(x);
854
855 gamma_ = std::make_unique<dealii::Vector<double>>(gamma.size());
856 }
857
858 bool re_orthogonalize = additional_data.force_re_orthogonalization;
859
860 ///////////////////////////////////////////////////////////////////////////
861 // outer iteration: loop until we either reach convergence or the maximum
862 // number of iterations is exceeded. each cycle of this loop amounts to one
863 // restart
864 do
865 {
866 // reset this vector to the right size
867 h.reinit(n_tmp_vectors - 1);
868
869 if (left_precondition)
870 {
871 A.vmult(p, x);
872 p.sadd(-1., 1., b);
873 preconditioner.vmult(v, p);
874 }
875 else
876 {
877 A.vmult(v, x);
878 v.sadd(-1., 1., b);
879 };
880
881 double rho = v.l2_norm();
882
883 // check the residual here as well since it may be that we got the exact
884 // (or an almost exact) solution vector at the outset. if we wouldn't
885 // check here, the next scaling operation would produce garbage
886 if (use_default_residual)
887 {
888 last_res = rho;
889 iteration_state =
890 this->iteration_status(accumulated_iterations, rho, x);
891
892 if (iteration_state != SolverControl::iterate)
893 break;
894 }
895 else
896 {
897 deallog << "default_res=" << rho << std::endl;
898
899 if (left_precondition)
900 {
901 A.vmult(*r, x);
902 r->sadd(-1., 1., b);
903 }
904 else
905 preconditioner.vmult(*r, v);
906
907 double res = r->l2_norm();
908 last_res = res;
909 iteration_state =
910 this->iteration_status(accumulated_iterations, res, x);
911
912 if (iteration_state != SolverControl::iterate)
913 break;
914 }
915
916 gamma(0) = rho;
917
918 v *= 1. / rho;
919
920 // inner iteration doing at most as many steps as there are temporary
921 // vectors. the number of steps actually been done is propagated outside
922 // through the @p dim variable
923 for (unsigned int inner_iteration = 0;
924 ((inner_iteration < n_tmp_vectors - 2) &&
925 (iteration_state == SolverControl::iterate));
926 ++inner_iteration)
927 {
928 ++accumulated_iterations;
929 // yet another alias
930 VectorType &vv = tmp_vectors(inner_iteration + 1, x);
931
932 if (left_precondition)
933 {
934 A.vmult(p, tmp_vectors[inner_iteration]);
935 preconditioner.vmult(vv, p);
936 }
937 else
938 {
939 preconditioner.vmult(p, tmp_vectors[inner_iteration]);
940 A.vmult(vv, p);
941 }
942
943 dim = inner_iteration + 1;
944
945 const double s = modified_gram_schmidt(tmp_vectors,
946 dim,
947 accumulated_iterations,
948 vv,
949 h,
950 re_orthogonalize,
951 re_orthogonalize_signal);
952 h(inner_iteration + 1) = s;
953
954 // s=0 is a lucky breakdown, the solver will reach convergence,
955 // but we must not divide by zero here.
956 if (s != 0)
957 vv *= 1. / s;
958
959 // for eigenvalues, get the resulting coefficients from the
960 // orthogonalization process
961 if (do_eigenvalues)
962 for (unsigned int i = 0; i < dim + 1; ++i)
963 H_orig(i, inner_iteration) = h(i);
964
965 // Transformation into tridiagonal structure
966 givens_rotation(h, gamma, ci, si, inner_iteration);
967
968 // append vector on matrix
969 for (unsigned int i = 0; i < dim; ++i)
970 H(i, inner_iteration) = h(i);
971
972 // default residual
973 rho = std::fabs(gamma(dim));
974
975 if (use_default_residual)
976 {
977 last_res = rho;
978 iteration_state =
979 this->iteration_status(accumulated_iterations, rho, x);
980 }
981 else
982 {
983 deallog << "default_res=" << rho << std::endl;
984
985 dealii::Vector<double> h_(dim);
986 *x_ = x;
987 *gamma_ = gamma;
988 H1.reinit(dim + 1, dim);
989
990 for (unsigned int i = 0; i < dim + 1; ++i)
991 for (unsigned int j = 0; j < dim; ++j)
992 H1(i, j) = H(i, j);
993
994 H1.backward(h_, *gamma_);
995
996 if (left_precondition)
997 for (unsigned int i = 0; i < dim; ++i)
998 x_->add(h_(i), tmp_vectors[i]);
999 else
1000 {
1001 p = 0.;
1002 for (unsigned int i = 0; i < dim; ++i)
1003 p.add(h_(i), tmp_vectors[i]);
1004 preconditioner.vmult(*r, p);
1005 x_->add(1., *r);
1006 };
1007 A.vmult(*r, *x_);
1008 r->sadd(-1., 1., b);
1009 // Now *r contains the unpreconditioned residual!!
1010 if (left_precondition)
1011 {
1012 const double res = r->l2_norm();
1013 last_res = res;
1014
1015 iteration_state =
1016 this->iteration_status(accumulated_iterations, res, x);
1017 }
1018 else
1019 {
1020 preconditioner.vmult(*x_, *r);
1021 const double preconditioned_res = x_->l2_norm();
1022 last_res = preconditioned_res;
1023
1024 iteration_state =
1025 this->iteration_status(accumulated_iterations,
1026 preconditioned_res,
1027 x);
1028 }
1029 }
1030 };
1031 // end of inner iteration. now calculate the solution from the temporary
1032 // vectors
1033 h.reinit(dim);
1034 H1.reinit(dim + 1, dim);
1035
1036 for (unsigned int i = 0; i < dim + 1; ++i)
1037 for (unsigned int j = 0; j < dim; ++j)
1038 H1(i, j) = H(i, j);
1039
1040 compute_eigs_and_cond(H_orig,
1041 dim,
1042 all_eigenvalues_signal,
1043 all_hessenberg_signal,
1044 condition_number_signal);
1045
1046 H1.backward(h, gamma);
1047
1048 if (left_precondition)
1049 for (unsigned int i = 0; i < dim; ++i)
1050 x.add(h(i), tmp_vectors[i]);
1051 else
1052 {
1053 p = 0.;
1054 for (unsigned int i = 0; i < dim; ++i)
1055 p.add(h(i), tmp_vectors[i]);
1056 preconditioner.vmult(v, p);
1057 x.add(1., v);
1058 };
1059 // end of outer iteration. restart if no convergence and the number of
1060 // iterations is not exceeded
1061 }
1062 while (iteration_state == SolverControl::iterate);
1063
1064 compute_eigs_and_cond(H_orig,
1065 dim,
1066 eigenvalues_signal,
1067 hessenberg_signal,
1068 condition_number_signal);
1069
1070 if (!krylov_space_signal.empty())
1071 krylov_space_signal(tmp_vectors);
1072
1073 // in case of failure: throw exception
1074 AssertThrow(iteration_state == SolverControl::success,
1075 SolverControl::NoConvergence(accumulated_iterations, last_res));
1076 }
1077
1078
1079
1080 template <class VectorType>
1081 boost::signals2::connection
connect_condition_number_slot(const std::function<void (double)> & slot,const bool every_iteration)1082 SolverGMRES<VectorType>::connect_condition_number_slot(
1083 const std::function<void(double)> &slot,
1084 const bool every_iteration)
1085 {
1086 if (every_iteration)
1087 {
1088 return all_condition_numbers_signal.connect(slot);
1089 }
1090 else
1091 {
1092 return condition_number_signal.connect(slot);
1093 }
1094 }
1095
1096
1097
1098 template <class VectorType>
1099 boost::signals2::connection
connect_eigenvalues_slot(const std::function<void (const std::vector<std::complex<double>> &)> & slot,const bool every_iteration)1100 SolverGMRES<VectorType>::connect_eigenvalues_slot(
1101 const std::function<void(const std::vector<std::complex<double>> &)> &slot,
1102 const bool every_iteration)
1103 {
1104 if (every_iteration)
1105 {
1106 return all_eigenvalues_signal.connect(slot);
1107 }
1108 else
1109 {
1110 return eigenvalues_signal.connect(slot);
1111 }
1112 }
1113
1114
1115
1116 template <class VectorType>
1117 boost::signals2::connection
connect_hessenberg_slot(const std::function<void (const FullMatrix<double> &)> & slot,const bool every_iteration)1118 SolverGMRES<VectorType>::connect_hessenberg_slot(
1119 const std::function<void(const FullMatrix<double> &)> &slot,
1120 const bool every_iteration)
1121 {
1122 if (every_iteration)
1123 {
1124 return all_hessenberg_signal.connect(slot);
1125 }
1126 else
1127 {
1128 return hessenberg_signal.connect(slot);
1129 }
1130 }
1131
1132
1133
1134 template <class VectorType>
1135 boost::signals2::connection
connect_krylov_space_slot(const std::function<void (const internal::SolverGMRESImplementation::TmpVectors<VectorType> &)> & slot)1136 SolverGMRES<VectorType>::connect_krylov_space_slot(
1137 const std::function<void(
1138 const internal::SolverGMRESImplementation::TmpVectors<VectorType> &)> &slot)
1139 {
1140 return krylov_space_signal.connect(slot);
1141 }
1142
1143
1144
1145 template <class VectorType>
1146 boost::signals2::connection
connect_re_orthogonalization_slot(const std::function<void (int)> & slot)1147 SolverGMRES<VectorType>::connect_re_orthogonalization_slot(
1148 const std::function<void(int)> &slot)
1149 {
1150 return re_orthogonalize_signal.connect(slot);
1151 }
1152
1153
1154
1155 template <class VectorType>
1156 double
criterion()1157 SolverGMRES<VectorType>::criterion()
1158 {
1159 // dummy implementation. this function is not needed for the present
1160 // implementation of gmres
1161 Assert(false, ExcInternalError());
1162 return 0;
1163 }
1164
1165
1166 //----------------------------------------------------------------------//
1167
1168 template <class VectorType>
SolverFGMRES(SolverControl & cn,VectorMemory<VectorType> & mem,const AdditionalData & data)1169 SolverFGMRES<VectorType>::SolverFGMRES(SolverControl & cn,
1170 VectorMemory<VectorType> &mem,
1171 const AdditionalData & data)
1172 : SolverBase<VectorType>(cn, mem)
1173 , additional_data(data)
1174 {}
1175
1176
1177
1178 template <class VectorType>
SolverFGMRES(SolverControl & cn,const AdditionalData & data)1179 SolverFGMRES<VectorType>::SolverFGMRES(SolverControl & cn,
1180 const AdditionalData &data)
1181 : SolverBase<VectorType>(cn)
1182 , additional_data(data)
1183 {}
1184
1185
1186
1187 template <class VectorType>
1188 template <typename MatrixType, typename PreconditionerType>
1189 void
solve(const MatrixType & A,VectorType & x,const VectorType & b,const PreconditionerType & preconditioner)1190 SolverFGMRES<VectorType>::solve(const MatrixType & A,
1191 VectorType & x,
1192 const VectorType & b,
1193 const PreconditionerType &preconditioner)
1194 {
1195 LogStream::Prefix prefix("FGMRES");
1196
1197 SolverControl::State iteration_state = SolverControl::iterate;
1198
1199 const unsigned int basis_size = additional_data.max_basis_size;
1200
1201 // Generate an object where basis vectors are stored.
1202 typename internal::SolverGMRESImplementation::TmpVectors<VectorType> v(
1203 basis_size, this->memory);
1204 typename internal::SolverGMRESImplementation::TmpVectors<VectorType> z(
1205 basis_size, this->memory);
1206
1207 // number of the present iteration; this number is not reset to zero upon a
1208 // restart
1209 unsigned int accumulated_iterations = 0;
1210
1211 // matrix used for the orthogonalization process later
1212 H.reinit(basis_size + 1, basis_size);
1213
1214 // Vectors for projected system
1215 Vector<double> projected_rhs;
1216 Vector<double> y;
1217
1218 // Iteration starts here
1219 double res = -std::numeric_limits<double>::max();
1220
1221 typename VectorMemory<VectorType>::Pointer aux(this->memory);
1222 aux->reinit(x);
1223 do
1224 {
1225 A.vmult(*aux, x);
1226 aux->sadd(-1., 1., b);
1227
1228 double beta = aux->l2_norm();
1229 res = beta;
1230 iteration_state = this->iteration_status(accumulated_iterations, res, x);
1231 if (iteration_state == SolverControl::success)
1232 break;
1233
1234 H.reinit(basis_size + 1, basis_size);
1235 double a = beta;
1236
1237 for (unsigned int j = 0; j < basis_size; ++j)
1238 {
1239 if (a != 0) // treat lucky breakdown
1240 v(j, x).equ(1. / a, *aux);
1241 else
1242 v(j, x) = 0.;
1243
1244
1245 preconditioner.vmult(z(j, x), v[j]);
1246 A.vmult(*aux, z[j]);
1247
1248 // Gram-Schmidt
1249 H(0, j) = *aux * v[0];
1250 for (unsigned int i = 1; i <= j; ++i)
1251 H(i, j) = aux->add_and_dot(-H(i - 1, j), v[i - 1], v[i]);
1252 H(j + 1, j) = a = std::sqrt(aux->add_and_dot(-H(j, j), v[j], *aux));
1253
1254 // Compute projected solution
1255
1256 if (j > 0)
1257 {
1258 H1.reinit(j + 1, j);
1259 projected_rhs.reinit(j + 1);
1260 y.reinit(j);
1261 projected_rhs(0) = beta;
1262 H1.fill(H);
1263
1264 // check convergence. note that the vector 'x' we pass to the
1265 // criterion is not the final solution we compute if we
1266 // decide to jump out of the iteration (we update 'x' again
1267 // right after the current loop)
1268 Householder<double> house(H1);
1269 res = house.least_squares(y, projected_rhs);
1270 iteration_state =
1271 this->iteration_status(++accumulated_iterations, res, x);
1272 if (iteration_state != SolverControl::iterate)
1273 break;
1274 }
1275 }
1276
1277 // Update solution vector
1278 for (unsigned int j = 0; j < y.size(); ++j)
1279 x.add(y(j), z[j]);
1280 }
1281 while (iteration_state == SolverControl::iterate);
1282
1283 // in case of failure: throw exception
1284 if (iteration_state != SolverControl::success)
1285 AssertThrow(false,
1286 SolverControl::NoConvergence(accumulated_iterations, res));
1287 }
1288
1289 #endif // DOXYGEN
1290
1291 DEAL_II_NAMESPACE_CLOSE
1292
1293 #endif
1294