Coverage Report

Created: 2024-10-11 06:23

/builds/MusicScience37Projects/numerical-analysis/numerical-collection-cpp/include/num_collect/regularization/full_gen_tikhonov.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2021 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 full_gen_tikhonov class.
19
 */
20
#pragma once
21
22
#include <type_traits>
23
24
#include <Eigen/Core>
25
#include <Eigen/Householder>
26
27
#include "num_collect/base/concepts/dense_matrix.h"
28
#include "num_collect/base/exception.h"
29
#include "num_collect/base/index_type.h"
30
#include "num_collect/logging/log_tag_view.h"
31
#include "num_collect/logging/logging_macros.h"
32
#include "num_collect/regularization/explicit_regularized_solver_base.h"
33
#include "num_collect/regularization/tikhonov.h"
34
#include "num_collect/util/assert.h"
35
36
namespace num_collect::regularization {
37
38
//! Tag of fista.
39
constexpr auto full_gen_tikhonov_tag =
40
    logging::log_tag_view("num_collect::regularization::full_gen_tikhonov");
41
42
/*!
43
 * \brief Class to perform generalized Tikhonov regularization on the condition
44
 * that the matrix in the regularization term which have full row rank
45
 * \cite Elden1982, \cite Hansen1994.
46
 *
47
 * \tparam Coeff Type of coefficient matrices.
48
 * \tparam Data Type of data vectors.
49
 */
50
template <base::concepts::dense_matrix Coeff, base::concepts::dense_matrix Data>
51
class full_gen_tikhonov
52
    : public explicit_regularized_solver_base<full_gen_tikhonov<Coeff, Data>,
53
          Data> {
54
public:
55
    //! Type of base class.
56
    using base_type =
57
        explicit_regularized_solver_base<full_gen_tikhonov<Coeff, Data>, Data>;
58
59
    using typename base_type::data_type;
60
    using typename base_type::scalar_type;
61
62
    //! Type of coefficient matrices.
63
    using coeff_type = Coeff;
64
65
    static_assert(std::is_same_v<typename coeff_type::Scalar,
66
        typename data_type::Scalar>);
67
    static_assert(data_type::RowsAtCompileTime == Eigen::Dynamic);
68
69
    /*!
70
     * \brief Constructor.
71
     */
72
5
    full_gen_tikhonov() : base_type(full_gen_tikhonov_tag) {}
73
74
    /*!
75
     * \brief Compute internal matrices.
76
     *
77
     * This generate arranged problem of Tikhonov regularization as in
78
     * \cite Elden1982, \cite Hansen1994.
79
     *
80
     * \param[in] coeff Coefficient matrix.
81
     * \param[in] data Data vector.
82
     * \param[in] reg_coeff Coefficient matrix for the regularization term.
83
     */
84
    void compute(const coeff_type& coeff, const data_type& data,
85
5
        const coeff_type& reg_coeff) {
86
5
        if (coeff.rows() != data.rows()) {
87
0
            NUM_COLLECT_LOG_AND_THROW(invalid_argument,
88
0
                "The number of rows in the coefficient matrix must match the "
89
0
                "number of rows in data.");
90
0
        }
91
5
        if (coeff.cols() != reg_coeff.cols()) {
92
0
            NUM_COLLECT_LOG_AND_THROW(invalid_argument,
93
0
                "The number of columns in the coefficient matrix must match "
94
0
                "the number of columns in the coefficient matrix of the "
95
0
                "regularization term.");
96
0
        }
97
5
        if (reg_coeff.rows() >= reg_coeff.cols()) {
98
0
            NUM_COLLECT_LOG_AND_THROW(invalid_argument,
99
0
                "Coefficient matrix for the regularization term must have rows "
100
0
                "less than columns.");
101
0
        }
102
103
        // How can I implement those complex formulas with good variable names.
104
105
5
        const index_type m = coeff.rows();
106
5
        const index_type n = coeff.cols();
107
5
        const index_type p = reg_coeff.rows();
108
109
5
        Eigen::ColPivHouseholderQR<coeff_type> qr_reg_adj;
110
5
        qr_reg_adj.compute(reg_coeff.adjoint());
111
5
        if (qr_reg_adj.rank() < qr_reg_adj.cols()) {
112
1
            NUM_COLLECT_LOG_AND_THROW(precondition_not_satisfied,
113
1
                "reg_coeff must have full row rank.");
114
1
        }
115
5
        const coeff_type v = qr_reg_adj.householderQ();
116
117
5
        Eigen::ColPivHouseholderQR<coeff_type> qr_coeff_v2;
118
5
        qr_coeff_v2.compute(coeff * v.rightCols(n - p));
119
5
        if (qr_coeff_v2.rank() < qr_coeff_v2.cols()) {
120
1
            NUM_COLLECT_LOG_AND_THROW(precondition_not_satisfied,
121
1
                "reg_coeff and coeff must not have common elements "
122
1
                "other than zero in their kernel.");
123
1
        }
124
5
        const coeff_type q = qr_coeff_v2.householderQ();
125
126
5
        const coeff_type coeff_arr =
127
5
            qr_reg_adj.solve(coeff.adjoint() * q.rightCols(m - n + p))
128
5
                .adjoint();
129
5
        const data_type data_arr = q.rightCols(m - n + p).adjoint() * data;
130
5
        tikhonov_.compute(coeff_arr, data_arr);
131
132
5
        const coeff_type coeff_v2_inv_coeff = qr_coeff_v2.solve(coeff);
133
5
        const coeff_type i_minus_v2_coeff_v2_inv_coeff =
134
5
            coeff_type::Identity(n, n) -
135
5
            v.rightCols(n - p) * coeff_v2_inv_coeff;
136
5
        coeff_actual_solution_ =
137
5
            qr_reg_adj.solve(i_minus_v2_coeff_v2_inv_coeff.adjoint()).adjoint();
138
139
5
        const data_type coeff_v2_inv_data = qr_coeff_v2.solve(data);
140
5
        offset_actual_solution_ = v.rightCols(n - p) * coeff_v2_inv_data;
141
5
    }
142
143
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::solve
144
3
    void solve(const scalar_type& param, data_type& solution) const {
145
3
        data_type tikhonov_solution;
146
3
        tikhonov_.solve(param, tikhonov_solution);
147
3
        solution = coeff_actual_solution_ * tikhonov_solution +
148
3
            offset_actual_solution_;
149
3
    }
150
151
    /*!
152
     * \brief Get the singular values.
153
     *
154
     * \return Singular values.
155
     */
156
    [[nodiscard]] auto singular_values() const -> const
157
1
        typename Eigen::BDCSVD<coeff_type>::SingularValuesType& {
158
1
        return tikhonov_.singular_values();
159
1
    }
160
161
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::residual_norm
162
    [[nodiscard]] auto residual_norm(
163
1
        const scalar_type& param) const -> scalar_type {
164
1
        return tikhonov_.residual_norm(param);
165
1
    }
_ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE13residual_normERKd
Line
Count
Source
163
1
        const scalar_type& param) const -> scalar_type {
164
1
        return tikhonov_.residual_norm(param);
165
1
    }
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES4_E13residual_normERKd
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixINSt3__17complexIdEELin1ELin1ELi0ELin1ELin1EEENS3_IS6_Lin1ELi1ELi0ELin1ELi1EEEE13residual_normERKd
166
167
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::regularization_term
168
    [[nodiscard]] auto regularization_term(
169
1
        const scalar_type& param) const -> scalar_type {
170
1
        return tikhonov_.regularization_term(param);
171
1
    }
_ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE19regularization_termERKd
Line
Count
Source
169
1
        const scalar_type& param) const -> scalar_type {
170
1
        return tikhonov_.regularization_term(param);
171
1
    }
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES4_E19regularization_termERKd
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixINSt3__17complexIdEELin1ELin1ELi0ELin1ELin1EEENS3_IS6_Lin1ELi1ELi0ELin1ELi1EEEE19regularization_termERKd
172
173
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::first_derivative_of_residual_norm
174
    [[nodiscard]] auto first_derivative_of_residual_norm(
175
1
        const scalar_type& param) const -> scalar_type {
176
1
        return tikhonov_.first_derivative_of_residual_norm(param);
177
1
    }
_ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE33first_derivative_of_residual_normERKd
Line
Count
Source
175
1
        const scalar_type& param) const -> scalar_type {
176
1
        return tikhonov_.first_derivative_of_residual_norm(param);
177
1
    }
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES4_E33first_derivative_of_residual_normERKd
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixINSt3__17complexIdEELin1ELin1ELi0ELin1ELin1EEENS3_IS6_Lin1ELi1ELi0ELin1ELi1EEEE33first_derivative_of_residual_normERKd
178
179
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::first_derivative_of_regularization_term
180
    [[nodiscard]] auto first_derivative_of_regularization_term(
181
1
        const scalar_type& param) const -> scalar_type {
182
1
        return tikhonov_.first_derivative_of_regularization_term(param);
183
1
    }
_ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE39first_derivative_of_regularization_termERKd
Line
Count
Source
181
1
        const scalar_type& param) const -> scalar_type {
182
1
        return tikhonov_.first_derivative_of_regularization_term(param);
183
1
    }
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES4_E39first_derivative_of_regularization_termERKd
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixINSt3__17complexIdEELin1ELin1ELi0ELin1ELin1EEENS3_IS6_Lin1ELi1ELi0ELin1ELi1EEEE39first_derivative_of_regularization_termERKd
184
185
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::second_derivative_of_residual_norm
186
    [[nodiscard]] auto second_derivative_of_residual_norm(
187
1
        const scalar_type& param) const -> scalar_type {
188
1
        return tikhonov_.second_derivative_of_residual_norm(param);
189
1
    }
_ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE34second_derivative_of_residual_normERKd
Line
Count
Source
187
1
        const scalar_type& param) const -> scalar_type {
188
1
        return tikhonov_.second_derivative_of_residual_norm(param);
189
1
    }
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES4_E34second_derivative_of_residual_normERKd
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixINSt3__17complexIdEELin1ELin1ELi0ELin1ELin1EEENS3_IS6_Lin1ELi1ELi0ELin1ELi1EEEE34second_derivative_of_residual_normERKd
190
191
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::second_derivative_of_regularization_term
192
    [[nodiscard]] auto second_derivative_of_regularization_term(
193
1
        const scalar_type& param) const -> scalar_type {
194
1
        return tikhonov_.second_derivative_of_regularization_term(param);
195
1
    }
_ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE40second_derivative_of_regularization_termERKd
Line
Count
Source
193
1
        const scalar_type& param) const -> scalar_type {
194
1
        return tikhonov_.second_derivative_of_regularization_term(param);
195
1
    }
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEES4_E40second_derivative_of_regularization_termERKd
Unexecuted instantiation: _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixINSt3__17complexIdEELin1ELin1ELi0ELin1ELin1EEENS3_IS6_Lin1ELi1ELi0ELin1ELi1EEEE40second_derivative_of_regularization_termERKd
196
197
    //! \copydoc num_collect::regularization::explicit_regularized_solver_base::sum_of_filter_factor
198
    [[nodiscard]] auto sum_of_filter_factor(
199
1
        const scalar_type& param) const -> scalar_type {
200
1
        return tikhonov_.sum_of_filter_factor(param);
201
1
    }
202
203
    //! \copydoc num_collect::regularization::regularized_solver_base::data_size
204
1
    [[nodiscard]] auto data_size() const -> index_type {
205
1
        return tikhonov_.data_size();
206
1
    }
207
208
    //! \copydoc num_collect::regularization::regularized_solver_base::param_search_region
209
    [[nodiscard]] auto param_search_region() const
210
1
        -> std::pair<scalar_type, scalar_type> {
211
1
        return tikhonov_.param_search_region();
212
1
    }
213
214
    /*!
215
     * \brief Access to the internal solver (for debug).
216
     *
217
     * \return Internal solver.
218
     */
219
    [[nodiscard]] auto internal_solver() const
220
10
        -> const tikhonov<coeff_type, data_type>& {
221
10
        return tikhonov_;
222
10
    }
223
224
private:
225
    //! Object to perform Tikhonov regularization.
226
    tikhonov<coeff_type, data_type> tikhonov_{};
227
228
    //! Coefficient matrix to calculate actual solution.
229
    Coeff coeff_actual_solution_{};
230
231
    //! Offset vector to calculate actual solution.
232
    Data offset_actual_solution_{};
233
};
234
235
}  // namespace num_collect::regularization