/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/base/precondition.h" |
34 | | #include "num_collect/linear/iterative_solver_base.h" |
35 | | #include "num_collect/logging/logging_macros.h" |
36 | | |
37 | | namespace num_collect::linear { |
38 | | |
39 | | template <base::concepts::sparse_matrix Matrix> |
40 | | class gauss_seidel_iterative_solver; |
41 | | |
42 | | namespace impl { |
43 | | |
44 | | /*! |
45 | | * \brief Traits of gauss_seidel_iterative_solver class. |
46 | | * |
47 | | * \tparam Matrix Type of the matrix. |
48 | | */ |
49 | | template <base::concepts::sparse_matrix Matrix> |
50 | | struct iterative_solver_traits<gauss_seidel_iterative_solver<Matrix>> { |
51 | | //! Type of the matrix. |
52 | | using matrix_type = Matrix; |
53 | | }; |
54 | | |
55 | | } // namespace impl |
56 | | |
57 | | /*! |
58 | | * \brief Class to solve linear equations using Gauss-Seidel iteration |
59 | | * \cite Golub2013. |
60 | | * |
61 | | * \tparam Matrix Type of the matrix. |
62 | | */ |
63 | | template <base::concepts::sparse_matrix Matrix> |
64 | | class gauss_seidel_iterative_solver |
65 | | : public iterative_solver_base<gauss_seidel_iterative_solver<Matrix>> { |
66 | | static_assert(Matrix::IsRowMajor == 1, "Row major matrix is required."); |
67 | | static_assert(base::concepts::real_scalar<typename Matrix::Scalar>, |
68 | | "Complex matrices are not supported."); |
69 | | |
70 | | public: |
71 | | //! Type of the base class. |
72 | | using base_type = |
73 | | iterative_solver_base<gauss_seidel_iterative_solver<Matrix>>; |
74 | | |
75 | | using typename base_type::matrix_type; |
76 | | using typename base_type::real_scalar_type; |
77 | | using typename base_type::scalar_type; |
78 | | using typename base_type::storage_index_type; |
79 | | |
80 | | protected: |
81 | | using base_type::coeff; |
82 | | |
83 | | public: |
84 | | //! Type of vectors. |
85 | | using vector_type = Eigen::VectorX<scalar_type>; |
86 | | |
87 | | /*! |
88 | | * \brief Constructor. |
89 | | */ |
90 | 13 | gauss_seidel_iterative_solver() = default; _ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEEC2Ev Line | Count | Source | 90 | 5 | gauss_seidel_iterative_solver() = default; |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEEC2Ev Line | Count | Source | 90 | 4 | gauss_seidel_iterative_solver() = default; |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEEC2Ev Line | Count | Source | 90 | 4 | gauss_seidel_iterative_solver() = default; |
|
91 | | |
92 | | /*! |
93 | | * \brief Prepare to solve. |
94 | | * |
95 | | * \param[in] coeff Coefficient matrix. |
96 | | */ |
97 | 13 | void compute(const matrix_type& coeff) { |
98 | 13 | base_type::compute(coeff); |
99 | 13 | diag_ = coeff.diagonal(); |
100 | 13 | inv_diag_ = diag_.cwiseInverse(); |
101 | 13 | NUM_COLLECT_PRECONDITION(inv_diag_.array().isFinite().all(), |
102 | 13 | "All diagonal elements of the coefficient matrix must not be " |
103 | 13 | "zero."); |
104 | 13 | } _ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEE7computeERKS4_ Line | Count | Source | 97 | 5 | void compute(const matrix_type& coeff) { | 98 | 5 | base_type::compute(coeff); | 99 | 5 | diag_ = coeff.diagonal(); | 100 | 5 | inv_diag_ = diag_.cwiseInverse(); | 101 | 5 | NUM_COLLECT_PRECONDITION(inv_diag_.array().isFinite().all(), | 102 | 5 | "All diagonal elements of the coefficient matrix must not be " | 103 | 5 | "zero."); | 104 | 5 | } |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEE7computeERKS4_ Line | Count | Source | 97 | 4 | void compute(const matrix_type& coeff) { | 98 | 4 | base_type::compute(coeff); | 99 | 4 | diag_ = coeff.diagonal(); | 100 | 4 | inv_diag_ = diag_.cwiseInverse(); | 101 | 4 | NUM_COLLECT_PRECONDITION(inv_diag_.array().isFinite().all(), | 102 | 4 | "All diagonal elements of the coefficient matrix must not be " | 103 | 4 | "zero."); | 104 | 4 | } |
_ZN11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEE7computeERKS4_ Line | Count | Source | 97 | 4 | void compute(const matrix_type& coeff) { | 98 | 4 | base_type::compute(coeff); | 99 | 4 | diag_ = coeff.diagonal(); | 100 | 4 | inv_diag_ = diag_.cwiseInverse(); | 101 | 4 | NUM_COLLECT_PRECONDITION(inv_diag_.array().isFinite().all(), | 102 | 4 | "All diagonal elements of the coefficient matrix must not be " | 103 | 4 | "zero."); | 104 | 4 | } |
|
105 | | |
106 | | /*! |
107 | | * \brief Iterate repeatedly until stop criterion is satisfied for a vector. |
108 | | * |
109 | | * \tparam Right Type of the right-hand-side vector. |
110 | | * \tparam Solution Type of the solution vector. |
111 | | * \param[in] right Right-hand-side vector. |
112 | | * \param[in,out] solution Solution vector. |
113 | | */ |
114 | | template <base::concepts::dense_vector_of<scalar_type> Right, |
115 | | base::concepts::dense_vector_of<scalar_type> Solution> |
116 | 10 | void solve_vector_in_place(const Right& right, Solution& solution) const { |
117 | 10 | const auto& coeff_ref = coeff(); |
118 | | |
119 | 10 | NUM_COLLECT_PRECONDITION(coeff_ref.rows() == coeff_ref.cols(), |
120 | 10 | "Coefficient matrix must be a square matrix."); |
121 | 10 | NUM_COLLECT_PRECONDITION(right.rows() == coeff_ref.cols(), |
122 | 10 | "Right-hand-side vector must have the number of elements same as " |
123 | 10 | "the size of the coefficient matrix."); |
124 | 10 | NUM_COLLECT_PRECONDITION(solution.rows() == coeff_ref.cols(), |
125 | 10 | "Solution vector must have the number of elements same as the size " |
126 | 10 | "of the coefficient matrix."); |
127 | | |
128 | 10 | iterations_ = 0; |
129 | 10 | const scalar_type right_norm = right.squaredNorm(); |
130 | 10 | const index_type max_iterations = base_type::max_iterations(); |
131 | 138 | while (iterations_ < max_iterations) { |
132 | 135 | iterate(coeff_ref, right, solution); |
133 | 135 | ++iterations_; |
134 | 135 | using std::sqrt; |
135 | 135 | residual_rate_ = sqrt(residual_ / right_norm); |
136 | 135 | if (residual_rate_ < base_type::tolerance()) { |
137 | 7 | break; |
138 | 7 | } |
139 | 135 | } |
140 | 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 | 116 | 4 | void solve_vector_in_place(const Right& right, Solution& solution) const { | 117 | 4 | const auto& coeff_ref = coeff(); | 118 | | | 119 | 4 | NUM_COLLECT_PRECONDITION(coeff_ref.rows() == coeff_ref.cols(), | 120 | 4 | "Coefficient matrix must be a square matrix."); | 121 | 4 | NUM_COLLECT_PRECONDITION(right.rows() == coeff_ref.cols(), | 122 | 4 | "Right-hand-side vector must have the number of elements same as " | 123 | 4 | "the size of the coefficient matrix."); | 124 | 4 | NUM_COLLECT_PRECONDITION(solution.rows() == coeff_ref.cols(), | 125 | 4 | "Solution vector must have the number of elements same as the size " | 126 | 4 | "of the coefficient matrix."); | 127 | | | 128 | 4 | iterations_ = 0; | 129 | 4 | const scalar_type right_norm = right.squaredNorm(); | 130 | 4 | const index_type max_iterations = base_type::max_iterations(); | 131 | 80 | while (iterations_ < max_iterations) { | 132 | 79 | iterate(coeff_ref, right, solution); | 133 | 79 | ++iterations_; | 134 | 79 | using std::sqrt; | 135 | 79 | residual_rate_ = sqrt(residual_ / right_norm); | 136 | 79 | if (residual_rate_ < base_type::tolerance()) { | 137 | 3 | break; | 138 | 3 | } | 139 | 79 | } | 140 | 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 | 116 | 3 | void solve_vector_in_place(const Right& right, Solution& solution) const { | 117 | 3 | const auto& coeff_ref = coeff(); | 118 | | | 119 | 3 | NUM_COLLECT_PRECONDITION(coeff_ref.rows() == coeff_ref.cols(), | 120 | 3 | "Coefficient matrix must be a square matrix."); | 121 | 3 | NUM_COLLECT_PRECONDITION(right.rows() == coeff_ref.cols(), | 122 | 3 | "Right-hand-side vector must have the number of elements same as " | 123 | 3 | "the size of the coefficient matrix."); | 124 | 3 | NUM_COLLECT_PRECONDITION(solution.rows() == coeff_ref.cols(), | 125 | 3 | "Solution vector must have the number of elements same as the size " | 126 | 3 | "of the coefficient matrix."); | 127 | | | 128 | 3 | iterations_ = 0; | 129 | 3 | const scalar_type right_norm = right.squaredNorm(); | 130 | 3 | const index_type max_iterations = base_type::max_iterations(); | 131 | 17 | while (iterations_ < max_iterations) { | 132 | 16 | iterate(coeff_ref, right, solution); | 133 | 16 | ++iterations_; | 134 | 16 | using std::sqrt; | 135 | 16 | residual_rate_ = sqrt(residual_ / right_norm); | 136 | 16 | if (residual_rate_ < base_type::tolerance()) { | 137 | 2 | break; | 138 | 2 | } | 139 | 16 | } | 140 | 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 | 116 | 3 | void solve_vector_in_place(const Right& right, Solution& solution) const { | 117 | 3 | const auto& coeff_ref = coeff(); | 118 | | | 119 | 3 | NUM_COLLECT_PRECONDITION(coeff_ref.rows() == coeff_ref.cols(), | 120 | 3 | "Coefficient matrix must be a square matrix."); | 121 | 3 | NUM_COLLECT_PRECONDITION(right.rows() == coeff_ref.cols(), | 122 | 3 | "Right-hand-side vector must have the number of elements same as " | 123 | 3 | "the size of the coefficient matrix."); | 124 | 3 | NUM_COLLECT_PRECONDITION(solution.rows() == coeff_ref.cols(), | 125 | 3 | "Solution vector must have the number of elements same as the size " | 126 | 3 | "of the coefficient matrix."); | 127 | | | 128 | 3 | iterations_ = 0; | 129 | 3 | const scalar_type right_norm = right.squaredNorm(); | 130 | 3 | const index_type max_iterations = base_type::max_iterations(); | 131 | 41 | while (iterations_ < max_iterations) { | 132 | 40 | iterate(coeff_ref, right, solution); | 133 | 40 | ++iterations_; | 134 | 40 | using std::sqrt; | 135 | 40 | residual_rate_ = sqrt(residual_ / right_norm); | 136 | 40 | if (residual_rate_ < base_type::tolerance()) { | 137 | 2 | break; | 138 | 2 | } | 139 | 40 | } | 140 | 3 | } |
|
141 | | |
142 | | /*! |
143 | | * \brief Get the number of iterations. |
144 | | * |
145 | | * \note This value won't be updated in iterate function. |
146 | | * |
147 | | * \return Number of iterations. |
148 | | */ |
149 | 9 | [[nodiscard]] auto iterations() const noexcept -> index_type { |
150 | 9 | return iterations_; |
151 | 9 | } _ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEE10iterationsEv Line | Count | Source | 149 | 3 | [[nodiscard]] auto iterations() const noexcept -> index_type { | 150 | 3 | return iterations_; | 151 | 3 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEE10iterationsEv Line | Count | Source | 149 | 3 | [[nodiscard]] auto iterations() const noexcept -> index_type { | 150 | 3 | return iterations_; | 151 | 3 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEE10iterationsEv Line | Count | Source | 149 | 3 | [[nodiscard]] auto iterations() const noexcept -> index_type { | 150 | 3 | return iterations_; | 151 | 3 | } |
|
152 | | |
153 | | /*! |
154 | | * \brief Get the rate of the last residual. |
155 | | * |
156 | | * \note This value won't be updated in iterate function. |
157 | | * |
158 | | * \return Rate of the last residual. |
159 | | */ |
160 | | [[nodiscard]] auto residual_rate() const noexcept -> scalar_type { |
161 | | return residual_rate_; |
162 | | } |
163 | | |
164 | | private: |
165 | | /*! |
166 | | * \brief Iterate once. |
167 | | * |
168 | | * \tparam Right Type of the right-hand-side vector. |
169 | | * \tparam Solution Type of the solution vector. |
170 | | * \param[in] coeff_ref Coefficient matrix. |
171 | | * \param[in] right Right-hand-side vector. |
172 | | * \param[in,out] solution Solution vector. |
173 | | */ |
174 | | template <base::concepts::dense_vector_of<scalar_type> Right, |
175 | | base::concepts::dense_vector_of<scalar_type> Solution> |
176 | | void iterate(const matrix_type& coeff_ref, const Right& right, |
177 | 135 | Solution& solution) const { |
178 | 135 | const index_type size = coeff_ref.rows(); |
179 | 135 | residual_ = static_cast<scalar_type>(0); |
180 | 1.67k | for (index_type i = 0; i < size; ++i) { |
181 | 1.54k | scalar_type numerator = right(i); |
182 | 10.5k | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; |
183 | 9.01k | ++iter) { |
184 | 9.01k | if (iter.index() != i) { |
185 | 7.46k | numerator -= iter.value() * solution(iter.index()); |
186 | 7.46k | } |
187 | 9.01k | } |
188 | 1.54k | const scalar_type row_residual = numerator - diag_(i) * solution(i); |
189 | 1.54k | solution(i) = numerator * inv_diag_(i); |
190 | 1.54k | residual_ += row_residual * row_residual; |
191 | 1.54k | } |
192 | 135 | } _ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIdLi1EiEEE7iterateITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIdLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKS4_RKT_RT0_ Line | Count | Source | 177 | 79 | Solution& solution) const { | 178 | 79 | const index_type size = coeff_ref.rows(); | 179 | 79 | residual_ = static_cast<scalar_type>(0); | 180 | 1.11k | for (index_type i = 0; i < size; ++i) { | 181 | 1.04k | scalar_type numerator = right(i); | 182 | 7.30k | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; | 183 | 6.26k | ++iter) { | 184 | 6.26k | if (iter.index() != i) { | 185 | 5.22k | numerator -= iter.value() * solution(iter.index()); | 186 | 5.22k | } | 187 | 6.26k | } | 188 | 1.04k | const scalar_type row_residual = numerator - diag_(i) * solution(i); | 189 | 1.04k | solution(i) = numerator * inv_diag_(i); | 190 | 1.04k | residual_ += row_residual * row_residual; | 191 | 1.04k | } | 192 | 79 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIfLi1EiEEE7iterateITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIfLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKS4_RKT_RT0_ Line | Count | Source | 177 | 16 | Solution& solution) const { | 178 | 16 | const index_type size = coeff_ref.rows(); | 179 | 16 | residual_ = static_cast<scalar_type>(0); | 180 | 160 | for (index_type i = 0; i < size; ++i) { | 181 | 144 | scalar_type numerator = right(i); | 182 | 928 | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; | 183 | 784 | ++iter) { | 184 | 784 | if (iter.index() != i) { | 185 | 640 | numerator -= iter.value() * solution(iter.index()); | 186 | 640 | } | 187 | 784 | } | 188 | 144 | const scalar_type row_residual = numerator - diag_(i) * solution(i); | 189 | 144 | solution(i) = numerator * inv_diag_(i); | 190 | 144 | residual_ += row_residual * row_residual; | 191 | 144 | } | 192 | 16 | } |
_ZNK11num_collect6linear29gauss_seidel_iterative_solverIN5Eigen12SparseMatrixIeLi1EiEEE7iterateITkNS_4base8concepts15dense_vector_ofINS0_29gauss_seidel_iterative_solver11scalar_typeEEENS2_6MatrixIeLin1ELi1ELi0ELin1ELi1EEETkNS9_ISB_EESD_EEvRKS4_RKT_RT0_ Line | Count | Source | 177 | 40 | Solution& solution) const { | 178 | 40 | const index_type size = coeff_ref.rows(); | 179 | 40 | residual_ = static_cast<scalar_type>(0); | 180 | 400 | for (index_type i = 0; i < size; ++i) { | 181 | 360 | scalar_type numerator = right(i); | 182 | 2.32k | for (typename matrix_type::InnerIterator iter(coeff_ref, i); iter; | 183 | 1.96k | ++iter) { | 184 | 1.96k | if (iter.index() != i) { | 185 | 1.60k | numerator -= iter.value() * solution(iter.index()); | 186 | 1.60k | } | 187 | 1.96k | } | 188 | 360 | const scalar_type row_residual = numerator - diag_(i) * solution(i); | 189 | 360 | solution(i) = numerator * inv_diag_(i); | 190 | 360 | residual_ += row_residual * row_residual; | 191 | 360 | } | 192 | 40 | } |
|
193 | | |
194 | | //! Number of iterations. |
195 | | mutable index_type iterations_{}; |
196 | | |
197 | | //! Last residual. |
198 | | mutable scalar_type residual_{}; |
199 | | |
200 | | //! Rate of last residual. |
201 | | mutable scalar_type residual_rate_{}; |
202 | | |
203 | | //! Diagonal coefficients. |
204 | | vector_type diag_{}; |
205 | | |
206 | | //! Inverse of diagonal coefficients. |
207 | | vector_type inv_diag_{}; |
208 | | }; |
209 | | |
210 | | } // namespace num_collect::linear |