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