0.5.1
Loading...
Searching...
No Matches
PolynomialRegressor.hpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9/// @file PolynomialRegressor.hpp
10/// @brief Polynomial curve fitting
11/// @author T. Topp (topp@ins.uni-stuttgart.de)
12/// @date 2023-10-23
13
14#pragma once
15
16#include <cstddef>
17#include <optional>
18#include <utility>
19
20#include <nlohmann/json.hpp>
21using json = nlohmann::json; ///< json namespace
22
23#include "util/Assert.h"
25
26#include "Polynomial.hpp"
32
33namespace NAV
34{
35
36/// @brief Polynomial Curve Fitting
37/// @tparam Scalar Data type to store
38template<typename Scalar = double>
40{
41 public:
42 /// Possible Fit strategies
43 enum class Strategy : uint8_t
44 {
45 IncrementalLeastSquares, ///< Incremental Least Squares (only polynomials of order <= 2)
46 LeastSquares, ///< Least Squares (bas if even mildly ill-conditioned)
47 HouseholderQR, ///< Householder QR decomposition
48 BDCSVD, ///< Bidiagonal Divide and Conquer SVD
49 COD, ///< Complete Orthogonal Decomposition
50 COUNT, ///< Amount of items in the enum
51 };
52
53 /// @brief Constructor
54 /// @param[in] polynomialDegree Degree of the polynomial to fit
55 /// @param[in] windowSize Amount of points to use for the fit (sliding window)
56 /// @param[in] strategy Strategy to use
57 PolynomialRegressor(size_t polynomialDegree, size_t windowSize, Strategy strategy = Strategy::HouseholderQR)
58 : _strategy(strategy), _polyDegree(polynomialDegree), _windowSize(windowSize), _incrementalLSQ(polynomialDegree)
59 {
60 setWindowSize(windowSize);
61 setPolynomialDegree(polynomialDegree);
62 }
63
64 /// @brief Sets the amount of points used for the fit (sliding window)
65 /// @param[in] windowSize Amount of points to use for the fit
66 void setWindowSize(size_t windowSize)
67 {
68 INS_ASSERT_USER_ERROR(windowSize > _polyDegree, "The window size needs to be greater than the polynomial degree.");
69
70 while (windowSize < _windowSize)
71 {
72 pop_front();
74 }
75
76 _windowSize = windowSize;
77 _data.resize(windowSize);
78 }
79
80 /// @brief Set the Polynomial Degree and resets the data
81 /// @param[in] polynomialDegree Degree of the polynomial to fit
82 void setPolynomialDegree(size_t polynomialDegree)
83 {
84 INS_ASSERT_USER_ERROR(polynomialDegree < _windowSize, "The polynomial degree needs to be smaller than the window size.");
85 _polyDegree = polynomialDegree;
86
87 _incrementalLSQ.setPolynomialDegree(polynomialDegree);
88
89 reset();
90 }
91
92 /// @brief Set the strategy for the fit and resets the data
93 /// @param strategy Strategy to use for fitting data
94 void setStrategy(Strategy strategy)
95 {
96 INS_ASSERT_USER_ERROR(strategy != Strategy::COUNT, "You cannot call this function with COUNT strategy");
97
98 _strategy = strategy;
99 reset();
100 }
101
102 /// @brief Add a data point to the polynomial
103 /// @param[in] dataPoint Data point
104 void push_back(const std::pair<Scalar, Scalar>& dataPoint)
105 {
106 push_back(dataPoint.first, dataPoint.second);
107 }
108
109 /// @brief Add a data point to the polynomial
110 /// @param[in] x X Value
111 /// @param[in] y Y Value
112 void push_back(const Scalar& x, const Scalar& y)
113 {
114 if (!_data.empty() && _data.back().first == x)
115 {
117 {
118 _incrementalLSQ.removeDataPoint(_data.back().first, _data.back().second);
119 _incrementalLSQ.addDataPoint(x, y);
120 }
121 _data.back().second = y;
122
123 return;
124 }
125
126 if (_data.full()) { pop_front(); }
127
129 {
130 _incrementalLSQ.addDataPoint(x, y);
131 }
132
133 _data.push_back(std::make_pair(x, y));
134 }
135
136 /// @brief Reset the polynomial coefficients and saved data
137 void reset()
138 {
139 _incrementalLSQ.reset();
140 _data.clear();
141 }
142
143 /// @brief Calculates the polynomial
144 [[nodiscard]] std::optional<Polynomial<Scalar>> calcPolynomial() const
145 {
146 if (_data.size() <= _polyDegree) { return std::nullopt; }
147
148 auto prepareDataVectors = [&]() {
149 auto n = static_cast<int>(_data.size());
150 Eigen::VectorX<Scalar> x = Eigen::VectorX<Scalar>(n);
151 Eigen::VectorX<Scalar> y = Eigen::VectorX<Scalar>(n);
152
153 for (size_t i = 0; i < _data.size(); i++)
154 {
155 x(static_cast<int>(i)) = _data.at(i).first;
156 y(static_cast<int>(i)) = _data.at(i).second;
157 }
158
159 return std::make_pair(x, y);
160 };
161
162 switch (_strategy)
163 {
165 return { _incrementalLSQ.calcCoefficients() };
167 {
168 auto [x, y] = prepareDataVectors();
170 }
172 {
173 auto [x, y] = prepareDataVectors();
175 }
176 case Strategy::BDCSVD:
177 {
178 auto [x, y] = prepareDataVectors();
180 }
181 case Strategy::COD:
182 {
183 auto [x, y] = prepareDataVectors();
185 }
186 case Strategy::COUNT:
187 break;
188 }
189 return { Eigen::VectorX<Scalar>() };
190 }
191
192 /// @brief Checks if the amount of data points equals the window size
193 [[nodiscard]] bool windowSizeReached() const
194 {
195 return _data.size() == _windowSize;
196 }
197
198 /// @brief Checks if the container has no elements
199 [[nodiscard]] bool empty() const { return _data.empty(); }
200
201 /// @brief Gets the underlying buffer
202 [[nodiscard]] const ScrollingBuffer<std::pair<Scalar, Scalar>>& data() const { return _data; }
203
204 private:
205 /// Strategy to use to fit the polynomial
207 /// Polynomial degree to fit
208 size_t _polyDegree = 2;
209 /// Amount of points to store
210 size_t _windowSize = 10;
211 /// Values added to the fit
213 /// Incremental LSQ Regressor
215
216 /// @brief Removes the first data point from the polynomial fit (sliding window)
218 {
219 if (_data.empty()) { return; }
220
221 auto [x, y] = _data.front();
222 _data.pop_front();
223
225 {
226 _incrementalLSQ.removeDataPoint(x, y);
227 }
228 }
229};
230
231/// @brief Converts the enum to a string
232/// @param[in] strategy Enum value to convert into text
233/// @return String representation of the enum
234const char* to_string(PolynomialRegressor<>::Strategy strategy);
235
236/// @brief Converts the provided object into json
237/// @param[out] j Json object which gets filled with the info
238/// @param[in] obj Object to convert into json
239template<typename Scalar = double>
241{
242 j = json{
243 { "strategy", obj._strategy },
244 { "polyDegree", obj._polyDegree },
245 { "windowSize", obj._windowSize },
246 };
247}
248
249/// @brief Converts the provided json object into a node object
250/// @param[in] j Json object with the needed values
251/// @param[out] obj Object to fill from the json
252template<typename Scalar = double>
254{
255 if (j.contains("strategy"))
256 {
257 j.at("strategy").get_to(obj._strategy);
258 }
259 if (j.contains("polyDegree"))
260 {
261 j.at("polyDegree").get_to(obj._polyDegree);
262 obj._incrementalLSQ.setPolynomialDegree(obj._polyDegree);
263 }
264 if (j.contains("windowSize"))
265 {
266 j.at("windowSize").get_to(obj._windowSize);
267 }
268}
269
270} // namespace NAV
Assertion helpers.
#define INS_ASSERT_USER_ERROR(_EXP, _MSG)
Assert function with message.
Definition Assert.h:21
Bidiagonal Divide and Conquer SVD Curve Fit.
Complete Orthogonal Decomposition Curve Fit.
nlohmann::json json
json namespace
Least Squares Curve Fit.
Incremental Least Squares Curve Fit.
Polynomial.
A buffer which is overwriting itself from the start when full.
static Eigen::VectorX< Scalar > calcCoefficients(const Eigen::MatrixBase< DerivedX > &x, const Eigen::MatrixBase< DerivedY > &y, size_t polynomialDegree=2)
Calculates the polynomial coefficients in order a0 + a1 * x + a2 * x^2 + ...
Definition BDCSVD.hpp:40
static Eigen::VectorX< Scalar > calcCoefficients(const Eigen::MatrixBase< DerivedX > &x, const Eigen::MatrixBase< DerivedY > &y, size_t polynomialDegree=2)
Calculates the polynomial coefficients in order a0 + a1 * x + a2 * x^2 + ...
Definition COD.hpp:40
static Eigen::VectorX< Scalar > calcCoefficients(const Eigen::MatrixBase< DerivedX > &x, const Eigen::MatrixBase< DerivedY > &y, size_t polynomialDegree=2)
Calculates the polynomial coefficients in order a0 + a1 * x + a2 * x^2 + ...
Incremental Least Squares Curve Fitting.
static Eigen::VectorX< Scalar > calcCoefficients(const Eigen::MatrixBase< DerivedX > &x, const Eigen::MatrixBase< DerivedY > &y, size_t polynomialDegree=2)
Calculates the polynomial coefficients in order a0 + a1 * x + a2 * x^2 + ...
Polynomial Curve Fitting.
IncrementalLeastSquares< double > _incrementalLSQ
PolynomialRegressor(size_t polynomialDegree, size_t windowSize, Strategy strategy=Strategy::HouseholderQR)
Constructor.
std::optional< Polynomial< Scalar > > calcPolynomial() const
Calculates the polynomial.
const ScrollingBuffer< std::pair< Scalar, Scalar > > & data() const
Gets the underlying buffer.
bool windowSizeReached() const
Checks if the amount of data points equals the window size.
bool empty() const
Checks if the container has no elements.
void push_back(const Scalar &x, const Scalar &y)
Add a data point to the polynomial.
void setStrategy(Strategy strategy)
Set the strategy for the fit and resets the data.
Strategy
Possible Fit strategies.
@ BDCSVD
Bidiagonal Divide and Conquer SVD.
@ IncrementalLeastSquares
Incremental Least Squares (only polynomials of order <= 2)
@ COUNT
Amount of items in the enum.
@ HouseholderQR
Householder QR decomposition.
@ COD
Complete Orthogonal Decomposition.
@ LeastSquares
Least Squares (bas if even mildly ill-conditioned)
void setWindowSize(size_t windowSize)
Sets the amount of points used for the fit (sliding window)
void setPolynomialDegree(size_t polynomialDegree)
Set the Polynomial Degree and resets the data.
void reset()
Reset the polynomial coefficients and saved data.
void push_back(const std::pair< Scalar, Scalar > &dataPoint)
Add a data point to the polynomial.
void pop_front()
Removes the first data point from the polynomial fit (sliding window)
ScrollingBuffer< std::pair< double, double > > _data
A buffer which is overwriting itself from the start when full.
Least Squares Curve Fit.
void to_json(json &j, const Node &node)
Converts the provided node into a json object.
Definition Node.cpp:1060
const char * to_string(gui::widgets::PositionWithFrame::ReferenceFrame refFrame)
Converts the enum to a string.
void from_json(const json &j, Node &node)
Converts the provided json object into a node object.
Definition Node.cpp:1077