summaryrefslogtreecommitdiff
path: root/fits.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'fits.hpp')
-rw-r--r--fits.hpp139
1 files changed, 139 insertions, 0 deletions
diff --git a/fits.hpp b/fits.hpp
new file mode 100644
index 0000000..c6e56fa
--- /dev/null
+++ b/fits.hpp
@@ -0,0 +1,139 @@
+#include "pcg-cpp/include/pcg_random.hpp"
+#include "randutils/randutils.hpp"
+
+#include "eigen/Eigen/Dense"
+
+using Rng = randutils::random_generator<pcg32>;
+
+using Real = double;
+using Vector = Eigen::Matrix<Real, Eigen::Dynamic, 1>;
+using Matrix = Eigen::Matrix<Real, Eigen::Dynamic, Eigen::Dynamic>;
+using Data = std::vector<std::tuple<Real, Real>>;
+
+#ifndef FITS_ABS_BASIS
+inline Real basisFunctions(unsigned N, unsigned i, Real x) {
+ return std::legendre(i, 2.0 * (x - 0.5));
+}
+#endif
+
+#ifdef FITS_ABS_BASIS
+inline Real basisFunctions(unsigned N, unsigned i, Real x) {
+ return abs(x - i / (N - 1.0));
+}
+#endif
+
+Real value(const Vector& coeffs, Real x) {
+ Real v = 0;
+
+ for (unsigned j = 0; j < coeffs.size(); j++) {
+ v += coeffs[j] * basisFunctions(coeffs.size(), j, x);
+ }
+
+ return v;
+}
+
+Real cost(Data::const_iterator batchBegin, Data::const_iterator batchEnd, const Vector& coeffs) {
+ Real c = 0;
+
+#pragma omp parallel for reduction(+:c)
+ for (Data::const_iterator it = batchBegin; it != batchEnd; it++) {
+ Real x = std::get<0>(*it);
+ Real y = std::get<1>(*it);
+ c += 0.5 * pow(value(coeffs, x) - y, 2);
+ }
+
+ return c;
+}
+
+Vector dCost(Data::const_iterator batchBegin, Data::const_iterator batchEnd, const Vector& coeffs) {
+ Vector dC = Vector::Zero(coeffs.size());
+
+ for (Data::const_iterator it = batchBegin; it != batchEnd; it++) {
+ Real x = std::get<0>(*it);
+ Real y = std::get<1>(*it);
+ Real Δc = value(coeffs, x) - y;
+
+#pragma omp parallel for
+ for (unsigned j = 0; j < coeffs.size(); j++) {
+ dC[j] += Δc * basisFunctions(coeffs.size(), j, x);
+ }
+ }
+
+ return dC;
+}
+
+Vector underSolve(const Data& data, unsigned N) {
+ unsigned M = data.size();
+ Matrix A(M, N);
+ Vector b(M);
+ for (unsigned i = 0; i < M; i++) {
+ Real x = std::get<0>(data[i]);
+ Real y = std::get<1>(data[i]);
+ b[i] = y;
+ for (unsigned j = 0; j < N; j++) {
+ A(i, j) = basisFunctions(N, j, x);
+ }
+ }
+
+ return A.colPivHouseholderQr().solve(b);
+}
+
+Vector stochasticGradientDescent(const Data& data, const Vector& a₀, unsigned nBatches, long unsigned maxSteps, Real ε = 1e-12) {
+ Vector xₜ = a₀;
+ Real Hₜ;
+ Real α = 1.0;
+ Real m;
+ Vector ∇H;
+ long unsigned steps = 0;
+
+ unsigned batchSize = data.size() / nBatches;
+ Data::const_iterator batchBegin = data.begin();
+ Data::const_iterator batchEnd = data.begin() + batchSize;
+ Data::const_iterator effectiveEnd = data.begin() + batchSize * nBatches;
+
+ while (
+ Hₜ = cost(batchBegin, batchEnd, xₜ),
+ ∇H = dCost(batchBegin, batchEnd, xₜ),
+ m = ∇H.squaredNorm(),
+ m > ε && steps < maxSteps
+ ) {
+ Vector xₜ₊₁;
+ Real Hₜ₊₁;
+
+ while (
+ xₜ₊₁ = xₜ - α * ∇H, Hₜ₊₁ = cost(batchBegin, batchEnd, xₜ₊₁),
+ Hₜ₊₁ > Hₜ - 0.25 * α * m
+ ) {
+ α /= 2;
+ }
+
+ xₜ = xₜ₊₁;
+ α *= 1.25;
+ steps++;
+
+ if (batchEnd == data.end()) {
+ batchBegin = data.begin();
+ batchEnd = data.begin() + batchSize;
+ } else if (batchEnd == effectiveEnd) {
+ batchBegin = effectiveEnd;
+ batchEnd = data.end();
+ } else {
+ batchBegin += batchSize;
+ batchEnd += batchSize;
+ }
+ }
+
+ return xₜ;
+}
+
+Data generateData(Real(*f)(Real), unsigned M, Real σ, Rng& r) {
+ Data data;
+ data.reserve(M);
+
+ for (unsigned i = 0; i < M; i++) {
+ Real x = ((Real)i) / (M - 1.0);
+ data.push_back({x, f(x) + r.variate<Real, std::normal_distribution>(0, σ)});
+ }
+
+ return data;
+}