/builds/MusicScience37Projects/numerical-analysis/numerical-collection-cpp/include/num_collect/linear/gauss_seidel_iterative_solver.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2023 MusicScience37 (Kenta Kabashima) |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | /*! |
17 | | * \file |
18 | | * \brief Definition of gauss_seidel_iterative_solver class. |
19 | | */ |
20 | | #pragma once |
21 | | |
22 | | // IWYU pragma: no_include <string_view> |
23 | | |
24 | | #include <cmath> |
25 | | |
26 | | #include <Eigen/Core> |
27 | | |
28 | | #include "num_collect/base/concepts/dense_vector_of.h" |
29 | | #include "num_collect/base/concepts/real_scalar.h" |
30 | | #include "num_collect/base/concepts/sparse_matrix.h" |
31 | | #include "num_collect/base/exception.h" |
32 | | #include "num_collect/base/index_type.h" |
33 | | #include "num_collect/linear/iterative_solver_base.h" |
34 | | #include "num_collect/logging/logging_macros.h" |
35 | | |
36 | | namespace num_collect::linear { |
37 | | |
38 | | template <base::concepts::sparse_matrix Matrix> |
39 | | class gauss_seidel_iterative_solver; |
40 | | |
41 | | namespace impl { |
42 | | |
43 | | /*! |
44 | | * \brief Traits of gauss_seidel_iterative_solver class. |
45 | | * |
46 | | * \tparam Matrix Type of the matrix. |
47 | | */ |
48 | | template <base::concepts::sparse_matrix Matrix> |
49 | | struct iterative_solver_traits<gauss_seidel_iterative_solver<Matrix>> { |
50 | | //! Type of the matrix. |
51 | | using matrix_type = Matrix; |
52 | | }; |
53 | | |
54 | | } // namespace impl |
55 | | |
56 | | /*! |
57 | | * \brief Class to solve linear equations using Gauss-Seidel iteration |
58 | | * \cite Golub2013. |
59 | | * |
60 | | * \tparam Matrix Type of the matrix. |
61 | | */ |
62 | | template <base::concepts::sparse_matrix Matrix> |
63 | | class gauss_seidel_iterative_solver |
64 | | : public iterative_solver_base<gauss_seidel_iterative_solver<Matrix>> { |
65 | | static_assert(Matrix::IsRowMajor == 1, "Row major matrix is required."); |
66 | | static_assert(base::concepts::real_scalar<typename Matrix::Scalar>, |
67 | | "Complex matrices are not supported."); |
68 | | |
69 | | public: |
70 | | //! Type of the base class. |
71 | | using base_type = |
72 | | iterative_solver_base<gauss_seidel_iterative_solver<Matrix>>; |
73 | | |
74 | | using typename base_type::matrix_type; |
75 | | using typename base_type::real_scalar_type; |
76 | | using typename base_type::scalar_type; |
77 | | using typename base_type::storage_index_type; |
78 | | |
79 | | protected: |
80 | | using base_type::coeff; |
81 | | |
82 | | public: |
83 | | //! Type of vectors. |
84 | | using vector_type = Eigen::VectorX<scalar_type>; |
85 | | |
86 | | /*! |
87 | | * \brief Constructor. |
88 | | */ |
89 | 13 | gauss_seidel_iterative_solver() = default; _ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEEC2Ev Line | Count | Source | 89 | 5 | gauss_seidel_iterative_solver() = default; |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEEC2Ev Line | Count | Source | 89 | 4 | gauss_seidel_iterative_solver() = default; |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEEC2Ev Line | Count | Source | 89 | 4 | gauss_seidel_iterative_solver() = default; |
|
90 | | |
91 | | /*! |
92 | | * \brief Prepare to solve. |
93 | | * |
94 | | * \param[in] coeff Coefficient matrix. |
95 | | */ |
96 | 13 | void compute(const matrix_type& coeff) { |
97 | 13 | base_type::compute(coeff); |
98 | 13 | diag_ = coeff.diagonal(); |
99 | 13 | inv_diag_ = diag_.cwiseInverse(); |
100 | 13 | if (!inv_diag_.array().isFinite().all()) { |
101 | 3 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, |
102 | 3 | "All diagonal elements of the coefficient matrix must not be " |
103 | 3 | "zero."); |
104 | 3 | } |
105 | 13 | } _ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEE7computeERKS4_ Line | Count | Source | 96 | 5 | void compute(const matrix_type& coeff) { | 97 | 5 | base_type::compute(coeff); | 98 | 5 | diag_ = coeff.diagonal(); | 99 | 5 | inv_diag_ = diag_.cwiseInverse(); | 100 | 5 | if (!inv_diag_.array().isFinite().all()) { | 101 | 1 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 102 | 1 | "All diagonal elements of the coefficient matrix must not be " | 103 | 1 | "zero."); | 104 | 1 | } | 105 | 5 | } |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEE7computeERKS4_ Line | Count | Source | 96 | 4 | void compute(const matrix_type& coeff) { | 97 | 4 | base_type::compute(coeff); | 98 | 4 | diag_ = coeff.diagonal(); | 99 | 4 | inv_diag_ = diag_.cwiseInverse(); | 100 | 4 | if (!inv_diag_.array().isFinite().all()) { | 101 | 1 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 102 | 1 | "All diagonal elements of the coefficient matrix must not be " | 103 | 1 | "zero."); | 104 | 1 | } | 105 | 4 | } |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEE7computeERKS4_ Line | Count | Source | 96 | 4 | void compute(const matrix_type& coeff) { | 97 | 4 | base_type::compute(coeff); | 98 | 4 | diag_ = coeff.diagonal(); | 99 | 4 | inv_diag_ = diag_.cwiseInverse(); | 100 | 4 | if (!inv_diag_.array().isFinite().all()) { | 101 | 1 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 102 | 1 | "All diagonal elements of the coefficient matrix must not be " | 103 | 1 | "zero."); | 104 | 1 | } | 105 | 4 | } |
|
106 | | |
107 | | /*! |
108 | | * \brief Iterate repeatedly until stop criterion is satisfied for a vector. |
109 | | * |
110 | | * \tparam Right Type of the right-hand-side vector. |
111 | | * \tparam Solution Type of the solution vector. |
112 | | * \param[in] right Right-hand-side vector. |
113 | | * \param[in,out] solution Solution vector. |
114 | | */ |
115 | | template <base::concepts::dense_vector_of<scalar_type> Right, |
116 | | base::concepts::dense_vector_of<scalar_type> Solution> |
117 | 10 | void solve_vector_in_place(const Right& right, Solution& solution) const { |
118 | 10 | const auto& coeff_ref = coeff(); |
119 | | |
120 | 10 | if (coeff_ref.rows() != coeff_ref.cols()) { |
121 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, |
122 | 0 | "Coefficient matrix must be a square matrix."); |
123 | 0 | } |
124 | 10 | if (right.rows() != coeff_ref.cols()) { |
125 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, |
126 | 0 | "Right-hand-side vector must have the number of elements same " |
127 | 0 | "as the size of the coefficient matrix."); |
128 | 0 | } |
129 | 10 | if (solution.rows() != coeff_ref.cols()) { |
130 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, |
131 | 0 | "Solution vector must have the number of elements same " |
132 | 0 | "as the size of the coefficient matrix."); |
133 | 0 | } |
134 | | |
135 | 10 | iterations_ = 0; |
136 | 10 | const scalar_type right_norm = right.squaredNorm(); |
137 | 10 | const index_type max_iterations = base_type::max_iterations(); |
138 | 138 | while (iterations_ < max_iterations) { |
139 | 135 | iterate(coeff_ref, right, solution); |
140 | 135 | ++iterations_; |
141 | 135 | using std::sqrt; |
142 | 135 | residual_rate_ = sqrt(residual_ / right_norm); |
143 | 135 | if (residual_rate_ < base_type::tolerance()) { |
144 | 7 | break; |
145 | 7 | } |
146 | 135 | } |
147 | 10 | } _ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEE21solve_vector_in_placeITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIdLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKT_RT0_ Line | Count | Source | 117 | 4 | void solve_vector_in_place(const Right& right, Solution& solution) const { | 118 | 4 | const auto& coeff_ref = coeff(); | 119 | | | 120 | 4 | if (coeff_ref.rows() != coeff_ref.cols()) { | 121 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 122 | 0 | "Coefficient matrix must be a square matrix."); | 123 | 0 | } | 124 | 4 | if (right.rows() != coeff_ref.cols()) { | 125 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 126 | 0 | "Right-hand-side vector must have the number of elements same " | 127 | 0 | "as the size of the coefficient matrix."); | 128 | 0 | } | 129 | 4 | if (solution.rows() != coeff_ref.cols()) { | 130 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 131 | 0 | "Solution vector must have the number of elements same " | 132 | 0 | "as the size of the coefficient matrix."); | 133 | 0 | } | 134 | | | 135 | 4 | iterations_ = 0; | 136 | 4 | const scalar_type right_norm = right.squaredNorm(); | 137 | 4 | const index_type max_iterations = base_type::max_iterations(); | 138 | 80 | while (iterations_ < max_iterations) { | 139 | 79 | iterate(coeff_ref, right, solution); | 140 | 79 | ++iterations_; | 141 | 79 | using std::sqrt; | 142 | 79 | residual_rate_ = sqrt(residual_ / right_norm); | 143 | 79 | if (residual_rate_ < base_type::tolerance()) { | 144 | 3 | break; | 145 | 3 | } | 146 | 79 | } | 147 | 4 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEE21solve_vector_in_placeITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIfLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKT_RT0_ Line | Count | Source | 117 | 3 | void solve_vector_in_place(const Right& right, Solution& solution) const { | 118 | 3 | const auto& coeff_ref = coeff(); | 119 | | | 120 | 3 | if (coeff_ref.rows() != coeff_ref.cols()) { | 121 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 122 | 0 | "Coefficient matrix must be a square matrix."); | 123 | 0 | } | 124 | 3 | if (right.rows() != coeff_ref.cols()) { | 125 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 126 | 0 | "Right-hand-side vector must have the number of elements same " | 127 | 0 | "as the size of the coefficient matrix."); | 128 | 0 | } | 129 | 3 | if (solution.rows() != coeff_ref.cols()) { | 130 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 131 | 0 | "Solution vector must have the number of elements same " | 132 | 0 | "as the size of the coefficient matrix."); | 133 | 0 | } | 134 | | | 135 | 3 | iterations_ = 0; | 136 | 3 | const scalar_type right_norm = right.squaredNorm(); | 137 | 3 | const index_type max_iterations = base_type::max_iterations(); | 138 | 17 | while (iterations_ < max_iterations) { | 139 | 16 | iterate(coeff_ref, right, solution); | 140 | 16 | ++iterations_; | 141 | 16 | using std::sqrt; | 142 | 16 | residual_rate_ = sqrt(residual_ / right_norm); | 143 | 16 | if (residual_rate_ < base_type::tolerance()) { | 144 | 2 | break; | 145 | 2 | } | 146 | 16 | } | 147 | 3 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEE21solve_vector_in_placeITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIeLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKT_RT0_ Line | Count | Source | 117 | 3 | void solve_vector_in_place(const Right& right, Solution& solution) const { | 118 | 3 | const auto& coeff_ref = coeff(); | 119 | | | 120 | 3 | if (coeff_ref.rows() != coeff_ref.cols()) { | 121 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 122 | 0 | "Coefficient matrix must be a square matrix."); | 123 | 0 | } | 124 | 3 | if (right.rows() != coeff_ref.cols()) { | 125 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 126 | 0 | "Right-hand-side vector must have the number of elements same " | 127 | 0 | "as the size of the coefficient matrix."); | 128 | 0 | } | 129 | 3 | if (solution.rows() != coeff_ref.cols()) { | 130 | 0 | NUM_COLLECT_LOG_AND_THROW(invalid_argument, | 131 | 0 | "Solution vector must have the number of elements same " | 132 | 0 | "as the size of the coefficient matrix."); | 133 | 0 | } | 134 | | | 135 | 3 | iterations_ = 0; | 136 | 3 | const scalar_type right_norm = right.squaredNorm(); | 137 | 3 | const index_type max_iterations = base_type::max_iterations(); | 138 | 41 | while (iterations_ < max_iterations) { | 139 | 40 | iterate(coeff_ref, right, solution); | 140 | 40 | ++iterations_; | 141 | 40 | using std::sqrt; | 142 | 40 | residual_rate_ = sqrt(residual_ / right_norm); | 143 | 40 | if (residual_rate_ < base_type::tolerance()) { | 144 | 2 | break; | 145 | 2 | } | 146 | 40 | } | 147 | 3 | } |
|
148 | | |
149 | | /*! |
150 | | * \brief Get the number of iterations. |
151 | | * |
152 | | * \note This value won't be updated in iterate function. |
153 | | * |
154 | | * \return Number of iterations. |
155 | | */ |
156 | 9 | [[nodiscard]] auto iterations() const noexcept -> index_type { |
157 | 9 | return iterations_; |
158 | 9 | } _ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEE10iterationsEv Line | Count | Source | 156 | 3 | [[nodiscard]] auto iterations() const noexcept -> index_type { | 157 | 3 | return iterations_; | 158 | 3 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEE10iterationsEv Line | Count | Source | 156 | 3 | [[nodiscard]] auto iterations() const noexcept -> index_type { | 157 | 3 | return iterations_; | 158 | 3 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEE10iterationsEv Line | Count | Source | 156 | 3 | [[nodiscard]] auto iterations() const noexcept -> index_type { | 157 | 3 | return iterations_; | 158 | 3 | } |
|
159 | | |
160 | | /*! |
161 | | * \brief Get the rate of the last residual. |
162 | | * |
163 | | * \note This value won't be updated in iterate function. |
164 | | * |
165 | | * \return Rate of the last residual. |
166 | | */ |
167 | | [[nodiscard]] auto residual_rate() const noexcept -> scalar_type { |
168 | | return residual_rate_; |
169 | | } |
170 | | |
171 | | private: |
172 | | /*! |
173 | | * \brief Iterate once. |
174 | | * |
175 | | * \tparam Right Type of the right-hand-side vector. |
176 | | * \tparam Solution Type of the solution vector. |
177 | | * \param[in] coeff_ref Coefficient matrix. |
178 | | * \param[in] right Right-hand-side vector. |
179 | | * \param[in,out] solution Solution vector. |
180 | | */ |
181 | | template <base::concepts::dense_vector_of<scalar_type> Right, |
182 | | base::concepts::dense_vector_of<scalar_type> Solution> |
183 | | void iterate(const matrix_type& coeff_ref, const Right& right, |
184 | 135 | Solution& solution) const { |
185 | 135 | const index_type size = coeff_ref.rows(); |
186 | 135 | residual_ = static_cast<scalar_type>(0); |
187 | 1.67k | for (index_type i = 0; i < size; ++i) { |
188 | 1.54k | scalar_type numerator = right(i); |
189 | 10.5k | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; |
190 | 9.01k | ++iter) { |
191 | 9.01k | if (iter.index() != i) { |
192 | 7.46k | numerator -= iter.value() * solution(iter.index()); |
193 | 7.46k | } |
194 | 9.01k | } |
195 | 1.54k | const scalar_type row_residual = numerator - diag_(i) * solution(i); |
196 | 1.54k | solution(i) = numerator * inv_diag_(i); |
197 | 1.54k | residual_ += row_residual * row_residual; |
198 | 1.54k | } |
199 | 135 | } _ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEE7iterateITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIdLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKS4_RKT_RT0_ Line | Count | Source | 184 | 79 | Solution& solution) const { | 185 | 79 | const index_type size = coeff_ref.rows(); | 186 | 79 | residual_ = static_cast<scalar_type>(0); | 187 | 1.11k | for (index_type i = 0; i < size; ++i) { | 188 | 1.04k | scalar_type numerator = right(i); | 189 | 7.30k | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; | 190 | 6.26k | ++iter) { | 191 | 6.26k | if (iter.index() != i) { | 192 | 5.22k | numerator -= iter.value() * solution(iter.index()); | 193 | 5.22k | } | 194 | 6.26k | } | 195 | 1.04k | const scalar_type row_residual = numerator - diag_(i) * solution(i); | 196 | 1.04k | solution(i) = numerator * inv_diag_(i); | 197 | 1.04k | residual_ += row_residual * row_residual; | 198 | 1.04k | } | 199 | 79 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEE7iterateITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIfLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKS4_RKT_RT0_ Line | Count | Source | 184 | 16 | Solution& solution) const { | 185 | 16 | const index_type size = coeff_ref.rows(); | 186 | 16 | residual_ = static_cast<scalar_type>(0); | 187 | 160 | for (index_type i = 0; i < size; ++i) { | 188 | 144 | scalar_type numerator = right(i); | 189 | 928 | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; | 190 | 784 | ++iter) { | 191 | 784 | if (iter.index() != i) { | 192 | 640 | numerator -= iter.value() * solution(iter.index()); | 193 | 640 | } | 194 | 784 | } | 195 | 144 | const scalar_type row_residual = numerator - diag_(i) * solution(i); | 196 | 144 | solution(i) = numerator * inv_diag_(i); | 197 | 144 | residual_ += row_residual * row_residual; | 198 | 144 | } | 199 | 16 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEE7iterateITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIeLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKS4_RKT_RT0_ Line | Count | Source | 184 | 40 | Solution& solution) const { | 185 | 40 | const index_type size = coeff_ref.rows(); | 186 | 40 | residual_ = static_cast<scalar_type>(0); | 187 | 400 | for (index_type i = 0; i < size; ++i) { | 188 | 360 | scalar_type numerator = right(i); | 189 | 2.32k | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; | 190 | 1.96k | ++iter) { | 191 | 1.96k | if (iter.index() != i) { | 192 | 1.60k | numerator -= iter.value() * solution(iter.index()); | 193 | 1.60k | } | 194 | 1.96k | } | 195 | 360 | const scalar_type row_residual = numerator - diag_(i) * solution(i); | 196 | 360 | solution(i) = numerator * inv_diag_(i); | 197 | 360 | residual_ += row_residual * row_residual; | 198 | 360 | } | 199 | 40 | } |
|
200 | | |
201 | | //! Number of iterations. |
202 | | mutable index_type iterations_{}; |
203 | | |
204 | | //! Last residual. |
205 | | mutable scalar_type residual_{}; |
206 | | |
207 | | //! Rate of last residual. |
208 | | mutable scalar_type residual_rate_{}; |
209 | | |
210 | | //! Diagonal coefficients. |
211 | | vector_type diag_{}; |
212 | | |
213 | | //! Inverse of diagonal coefficients. |
214 | | vector_type inv_diag_{}; |
215 | | }; |
216 | | |
217 | | } // namespace num_collect::linear |