/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/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(const scalar_type& param) const |
163 | 1 | -> scalar_type { |
164 | 1 | return tikhonov_.residual_norm(param); |
165 | 1 | } _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE13residual_normERKd Line | Count | Source | 163 | 1 | -> 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(const scalar_type& param) const |
169 | 1 | -> scalar_type { |
170 | 1 | return tikhonov_.regularization_term(param); |
171 | 1 | } _ZNK11num_collect14regularization17full_gen_tikhonovIN5Eigen6MatrixIdLin1ELin1ELi0ELin1ELin1EEENS3_IdLin1ELi1ELi0ELin1ELi1EEEE19regularization_termERKd Line | Count | Source | 169 | 1 | -> 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(const scalar_type& param) const |
199 | 1 | -> 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 |