deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
solver_gmres.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 1999 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_solver_gmres_h
14#define dealii_solver_gmres_h
15
16
17
18#include <deal.II/base/config.h>
19
23
28#include <deal.II/lac/solver.h>
30#include <deal.II/lac/vector.h>
31
32#include <boost/signals2/signal.hpp>
33
34#include <algorithm>
35#include <cmath>
36#include <complex>
37#include <limits>
38#include <memory>
39#include <utility>
40#include <vector>
41
43
44// forward declarations
45#ifndef DOXYGEN
46namespace LinearAlgebra
47{
48 namespace distributed
49 {
50 template <typename, typename>
51 class Vector;
52 } // namespace distributed
53} // namespace LinearAlgebra
54#endif
55
61namespace internal
62{
66 namespace SolverGMRESImplementation
67 {
75 template <typename VectorType>
77 {
78 public:
83 TmpVectors(const unsigned int max_size, VectorMemory<VectorType> &vmem);
84
88 ~TmpVectors() = default;
89
94 VectorType &
95 operator[](const unsigned int i) const;
96
103 VectorType &
104 operator()(const unsigned int i, const VectorType &temp);
105
110 unsigned int
111 size() const;
112
113
114 private:
119
123 std::vector<typename VectorMemory<VectorType>::Pointer> data;
124 };
125
126
127
137 template <typename Number>
139 {
140 public:
145 void
148 const unsigned int max_basis_size,
149 const bool force_reorthogonalization);
150
173 template <typename VectorType>
174 double
176 const unsigned int n,
177 TmpVectors<VectorType> &orthogonal_vectors,
178 const unsigned int accumulated_iterations = 0,
179 const boost::signals2::signal<void(int)> &reorthogonalize_signal =
180 boost::signals2::signal<void(int)>());
181
189 const Vector<double> &
190 solve_projected_system(const bool orthogonalization_finished);
191
196 const FullMatrix<double> &
198
202 std::vector<const Number *> vector_ptrs;
203
204 private:
209
216
221 std::vector<std::pair<double, double>> givens_rotations;
222
227
233
238
248
253
281 double
282 do_givens_rotation(const bool delayed_reorthogonalization,
283 const int col,
284 FullMatrix<double> &matrix,
285 std::vector<std::pair<double, double>> &rotations,
286 Vector<double> &rhs);
287 };
288 } // namespace SolverGMRESImplementation
289} // namespace internal
290
291
292
412template <typename VectorType = Vector<double>>
414class SolverGMRES : public SolverBase<VectorType>
415{
416public:
421 {
432 explicit AdditionalData(const unsigned int max_basis_size = 30,
433 const bool right_preconditioning = false,
434 const bool use_default_residual = true,
435 const bool force_re_orthogonalization = false,
436 const bool batched_mode = false,
438 orthogonalization_strategy =
440 delayed_classical_gram_schmidt);
441
453 unsigned int max_n_tmp_vectors;
454
462 unsigned int max_basis_size;
463
472
477
485
498
503 };
504
511
517
522
526 template <typename MatrixType, typename PreconditionerType>
530 void solve(const MatrixType &A,
531 VectorType &x,
532 const VectorType &b,
533 const PreconditionerType &preconditioner);
534
541 boost::signals2::connection
542 connect_condition_number_slot(const std::function<void(double)> &slot,
543 const bool every_iteration = false);
544
551 boost::signals2::connection
552 connect_eigenvalues_slot(
553 const std::function<void(const std::vector<std::complex<double>> &)> &slot,
554 const bool every_iteration = false);
555
563 boost::signals2::connection
564 connect_hessenberg_slot(
565 const std::function<void(const FullMatrix<double> &)> &slot,
566 const bool every_iteration = true);
567
574 boost::signals2::connection
575 connect_krylov_space_slot(
576 const std::function<
577 void(const internal::SolverGMRESImplementation::TmpVectors<VectorType> &)>
578 &slot);
579
580
585 boost::signals2::connection
586 connect_re_orthogonalization_slot(const std::function<void(int)> &slot);
587
588
589 DeclException1(ExcTooFewTmpVectors,
590 int,
591 << "The number of temporary vectors you gave (" << arg1
592 << ") is too small. It should be at least 10 for "
593 << "any results, and much more for reasonable ones.");
594
595protected:
599 AdditionalData additional_data;
600
605 boost::signals2::signal<void(double)> condition_number_signal;
606
611 boost::signals2::signal<void(double)> all_condition_numbers_signal;
612
617 boost::signals2::signal<void(const std::vector<std::complex<double>> &)>
618 eigenvalues_signal;
619
624 boost::signals2::signal<void(const std::vector<std::complex<double>> &)>
625 all_eigenvalues_signal;
626
631 boost::signals2::signal<void(const FullMatrix<double> &)> hessenberg_signal;
632
637 boost::signals2::signal<void(const FullMatrix<double> &)>
638 all_hessenberg_signal;
639
644 boost::signals2::signal<void(
645 const internal::SolverGMRESImplementation::TmpVectors<VectorType> &)>
646 krylov_space_signal;
647
652 boost::signals2::signal<void(int)> re_orthogonalize_signal;
653
659 SolverControl &solver_control;
660
664 virtual double
665 criterion();
666
673 static void
674 compute_eigs_and_cond(
675 const FullMatrix<double> &H_orig,
676 const unsigned int n,
677 const boost::signals2::signal<
678 void(const std::vector<std::complex<double>> &)> &eigenvalues_signal,
679 const boost::signals2::signal<void(const FullMatrix<double> &)>
680 &hessenberg_signal,
681 const boost::signals2::signal<void(double)> &cond_signal);
682
687 internal::SolverGMRESImplementation::ArnoldiProcess<
688 typename VectorType::value_type>
689 arnoldi_process;
690};
691
692
756template <typename VectorType = Vector<double>>
757DEAL_II_CXX20_REQUIRES(concepts::is_vector_space_vector<VectorType>)
758class SolverMPGMRES : public SolverBase<VectorType>
759{
760public:
765 {
769 explicit AdditionalData(const unsigned int max_basis_size = 30,
771 orthogonalization_strategy =
773 delayed_classical_gram_schmidt,
774 const bool use_truncated_mpgmres_strategy = true)
775 : max_basis_size(max_basis_size)
776 , orthogonalization_strategy(orthogonalization_strategy)
777 , use_truncated_mpgmres_strategy(use_truncated_mpgmres_strategy)
778 {}
779
783 unsigned int max_basis_size;
784
789
800 };
801
808
815
819 template <typename MatrixType, typename... PreconditionerTypes>
823 void solve(const MatrixType &A,
824 VectorType &x,
825 const VectorType &b,
826 const PreconditionerTypes &...preconditioners);
827
828protected:
836 {
837 fgmres,
838 truncated_mpgmres,
839 full_mpgmres,
840 };
841
845 template <typename MatrixType, typename... PreconditionerTypes>
846 void
847 solve_internal(const MatrixType &A,
848 VectorType &x,
849 const VectorType &b,
850 const IndexingStrategy &indexing_strategy,
851 const PreconditionerTypes &...preconditioners);
852
853private:
858
864 typename VectorType::value_type>
866};
867
868
869
895template <typename VectorType = Vector<double>>
897class SolverFGMRES : public SolverMPGMRES<VectorType>
898{
899public:
904 {
908 explicit AdditionalData( //
909 const unsigned int max_basis_size = 30,
911 orthogonalization_strategy = LinearAlgebra::OrthogonalizationStrategy::
912 delayed_classical_gram_schmidt)
913 : max_basis_size(max_basis_size)
914 , orthogonalization_strategy(orthogonalization_strategy)
915 {}
916
920 unsigned int max_basis_size;
921
926 };
927
928
935
942
946 template <typename MatrixType, typename... PreconditionerTypes>
950 void solve(const MatrixType &A,
951 VectorType &x,
952 const VectorType &b,
953 const PreconditionerTypes &...preconditioners);
954};
955
959/* --------------------- Inline and template functions ------------------- */
960
961#ifndef DOXYGEN
962
963template <typename VectorType>
966 const unsigned int max_basis_size,
967 const bool right_preconditioning,
968 const bool use_default_residual,
969 const bool force_re_orthogonalization,
970 const bool batched_mode,
971 const LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy)
972 : max_n_tmp_vectors(0)
973 , max_basis_size(max_basis_size)
974 , right_preconditioning(right_preconditioning)
975 , use_default_residual(use_default_residual)
976 , force_re_orthogonalization(force_re_orthogonalization)
977 , batched_mode(batched_mode)
978 , orthogonalization_strategy(orthogonalization_strategy)
979{
980 Assert(max_basis_size >= 1,
981 ExcMessage("SolverGMRES needs at least one vector in the "
982 "Arnoldi basis."));
983}
984
985
986
987template <typename VectorType>
991 const AdditionalData &data)
992 : SolverBase<VectorType>(cn, mem)
993 , additional_data(data)
994 , solver_control(cn)
995{}
996
997
998
999template <typename VectorType>
1002 const AdditionalData &data)
1003 : SolverBase<VectorType>(cn)
1004 , additional_data(data)
1005 , solver_control(cn)
1006{}
1007
1008
1009
1010namespace internal
1011{
1012 namespace SolverGMRESImplementation
1013 {
1014 template <typename VectorType>
1015 inline TmpVectors<VectorType>::TmpVectors(const unsigned int max_size,
1017 : mem(vmem)
1018 , data(max_size)
1019 {}
1020
1021
1022
1023 template <typename VectorType>
1024 inline VectorType &
1025 TmpVectors<VectorType>::operator[](const unsigned int i) const
1026 {
1027 AssertIndexRange(i, data.size());
1028
1029 Assert(data[i] != nullptr, ExcNotInitialized());
1030 return *data[i];
1031 }
1032
1033
1034
1035 template <typename VectorType>
1036 inline VectorType &
1037 TmpVectors<VectorType>::operator()(const unsigned int i,
1038 const VectorType &temp)
1039 {
1040 AssertIndexRange(i, data.size());
1041 if (data[i] == nullptr)
1042 {
1043 data[i] = std::move(typename VectorMemory<VectorType>::Pointer(mem));
1044 data[i]->reinit(temp, true);
1045 }
1046 return *data[i];
1047 }
1048
1049
1050
1051 template <typename VectorType>
1052 unsigned int
1053 TmpVectors<VectorType>::size() const
1054 {
1055 return (data.size() > 0 ? data.size() - 1 : 0);
1056 }
1057
1058
1059
1060 template <typename VectorType, typename Enable = void>
1061 struct is_dealii_compatible_vector;
1062
1063 template <typename VectorType>
1064 struct is_dealii_compatible_vector<
1065 VectorType,
1066 std::enable_if_t<!internal::is_block_vector<VectorType>>>
1067 {
1068 static constexpr bool value =
1069 std::is_same_v<
1070 VectorType,
1071 LinearAlgebra::distributed::Vector<typename VectorType::value_type,
1073 std::is_same_v<VectorType, Vector<typename VectorType::value_type>>;
1074 };
1075
1076
1077
1078 template <typename VectorType>
1079 struct is_dealii_compatible_vector<
1080 VectorType,
1081 std::enable_if_t<internal::is_block_vector<VectorType>>>
1082 {
1083 static constexpr bool value =
1084 std::is_same_v<
1085 typename VectorType::BlockType,
1086 LinearAlgebra::distributed::Vector<typename VectorType::value_type,
1088 std::is_same_v<VectorType, Vector<typename VectorType::value_type>>;
1089 };
1090
1091
1092
1093 template <typename VectorType,
1094 std::enable_if_t<!IsBlockVector<VectorType>::value, VectorType>
1095 * = nullptr>
1096 unsigned int
1097 n_blocks(const VectorType &)
1098 {
1099 return 1;
1100 }
1101
1102
1103
1104 template <typename VectorType,
1105 std::enable_if_t<IsBlockVector<VectorType>::value, VectorType> * =
1106 nullptr>
1107 unsigned int
1108 n_blocks(const VectorType &vector)
1109 {
1110 return vector.n_blocks();
1111 }
1112
1113
1114
1115 template <typename VectorType,
1116 std::enable_if_t<!IsBlockVector<VectorType>::value, VectorType>
1117 * = nullptr>
1118 VectorType &
1119 block(VectorType &vector, const unsigned int b)
1120 {
1121 AssertDimension(b, 0);
1122 return vector;
1123 }
1124
1125
1126
1127 template <typename VectorType,
1128 std::enable_if_t<!IsBlockVector<VectorType>::value, VectorType>
1129 * = nullptr>
1130 const VectorType &
1131 block(const VectorType &vector, const unsigned int b)
1132 {
1133 AssertDimension(b, 0);
1134 return vector;
1135 }
1136
1137
1138
1139 template <typename VectorType,
1140 std::enable_if_t<IsBlockVector<VectorType>::value, VectorType> * =
1141 nullptr>
1142 typename VectorType::BlockType &
1143 block(VectorType &vector, const unsigned int b)
1144 {
1145 return vector.block(b);
1146 }
1147
1148
1149
1150 template <typename VectorType,
1151 std::enable_if_t<IsBlockVector<VectorType>::value, VectorType> * =
1152 nullptr>
1153 const typename VectorType::BlockType &
1154 block(const VectorType &vector, const unsigned int b)
1155 {
1156 return vector.block(b);
1157 }
1158
1159
1160
1161 template <bool delayed_reorthogonalization,
1162 typename VectorType,
1163 std::enable_if_t<!is_dealii_compatible_vector<VectorType>::value,
1164 VectorType> * = nullptr>
1165 void
1166 Tvmult_add(const unsigned int n,
1167 const VectorType &vv,
1168 const TmpVectors<VectorType> &orthogonal_vectors,
1169 Vector<double> &h,
1170 std::vector<const typename VectorType::value_type *> &)
1171 {
1172 for (unsigned int i = 0; i < n; ++i)
1173 {
1174 h(i) += vv * orthogonal_vectors[i];
1175 if (delayed_reorthogonalization)
1176 h(n + i) += orthogonal_vectors[i] * orthogonal_vectors[n - 1];
1177 }
1178 if (delayed_reorthogonalization)
1179 h(n + n) += vv * vv;
1180 }
1181
1182
1183
1184 // worker method for deal.II's vector types implemented in .cc file
1185 template <bool delayed_reorthogonalization, typename Number>
1186 void
1187 do_Tvmult_add(const unsigned int n_vectors,
1188 const std::size_t locally_owned_size,
1189 const Number *current_vector,
1190 const std::vector<const Number *> &orthogonal_vectors,
1191 Vector<double> &b);
1192
1193
1194
1195 template <bool delayed_reorthogonalization,
1196 typename VectorType,
1197 std::enable_if_t<is_dealii_compatible_vector<VectorType>::value,
1198 VectorType> * = nullptr>
1199 void
1200 Tvmult_add(
1201 const unsigned int n,
1202 const VectorType &vv,
1203 const TmpVectors<VectorType> &orthogonal_vectors,
1204 Vector<double> &h,
1205 std::vector<const typename VectorType::value_type *> &vector_ptrs)
1206 {
1207 for (unsigned int b = 0; b < n_blocks(vv); ++b)
1208 {
1209 vector_ptrs.resize(n);
1210 for (unsigned int i = 0; i < n; ++i)
1211 vector_ptrs[i] = block(orthogonal_vectors[i], b).begin();
1212
1213 do_Tvmult_add<delayed_reorthogonalization>(n,
1214 block(vv, b).end() -
1215 block(vv, b).begin(),
1216 block(vv, b).begin(),
1217 vector_ptrs,
1218 h);
1219 }
1220
1221 Utilities::MPI::sum(h, block(vv, 0).get_mpi_communicator(), h);
1222 }
1223
1224
1225
1226 template <bool delayed_reorthogonalization,
1227 typename VectorType,
1228 std::enable_if_t<!is_dealii_compatible_vector<VectorType>::value,
1229 VectorType> * = nullptr>
1230 double
1231 subtract_and_norm(const unsigned int n,
1232 const TmpVectors<VectorType> &orthogonal_vectors,
1233 const Vector<double> &h,
1234 VectorType &vv,
1235 std::vector<const typename VectorType::value_type *> &)
1236 {
1237 Assert(n > 0, ExcInternalError());
1238
1239 VectorType &last_vector =
1240 const_cast<VectorType &>(orthogonal_vectors[n - 1]);
1241 for (unsigned int i = 0; i < n - 1; ++i)
1242 {
1243 if (delayed_reorthogonalization && i + 2 < n)
1244 last_vector.add(-h(n + i), orthogonal_vectors[i]);
1245 vv.add(-h(i), orthogonal_vectors[i]);
1246 }
1247
1248 if (delayed_reorthogonalization)
1249 {
1250 if (n > 1)
1251 last_vector.sadd(1. / h(n + n - 1),
1252 -h(n + n - 2) / h(n + n - 1),
1253 orthogonal_vectors[n - 2]);
1254
1255 // h(n + n) = 0 is lucky breakdown
1256 const double scaling_factor_vv = h(n + n) > 0.0 ?
1257 1. / (h(n + n - 1) * h(n + n)) :
1258 1. / (h(n + n - 1) * h(n + n - 1));
1259 vv.sadd(scaling_factor_vv,
1260 -h(n - 1) * scaling_factor_vv,
1261 last_vector);
1262
1263 // the delayed reorthogonalization computes the norm from other
1264 // quantities
1265 return std::numeric_limits<double>::signaling_NaN();
1266 }
1267 else
1268 return std::sqrt(
1269 vv.add_and_dot(-h(n - 1), orthogonal_vectors[n - 1], vv));
1270 }
1271
1272
1273
1274 // worker method for deal.II's vector types implemented in .cc file
1275 template <bool delayed_reorthogonalization, typename Number>
1276 double
1277 do_subtract_and_norm(const unsigned int n_vectors,
1278 const std::size_t locally_owned_size,
1279 const std::vector<const Number *> &orthogonal_vectors,
1280 const Vector<double> &h,
1281 Number *current_vector);
1282
1283
1284
1285 template <bool delayed_reorthogonalization,
1286 typename VectorType,
1287 std::enable_if_t<is_dealii_compatible_vector<VectorType>::value,
1288 VectorType> * = nullptr>
1289 double
1290 subtract_and_norm(
1291 const unsigned int n,
1292 const TmpVectors<VectorType> &orthogonal_vectors,
1293 const Vector<double> &h,
1294 VectorType &vv,
1295 std::vector<const typename VectorType::value_type *> &vector_ptrs)
1296 {
1297 double norm_vv_temp = 0.0;
1298
1299 for (unsigned int b = 0; b < n_blocks(vv); ++b)
1300 {
1301 vector_ptrs.resize(n);
1302 for (unsigned int i = 0; i < n; ++i)
1303 vector_ptrs[i] = block(orthogonal_vectors[i], b).begin();
1304
1305 norm_vv_temp += do_subtract_and_norm<delayed_reorthogonalization>(
1306 n,
1307 block(vv, b).end() - block(vv, b).begin(),
1308 vector_ptrs,
1309 h,
1310 block(vv, b).begin());
1311 }
1312
1313 return std::sqrt(
1314 Utilities::MPI::sum(norm_vv_temp, block(vv, 0).get_mpi_communicator()));
1315 }
1316
1317
1318
1319 template <typename VectorType,
1320 std::enable_if_t<!is_dealii_compatible_vector<VectorType>::value,
1321 VectorType> * = nullptr>
1322 void
1323 add(VectorType &p,
1324 const unsigned int n,
1325 const Vector<double> &h,
1326 const TmpVectors<VectorType> &tmp_vectors,
1327 const bool zero_out,
1328 std::vector<const typename VectorType::value_type *> &)
1329 {
1330 if (zero_out)
1331 p.equ(h(0), tmp_vectors[0]);
1332 else
1333 p.add(h(0), tmp_vectors[0]);
1334
1335 for (unsigned int i = 1; i < n; ++i)
1336 p.add(h(i), tmp_vectors[i]);
1337 }
1338
1339
1340
1341 // worker method for deal.II's vector types implemented in .cc file
1342 template <typename Number>
1343 void
1344 do_add(const unsigned int n_vectors,
1345 const std::size_t locally_owned_size,
1346 const std::vector<const Number *> &tmp_vectors,
1347 const Vector<double> &h,
1348 const bool zero_out,
1349 Number *output);
1350
1351
1352
1353 template <typename VectorType,
1354 std::enable_if_t<is_dealii_compatible_vector<VectorType>::value,
1355 VectorType> * = nullptr>
1356 void
1357 add(VectorType &p,
1358 const unsigned int n,
1359 const Vector<double> &h,
1360 const TmpVectors<VectorType> &tmp_vectors,
1361 const bool zero_out,
1362 std::vector<const typename VectorType::value_type *> &vector_ptrs)
1363 {
1364 for (unsigned int b = 0; b < n_blocks(p); ++b)
1365 {
1366 vector_ptrs.resize(n);
1367 for (unsigned int i = 0; i < n; ++i)
1368 vector_ptrs[i] = block(tmp_vectors[i], b).begin();
1369 do_add(n,
1370 block(p, b).end() - block(p, b).begin(),
1371 vector_ptrs,
1372 h,
1373 zero_out,
1374 block(p, b).begin());
1375 }
1376 }
1377
1378
1379
1380 template <typename Number>
1381 inline void
1382 ArnoldiProcess<Number>::initialize(
1383 const LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy,
1384 const unsigned int basis_size,
1385 const bool force_reorthogonalization)
1386 {
1387 this->orthogonalization_strategy = orthogonalization_strategy;
1388 this->do_reorthogonalization = force_reorthogonalization;
1389
1390 hessenberg_matrix.reinit(basis_size + 1, basis_size);
1391 triangular_matrix.reinit(basis_size + 1, basis_size, true);
1392
1393 // some additional vectors, also used in the orthogonalization
1394 projected_rhs.reinit(basis_size + 1, true);
1395 givens_rotations.reserve(basis_size);
1396
1397 if (orthogonalization_strategy ==
1400 h.reinit(2 * basis_size + 3);
1401 else
1402 h.reinit(basis_size + 1);
1403 }
1404
1405
1406
1407 template <typename Number>
1408 template <typename VectorType>
1409 inline double
1410 ArnoldiProcess<Number>::orthonormalize_nth_vector(
1411 const unsigned int n,
1412 TmpVectors<VectorType> &orthogonal_vectors,
1413 const unsigned int accumulated_iterations,
1414 const boost::signals2::signal<void(int)> &reorthogonalize_signal)
1415 {
1416 AssertIndexRange(n, hessenberg_matrix.m());
1417 AssertIndexRange(n, orthogonal_vectors.size() + 1);
1418
1419 VectorType &vv = orthogonal_vectors[n];
1420
1421 double residual_estimate = std::numeric_limits<double>::signaling_NaN();
1422 if (n == 0)
1423 {
1424 givens_rotations.clear();
1425 residual_estimate = vv.l2_norm();
1426 if (residual_estimate != 0.)
1427 vv /= residual_estimate;
1428 projected_rhs(0) = residual_estimate;
1429 }
1430 else if (orthogonalization_strategy ==
1433 {
1434 // The algorithm implemented in the following few lines is algorithm
1435 // 4 of Bielich et al. (2022).
1436
1437 // To avoid un-scaled numbers as appearing with the original
1438 // algorithm by Bielich et al., we use a preliminary scaling of the
1439 // last vector. This will be corrected in the delayed step.
1440 const double previous_scaling = n > 0 ? h(n + n - 2) : 1.;
1441
1442 // Reset h to zero
1443 h.reinit(n + n + 1);
1444
1445 // global reduction
1446 Tvmult_add<true>(n, vv, orthogonal_vectors, h, vector_ptrs);
1447
1448 // delayed correction terms
1449 double tmp = 0;
1450 for (unsigned int i = 0; i < n - 1; ++i)
1451 tmp += h(n + i) * h(n + i);
1452
1453 // catch the case of lucky breakdown (= convergence), when h(n + n -
1454 // 1) = 0 and the algorithm terminates as the h entry will control
1455 // the residual, but we must avoid dividing by zero
1456 const double alpha_j =
1457 h(n + n - 1) == 0. ?
1458 1. :
1459 (h(n + n - 1) > tmp ? std::sqrt(h(n + n - 1) - tmp) :
1460 std::sqrt(h(n + n - 1)));
1461 h(n + n - 1) = alpha_j;
1462
1463 tmp = 0;
1464 for (unsigned int i = 0; i < n - 1; ++i)
1465 tmp += h(i) * h(n + i);
1466 h(n - 1) = (h(n - 1) - tmp) / alpha_j;
1467
1468 // representation of H(j-1)
1469 if (n > 1)
1470 {
1471 for (unsigned int i = 0; i < n - 1; ++i)
1472 hessenberg_matrix(i, n - 2) += h(n + i) * previous_scaling;
1473 hessenberg_matrix(n - 1, n - 2) = alpha_j * previous_scaling;
1474 }
1475 for (unsigned int i = 0; i < n; ++i)
1476 {
1477 double sum = 0;
1478 for (unsigned int j = (i == 0 ? 0 : i - 1); j < n - 1; ++j)
1479 sum += hessenberg_matrix(i, j) * h(n + j);
1480 hessenberg_matrix(i, n - 1) = (h(i) - sum) / alpha_j;
1481 }
1482
1483 // compute norm estimate for approximate convergence criterion
1484 // (value of norm to be corrected in next iteration)
1485 double sum = 0;
1486 for (unsigned int i = 0; i < n - 1; ++i)
1487 sum += h(i) * h(i);
1488 sum += (2. - 1.) * h(n - 1) * h(n - 1);
1489 hessenberg_matrix(n, n - 1) =
1490 std::sqrt(std::abs(h(n + n) - sum)) / alpha_j;
1491
1492 // projection and delayed reorthogonalization. We scale the vector
1493 // vv here by the preliminary norm to avoid working with too large
1494 // values and correct the actual norm in the Hessenberg matrix in
1495 // high precision in the next iteration.
1496 h(n + n) = hessenberg_matrix(n, n - 1);
1497 subtract_and_norm<true>(n, orthogonal_vectors, h, vv, vector_ptrs);
1498
1499 // transform new column of upper Hessenberg matrix into upper
1500 // triangular form by computing the respective factor
1501 residual_estimate = do_givens_rotation(
1502 true, n - 2, triangular_matrix, givens_rotations, projected_rhs);
1503 }
1504 else
1505 {
1506 // need initial norm for detection of re-orthogonalization, see below
1507 double norm_vv = 0.0;
1508 double norm_vv_start = 0;
1509 const bool consider_reorthogonalize =
1510 (do_reorthogonalization == false) && (n % 5 == 0);
1511 if (consider_reorthogonalize)
1512 norm_vv_start = vv.l2_norm();
1513
1514 // Reset h to zero
1515 h.reinit(n);
1516
1517 // run two loops with index 0: orthogonalize, 1: reorthogonalize
1518 for (unsigned int c = 0; c < 2; ++c)
1519 {
1520 // Orthogonalization
1521 if (orthogonalization_strategy ==
1524 {
1525 double htmp = vv * orthogonal_vectors[0];
1526 h(0) += htmp;
1527 for (unsigned int i = 1; i < n; ++i)
1528 {
1529 htmp = vv.add_and_dot(-htmp,
1530 orthogonal_vectors[i - 1],
1531 orthogonal_vectors[i]);
1532 h(i) += htmp;
1533 }
1534
1535 norm_vv = std::sqrt(
1536 vv.add_and_dot(-htmp, orthogonal_vectors[n - 1], vv));
1537 }
1538 else if (orthogonalization_strategy ==
1541 {
1542 Tvmult_add<false>(n, vv, orthogonal_vectors, h, vector_ptrs);
1543 norm_vv = subtract_and_norm<false>(
1544 n, orthogonal_vectors, h, vv, vector_ptrs);
1545 }
1546 else
1547 {
1549 }
1550
1551 if (c == 1)
1552 break; // reorthogonalization already performed -> finished
1553
1554 // Re-orthogonalization if loss of orthogonality detected. For the
1555 // test, use a strategy discussed in C. T. Kelley, Iterative
1556 // Methods for Linear and Nonlinear Equations, SIAM, Philadelphia,
1557 // 1995: Compare the norm of vv after orthogonalization with its
1558 // norm when starting the orthogonalization. If vv became very
1559 // small (here: less than the square root of the machine precision
1560 // times 10), it is almost in the span of the previous vectors,
1561 // which indicates loss of precision.
1562 if (consider_reorthogonalize)
1563 {
1564 if (norm_vv >
1565 10. * norm_vv_start *
1566 std::sqrt(std::numeric_limits<
1567 typename VectorType::value_type>::epsilon()))
1568 break;
1569
1570 else
1571 {
1572 do_reorthogonalization = true;
1573 if (!reorthogonalize_signal.empty())
1574 reorthogonalize_signal(accumulated_iterations);
1575 }
1576 }
1577
1578 if (do_reorthogonalization == false)
1579 break; // no reorthogonalization needed -> finished
1580 }
1581
1582 for (unsigned int i = 0; i < n; ++i)
1583 hessenberg_matrix(i, n - 1) = h(i);
1584 hessenberg_matrix(n, n - 1) = norm_vv;
1585
1586 // norm_vv is a lucky breakdown, the solver will reach convergence,
1587 // but we must not divide by zero here.
1588 if (norm_vv != 0)
1589 vv /= norm_vv;
1590
1591 residual_estimate = do_givens_rotation(
1592 false, n - 1, triangular_matrix, givens_rotations, projected_rhs);
1593 }
1594
1595 return residual_estimate;
1596 }
1597
1598
1599
1600 template <typename Number>
1601 inline double
1602 ArnoldiProcess<Number>::do_givens_rotation(
1603 const bool delayed_reorthogonalization,
1604 const int col,
1605 FullMatrix<double> &matrix,
1606 std::vector<std::pair<double, double>> &rotations,
1607 Vector<double> &rhs)
1608 {
1609 // for the delayed orthogonalization, we can only compute the column of
1610 // the previous iteration (as there will be correction terms added to the
1611 // present column for stability reasons), but we still want to compute
1612 // the residual estimate from the accumulated work; we therefore perform
1613 // givens rotations on two columns simultaneously
1614 if (delayed_reorthogonalization)
1615 {
1616 if (col >= 0)
1617 {
1618 AssertDimension(rotations.size(), static_cast<std::size_t>(col));
1619 matrix(0, col) = hessenberg_matrix(0, col);
1620 }
1621 double H_next = hessenberg_matrix(0, col + 1);
1622 for (int i = 0; i < col; ++i)
1623 {
1624 const double c = rotations[i].first;
1625 const double s = rotations[i].second;
1626 const double Hi = matrix(i, col);
1627 const double Hi1 = hessenberg_matrix(i + 1, col);
1628 H_next = -s * H_next + c * hessenberg_matrix(i + 1, col + 1);
1629 matrix(i, col) = c * Hi + s * Hi1;
1630 matrix(i + 1, col) = -s * Hi + c * Hi1;
1631 }
1632
1633 if (col >= 0)
1634 {
1635 const double H_col1 = hessenberg_matrix(col + 1, col);
1636 const double H_col = matrix(col, col);
1637 const double r = 1. / std::sqrt(H_col * H_col + H_col1 * H_col1);
1638 rotations.emplace_back(H_col * r, H_col1 * r);
1639 matrix(col, col) =
1640 rotations[col].first * H_col + rotations[col].second * H_col1;
1641
1642 rhs(col + 1) = -rotations[col].second * rhs(col);
1643 rhs(col) *= rotations[col].first;
1644
1645 H_next =
1646 -rotations[col].second * H_next +
1647 rotations[col].first * hessenberg_matrix(col + 1, col + 1);
1648 }
1649
1650 const double H_last = hessenberg_matrix(col + 2, col + 1);
1651
1652 // Catch the lucky breakdown case when both the current residual and
1653 // the previous one (after applying the delayed correction from
1654 // re-orthogonalization) are exactly zero. We will finish after this
1655 // step, but must not divide by zero.
1656 if (H_next == 0. && H_last == 0.)
1657 return 0;
1658 else
1659 {
1660 const double r =
1661 1. / std::sqrt(H_next * H_next + H_last * H_last);
1662 return std::abs(H_last * r * rhs(col + 1));
1663 }
1664 }
1665 else
1666 {
1667 AssertDimension(rotations.size(), static_cast<std::size_t>(col));
1668
1669 matrix(0, col) = hessenberg_matrix(0, col);
1670 for (int i = 0; i < col; ++i)
1671 {
1672 const double c = rotations[i].first;
1673 const double s = rotations[i].second;
1674 const double Hi = matrix(i, col);
1675 const double Hi1 = hessenberg_matrix(i + 1, col);
1676 matrix(i, col) = c * Hi + s * Hi1;
1677 matrix(i + 1, col) = -s * Hi + c * Hi1;
1678 }
1679
1680 const double Hi = matrix(col, col);
1681 const double Hi1 = hessenberg_matrix(col + 1, col);
1682
1683 // Catch (rare) lucky breakdown where the previous iteration would
1684 // have been ready but the delayed orthogonalization has prevented
1685 // us from seeing it at that stage, so we must enter also this path
1686 // besides the one a few lines above.
1687 if (Hi == 0. && Hi1 == 0.)
1688 {
1689 rotations.emplace_back(1, 0);
1690 matrix(col, col) = 1;
1691 }
1692 else
1693 {
1694 const double r = (Hi * Hi + Hi1 * Hi1 == 0 ?
1695 1 :
1696 1. / std::sqrt(Hi * Hi + Hi1 * Hi1));
1697 rotations.emplace_back(Hi * r, Hi1 * r);
1698 matrix(col, col) =
1699 rotations[col].first * Hi + rotations[col].second * Hi1;
1700 }
1701
1702 rhs(col + 1) = -rotations[col].second * rhs(col);
1703 rhs(col) *= rotations[col].first;
1704
1705 return std::abs(rhs(col + 1));
1706 }
1707 }
1708
1709
1710
1711 template <typename Number>
1712 inline const Vector<double> &
1713 ArnoldiProcess<Number>::solve_projected_system(
1714 const bool orthogonalization_finished)
1715 {
1716 FullMatrix<double> tmp_triangular_matrix;
1717 Vector<double> tmp_rhs;
1718 FullMatrix<double> *matrix = &triangular_matrix;
1719 Vector<double> *rhs = &projected_rhs;
1720 unsigned int n = givens_rotations.size();
1721
1722 // If we solve with the delayed orthogonalization, we still need to
1723 // perform the elimination of the last column before we can solve the
1724 // projected system. We distinguish two cases, one where the
1725 // orthogonalization has finished (i.e., end of inner iteration in
1726 // GMRES) and we can safely overwrite the content of the tridiagonal
1727 // matrix and right hand side, and the case during the inner iterations,
1728 // where we need to create copies of the matrices in the QR
1729 // decomposition as well as the right hand side.
1730 if (orthogonalization_strategy ==
1733 {
1734 n += 1;
1735 if (!orthogonalization_finished)
1736 {
1737 tmp_triangular_matrix = triangular_matrix;
1738 tmp_rhs = projected_rhs;
1739 std::vector<std::pair<double, double>> tmp_givens_rotations(
1740 givens_rotations);
1741 do_givens_rotation(false,
1742 givens_rotations.size(),
1743 tmp_triangular_matrix,
1744 tmp_givens_rotations,
1745 tmp_rhs);
1746 matrix = &tmp_triangular_matrix;
1747 rhs = &tmp_rhs;
1748 }
1749 else
1750 do_givens_rotation(false,
1751 givens_rotations.size(),
1752 triangular_matrix,
1753 givens_rotations,
1754 projected_rhs);
1755 }
1756
1757 // Now solve the triangular system by backward substitution
1758 projected_solution.reinit(n);
1759 for (int i = n - 1; i >= 0; --i)
1760 {
1761 double s = (*rhs)(i);
1762 for (unsigned int j = i + 1; j < n; ++j)
1763 s -= projected_solution(j) * (*matrix)(i, j);
1764
1765 projected_solution(i) = s / (*matrix)(i, i);
1766 AssertIsFinite(projected_solution(i));
1767 }
1768
1769 return projected_solution;
1770 }
1771
1772
1773
1774 template <typename Number>
1775 inline const FullMatrix<double> &
1776 ArnoldiProcess<Number>::get_hessenberg_matrix() const
1777 {
1778 return hessenberg_matrix;
1779 }
1780
1781
1782
1783 // A comparator for better printing eigenvalues
1784 inline bool
1785 complex_less_pred(const std::complex<double> &x,
1786 const std::complex<double> &y)
1787 {
1788 return x.real() < y.real() ||
1789 (x.real() == y.real() && x.imag() < y.imag());
1790 }
1791 } // namespace SolverGMRESImplementation
1792} // namespace internal
1793
1794
1795
1796template <typename VectorType>
1799 const FullMatrix<double> &H_orig,
1800 const unsigned int n,
1801 const boost::signals2::signal<void(const std::vector<std::complex<double>> &)>
1802 &eigenvalues_signal,
1803 const boost::signals2::signal<void(const FullMatrix<double> &)>
1804 &hessenberg_signal,
1805 const boost::signals2::signal<void(double)> &cond_signal)
1806{
1807 // Avoid copying the Hessenberg matrix if it isn't needed.
1808 if ((!eigenvalues_signal.empty() || !hessenberg_signal.empty() ||
1809 !cond_signal.empty()) &&
1810 n > 0)
1811 {
1812 LAPACKFullMatrix<double> mat(n, n);
1813 for (unsigned int i = 0; i < n; ++i)
1814 for (unsigned int j = 0; j < n; ++j)
1815 mat(i, j) = H_orig(i, j);
1816 hessenberg_signal(H_orig);
1817 // Avoid computing eigenvalues if they are not needed.
1818 if (!eigenvalues_signal.empty())
1819 {
1820 // Copy mat so that we can compute svd below. Necessary since
1821 // compute_eigenvalues will leave mat in state
1822 // LAPACKSupport::unusable.
1823 LAPACKFullMatrix<double> mat_eig(mat);
1824 mat_eig.compute_eigenvalues();
1825 std::vector<std::complex<double>> eigenvalues(n);
1826 for (unsigned int i = 0; i < mat_eig.n(); ++i)
1827 eigenvalues[i] = mat_eig.eigenvalue(i);
1828 // Sort eigenvalues for nicer output.
1829 std::sort(eigenvalues.begin(),
1830 eigenvalues.end(),
1831 internal::SolverGMRESImplementation::complex_less_pred);
1832 eigenvalues_signal(eigenvalues);
1833 }
1834 // Calculate condition number, avoid calculating the svd if a slot
1835 // isn't connected. Need at least a 2-by-2 matrix to do the estimate.
1836 if (!cond_signal.empty() && (mat.n() > 1))
1837 {
1838 mat.compute_svd();
1839 double condition_number =
1840 mat.singular_value(0) / mat.singular_value(mat.n() - 1);
1841 cond_signal(condition_number);
1842 }
1843 }
1844}
1845
1846
1847
1848template <typename VectorType>
1850template <typename MatrixType, typename PreconditionerType>
1854void SolverGMRES<VectorType>::solve(const MatrixType &A,
1855 VectorType &x,
1856 const VectorType &b,
1857 const PreconditionerType &preconditioner)
1858{
1859 std::unique_ptr<LogStream::Prefix> prefix;
1860 if (!additional_data.batched_mode)
1861 prefix = std::make_unique<LogStream::Prefix>("GMRES");
1862
1863 // extra call to std::max to placate static analyzers: coverity rightfully
1864 // complains that data.max_n_tmp_vectors - 2 may overflow
1865 const unsigned int basis_size =
1866 (additional_data.max_basis_size > 0 ?
1867 additional_data.max_basis_size :
1868 std::max(additional_data.max_n_tmp_vectors, 3u) - 2);
1869
1870 // Generate an object where basis vectors are stored.
1872 basis_size + 2, this->memory);
1873
1874 // number of the present iteration; this number is not reset to zero upon a
1875 // restart
1876 unsigned int accumulated_iterations = 0;
1877
1878 const bool do_eigenvalues =
1879 !additional_data.batched_mode &&
1880 (!condition_number_signal.empty() ||
1881 !all_condition_numbers_signal.empty() || !eigenvalues_signal.empty() ||
1882 !all_eigenvalues_signal.empty() || !hessenberg_signal.empty() ||
1883 !all_hessenberg_signal.empty());
1884
1886 double res = std::numeric_limits<double>::lowest();
1887
1888 // switch to determine whether we want a left or a right preconditioner. at
1889 // present, left is default, but both ways are implemented
1890 const bool left_precondition = !additional_data.right_preconditioning;
1891
1892 // Per default the left preconditioned GMRES uses the preconditioned
1893 // residual and the right preconditioned GMRES uses the unpreconditioned
1894 // residual as stopping criterion.
1895 const bool use_default_residual = additional_data.use_default_residual;
1896
1897 // define an alias
1898 VectorType &p = basis_vectors(basis_size + 1, x);
1899
1900 // Following vectors are needed when we are not using the default residuals
1901 // as stopping criterion
1904 if (!use_default_residual)
1905 {
1906 r = std::move(typename VectorMemory<VectorType>::Pointer(this->memory));
1907 x_ = std::move(typename VectorMemory<VectorType>::Pointer(this->memory));
1908 r->reinit(x);
1909 x_->reinit(x);
1910 }
1911
1912 arnoldi_process.initialize(additional_data.orthogonalization_strategy,
1913 basis_size,
1914 additional_data.force_re_orthogonalization);
1915
1917 // outer iteration: loop until we either reach convergence or the maximum
1918 // number of iterations is exceeded. each cycle of this loop amounts to one
1919 // restart
1920 do
1921 {
1922 VectorType &v = basis_vectors(0, x);
1923
1924 // Compute the preconditioned/unpreconditioned residual for left/right
1925 // preconditioning. If 'x' is the zero vector, then we can bypass the
1926 // full computation. But 'x' is only likely to be the zero vector if
1927 // that's what the user provided as the starting guess, so it's only
1928 // worth checking for this in the first iteration. (Calling all_zero()
1929 // costs as much in memory transfer and communication as computing the
1930 // norm of a vector.)
1931 if (left_precondition)
1932 {
1933 if (accumulated_iterations == 0 && x.all_zero())
1934 preconditioner.vmult(v, b);
1935 else
1936 {
1937 A.vmult(p, x);
1938 p.sadd(-1., 1., b);
1939 preconditioner.vmult(v, p);
1940 }
1941 }
1942 else
1943 {
1944 if (accumulated_iterations == 0 && x.all_zero())
1945 v = b;
1946 else
1947 {
1948 A.vmult(v, x);
1949 v.sadd(-1., 1., b);
1950 }
1951 }
1952
1953 const double norm_v = arnoldi_process.orthonormalize_nth_vector(
1954 0, basis_vectors, accumulated_iterations, re_orthogonalize_signal);
1955
1956 // check the residual here as well since it may be that we got the exact
1957 // (or an almost exact) solution vector at the outset. if we wouldn't
1958 // check here, the next scaling operation would produce garbage
1959 if (use_default_residual)
1960 {
1961 res = norm_v;
1962 if (additional_data.batched_mode)
1963 iteration_state = solver_control.check(accumulated_iterations, res);
1964 else
1965 iteration_state =
1966 this->iteration_status(accumulated_iterations, res, x);
1967
1968 if (iteration_state != SolverControl::iterate)
1969 break;
1970 }
1971 else
1972 {
1973 deallog << "default_res=" << norm_v << std::endl;
1974
1975 if (left_precondition)
1976 {
1977 A.vmult(*r, x);
1978 r->sadd(-1., 1., b);
1979 }
1980 else
1981 preconditioner.vmult(*r, v);
1982
1983 res = r->l2_norm();
1984 if (additional_data.batched_mode)
1985 iteration_state = solver_control.check(accumulated_iterations, res);
1986 else
1987 iteration_state =
1988 this->iteration_status(accumulated_iterations, res, x);
1989
1990 if (iteration_state != SolverControl::iterate)
1991 break;
1992 }
1993
1994 // inner iteration doing at most as many steps as the size of the
1995 // Arnoldi basis
1996 unsigned int inner_iteration = 0;
1997 for (; (inner_iteration < basis_size &&
1998 iteration_state == SolverControl::iterate);
1999 ++inner_iteration)
2000 {
2001 ++accumulated_iterations;
2002 // yet another alias
2003 VectorType &vv = basis_vectors(inner_iteration + 1, x);
2004
2005 if (left_precondition)
2006 {
2007 A.vmult(p, basis_vectors[inner_iteration]);
2008 preconditioner.vmult(vv, p);
2009 }
2010 else
2011 {
2012 preconditioner.vmult(p, basis_vectors[inner_iteration]);
2013 A.vmult(vv, p);
2014 }
2015
2016 res =
2017 arnoldi_process.orthonormalize_nth_vector(inner_iteration + 1,
2018 basis_vectors,
2019 accumulated_iterations,
2020 re_orthogonalize_signal);
2021
2022 if (use_default_residual)
2023 {
2024 if (additional_data.batched_mode)
2025 iteration_state =
2026 solver_control.check(accumulated_iterations, res);
2027 else
2028 iteration_state =
2029 this->iteration_status(accumulated_iterations, res, x);
2030 }
2031 else
2032 {
2033 if (!additional_data.batched_mode)
2034 deallog << "default_res=" << res << std::endl;
2035
2036 *x_ = x;
2037 const Vector<double> &projected_solution =
2038 arnoldi_process.solve_projected_system(false);
2039
2040 if (left_precondition)
2041 for (unsigned int i = 0; i < inner_iteration + 1; ++i)
2042 x_->add(projected_solution(i), basis_vectors[i]);
2043 else
2044 {
2045 p = 0.;
2046 for (unsigned int i = 0; i < inner_iteration + 1; ++i)
2047 p.add(projected_solution(i), basis_vectors[i]);
2048 preconditioner.vmult(*r, p);
2049 x_->add(1., *r);
2050 };
2051 A.vmult(*r, *x_);
2052 r->sadd(-1., 1., b);
2053
2054 // Now *r contains the unpreconditioned residual!!
2055 if (left_precondition)
2056 {
2057 res = r->l2_norm();
2058 iteration_state =
2059 this->iteration_status(accumulated_iterations, res, x);
2060 }
2061 else
2062 {
2063 preconditioner.vmult(*x_, *r);
2064 res = x_->l2_norm();
2065
2066 if (additional_data.batched_mode)
2067 iteration_state =
2068 solver_control.check(accumulated_iterations, res);
2069 else
2070 iteration_state =
2071 this->iteration_status(accumulated_iterations, res, x);
2072 }
2073 }
2074 }
2075
2076 // end of inner iteration; now update the global solution vector x with
2077 // the solution of the projected system (least-squares solution)
2078 const Vector<double> &projected_solution =
2079 arnoldi_process.solve_projected_system(true);
2080
2081 if (do_eigenvalues)
2082 compute_eigs_and_cond(arnoldi_process.get_hessenberg_matrix(),
2083 inner_iteration,
2084 all_eigenvalues_signal,
2085 all_hessenberg_signal,
2086 condition_number_signal);
2087
2088 if (left_precondition)
2089 ::internal::SolverGMRESImplementation::add(
2090 x,
2091 inner_iteration,
2092 projected_solution,
2093 basis_vectors,
2094 false,
2095 arnoldi_process.vector_ptrs);
2096 else
2097 {
2098 ::internal::SolverGMRESImplementation::add(
2099 p,
2100 inner_iteration,
2101 projected_solution,
2102 basis_vectors,
2103 true,
2104 arnoldi_process.vector_ptrs);
2105 preconditioner.vmult(v, p);
2106 x.add(1., v);
2107 }
2108
2109 // in the last round, print the eigenvalues from the last Arnoldi step
2110 if (iteration_state != SolverControl::iterate)
2111 {
2112 if (do_eigenvalues)
2113 compute_eigs_and_cond(arnoldi_process.get_hessenberg_matrix(),
2114 inner_iteration,
2115 eigenvalues_signal,
2116 hessenberg_signal,
2117 condition_number_signal);
2118
2119 if (!additional_data.batched_mode && !krylov_space_signal.empty())
2120 krylov_space_signal(basis_vectors);
2121
2122 // end of outer iteration. restart if no convergence and the number of
2123 // iterations is not exceeded
2124 }
2125 }
2126 while (iteration_state == SolverControl::iterate);
2127
2128 // in case of failure: throw exception
2129 AssertThrow(iteration_state == SolverControl::success,
2130 SolverControl::NoConvergence(accumulated_iterations, res));
2131}
2132
2133
2134
2135template <typename VectorType>
2137boost::signals2::connection
2139 const std::function<void(double)> &slot,
2140 const bool every_iteration)
2141{
2142 if (every_iteration)
2143 {
2144 return all_condition_numbers_signal.connect(slot);
2145 }
2146 else
2147 {
2148 return condition_number_signal.connect(slot);
2149 }
2150}
2151
2152
2153
2154template <typename VectorType>
2156boost::signals2::connection SolverGMRES<VectorType>::connect_eigenvalues_slot(
2157 const std::function<void(const std::vector<std::complex<double>> &)> &slot,
2158 const bool every_iteration)
2159{
2160 if (every_iteration)
2161 {
2162 return all_eigenvalues_signal.connect(slot);
2163 }
2164 else
2165 {
2166 return eigenvalues_signal.connect(slot);
2167 }
2168}
2169
2170
2171
2172template <typename VectorType>
2174boost::signals2::connection SolverGMRES<VectorType>::connect_hessenberg_slot(
2175 const std::function<void(const FullMatrix<double> &)> &slot,
2176 const bool every_iteration)
2177{
2178 if (every_iteration)
2179 {
2180 return all_hessenberg_signal.connect(slot);
2181 }
2182 else
2183 {
2184 return hessenberg_signal.connect(slot);
2185 }
2186}
2187
2188
2189
2190template <typename VectorType>
2192boost::signals2::connection SolverGMRES<VectorType>::connect_krylov_space_slot(
2193 const std::function<void(
2195{
2196 return krylov_space_signal.connect(slot);
2197}
2198
2199
2200
2201template <typename VectorType>
2203boost::signals2::connection
2205 const std::function<void(int)> &slot)
2206{
2207 return re_orthogonalize_signal.connect(slot);
2208}
2209
2210
2211
2212template <typename VectorType>
2215{
2216 // dummy implementation. this function is not needed for the present
2217 // implementation of gmres
2219 return 0;
2220}
2221
2222
2223
2224//----------------------------------------------------------------------//
2225
2226
2227
2228template <typename VectorType>
2232 const AdditionalData &data)
2233 : SolverBase<VectorType>(cn, mem)
2234 , additional_data(data)
2235{}
2236
2237
2238
2239template <typename VectorType>
2242 const AdditionalData &data)
2243 : SolverBase<VectorType>(cn)
2244 , additional_data(data)
2245{}
2246
2247
2248
2249template <typename VectorType>
2251template <typename MatrixType, typename... PreconditionerTypes>
2255void SolverMPGMRES<VectorType>::solve(
2256 const MatrixType &A,
2257 VectorType &x,
2258 const VectorType &b,
2259 const PreconditionerTypes &...preconditioners)
2260{
2261 LogStream::Prefix prefix("MPGMRES");
2262
2263 if (additional_data.use_truncated_mpgmres_strategy)
2265 A, x, b, IndexingStrategy::truncated_mpgmres, preconditioners...);
2266 else
2268 A, x, b, IndexingStrategy::full_mpgmres, preconditioners...);
2269}
2270
2271
2272
2273template <typename VectorType>
2275template <typename MatrixType, typename... PreconditionerTypes>
2277 const MatrixType &A,
2278 VectorType &x,
2279 const VectorType &b,
2280 const IndexingStrategy &indexing_strategy,
2281 const PreconditionerTypes &...preconditioners)
2282{
2283 constexpr std::size_t n_preconditioners = sizeof...(PreconditionerTypes);
2284
2285 // A lambda for applying the nth preconditioner to a vector src storing
2286 // the result in dst:
2287
2288 const auto apply_nth_preconditioner = [&](unsigned int n,
2289 auto &dst,
2290 const auto &src) {
2291 // We cycle through all preconditioners and call the nth one:
2292 std::size_t i = 0;
2293
2294 [[maybe_unused]] bool preconditioner_called = false;
2295
2296 const auto call_matching_preconditioner = [&](const auto &preconditioner) {
2297 if (i++ == n)
2298 {
2299 Assert(!preconditioner_called, ::ExcInternalError());
2300 preconditioner_called = true;
2301 preconditioner.vmult(dst, src);
2302 }
2303 };
2304
2305 // https://en.cppreference.com/w/cpp/language/fold
2306 (call_matching_preconditioner(preconditioners), ...);
2307 Assert(preconditioner_called, ::ExcInternalError());
2308 };
2309
2310 std::size_t current_index = 0;
2311
2312 // A lambda that cycles through all preconditioners in sequence while
2313 // applying exactly one preconditioner with each function invocation to
2314 // the vector src and storing the result in dst:
2315
2316 const auto preconditioner_vmult = [&](auto &dst, const auto &src) {
2317 // We have no preconditioner that we could apply
2318 if constexpr (n_preconditioners == 0)
2319 dst = src;
2320 else
2321 {
2322 apply_nth_preconditioner(current_index, dst, src);
2323 current_index = (current_index + 1) % n_preconditioners;
2324 }
2325 };
2326
2327 // Return the correct index for constructing the next vector in the
2328 // Krylov space sequence according to the chosen indexing strategy
2329
2330 const auto previous_vector_index =
2331 [indexing_strategy](unsigned int i) -> unsigned int {
2332 // In the special case of no preconditioners we simply fall back to the
2333 // FGMRES indexing strategy.
2334 if constexpr (n_preconditioners == 0)
2335 return i;
2336 else
2337 {
2338 switch (indexing_strategy)
2339 {
2340 case IndexingStrategy::fgmres:
2341 // 0, 1, 2, 3, ...
2342 return i;
2343 case IndexingStrategy::full_mpgmres:
2344 // 0, 0, ..., 1, 1, ..., 2, 2, ..., 3, 3, ...
2345 return i / n_preconditioners;
2346 case IndexingStrategy::truncated_mpgmres:
2347 // 0, 0, ..., 1, 2, 3, ...
2348 return (1 + i >= n_preconditioners) ?
2349 (1 + i - n_preconditioners) :
2350 0;
2351 default:
2353 return 0;
2354 }
2355 }
2356 };
2357
2359
2360 const unsigned int basis_size = additional_data.max_basis_size;
2361
2362 // Generate an object where basis vectors are stored.
2364 basis_size + 1, this->memory);
2366 basis_size, this->memory);
2367
2368 // number of the present iteration; this number is not reset to zero upon a
2369 // restart
2370 unsigned int accumulated_iterations = 0;
2371
2372 // matrix used for the orthogonalization process later
2373 arnoldi_process.initialize(additional_data.orthogonalization_strategy,
2374 basis_size,
2375 false);
2376
2377 // Iteration starts here
2378 double res = std::numeric_limits<double>::lowest();
2379
2380 do
2381 {
2382 // Compute the residual. If 'x' is the zero vector, then we can bypass
2383 // the full computation. But 'x' is only likely to be the zero vector if
2384 // that's what the user provided as the starting guess, so it's only
2385 // worth checking for this in the first iteration. (Calling all_zero()
2386 // costs as much in memory transfer and communication as computing the
2387 // norm of a vector.)
2388 if (accumulated_iterations == 0 && x.all_zero())
2389 v(0, x) = b;
2390 else
2391 {
2392 A.vmult(v(0, x), x);
2393 v[0].sadd(-1., 1., b);
2394 }
2395
2396 res = arnoldi_process.orthonormalize_nth_vector(0, v);
2397 iteration_state = this->iteration_status(accumulated_iterations, res, x);
2398 if (iteration_state == SolverControl::success)
2399 break;
2400
2401 unsigned int inner_iteration = 0;
2402 for (; (inner_iteration < basis_size &&
2403 iteration_state == SolverControl::iterate);
2404 ++inner_iteration)
2405 {
2406 preconditioner_vmult(z(inner_iteration, x),
2407 v[previous_vector_index(inner_iteration)]);
2408 A.vmult(v(inner_iteration + 1, x), z[inner_iteration]);
2409
2410 res =
2411 arnoldi_process.orthonormalize_nth_vector(inner_iteration + 1, v);
2412
2413 // check convergence. note that the vector 'x' we pass to the
2414 // criterion is not the final solution we compute if we
2415 // decide to jump out of the iteration (we update 'x' again
2416 // right after the current loop)
2417 iteration_state =
2418 this->iteration_status(++accumulated_iterations, res, x);
2419 }
2420
2421 // Solve triangular system with projected quantities and update solution
2422 // vector
2423 const Vector<double> &projected_solution =
2424 arnoldi_process.solve_projected_system(true);
2425 ::internal::SolverGMRESImplementation::add(
2426 x,
2427 inner_iteration,
2428 projected_solution,
2429 z,
2430 false,
2431 arnoldi_process.vector_ptrs);
2432 }
2433 while (iteration_state == SolverControl::iterate);
2434
2435 // in case of failure: throw exception
2436 if (iteration_state != SolverControl::success)
2437 AssertThrow(false,
2438 SolverControl::NoConvergence(accumulated_iterations, res));
2439}
2440
2441
2442
2443//----------------------------------------------------------------------//
2444
2445
2446
2447template <typename VectorType>
2451 const AdditionalData &data)
2453 cn,
2454 mem,
2455 typename SolverMPGMRES<VectorType>::AdditionalData{
2456 data.max_basis_size,
2457 data.orthogonalization_strategy,
2458 true})
2459{}
2460
2461
2462
2463template <typename VectorType>
2466 const AdditionalData &data)
2468 cn,
2469 typename SolverMPGMRES<VectorType>::AdditionalData{
2470 data.max_basis_size,
2471 data.orthogonalization_strategy,
2472 true})
2473{}
2474
2475
2476
2477template <typename VectorType>
2479template <typename MatrixType, typename... PreconditionerTypes>
2483void SolverFGMRES<VectorType>::solve(
2484 const MatrixType &A,
2485 VectorType &x,
2486 const VectorType &b,
2487 const PreconditionerTypes &...preconditioners)
2488{
2489 LogStream::Prefix prefix("FGMRES");
2491 A, x, b, SolverFGMRES::IndexingStrategy::fgmres, preconditioners...);
2492}
2493
2494
2495#endif // DOXYGEN
2496
2498
2499#endif
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  iterator begin()
virtual State check(const unsigned int step, const double check_value)
@ iterate
Continue iteration.
@ success
Stop iteration, goal reached.
SolverFGMRES(SolverControl &cn, VectorMemory< VectorType > &mem, const AdditionalData &data=AdditionalData())
SolverFGMRES(SolverControl &cn, const AdditionalData &data=AdditionalData())
SolverGMRES(const SolverGMRES< VectorType > &)=delete
boost::signals2::connection connect_condition_number_slot(const std::function< void(double)> &slot, const bool every_iteration=false)
boost::signals2::connection connect_krylov_space_slot(const std::function< void(const internal::SolverGMRESImplementation::TmpVectors< VectorType > &)> &slot)
boost::signals2::connection connect_re_orthogonalization_slot(const std::function< void(int)> &slot)
static void compute_eigs_and_cond(const FullMatrix< double > &H_orig, const unsigned int n, 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)
SolverGMRES(SolverControl &cn, VectorMemory< VectorType > &mem, const AdditionalData &data=AdditionalData())
boost::signals2::connection connect_hessenberg_slot(const std::function< void(const FullMatrix< double > &)> &slot, const bool every_iteration=true)
SolverGMRES(SolverControl &cn, const AdditionalData &data=AdditionalData())
void solve(const MatrixType &A, VectorType &x, const VectorType &b, const PreconditionerType &preconditioner)
virtual double criterion()
boost::signals2::connection connect_eigenvalues_slot(const std::function< void(const std::vector< std::complex< double > > &)> &slot, const bool every_iteration=false)
SolverMPGMRES(SolverControl &cn, VectorMemory< VectorType > &mem, const AdditionalData &data=AdditionalData())
internal::SolverGMRESImplementation::ArnoldiProcess< typename VectorType::value_type > arnoldi_process
SolverMPGMRES(SolverControl &cn, const AdditionalData &data=AdditionalData())
AdditionalData additional_data
void solve_internal(const MatrixType &A, VectorType &x, const VectorType &b, const IndexingStrategy &indexing_strategy, const PreconditionerTypes &...preconditioners)
virtual size_type size() const override
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
const FullMatrix< double > & get_hessenberg_matrix() const
LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy
double orthonormalize_nth_vector(const unsigned int n, TmpVectors< VectorType > &orthogonal_vectors, const unsigned int accumulated_iterations=0, const boost::signals2::signal< void(int)> &reorthogonalize_signal=boost::signals2::signal< void(int)>())
const Vector< double > & solve_projected_system(const bool orthogonalization_finished)
void initialize(const LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy, const unsigned int max_basis_size, const bool force_reorthogonalization)
std::vector< std::pair< double, double > > givens_rotations
double do_givens_rotation(const bool delayed_reorthogonalization, const int col, FullMatrix< double > &matrix, std::vector< std::pair< double, double > > &rotations, Vector< double > &rhs)
std::vector< typename VectorMemory< VectorType >::Pointer > data
TmpVectors(const unsigned int max_size, VectorMemory< VectorType > &vmem)
VectorType & operator[](const unsigned int i) const
VectorType & operator()(const unsigned int i, const VectorType &temp)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:249
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertIsFinite(number)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcNotInitialized()
#define DeclException1(Exception1, type1, outsequence)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
LogStream deallog
Definition logstream.cc:36
std::vector< index_type > data
Definition mpi.cc:734
types::global_dof_index locally_owned_size
Definition mpi.cc:821
@ matrix
Contents is actually a matrix.
constexpr char A
Tpetra::Vector< Number, LO, GO, NodeType< MemorySpace > > VectorType
Tpetra::CrsMatrix< Number, LO, GO, NodeType< MemorySpace > > MatrixType
std::enable_if_t< IsBlockVector< VectorType >::value, unsigned int > n_blocks(const VectorType &vector)
Definition operators.h:47
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
T sum(const T &t, const MPI_Comm mpi_communicator)
void do_Tvmult_add(const unsigned int n_vectors, const std::size_t locally_owned_size, const Number *current_vector, const std::vector< const Number * > &orthogonal_vectors, Vector< double > &h)
double do_subtract_and_norm(const unsigned int n_vectors, const std::size_t locally_owned_size, const std::vector< const Number * > &orthogonal_vectors, const Vector< double > &h, Number *current_vector)
void do_add(const unsigned int n_vectors, const std::size_t locally_owned_size, const std::vector< const Number * > &tmp_vectors, const Vector< double > &h, const bool zero_out, Number *output)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
STL namespace.
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
AdditionalData(const unsigned int max_basis_size=30, const LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy=LinearAlgebra::OrthogonalizationStrategy::delayed_classical_gram_schmidt)
LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy
AdditionalData(const unsigned int max_basis_size=30, const bool right_preconditioning=false, const bool use_default_residual=true, const bool force_re_orthogonalization=false, const bool batched_mode=false, const LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy=LinearAlgebra::OrthogonalizationStrategy::delayed_classical_gram_schmidt)
LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy
AdditionalData(const unsigned int max_basis_size=30, const LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy=LinearAlgebra::OrthogonalizationStrategy::delayed_classical_gram_schmidt, const bool use_truncated_mpgmres_strategy=true)
LinearAlgebra::OrthogonalizationStrategy orthogonalization_strategy
std::array< Number, 1 > eigenvalues(const SymmetricTensor< 2, 1, Number > &T)