Coverage Report

Created: 2025-01-24 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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