Loading...
Searching...
No Matches
splinterRBF.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 "splinterRBF.H"
32
33splinterRBF::splinterRBF(const Foam::word& kernelType, bool normalize, Foam::scalar epsilon)
34: kernelType_(kernelType),
35 normalize_(normalize),
36 epsilon_(epsilon)
37{
38}
39
40splinterRBF::splinterRBF(const Foam::dictionary& dict)
41{
42 kernelType_ = dict.lookupOrDefault<Foam::word>("kernel", "gaussian");
43 normalize_ = dict.lookupOrDefault<bool>("normalize", true);
44 epsilon_ = dict.lookupOrDefault<Foam::scalar>("epsilon", 1.0);
45}
46
47splinterRBF::~splinterRBF() = default;
48
49SPLINTER::RadialBasisFunctionType splinterRBF::getKernelType(const Foam::word& kernelType)
50{
51 if (kernelType == "gaussian")
52 {
53 return SPLINTER::RadialBasisFunctionType::GAUSSIAN;
54 }
55 else if (kernelType == "linear")
56 {
57 return SPLINTER::RadialBasisFunctionType::LINEAR;
58 }
59 else if (kernelType == "cubic")
60 {
61 return SPLINTER::RadialBasisFunctionType::CUBIC;
62 }
63 else if (kernelType == "quintic")
64 {
65 return SPLINTER::RadialBasisFunctionType::QUINTIC;
66 }
67 else if (kernelType == "multiquadric")
68 {
69 return SPLINTER::RadialBasisFunctionType::MULTIQUADRIC;
70 }
71 else if (kernelType == "inverse_multiquadric")
72 {
73 return SPLINTER::RadialBasisFunctionType::INVERSE_MULTIQUADRIC;
74 }
75 else if (kernelType == "inverse_quadratic")
76 {
77 return SPLINTER::RadialBasisFunctionType::INVERSE_QUADRIC;
78 }
79 else if (kernelType == "thin_plate_spline")
80 {
81 return SPLINTER::RadialBasisFunctionType::THIN_PLATE_SPLINE;
82 }
83 else
84 {
85 FatalErrorInFunction
86 << "Unknown kernel type: " << kernelType
87 << ". Valid options are: gaussian, linear, cubic, quintic, multiquadric, "
88 << "inverse_multiquadric, inverse_quadratic, thin_plate_spline"
89 << Foam::exit(Foam::FatalError);
90 return SPLINTER::RadialBasisFunctionType::GAUSSIAN; // Unreachable, but suppresses warning
91 }
92}
93
94void splinterRBF::fit(const Eigen::MatrixXd& X, const Eigen::VectorXd& y)
95{
96 SPLINTER::DataTable data;
97
98 if (normalize_)
99 {
100 // Input normalization
101 xMean_ = X.rowwise().mean();
102 if (X.cols() > 1)
103 {
104 Eigen::MatrixXd centered = X.colwise() - xMean_;
105 xStd_ = (centered.array().square().rowwise().sum() / (X.cols() - 1)).sqrt();
106 }
107 else
108 {
109 xStd_ = Eigen::VectorXd::Ones(X.rows());
110 }
111
112 for(int i=0; i<xStd_.size(); ++i) {
113 if(xStd_(i) < 1e-16) xStd_(i) = 1.0;
114 }
115
116 Eigen::MatrixXd X_norm = (X.colwise() - xMean_).array().colwise() / xStd_.array();
117
118 // Output normalization
119 yMean_ = y.mean();
120 if (y.size() > 1)
121 {
122 Eigen::VectorXd yCentered = y.array() - yMean_;
123 yStd_ = std::sqrt(yCentered.array().square().sum() / (y.size() - 1));
124 }
125 else
126 {
127 yStd_ = 1.0;
128 }
129
130 if(yStd_ < 1e-16) yStd_ = 1.0;
131
132 Eigen::VectorXd y_norm = (y.array() - yMean_) / yStd_;
133
134 for (int i = 0; i < X_norm.cols(); ++i)
135 {
136 data.addSample(X_norm.col(i), y_norm(i));
137 }
138 }
139 else
140 {
141 for (int i = 0; i < X.cols(); ++i)
142 {
143 data.addSample(X.col(i), y(i));
144 }
145 }
146
147 impl_ = std::make_unique<SPLINTER::RBFSpline>(data, getKernelType(kernelType_), epsilon_);
148}
149
150Foam::scalar splinterRBF::predict(const Eigen::VectorXd& x)
151{
152 if (normalize_)
153 {
154 Eigen::VectorXd x_norm = (x - xMean_).array() / xStd_.array();
155 Foam::scalar y_pred_norm = impl_->eval(x_norm);
156 return y_pred_norm * yStd_ + yMean_;
157 }
158 return impl_->eval(x);
159}
160
161Eigen::VectorXd splinterRBF::predict(const Eigen::MatrixXd& X)
162{
163 Eigen::VectorXd result(X.cols());
164
165 if (normalize_)
166 {
167 Eigen::MatrixXd X_norm = (X.colwise() - xMean_).array().colwise() / xStd_.array();
168
169 for (int i = 0; i < X.cols(); ++i)
170 {
171 result(i) = impl_->eval(X_norm.col(i));
172 }
173 return result.array() * yStd_ + yMean_;
174 }
175
176 for (int i = 0; i < X.cols(); ++i)
177 {
178 result(i) = impl_->eval(X.col(i));
179 }
180 return result;
181}
182
183void splinterRBF::printInfo()
184{
185 Foam::Info << "splinterRBF Model Info:" << Foam::endl;
186 Foam::Info << "\t Kernel Type: " << kernelType_ << Foam::endl;
187 Foam::Info << "\t Normalize: " << (normalize_ ? "true" : "false") << Foam::endl;
188 Foam::Info << "\t Epsilon: " << epsilon_ << Foam::endl;
189}