Coverage Report

Created: 2024-10-11 06:23

/builds/MusicScience37Projects/numerical-analysis/numerical-collection-cpp/include/num_collect/linear/gauss_seidel_iterative_solver.h
Line
Count
Source (jump to first uncovered line)
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