I'm working on some code that I'm writing which uses the [GNU Scientific Library (GSL)][1]'s Nonlinear least-squares algorithm for curve fitting.
I have been successful in obtaining a working code that estimate the right parameters from the fitting analysis using a C++ wrapper from https://github.com/Eleobert/gsl-curve-fit/blob/master/example.cpp.
Now, I would like to fix some of the parameters of the function to be fit. And I would like to modify the function in such a way that I can already input the value of the parameter to be fixed.
Any idea on how to do?
I'm showing here the full code.
This is the code for performing nonlinear least-squares fitting:
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multifit_nlinear.h>
#include <iostream>
#include <random>
#include <vector>
#include <cassert>
#include <functional>
template <typename F, size_t... Is>
auto gen_tuple_impl(F func, std::index_sequence<Is...> )
{
return std::make_tuple(func(Is)...);
}
template <size_t N, typename F>
auto gen_tuple(F func)
{
return gen_tuple_impl(func, std::make_index_sequence<N>{} );
}
auto internal_solve_system(gsl_vector* initial_params, gsl_multifit_nlinear_fdf *fdf,
gsl_multifit_nlinear_parameters *params) -> std::vector<double>
{
// This specifies a trust region method
const gsl_multifit_nlinear_type *T = gsl_multifit_nlinear_trust;
const size_t max_iter = 200;
const double xtol = 1.0e-8;
const double gtol = 1.0e-8;
const double ftol = 1.0e-8;
auto *work = gsl_multifit_nlinear_alloc(T, params, fdf->n, fdf->p);
int info;
// initialize solver
gsl_multifit_nlinear_init(initial_params, fdf, work);
//iterate until convergence
gsl_multifit_nlinear_driver(max_iter, xtol, gtol, ftol, nullptr, nullptr, &info, work);
// result will be stored here
gsl_vector * y = gsl_multifit_nlinear_position(work);
auto result = std::vector<double>(initial_params->size);
for(int i = 0; i < result.size(); i++)
{
result[i] = gsl_vector_get(y, i);
}
auto niter = gsl_multifit_nlinear_niter(work);
auto nfev = fdf->nevalf;
auto njev = fdf->nevaldf;
auto naev = fdf->nevalfvv;
// nfev - number of function evaluations
// njev - number of Jacobian evaluations
// naev - number of f_vv evaluations
//logger::debug("curve fitted after ", niter, " iterations {nfev = ", nfev, "} {njev = ", njev, "} {naev = ", naev, "}");
gsl_multifit_nlinear_free(work);
gsl_vector_free(initial_params);
return result;
}
auto internal_make_gsl_vector_ptr(const std::vector<double>& vec) -> gsl_vector*
{
auto* result = gsl_vector_alloc(vec.size());
int i = 0;
for(const auto e: vec)
{
gsl_vector_set(result, i, e);
i++;
}
return result;
}
template<typename C1>
struct fit_data
{
const std::vector<double>& t;
const std::vector<double>& y;
// the actual function to be fitted
C1 f;
};
template<typename FitData, int n_params>
int internal_f(const gsl_vector* x, void* params, gsl_vector *f)
{
auto* d = static_cast<FitData*>(params);
// Convert the parameter values from gsl_vector (in x) into std::tuple
auto init_args = [x](int index)
{
return gsl_vector_get(x, index);
};
auto parameters = gen_tuple<n_params>(init_args);
// Calculate the error for each...
for (size_t i = 0; i < d->t.size(); ++i)
{
double ti = d->t[i];
double yi = d->y[i];
auto func = [ti, &d](auto ...xs)
{
// call the actual function to be fitted
return d->f(ti, xs...);
};
auto y = std::apply(func, parameters);
gsl_vector_set(f, i, yi - y);
}
return GSL_SUCCESS;
}
using func_f_type = int (*) (const gsl_vector*, void*, gsl_vector*);
using func_df_type = int (*) (const gsl_vector*, void*, gsl_matrix*);
using func_fvv_type = int (*) (const gsl_vector*, const gsl_vector *, void *, gsl_vector *);
auto internal_make_gsl_vector_ptr(const std::vector<double>& vec) -> gsl_vector*;
auto internal_solve_system(gsl_vector* initial_params, gsl_multifit_nlinear_fdf *fdf,
gsl_multifit_nlinear_parameters *params) -> std::vector<double>;
template<typename C1>
auto curve_fit_impl(func_f_type f, func_df_type df, func_fvv_type fvv, gsl_vector* initial_params, fit_data<C1>& fd) -> std::vector<double>
{
assert(fd.t.size() == fd.y.size());
auto fdf = gsl_multifit_nlinear_fdf();
auto fdf_params = gsl_multifit_nlinear_default_parameters();
fdf.f = f;
fdf.df = df;
fdf.fvv = fvv;
fdf.n = fd.t.size();
fdf.p = initial_params->size;
fdf.params = &fd;
// "This selects the Levenberg-Marquardt algorithm with geodesic acceleration."
fdf_params.trs = gsl_multifit_nlinear_trs_lmaccel;
return internal_solve_system(initial_params, &fdf, &fdf_params);
}
template<typename Callable>
auto curve_fit(Callable f, const std::vector<double>& initial_params, const std::vector<double>& x, const std::vector<double>& y) -> std::vector<double>
{
// We can't pass lambdas without convert to std::function.
constexpr auto n = 3;
assert(initial_params.size() == n);
auto params = internal_make_gsl_vector_ptr(initial_params);
auto fd = fit_data<Callable>{x, y, f};
return curve_fit_impl(internal_f<decltype(fd), n>, nullptr, nullptr, params, fd);
}
// linspace from https://github.com/Eleobert/meth/blob/master/interpolators.hpp
template <typename Container>
auto linspace(typename Container::value_type a, typename Container::value_type b, size_t n)
{
assert(b > a);
assert(n > 1);
Container res(n);
const auto step = (b - a) / (n - 1);
auto val = a;
for(auto& e: res)
{
e = val;
val += step;
}
return res;
}
This is the function I use for fitting:
double gaussian(double x, double a, double b, double c)
{
const double z = (x - b) / c;
return a * std::exp(-0.5 * z * z);
}
And these last lines create a fake dataset of observed data (with some noise which is normally distributed) and test the fitting curve function.
int main()
{
auto device = std::random_device();
auto gen = std::mt19937(device());
auto xs = linspace<std::vector<double>>(0.0, 1.0, 300);
auto ys = std::vector<double>(xs.size());
double a = 5.0, b = 0.4, c = 0.15;
for(size_t i = 0; i < xs.size(); i++)
{
auto y = gaussian(xs[i], a, b, c);
auto dist = std::normal_distribution(0.0, 0.1 * y);
ys[i] = y + dist(gen);
}
auto r = curve_fit(gaussian, {1.0, 0.0, 1.0}, xs, ys);
std::cout << "result: " << r[0] << ' ' << r[1] << ' ' << r[2] << '\n';
std::cout << "error : " << r[0] - a << ' ' << r[1] - b << ' ' << r[2] - c << '\n';
}
In this case, I would like to fix one of the a, b, c parameters and estimate the remaining two. For example, fix a and estimate b and c. But I would like to find a solution such that I can input any value to the fixed parameter a, without needing to modify the gaussian function every time.
Ok. Here's the answer based on the code linked in http://github.com/Eleobert/gsl-curve-fit/blob/master/example.cpp. However, this is not the code posted in the question: you should update your question accordingly so that others may take advantage from both the question & answer.
So, basically, the main problem is that GSL is a library written in pure C, whereas you use a high-level wrapper written in C++, published in the aforementioned link . While the wrapper is written pretty well in modern C++, it has one basic problem: it is "stiff" - it can be used only for a subclass of problems it was designed for, and this subclass is a rather narrow subset of the capabilities offered by the original C code.
Let's try to improve it a bit and start from how the wrapper is supposed to be used:
double gaussian(double x, double a, double b, double c)
{
const double z = (x - b) / c;
return a * std::exp(-0.5 * z * z);
}
int main()
{
auto device = std::random_device();
auto gen = std::mt19937(device());
auto xs = linspace<std::vector<double>>(0.0, 1.0, 300);
auto ys = std::vector<double>(xs.size());
double a = 5.0, b = 0.4, c = 0.15;
for (size_t i = 0; i < xs.size(); i++)
{
auto y = gaussian(xs[i], a, b, c);
auto dist = std::normal_distribution(0.0, 0.1 * y);
ys[i] = y + dist(gen);
}
auto result = curve_fit(gaussian, {1.0, 0.0, 1.0}, xs, ys);
// use result
}
This code is amazingly simple compared to its original, C-language counterpart. One initializes the x-y pairs of values, here stored as vectors xs and ys and executes a single function that take 4 easy to understand parameters: the function to be fitted to the data, the initial values of the fitting parameters the function depends on, the x values and the corresponding y values of the data to which the function must be fitted.
Your problem is how to keep this high-level interface, but use it for fitting functions where only some parameters are "free", that is, can be changed during the fitting procedure, while the values of others must be fixed. This could be easily achieved using, e.g., global variables that the function has access to, but we hate global variables and never use them without a real cause.
I suggest using a well-known C++ alternative: functors. Look:
struct gaussian_fixed_a
{
double a;
gaussian_fixed_a(double a) : a{a} {}
double operator()(double x, double b, double c) const { return gaussian(x, a, b, c); }
};
This struct/class introduces a new type of function objects. In the constructor, parameter a is passed and stored in an object. Then, there's a function call operator that takes only 3 parameters rather than 4, substituting a from its stored value. This object can pretend to be a Gaussian with a fixed constant and only other 3 arguments, x, b, and c, that can vary.
We would like to use it like this:
gaussian_fixed_a g(a);
auto r2 = curve_fit(g, std::array{0.444, 0.11}, xs, ys);
This is almost the same code you'd use for the original wrapper save for 2 differences:
You now use an object name (here: g) rather than a function name
You have to pass the number of arguments to curve_fit as a compile-time constant, as it is then used internally by it to call a template parametrized by this number. I achieve it by using std::array, for which the compiler can deduce its size at compile time, just as needed. An alternative would ba a nasty template syntax, curve_fit<2>(...
For this to work, you need to change the interface of curve_fit, from
template <typename Callable>
auto curve_fit(Callable f, const std::vector<double>& initial_params, const std::vector<double>& x,
const std::vector<double>& y) -> std::vector<double>
to
template <typename Callable, auto n>
auto curve_fit(Callable f, const std::array<double, n>& initial_params, const std::vector<double>& x,
const std::vector<double>& y) -> std::vector<double>
(btw: this -> syntax with well-known type on its right-hand side is not the best one, IMHO, but let it be). The idea is to force the compiler to read the number of fitting parameters at compile time form the size of the array.
Then you need to make a similar adjustment in the argument list of curve_fit_impl - and this is almost it.
Here I spent quite a lot of time trying to figure out why this code does not work. It turned out it had worked all the time, the secret is that if you fit a function to some data, you'd better provide initial values reasonably close to the solution. That's why used this initializer std::array{0.444, 0.11} rather than the original {0, 1}, as the latter does not converge to anything close to the correct answer.
Do we really need to use explicit function objects? Perhaps lambdas will do? Yes, they will - this compiles and runs as expected:
auto r3 = curve_fit([a](double x, double b, double c) { return gaussian(x, a, b, c); }, std::array{0.444, 0.11}, xs, ys);
Here's the full diff between the original and modified code (without lambda):
7a8
> #include <array>
72c73,74
< auto internal_make_gsl_vector_ptr(const std::vector<double>& vec) -> gsl_vector*
---
> template<auto n>
> auto internal_make_gsl_vector_ptr(const std::array<double, n>& vec) -> gsl_vector*
158,159c160,161
< template <typename Callable>
< auto curve_fit(Callable f, const std::vector<double>& initial_params, const std::vector<double>& x,
---
> template <typename Callable, auto n>
> auto curve_fit(Callable f, const std::array<double, n>& initial_params, const std::vector<double>& x,
163,164c165,166
< constexpr auto n = 3;
< assert(initial_params.size() == n);
---
> // constexpr auto n = 2;
> // assert(initial_params.size() == n);
194a197,204
>
> struct gaussian_fixed_a
> {
> double a;
> gaussian_fixed_a(double a) : a{a} {}
> double operator()(double x, double b, double c) const { return gaussian(x, a, b, c); }
> };
>
212c222,224
< auto r = curve_fit(gaussian, {1.0, 0.0, 1.0}, xs, ys);
---
> auto r = curve_fit(gaussian, std::array{1.0, 0.0, 1.0}, xs, ys);
> gaussian_fixed_a g(a);
> auto r2 = curve_fit(g, std::array{0.444, 0.11}, xs, ys);
215a228,230
> std::cout << "\n";
> std::cout << "result: " << r2[0] << ' ' << r2[1] << '\n';
> std::cout << "error : " << r2[0] - b << ' ' << r2[1] - c << '\n';
Related
I need to integrate the following function:
where z > 0. The problem is that the integrand is very small for large z and high precision is required in the integration. So far, I have written the integrand as
double integrand__W(double x, double z){
double arg = z*z/(4.0*x);
double num = exp(arg+x)+1;
double den1 = expm1(arg);
double den2 = exp(x);
num = isinf(num) ? arg+x : log(num);
den1 = isinf(den1) ? arg : log(den1);
den2 = x; //log(exp(x))=x
double t1 = num-den1-den2;
num = exp(x);
double den = exp(x)+1;
double t2 = isinf(den) ? exp(-x) : num/(den*den);
return t1*t2;
}
For numerical integration, I'm using Cubature, a simple C-package for adaptive multidimensional integration:
//integrator
struct fparams {
double z;
};
int inf_W(unsigned ndim, const double *x, void *fdata, unsigned fdim, double *fval){
struct fparams * fp = (struct fparams *)fdata;
double z = fp->z;
double t = x[0];
double aux = integrand__W(a_int+t*pow(1.0-t, -1.0), z)*pow(1.0-t, -2.0);
if (!isnan(aux) && !isinf(aux))
{
fval[0] = aux;
}
else
{
fval[0] = 0.0;
}
return 0;
}
//range integration 1D
size_t maxEval = 1e7;
double xl[1] = { 0 };
double xu[1] = { 1 };
double W, W_ERR;
struct fparams params = {z};
hcubature(1, inf_W, ¶ms, 1, xl, xu, maxEval, 0, 1e-5, ERROR_INDIVIDUAL, &W, &W_ERR);
cout << "z: " << z << " | " << W << " , " << W_ERR << endl;
where the integration over the semi-infinite interval is possible by a change of variables:
Analytically, I know that the integrated is non-negative, so the integral itself should be non-negative. However, I'm getting some incorrect results due to a lack of accuracy:
z: 100 | -3.97632e-17 , 1.24182e-16
In Mathematica, working with high precision, I can get the desired result:
w[x_, z_] := E^x/(E^x + 1)^2 Log[(E^(z^2/(4 x)) + E^-x)/(E^(z^2/(4 x)) - 1)]
W[z_?NumericQ] := NIntegrate[w[x, z], {x, 0, ∞},
WorkingPrecision -> 40,
Method -> "LocalAdaptive"]
W[100]
(* 4.679853458969239635780655689865016458810*10^-43 *)
My question: Is there any way to write my integrand such that I can reach the required precision? Thanks.
There are integration schemes which only use positive weights, resulting in nonnegative integral values if the evaluated function values of the integrand are all nonnegative. Some other integration schemes permit negative weights, resulting in a possibly higher accuracy for integration. Cubature probably uses one of those.
Your actual integral value is very close to 0 for z=100, and that's what you're getting, too, so there's really nothing wrong with the integration scheme. If you absolutely need nonnegativity, one option is to simply set the negative results to 0.
After asking the same question to a different community, I got two suggestions that seem to work:
Avoiding subtractive cancellation
Manipulate the integral a little bit first:
and then rewrite the integrand as
double integrand__W(double x, double z){
double arg = z*z/(4.0*x);
double t1 = log1p((exp(-x)+1)/expm1(arg));
double num = exp(x);
double den = exp(x)+1;
double t2 = isinf(den) ? exp(-x) : num/(den*den);
return t1*t2;
}
Use of Exp-Sinh quadrature
This integration scheme is provided by the Boost library:
#include <iostream>
#include <cmath>
#include <boost/math/quadrature/exp_sinh.hpp>
using boost::math::quadrature::exp_sinh;
using std::exp;
using std::expm1;
using std::log;
int main() {
exp_sinh<double> integrator;
double z = 100.0;
auto f = [z](double x) {
double k1 = 1.0/(2 + exp(-x) +exp(x));
double t = z*z/(4*x);
double log_arg;
if (t > 1) {
log_arg = (1 + exp(-x)*exp(-t))/(1 - exp(-t));
} else {
log_arg = (exp(t) + exp(-x))/expm1(t);
}
return k1*log(log_arg);
};
double termination = sqrt(std::numeric_limits<double>::epsilon());
double error;
double L1;
double Q = integrator.integrate(f, termination, &error, &L1);
std::cout << "Q = " << Q << ", error estimate: " << error << "\n";
}
I can't say much about the mathematics (i have a love/hate relationship with math) but higher precision can be achieved via long double and the related math functions in the standard math library.
But long double does not necessarily mean higher precision, dependend on your compiler and system architecture it could be simply a double or 80 bit extended precision or more.
More info:
https://en.wikipedia.org/wiki/Long_double
https://en.wikipedia.org/wiki/Extended_precision
So I wanted to implement the path of the Moon around the Earth with a C program.
My problem is that you know the Moon's velocity and position at Apogee and Perigee.
So I started to solve it from Apogee, but I cannot figure out how I could add the second velocity and position as "initial value" for it. I tried it with an if but I don't see any difference between the results. Any help is appreciated!
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
typedef void (*ode)(double* p, double t, double* k, double* dk);
void euler(ode f, double *p, double t, double* k, double h, int n, int N)
{
double kn[N];
double dk[N];
double Rp = - 3.633 * pow(10,8); // x position at Perigee
for(int i = 0; i < n; i++)
{
f(p, 0, k, dk);
for (int j = 0; j < N; j++)
{
if (k[0] == Rp) // this is the "if" I mentioned in my comment
// x coordinate at Perigee
{
k[1] = 0; // y coordinate at Perigee
k[2] = 0; // x velocity component at Perigee
k[3] = 1076; // y velocity component at Perigee
}
kn[j] = k[j] + h * dk[j];
printf("%f ", kn[j]);
k[j] = kn[j];
}
printf("\n");
}
}
void gravity_equation(double* p, double t, double* k, double* dk)
{
// Earth is at the (0, 0)
double G = p[0]; // Gravitational constant
double m = p[1]; // Earth mass
double x = k[0]; // x coordinate at Apogee
double y = k[1]; // y coordinate at Apogee
double Vx = k[2]; // x velocity component at Apogee
double Vy = k[3]; // y velocity component at Apogee
dk[0] = Vx;
dk[1] = Vy;
dk[2] = (- G * m * x) / pow(sqrt((x * x)+(y * y)),3);
dk[3] = (- G * m * y) / pow(sqrt((x * x)+(y * y)),3);
}
void run_gravity_equation()
{
int N = 4; // how many equations there are
double initial_values[N];
initial_values[0] = 4.055*pow(10,8); // x position at Apogee
initial_values[1] = 0; // y position at Apogee
initial_values[2] = 0; // x velocity component at Apogee
initial_values[3] = (-1) * 964; //y velocity component at Perigee
int p = 2; // how many parameters there are
double parameters[p];
parameters[0] = 6.67384 * pow(10, -11); // Gravitational constant
parameters[1] = 5.9736 * pow(10, 24); // Earth mass
double h = 3600; // step size
int n = 3000; // the number of steps
euler(&gravity_equation, parameters, 0, initial_values, h, n, N);
}
int main()
{
run_gravity_equation();
return 0;
}
Your interface is
euler(odefun, params, t0, y0, h, n, N)
where
N = dimension of state space
n = number of steps to perform
h = step size
t0, y0 = initial time and value
The intended function of this procedure seems to be that the updated values are returned inside the array y0. There is no reason to insert some hack to force the state to have some initial conditions. The initial condition is passed as argument. As you are doing in void run_gravity_equation(). The integration routine should remain agnostic of the details of the physical model.
It is extremely improbable that you will hit the same value in k[0] == Rp a second time. What you can do is to check for sign changes in Vx, that is, k[1] to find points or segments of extremal x coordinate.
Trying to interpret your description closer, what you want to do is to solve a boundary value problem where x(0)=4.055e8, x'(0)=0, y'(0)=-964 and x(T)=-3.633e8, x'(T)=0. This has the advanced tasks to solve a boundary value problem with single or multiple shooting and additionally, that the upper boundary is variable.
You might want to to use the Kepler laws to get further insights into the parameters of this problem so that you can solve it just with a forward integration. The Kepler ellipse of the first Kepler law has the formula (scaled for Apogee at phi=0, Perigee at phi=pi)
r = R/(1-E*cos(phi))
so that
R/(1-E)=4.055e8 and R/(1+E)=3.633e8,
which gives
R=3.633*(1+E)=4.055*(1-E)
==> E = (4.055-3.633)/(4.055+3.633) = 0.054891,
R = 3.633e8*(1+0.05489) = 3.8324e8
Further, the angular velocity is given by the second Kepler law
phi'*r^2 = const. = sqrt(R*G*m)
which gives tangential velocities at Apogee (r=R/(1-E))
y'(0)=phi'*r = sqrt(R*G*m)*(1-E)/R = 963.9438
and Perigee (r=R/(1+E))
-y'(T)=phi'*r = sqrt(R*G*m)*(1+E)/R = 1075.9130
which indeed reproduces the constants you used in your code.
The area of the Kepler ellipse is pi/4 times the product of smallest and largest diameter. The smallest diameter can be found at cos(phi)=E, the largest is the sum of apogee and perigee radius, so that the area is
pi*R/sqrt(1-E^2)*(R/(1+E)+R/(1-E))/2= pi*R^2/(1-E^2)^1.5
At the same time it is the integral over 0.5*phi*r^2 over the full period 2*T, thus equal to
sqrt(R*G*m)*T
which is the third Kepler law. This allows to compute the half-period as
T = pi/sqrt(G*m)*(R/(1-E^2))^1.5 = 1185821
With h = 3600 the half point should be reached between n=329 and n=330 (n=329.395). Integration with scipy.integrate.odeint vs. Euler steps gives the following table for h=3600:
n [ x[n], y[n] ] for odeint/lsode for Euler
328 [ -4.05469444e+08, 4.83941626e+06] [ -4.28090166e+08, 3.81898023e+07]
329 [ -4.05497554e+08, 1.36933874e+06] [ -4.28507841e+08, 3.48454695e+07]
330 [ -4.05494242e+08, -2.10084488e+06] [ -4.28897657e+08, 3.14986514e+07]
The same for h=36, n=32939..32940
n [ x[n], y[n] ] for odeint/lsode for Euler
32938 [ -4.05499997e+08 5.06668940e+04] [ -4.05754415e+08 3.93845978e+05]
32939 [ -4.05500000e+08 1.59649309e+04] [ -4.05754462e+08 3.59155385e+05]
32940 [ -4.05500000e+08 -1.87370323e+04] [ -4.05754505e+08 3.24464789e+05]
32941 [ -4.05499996e+08 -5.34389954e+04] [ -4.05754545e+08 2.89774191e+05]
which is a little closer for the Euler method, but not much better.
I am pretty new to coding and I have been having an impossible time trying to find online help writing a C code that will use the golden section method (which apparently the GNU Scientific Library has, although I haven't had any luck finding it) to find the minimum of functions that Newton's method of minimization fails for.
Specifically I want to input an x-value as a starting point and have the code output the function's minimum value and the x coordinate of the minimum value point. My function is f(x) = x20. I am also allowed some error (< 10-3).
I don't even know where to begin with this, I have been ALL over the internet and haven't found anything helpful. I would seriously appreciate some help as to where I might find more information, or how I might implement this method.
Edit:
This is my code as of now:
#include <gsl/gsl_errno.h> /* Defines GSL_SUCCESS, etc. */
#include <gsl/gsl_math.h>
#include <gsl/gsl_min.h>
int minimize_convex(gsl_function *F,double a, double b, double *x_min, double tol)
{
int status;
double h = (b - a) * .0000001; /* Used to test slope at boundaries */
/* First deal with the special cases */
if (b - a < tol)
{
*x_min = b;
status = GSL_SUCCESS;
}
/* If the min is at a, then the derivative at a is >= 0. Test for
* this case. */
else if (GSL_FN_EVAL(F, a + h) - GSL_FN_EVAL(F, a) >= 0)
{
*x_min = a;
status = GSL_SUCCESS;
}
/* If the min is at b, then the derivative at b is >= 0. Test for
* this case. */
else if (GSL_FN_EVAL(F, b - h) - GSL_FN_EVAL(F, b) >= 0)
{
*x_min = b;
status = GSL_SUCCESS;
}
else
{
/* Choose x_guess so that it's value is less than either of the two
* endpoint values. Since we've got this far, we know that at least
* of of F(a + h) and F(b - h) has this property. */
double x_guess;
x_guess = (GSL_FN_EVAL(F, a + h) < GSL_FN_EVAL(F, b - h)) ?
a + h : b - h;
int iter = 0, max_iter = 200;
const gsl_min_fminimizer_type *T;
gsl_min_fminimizer *s;
T = gsl_min_fminimizer_goldensection;
s = gsl_min_fminimizer_alloc(T);
gsl_min_fminimizer_set(s, F, x_guess, a, b);
do
{
iter++;
status = gsl_min_fminimizer_iterate(s); /* perform iteration */
status =
gsl_min_test_interval(a, b, tol, 0.0); /* |a - b| < tol? */
a = gsl_min_fminimizer_x_lower(s);
b = gsl_min_fminimizer_x_upper(s);
if (status == GSL_SUCCESS)
{
*x_min = gsl_min_fminimizer_x_minimum(s); /* current est */
}
}
while (status == GSL_CONTINUE && iter < max_iter);
gsl_min_fminimizer_free(s);
}
return status;
}
double f(double x, void *params)
{
double *p = (double *) params;
return (x^(50)) + *p;
}
double C = 0.0;
int main (void)
{
double m = 0.0, result;
double a = -1.0, b = 1.0;
double epsilon = 0.001;
int exit_val;
gsl_function F;
F.function = &f;
F.params = &C;
exit_val = minimize_convex(&F, a, b, m, &result, epsilon);
printf("Minimizer: %g\n", result);
printf("Function value: %g\n", f(result, &C));
printf("%d\n", exit_val);
return 0;
}
I am getting the following errors:
try.c:69:14: error: invalid operands to binary
expression ('double' and 'double')
return (x^(50)) + *p;
try.c:81:54: error: too many arguments to function
call, expected 5, have 6
exit_val = minimize_convex(&F, a, b, m, &result, epsilon);
Any thoughts?
gsl has a generic minimizer that can use multiple methods to acheive the minimization. The description of how to use the minimizer can be found in the documentation. You can set it to the golden section method by delcaring the method as gsl_min_fminimizer_goldensection.
I'm working on non-linear differential equation using GSL. The thing is I'm quite new on C stuffs. I just adapted the sample on GNU site into the equation I'm interested in right now.
This is the equation:
d2x/dt2 + r*dx/dy + cos(x) + v*cos(2*x+0.4) E1*sin(wt) + E2*sin(2*w*t+a) = 0
What I am stuck is I have no idea how to plug in multiple parameters in the codes. Moreover, I don't know how to employ cosine or sine function in this code.
I tried to figure out this problem, by searching on Google all the way. I couldn't find any thing that helps me.
#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv2.h>
int func (double t, const double x[], double y[], void *params)
{
double r = *(double *)params;
double v = *(double *)params;
double w = *(double *)params;
double E1 = *(double *)params;
double E2 = *(double *)params;
double a = *(double *)params;
y[0] = x[1];
y[1] = -r*x[1] - cos(x[0]) - v*cos(2*x[0]+0.4) - E1*sin(w*t) - E2*sin(2*w*t+a);
return GSL_SUCCESS;
}
int jac (double t, const double x[], double *dydx, double dydt[], void *params)
{
double r = *(double *)params;
double v = *(double *)params;
double w = *(double *)params;
double E1 = *(double *)params;
double E2 = *(double *)params;
double a = *(double *)params;
gsl_matrix_view dydx_mat = gsl_matrix_view_array (dydx, 2, 2);
gsl_matrix * m = &dydx_mat.matrix;
gsl_matrix_set (m, 0, 0, 0.0);
gsl_matrix_set (m, 0, 1, 1.0);
gsl_matrix_set (m, 1, 0, sin(x[0]) + 2*v*sin(2*x[0]+0.4));
gsl_matrix_set (m, 1, 1, -r);
dydt[0] = 0.0;
dydt[1] = 0.0;
return GSL_SUCCESS;
}
int main (void)
{
double r = 0.0;
double v = 0.0;
double w = 2.4;
double E1 = -2.3;
double E2 = 0;
double a = 0.7;
gsl_odeiv2_system sys = {func, jac, 2, &r, &v, &w, &E1, &E2, &a};
gsl_odeiv2_driver *d = gsl_odeiv2_driver_alloc_x_new (&sys, gsl_odeiv2_step_rk8pd, 1e-6, 1e-6, 0.0);
int i;
double t = 0.0, t1 = 10000;
double x[2] = {0.0, 0.0};
for (i = 1 ; i<=10000; i++)
{
double ti = i*t1/10000;
int status = gsl_odeiv2_driver_apply (d, &t, ti, x);
if (status != GSL_SUCCESS)
{
printf("error, return value%d\n", status);
break;
}
printf("%.5e %.5e %.5e\n", t, x[0], x[1]);
}
gsl_odeiv2_driver_free (d);
return 0;
}
The params argument is a pointer (address / memory location) to some arbitrary data structure. In the example from the GSL documentation, their equation contained only one parameter, which means it's okay to just pass the address of a double-precision number.
However, for your problem, you need to access 6 different parameters. You can't access every parameter with the same address!
/* this doesn't work! */
double r = *(double *)params;
double v = *(double *)params;
double w = *(double *)params;
double E1 = *(double *)params;
double E2 = *(double *)params;
double a = *(double *)params;
Since all the addresses are the same, you are referring to the same number. To remedy this, you can either: store all the parameters in an array of length 6, or store them in a predefined data structure. The latter approach is more readable so I will demonstrate that.
First define a data type to specify what parameters you will store:
struct param_type {
double r;
double v;
double w;
double E1;
double E2;
double a;
};
Now, create a structure of this type in the main function and store the actual values of the parameter:
struct param_type my_params = {r, v, w, E1, E2, a};
When defining the system, you store a pointer to that struct param_type:
gsl_odeiv2_system sys = {func, jac, 2, &my_params};
To use the parameter inside func and jac, you simply cast the params argument from a generic pointer (void *) to a pointer for your specific data type (struct param_type *):
struct param_type *my_params_pointer = params;
(Note that in C++ this must be written with an explicit cast.) Finally, you can access the parameters via:
double r = my_params_pointer->r;
double v = my_params_pointer->v;
double w = my_params_pointer->w;
double E1 = my_params_pointer->E1;
double E2 = my_params_pointer->E2;
double a = my_params_pointer->a;
The arrow -> is used here instead of the dot . because my_params_pointer is a pointer and needs to be dereferenced before use.
If you are working with parameters, most likely they are of the same type (double). In that case this can be solved too using an array and then access the elements from func and/or jac.
Another option could be use a gsl_vector and then "get" the values inside the functions. This will involve use free.
I am trying to find the right parameters to input to my code to produced the desired results. Instead of guessing and checking I am using a root find to find the parameters that give the desired results. There are two variables that are free to vary, but I was having difficulty running the root finder. I changed the code to solve for each variable individually and found out that I was having trouble optimizing one variable.
It seems that the problem is that gsl_multiroot_iterate is guessing nan for x1 after the first iteration. At least that is what the value of x1 is returning in the function() call after that point, when I put in a printf statement for x1.
The simulation I am running only allows values of x1 between 0 and 1. It could be possible that this is causing the issue, though I check in the simulation to make sure x1 is between 0 and 1, and never throws an issue besides when x1 is nan. Is there anyway to set a range for what values the iteration tries for x1? And would anyone know what the iteration tries using nan for x1?
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multiroots.h>
struct rparams{
double target1;
};
int function(const gsl_vector * x, void *params, gsl_vector * f);
int main(int argc, char* argv[]) {
double target1;
sscanf(argv[1],"%lf",&target1);
const gsl_multiroot_fsolver_type *T;
gsl_multiroot_fsolver *s;
int status;
unsigned int iter = 0;
const size_t n = 1;
struct rparams p;
p.target1 = target1;
gsl_multiroot_function f = {&function, n, &p};
double x_init[1] = {.1};
gsl_vector * x = gsl_vector_alloc(n);
gsl_vector_set(x, 0, x_init[0]);
T = gsl_multiroot_fsolver_hybrid;
s = gsl_multiroot_fsolver_alloc(T, 1);
gsl_multiroot_fsolver_set(s, &f, x);
print_state(iter, s);
do
{
iter++;
status = gsl_multiroot_fsolver_iterate (s);
print_state(iter, s);
/* check if solver is stuck */
if (status){
break;
}
status = gsl_multiroot_test_residual (s->f, 1e-7);
}
while (status == GSL_CONTINUE && iter < 1000);
printf("status = %s\n", gsl_strerror (status));
gsl_multiroot_fsolver_free (s);
gsl_vector_free (x);
return 0;
}
int function(const gsl_vector * x, void *params, gsl_vector * f){
double target1 = ((struct rparams *) params)->target1;
double x1 = gsl_vector_get(x, 0);
/* Run simulation here using x1 parameter */
/* Assign output to temp1, which I am trying to match to target1 */
const double y1 = temp1 - target1;
gsl_vector_set (f, 0, y1);
return GSL_SUCCESS;
}
Be careful in designing the function you want to obtain the root from. In fact, for a test, I tried a function that had a constant output. This caused the algorithm to throw out the NaNs.
If you only need to find the root of a single equation, you can use the gsl_roots library instead of gsl_multiroots. The gsl_roots library has several bisection algorithms for which you specify a range instead of an initial guess. If you know your root is inside the interval (0, 1), you would set that as the target interval and the algorithm would never go outside that range. A minimal, complete example in C++ demonstrating the bisection method is below. If you can't use C++11 lambda functions, then you'd have to define the objective function like you did in your original question.
#include <iostream>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>
using namespace std;
int
main (void)
{
//Set the solver type (bisection method)
gsl_root_fsolver* s = gsl_root_fsolver_alloc(gsl_root_fsolver_bisection);
//Use a lambda to define the objective function.
//This is a parabola with the equation: y = (x-1)^2 - 1
//It has roots at x = 0 and x = 2.
gsl_function F;
F.function = [](double x, void*){return ((x-1) * (x-1)) - 1;};
//Initialize the solver; make a guess that the root is between x = 0.5 and x = 10
gsl_root_fsolver_set(s, &F, 0.5, 10.0);
//Run the solver until the root is found to within 0.001
int status;
do {
gsl_root_fsolver_iterate(s);
double r = gsl_root_fsolver_root(s);
double x_low = gsl_root_fsolver_x_lower(s);
double x_high = gsl_root_fsolver_x_upper(s);
status = gsl_root_test_interval(x_low, x_high, 0, 0.001);
if (status == GSL_SUCCESS)
cout << "Converged" << endl;
cout << "x_low = " << x_low;
cout << "; x_high = " << x_high;
cout << "; root = " << r << endl;
}
while (status == GSL_CONTINUE);
return status;
}