Loading...
Searching...
No Matches
mtbRBF.C
1/*---------------------------------------------------------------------------*\
2 ██╗████████╗██╗ ██╗ █████╗ ██████╗ █████╗ ███████╗██╗ ██╗
3 ██║╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔══██╗ ██╔════╝██║ ██║
4 ██║ ██║ ███████║███████║██║ ███████║█████╗█████╗ ██║ ██║
5 ██║ ██║ ██╔══██║██╔══██║██║ ██╔══██║╚════╝██╔══╝ ╚██╗ ██╔╝
6 ██║ ██║ ██║ ██║██║ ██║╚██████╗██║ ██║ ██║ ╚████╔╝
7 ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═══╝
8
9 * In real Time Highly Advanced Computational Applications for Finite Volumes
10 * Copyright (C) 2017 by the ITHACA-FV authors
11-------------------------------------------------------------------------------
12
13License
14 This file is part of ITHACA-FV
15
16 ITHACA-FV is free software: you can redistribute it and/or modify
17 it under the terms of the GNU Lesser General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 ITHACA-FV is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU Lesser General Public License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with ITHACA-FV. If not, see <http://www.gnu.org/licenses/>.
28
29\*---------------------------------------------------------------------------*/
30
31#include "mtbRBF.H"
32
33namespace {
34 struct QuinticRbfKernel {
35 double operator()(double r) const {
36 return std::pow(r, 5);
37 }
38 };
39
40 struct MultiquadricRbfKernel {
41 double epsilon;
42 MultiquadricRbfKernel(double eps = 1.0) : epsilon(eps) {}
43 double operator()(double r) const {
44 return std::sqrt(1.0 + (epsilon * r) * (epsilon * r));
45 }
46 };
47
48 struct InverseMultiquadricRbfKernel {
49 double epsilon;
50 InverseMultiquadricRbfKernel(double eps = 1.0) : epsilon(eps) {}
51 double operator()(double r) const {
52 return 1.0 / std::sqrt(1.0 + (epsilon * r) * (epsilon * r));
53 }
54 };
55
56 struct InverseQuadraticRbfKernel {
57 double epsilon;
58 InverseQuadraticRbfKernel(double eps = 1.0) : epsilon(eps) {}
59 double operator()(double r) const {
60 return 1.0 / (1.0 + (epsilon * r) * (epsilon * r));
61 }
62 };
63
64 std::function<double(const double)> getKernelFunction(const std::string& kernelType, double epsilon)
65 {
66 if (kernelType == "gaussian")
67 {
68 return mathtoolbox::GaussianRbfKernel(epsilon);
69 }
70 else if (kernelType == "linear")
71 {
72 return mathtoolbox::LinearRbfKernel();
73 }
74 else if (kernelType == "cubic")
75 {
76 return mathtoolbox::CubicRbfKernel();
77 }
78 else if (kernelType == "quintic")
79 {
80 return QuinticRbfKernel();
81 }
82 else if (kernelType == "multiquadric")
83 {
84 return MultiquadricRbfKernel(epsilon);
85 }
86 else if (kernelType == "inverse_multiquadric")
87 {
88 return InverseMultiquadricRbfKernel(epsilon);
89 }
90 else if (kernelType == "inverse_quadratic")
91 {
92 return InverseQuadraticRbfKernel(epsilon);
93 }
94 else if (kernelType == "thin_plate_spline")
95 {
96 return mathtoolbox::ThinPlateSplineRbfKernel();
97 }
98 else
99 {
100 FatalErrorInFunction
101 << "Unknown kernel type: " << kernelType
102 << ". Valid options are: gaussian, linear, cubic, quintic, multiquadric, "
103 << "inverse_multiquadric, inverse_quadratic, thin_plate_spline"
104 << Foam::exit(Foam::FatalError);
105 return mathtoolbox::GaussianRbfKernel(1.0); // Unreachable, but suppresses warning
106 }
107 }
108}
109
110mtbRBF::mtbRBF(const Foam::word& kernelType, bool usePolynomialTerm, bool useRegularization, Foam::scalar lambda, bool normalize)
111: kernelType_(kernelType),
112 useRegularization_(useRegularization),
113 usePolynomialTerm_(usePolynomialTerm),
114 lambda_(lambda),
115 normalize_(normalize)
116{
117 auto kernel = getKernelFunction(kernelType_, 1.0);
118 impl_ = std::make_unique<mathtoolbox::RbfInterpolator>(kernel, usePolynomialTerm_);
119}
120
121mtbRBF::mtbRBF(const Foam::dictionary& dict)
122{
123 kernelType_ = dict.lookupOrDefault<Foam::word>("kernel", "gaussian");
124 usePolynomialTerm_ = dict.lookupOrDefault<bool>("polynomial", true);
125 useRegularization_ = dict.lookupOrDefault<bool>("regularization", true);
126 lambda_ = dict.lookupOrDefault<Foam::scalar>("lambda", 0.001);
127 normalize_ = dict.lookupOrDefault<bool>("normalize", true);
128 epsilon_ = dict.lookupOrDefault<Foam::scalar>("epsilon", 1.0);
129
130 auto kernel = getKernelFunction(kernelType_, epsilon_);
131 impl_ = std::make_unique<mathtoolbox::RbfInterpolator>(kernel, usePolynomialTerm_);
132}
133
134mtbRBF::~mtbRBF() = default;
135
136void mtbRBF::fit(const Eigen::MatrixXd& X, const Eigen::VectorXd& y)
137{
138 if (normalize_)
139 {
140 // Input normalization
141 xMean_ = X.rowwise().mean();
142 if (X.cols() > 1)
143 {
144 Eigen::MatrixXd centered = X.colwise() - xMean_;
145 xStd_ = (centered.array().square().rowwise().sum() / (X.cols() - 1)).sqrt();
146 }
147 else
148 {
149 xStd_ = Eigen::VectorXd::Ones(X.rows());
150 }
151
152 for(int i=0; i<xStd_.size(); ++i) {
153 if(xStd_(i) < 1e-16) xStd_(i) = 1.0;
154 }
155
156 Eigen::MatrixXd X_norm = (X.colwise() - xMean_).array().colwise() / xStd_.array();
157
158 // Output normalization
159 yMean_ = y.mean();
160 if (y.size() > 1)
161 {
162 Eigen::VectorXd yCentered = y.array() - yMean_;
163 yStd_ = std::sqrt(yCentered.array().square().sum() / (y.size() - 1));
164 }
165 else
166 {
167 yStd_ = 1.0;
168 }
169
170 if(yStd_ < 1e-16) yStd_ = 1.0;
171
172 Eigen::VectorXd y_norm = (y.array() - yMean_) / yStd_;
173
174 impl_->SetData(X_norm, y_norm);
175 }
176 else
177 {
178 impl_->SetData(X, y);
179 }
180 impl_->CalcWeights(useRegularization_, lambda_);
181}
182
183Foam::scalar mtbRBF::predict(const Eigen::VectorXd& x)
184{
185 if (normalize_)
186 {
187 Eigen::VectorXd x_norm = (x - xMean_).array() / xStd_.array();
188 Foam::scalar y_pred_norm = impl_->CalcValue(x_norm);
189 return y_pred_norm * yStd_ + yMean_;
190 }
191 return impl_->CalcValue(x);
192}
193
194Eigen::VectorXd mtbRBF::predict(const Eigen::MatrixXd& X)
195{
196 if (normalize_)
197 {
198 Eigen::MatrixXd X_norm = (X.colwise() - xMean_).array().colwise() / xStd_.array();
199
200 Eigen::VectorXd result(X.cols());
201 for (int i = 0; i < X.cols(); ++i)
202 {
203 result(i) = impl_->CalcValue(X_norm.col(i));
204 }
205 return result.array() * yStd_ + yMean_;
206 }
207
208 Eigen::VectorXd result(X.cols());
209 for (int i = 0; i < X.cols(); ++i)
210 {
211 result(i) = impl_->CalcValue(X.col(i));
212 }
213 return result;
214}
215
216void mtbRBF::printInfo()
217{
218 Foam::Info << "mtbRBF Model Info:" << Foam::endl;
219 Foam::Info << "\t useKernel: " << kernelType_ << Foam::endl;
220 Foam::Info << "\t usePolynomialTerm: " << (usePolynomialTerm_ ? "true" : "false") << Foam::endl;
221 Foam::Info << "\t useRegularization: " << (useRegularization_ ? "true" : "false") << Foam::endl;
222 Foam::Info << "\t lambda: " << lambda_ << Foam::endl;
223 Foam::Info << "\t normalize: " << (normalize_ ? "true" : "false") << Foam::endl;
224}