Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
solve.hpp
Go to the documentation of this file.
1// -------------------------------------------------------//
2//
3// SHAMROCK code for hydrodynamics
4// Copyright (c) 2021-2026 Timothée David--Cléris <tim.shamrock@proton.me>
5// SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1
6// Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information
7//
8// -------------------------------------------------------//
9
10#pragma once
11
19
20#include "shammath/matrix.hpp"
22#include <cmath>
23#include <functional>
24
25namespace shammath {
26
27 template<class T>
28 float newton_rhaphson(std::function<T(T)> &&f, std::function<T(T)> &&df, T epsilon_c, T x_0) {
29
30 auto iterate_newton = [](T f, T df, T xk) -> T {
31 return xk - (f / df);
32 };
33
34 T xk = x_0;
35 T epsilon = 100000;
36
37 while (epsilon > epsilon_c) {
38 T xkp1 = iterate_newton(f(xk), df(xk), xk);
39
40 epsilon = std::fabs(xk - xkp1);
41
42 xk = xkp1;
43 }
44
45 return xk;
46 }
47
69 template<class T, class Lambda>
70 std::pair<std::vector<T>, T> least_squares(
71 const Lambda &f,
72 const std::vector<T> &X,
73 const std::vector<T> &Y,
74 const std::vector<T> &p0,
75 int maxits = 1000,
76 T tolerance = 1e-9) {
77 SHAM_ASSERT(X.size() == Y.size());
78 SHAM_ASSERT(X.size() >= p0.size());
79 SHAM_ASSERT(p0.size() > 0);
80
81 const int params_nb = p0.size();
82 const int data_size = X.size();
83
84 std::vector<T> p = p0;
85 T mu = 1e-2; // damping parameter
86 T beta = 0.1; // decay rate
87 int it = 0;
88
89 auto evaluate_sse = [&](const std::vector<T> &params) -> T {
90 T sse = 0.0;
91 for (int k = 0; k < data_size; k++) {
92 T r = Y[k] - f(params, X[k]);
93 sse += r * r;
94 }
95 return sse;
96 };
97 T sse = evaluate_sse(p);
98 T sse_trial = sse + 2 * tolerance;
99 while (it < maxits and sham::abs(sse_trial - sse) > tolerance) {
100 sse = evaluate_sse(p);
101
102 // Construct the Jacobian (finite differences)
103 shammath::mat_d<T> J(data_size, params_nb);
104 std::vector<T> f_at_p(data_size);
105 for (int i = 0; i < data_size; i++) {
106 f_at_p[i] = f(p, X[i]);
107 }
108 mat_set_vals(J.get_mdspan(), [&](auto i, auto j) -> T {
109 // This part can be improved if necessary (p is modified then restored).
110 T original_p_j = p[j];
111 const T MIN_STEP_SCALE_EPSILON = 1e-6;
112 T step_scale = (std::abs(original_p_j) < MIN_STEP_SCALE_EPSILON)
113 ? MIN_STEP_SCALE_EPSILON
114 : original_p_j;
115 const T FINITE_DIFF_STEP_FACTOR = 0.001;
116 T dpj = step_scale * FINITE_DIFF_STEP_FACTOR;
117
118 p[j] += dpj;
119 T f_perturbed = f(p, X[i]);
120 p[j] = original_p_j; // Restore
121
122 return (f_perturbed - f_at_p[i]) / dpj;
123 });
124
125 shammath::vec_d<T> R(data_size);
126 shammath::vec_set_vals(R.get_mdspan(), [&](auto i) -> T {
127 return Y[i] - f(p, X[i]);
128 });
129
130 shammath::mat_d<T> J_T(params_nb, data_size); // Jacobian transposed
131 shammath::mat_transpose(J.get_mdspan(), J_T.get_mdspan());
132
133 shammath::mat_d<T> G(params_nb, params_nb); // left hand side
134 shammath::mat_prod(J_T.get_mdspan(), J.get_mdspan(), G.get_mdspan());
135 shammath::mat_plus_equal_scalar_id(G.get_mdspan(), mu);
136
137 shammath::vec_d<T> d(params_nb); // right hand side
138 shammath::mat_gemv(1.0, J_T.get_mdspan(), R.get_mdspan(), 0.0, d.get_mdspan());
139
140 shammath::vec_d<T> delta(params_nb); // increment for p
141 shammath::Cholesky_solve(G.get_mdspan(), d.get_mdspan(), delta.get_mdspan());
142
143 std::vector<T> p_trial = p;
144 for (int i = 0; i < params_nb; i++) {
145 p_trial[i] += delta.data[i];
146 };
147
148 sse_trial = 0.0;
149 for (int k = 0; k < data_size; k++) {
150 T residual = Y[k] - f(p_trial, X[k]);
151 sse_trial += residual * residual;
152 };
153 if (sse_trial > sse) { // Fail -> gradient descent
154 mu /= beta;
155 } else { // Not bad -> Gauss-Newton
156 mu *= beta;
157 p = p_trial;
158 }
159 it++;
160 };
161
162 T total_sum_squares = 0.0;
163 T mean_Y = 0.0;
164 for (int k = 0; k < data_size; k++) {
165 mean_Y += Y[k];
166 }
167 mean_Y /= data_size;
168 for (int k = 0; k < data_size; k++) {
169 total_sum_squares += (Y[k] - mean_Y) * (Y[k] - mean_Y);
170 }
171 T R2 = 1 - sse / total_sum_squares;
172
173 shamlog_debug_ln(
174 "least_squares", "Least squares stopped after", it, "iterations with R^2=", R2);
175 return {p, R2};
176 }
177} // namespace shammath
#define SHAM_ASSERT(x)
Shorthand for SHAM_ASSERT_NAMED without a message.
Definition assert.hpp:67
Matrix class with runtime size based on std::vector storage and mdspan.
Definition matrix.hpp:130
constexpr auto get_mdspan()
Get the matrix data as a mdspan.
Definition matrix.hpp:143
Vector class with runtime size based on std::vector storage and mdspan.
Definition matrix.hpp:190
constexpr auto get_mdspan()
Get the vector data as a mdspan.
Definition matrix.hpp:201
std::vector< T > data
The vector data.
Definition matrix.hpp:193
namespace for math utility
Definition AABB.hpp:26
void vec_set_vals(const std::mdspan< T, Extents, Layout, Accessor > &input, Func &&func)
Set the elements of a vector according to a user-provided function.
Definition matrix_op.hpp:68
void mat_transpose(const std::mdspan< T, Extents1, Layout1, Accessor1 > &input, const std::mdspan< T, Extents2, Layout2, Accessor2 > &output)
This function transposes a matrix.
std::pair< std::vector< T >, T > least_squares(const Lambda &f, const std::vector< T > &X, const std::vector< T > &Y, const std::vector< T > &p0, int maxits=1000, T tolerance=1e-9)
This function determines the best fit parameters for a given function with least squares.
Definition solve.hpp:70
void mat_set_vals(const std::mdspan< T, Extents, Layout, Accessor > &input, Func &&func)
Set the elements of a matrix according to a user-provided function.
Definition matrix_op.hpp:45
void Cholesky_solve(const std::mdspan< T, Extents1, Layout1, Accessor1 > &M, const std::mdspan< T, Extents2, Layout2, Accessor2 > &y, const std::mdspan< T, Extents3, Layout3, Accessor3 > &x)
This function solves a system of linear equations with Cholesky decomposition. The system must have t...
void mat_plus_equal_scalar_id(const std::mdspan< T, Extents1, Layout1, Accessor1 > &inout, const U beta)
This function compute addition of a matrix with mutiple of identity matrix A +=beta * I,...
void mat_prod(const std::mdspan< Ta, Extents1, Layout1, Accessor1 > &input1, const std::mdspan< Ta, Extents2, Layout2, Accessor2 > &input2, const std::mdspan< Tb, Extents3, Layout3, Accessor3 > &output)
Compute the product of two matrices.
void mat_gemv(const U alpha, const std::mdspan< T, Extents1, Layout1, Accessor1 > &M, const std::mdspan< T, Extents2, Layout2, Accessor2 > &x, const U beta, const std::mdspan< T, Extents3, Layout3, Accessor3 > &y)
This function performs matrix-vector multiplication as y = a*Mx + b*y.