使用 GSL 的非线性拟合
Non-Linear Fit Using GSL
所以我试图修改我发现 here 的一些代码以适应不同的功能,但我稍微修改后的版本无法收敛,我不明白为什么。
我试图找到最小二乘拟合的函数是“A + lambdalog(t) + blog(t)^2。这是代码
main.cpp
#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_multifit_nlin.h>
#include "expfit.c"
#define N 40 // All "N"s are 40
void print_state (size_t iter, gsl_multifit_fdfsolver * s);//The prototype of some function
int main (void){
const gsl_multifit_fdfsolver_type *T; //pointer to a newly allocated instance of a solver of type T
gsl_multifit_fdfsolver *s;
int status;
unsigned int i, iter = 0;
const size_t n = N; //N number of observations
const size_t p = 3;//3 parameters
gsl_matrix *covar = gsl_matrix_alloc (p, p); // creates a pxp gsl_matrix
double y[N], sigma[N]; //declares vector variables that will hold the noise data. They are "N" long
struct data d = { n, y, sigma}; //Populates struct d with variables n, y and sigma. Struct data is defined in expfit.c
gsl_multifit_function_fdf f;
double x_init[3] = { 0, -.1, -.1 }; //initial x values !These are initial guesses to the solution!
gsl_vector_view x = gsl_vector_view_array (x_init, p);//view arrays allow one to litterally view elements of a certain array without modifying or created a copy of the original array. Essentially a pointer to the original data.
const gsl_rng_type * type; // pointer to a new random number generator type. RNG type will be assigned later
gsl_rng * r; //Pointer to a new RNG
gsl_rng_env_setup();
type = gsl_rng_default; //Assigns random number generator type
r = gsl_rng_alloc (type); // Allocates memory for new RNG of type "type"
f.f = &logb_f;
f.df = &logb_df;
f.fdf = &logb_fdf;
f.n = n;
f.p = p;
f.params = &d;
for (i = 0; i < n; i++){// This is where the data is being generated
double t = i; // t is being redclared at each iteration for some reason wtf
if(t==0){//since log(0) is undefined, I said they equal 0 at t=0
y[i] = 2.0 -.5 * 0 - 0 + gsl_ran_gaussian (r, 0.1);
}else{
y[i] = 2.0 -.5 * log (t) - pow(log(t),2) + gsl_ran_gaussian (r, 0.1); //This is the noised up data
}
sigma[i] = .1; //not sure what this sigma does
printf ("data: %u %g %g\n", i, y[i], sigma[i]);//Printing out the data at each iteration
};
T = gsl_multifit_fdfsolver_lmsder; // Not sure what this is doing
s = gsl_multifit_fdfsolver_alloc (T, n, p);
gsl_multifit_fdfsolver_set (s, &f, &x.vector);
print_state (iter, s);
do{
iter++;
status = gsl_multifit_fdfsolver_iterate (s);
printf ("status = %s\n", gsl_strerror (status));
print_state (iter, s);
if (status)
break;
status = gsl_multifit_test_delta (s->dx, s->x, 1e-4, 1e-4);
}while (status == GSL_CONTINUE && iter < 500);
gsl_multifit_covar (s->J, 0.0, covar);
#define FIT(i) gsl_vector_get(s->x, i)
#define ERR(i) sqrt(gsl_matrix_get(covar,i,i))
{
double chi = gsl_blas_dnrm2(s->f);
double dof = n - p;
double c = GSL_MAX_DBL(1, chi / sqrt(dof));
printf("chisq/dof = %g\n", pow(chi, 2.0) / dof);
printf ("A = %.5f +/- %.5f\n", FIT(0), c*ERR(0));
printf ("lambda = %.5f +/- %.5f\n", FIT(1), c*ERR(1));
printf ("b = %.5f +/- %.5f\n", FIT(2), c*ERR(2));
}
printf ("status = %s\n", gsl_strerror (status));
gsl_multifit_fdfsolver_free (s);
gsl_matrix_free (covar);
gsl_rng_free (r);
return 0;
}
void print_state (size_t iter, gsl_multifit_fdfsolver * s){
printf ("iter: %3zu x = % 15.8f % 15.8f % 15.8f "
"|f(x)| = %g\n",
iter,
gsl_vector_get (s->x, 0),
gsl_vector_get (s->x, 1),
gsl_vector_get (s->x, 2),
gsl_blas_dnrm2 (s->f));
}
expfit.c
//
// expfit.c
// test
//
// Created by [] on 4/11/15.
// Copyright (c) 2015 []. All rights reserved.
//
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_multifit_nlin.h>
#include "expfit.h"
/* expfit.c -- model functions for exponential + background */
struct data {
size_t n;
double * y;
double * sigma;
};
int logb_f (const gsl_vector * x, void *data, gsl_vector * f){
size_t n = ((struct data *)data)->n;
double *y = ((struct data *)data)->y;
double *sigma = ((struct data *) data)->sigma;
double A = gsl_vector_get (x, 0);
double lambda = gsl_vector_get (x, 1);
double b = gsl_vector_get (x, 2);
double Yi;//will hold the value of the function to be stored into the vector set
double t;//time variable.
size_t i;//iterative variable
for (i = 0; i < n; i++){
/* Model Yi = A + lambda*log(i) + b*lambda*log(i)^2 */
t = i;
if(t==0){ //need if statement to bypass log(0) when t==0 since the value of log is undefined there
Yi = A + lambda * log(t) + b * pow(log(t),2);//function for t==0
}else{
Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
gsl_vector_set (f, i, (Yi - y[i])/sigma[i]);
}
return GSL_SUCCESS;
}
int logb_df (const gsl_vector * x, void *data, gsl_matrix * J){
size_t n = ((struct data *)data)->n;
double *sigma = ((struct data *) data)->sigma;
//double A = gsl_vector_get (x, 0);
//double lambda = gsl_vector_get (x, 1);
//double b = gsl_vector_get(x,2);
size_t i;
for (i = 0; i < n; i++){
/* Jacobian matrix J(i,j) = dfi / dxj, */
/* where fi = (Yi - yi)/sigma[i], */
/* Yi = A + lambda*log(i) + b*log(i)^2 */
/* and the xj are the parameters (A,lambda,b) */
double t = i;
double s = sigma[i];
gsl_matrix_set (J, i, 0, 1/s);
gsl_matrix_set (J, i, 1, log(t)/s);
gsl_matrix_set (J, i, 2, pow(log(t),2)/s);
}
return GSL_SUCCESS;
}
int logb_fdf (const gsl_vector * x, void *data, gsl_vector * f, gsl_matrix * J){
logb_f (x, data, f);
logb_df (x, data, J);
return GSL_SUCCESS;
}
这是头文件,以备不时之需
//
// expfit.h
// test
//
// Created by [] on 4/11/15.
// Copyright (c) 2015 []. All rights reserved.
//
#ifndef __test__expfit__
#define __test__expfit__
#include <stdio.h>
#endif /* defined(__test__expfit__) */
计算拟合函数时,考虑特例t=0以避免log(0),但函数值没有区别:
if(t==0){
Yi = A + lambda * log(t) + b * pow(log(t),2);//function for t==0
}else{
Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
此外,您在计算导数时没有考虑这种情况。
因此,我将函数和导数更改如下:
int logb_f (const gsl_vector * x, void *data, gsl_vector * f){
size_t n = ((struct data *)data)->n;
double *y = ((struct data *)data)->y;
double *sigma = ((struct data *) data)->sigma;
double A = gsl_vector_get (x, 0);
double lambda = gsl_vector_get (x, 1);
double b = gsl_vector_get (x, 2);
double Yi;//will hold the value of the function to be stored into the vector set
double t;//time variable.
size_t i;//iterative variable
for (i = 0; i < n; i++){
/* Model Yi = A + lambda*log(i) + b*lambda*log(i)^2 */
t = i;
if(t==0){ //need if statement to bypass log(0) when t==0 since the value of log is undefined there
Yi = A ;
}else{
Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
//Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
gsl_vector_set (f, i, (Yi - y[i])/sigma[i]);
}
return GSL_SUCCESS;
}
int logb_df (const gsl_vector * x, void *data, gsl_matrix * J){
size_t n = ((struct data *)data)->n;
double *sigma = ((struct data *) data)->sigma;
//double A = gsl_vector_get (x, 0);
//double lambda = gsl_vector_get (x, 1);
//double b = gsl_vector_get(x,2);
size_t i;
for (i = 0; i < n; i++){
/* Jacobian matrix J(i,j) = dfi / dxj, */
/* where fi = (Yi - yi)/sigma[i], */
/* Yi = A + lambda*log(i) + b*log(i)^2 */
/* and the xj are the parameters (A,lambda,b) */
// d
double t = i;
double s = sigma[i];
if(i == 0)
{
gsl_matrix_set (J, i, 0, 0);
gsl_matrix_set (J, i, 1, 0);
gsl_matrix_set (J, i, 2, 0);
}
else
{
gsl_matrix_set (J, i, 0, 1/s);
gsl_matrix_set (J, i, 1, log(t)/s);
gsl_matrix_set (J, i, 2, pow(log(t),2)/s);
}
//Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
return GSL_SUCCESS;
}
在这种情况下迭代收敛:
iter: 0 x = 0.00000000 -0.10000000 -0.10000000 |f(x)| = 470.77
status = success
iter: 1 x = 2.08763815 -0.60282892 -0.97819822 |f(x)| = 5.5047
status = success
iter: 2 x = 2.08763815 -0.60282892 -0.97819822 |f(x)| = 5.5047
chisq/dof = 0.818964
A = 2.08764 +/- 0.08245
lambda = -0.60283 +/- 0.07702
b = -0.97820 +/- 0.01722
status = success
但是,我建议验证拟合函数对 t -> 0 的可微性。
如果有疑问,您还可以通过仅考虑 t > 0 的值来限制上述函数中的拟合范围:
for (i = 1; i < n; i++) ...
而不是 for (i = 0; i < n; i++) ...
所以我试图修改我发现 here 的一些代码以适应不同的功能,但我稍微修改后的版本无法收敛,我不明白为什么。
我试图找到最小二乘拟合的函数是“A + lambdalog(t) + blog(t)^2。这是代码
main.cpp
#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_multifit_nlin.h>
#include "expfit.c"
#define N 40 // All "N"s are 40
void print_state (size_t iter, gsl_multifit_fdfsolver * s);//The prototype of some function
int main (void){
const gsl_multifit_fdfsolver_type *T; //pointer to a newly allocated instance of a solver of type T
gsl_multifit_fdfsolver *s;
int status;
unsigned int i, iter = 0;
const size_t n = N; //N number of observations
const size_t p = 3;//3 parameters
gsl_matrix *covar = gsl_matrix_alloc (p, p); // creates a pxp gsl_matrix
double y[N], sigma[N]; //declares vector variables that will hold the noise data. They are "N" long
struct data d = { n, y, sigma}; //Populates struct d with variables n, y and sigma. Struct data is defined in expfit.c
gsl_multifit_function_fdf f;
double x_init[3] = { 0, -.1, -.1 }; //initial x values !These are initial guesses to the solution!
gsl_vector_view x = gsl_vector_view_array (x_init, p);//view arrays allow one to litterally view elements of a certain array without modifying or created a copy of the original array. Essentially a pointer to the original data.
const gsl_rng_type * type; // pointer to a new random number generator type. RNG type will be assigned later
gsl_rng * r; //Pointer to a new RNG
gsl_rng_env_setup();
type = gsl_rng_default; //Assigns random number generator type
r = gsl_rng_alloc (type); // Allocates memory for new RNG of type "type"
f.f = &logb_f;
f.df = &logb_df;
f.fdf = &logb_fdf;
f.n = n;
f.p = p;
f.params = &d;
for (i = 0; i < n; i++){// This is where the data is being generated
double t = i; // t is being redclared at each iteration for some reason wtf
if(t==0){//since log(0) is undefined, I said they equal 0 at t=0
y[i] = 2.0 -.5 * 0 - 0 + gsl_ran_gaussian (r, 0.1);
}else{
y[i] = 2.0 -.5 * log (t) - pow(log(t),2) + gsl_ran_gaussian (r, 0.1); //This is the noised up data
}
sigma[i] = .1; //not sure what this sigma does
printf ("data: %u %g %g\n", i, y[i], sigma[i]);//Printing out the data at each iteration
};
T = gsl_multifit_fdfsolver_lmsder; // Not sure what this is doing
s = gsl_multifit_fdfsolver_alloc (T, n, p);
gsl_multifit_fdfsolver_set (s, &f, &x.vector);
print_state (iter, s);
do{
iter++;
status = gsl_multifit_fdfsolver_iterate (s);
printf ("status = %s\n", gsl_strerror (status));
print_state (iter, s);
if (status)
break;
status = gsl_multifit_test_delta (s->dx, s->x, 1e-4, 1e-4);
}while (status == GSL_CONTINUE && iter < 500);
gsl_multifit_covar (s->J, 0.0, covar);
#define FIT(i) gsl_vector_get(s->x, i)
#define ERR(i) sqrt(gsl_matrix_get(covar,i,i))
{
double chi = gsl_blas_dnrm2(s->f);
double dof = n - p;
double c = GSL_MAX_DBL(1, chi / sqrt(dof));
printf("chisq/dof = %g\n", pow(chi, 2.0) / dof);
printf ("A = %.5f +/- %.5f\n", FIT(0), c*ERR(0));
printf ("lambda = %.5f +/- %.5f\n", FIT(1), c*ERR(1));
printf ("b = %.5f +/- %.5f\n", FIT(2), c*ERR(2));
}
printf ("status = %s\n", gsl_strerror (status));
gsl_multifit_fdfsolver_free (s);
gsl_matrix_free (covar);
gsl_rng_free (r);
return 0;
}
void print_state (size_t iter, gsl_multifit_fdfsolver * s){
printf ("iter: %3zu x = % 15.8f % 15.8f % 15.8f "
"|f(x)| = %g\n",
iter,
gsl_vector_get (s->x, 0),
gsl_vector_get (s->x, 1),
gsl_vector_get (s->x, 2),
gsl_blas_dnrm2 (s->f));
}
expfit.c
//
// expfit.c
// test
//
// Created by [] on 4/11/15.
// Copyright (c) 2015 []. All rights reserved.
//
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_multifit_nlin.h>
#include "expfit.h"
/* expfit.c -- model functions for exponential + background */
struct data {
size_t n;
double * y;
double * sigma;
};
int logb_f (const gsl_vector * x, void *data, gsl_vector * f){
size_t n = ((struct data *)data)->n;
double *y = ((struct data *)data)->y;
double *sigma = ((struct data *) data)->sigma;
double A = gsl_vector_get (x, 0);
double lambda = gsl_vector_get (x, 1);
double b = gsl_vector_get (x, 2);
double Yi;//will hold the value of the function to be stored into the vector set
double t;//time variable.
size_t i;//iterative variable
for (i = 0; i < n; i++){
/* Model Yi = A + lambda*log(i) + b*lambda*log(i)^2 */
t = i;
if(t==0){ //need if statement to bypass log(0) when t==0 since the value of log is undefined there
Yi = A + lambda * log(t) + b * pow(log(t),2);//function for t==0
}else{
Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
gsl_vector_set (f, i, (Yi - y[i])/sigma[i]);
}
return GSL_SUCCESS;
}
int logb_df (const gsl_vector * x, void *data, gsl_matrix * J){
size_t n = ((struct data *)data)->n;
double *sigma = ((struct data *) data)->sigma;
//double A = gsl_vector_get (x, 0);
//double lambda = gsl_vector_get (x, 1);
//double b = gsl_vector_get(x,2);
size_t i;
for (i = 0; i < n; i++){
/* Jacobian matrix J(i,j) = dfi / dxj, */
/* where fi = (Yi - yi)/sigma[i], */
/* Yi = A + lambda*log(i) + b*log(i)^2 */
/* and the xj are the parameters (A,lambda,b) */
double t = i;
double s = sigma[i];
gsl_matrix_set (J, i, 0, 1/s);
gsl_matrix_set (J, i, 1, log(t)/s);
gsl_matrix_set (J, i, 2, pow(log(t),2)/s);
}
return GSL_SUCCESS;
}
int logb_fdf (const gsl_vector * x, void *data, gsl_vector * f, gsl_matrix * J){
logb_f (x, data, f);
logb_df (x, data, J);
return GSL_SUCCESS;
}
这是头文件,以备不时之需
//
// expfit.h
// test
//
// Created by [] on 4/11/15.
// Copyright (c) 2015 []. All rights reserved.
//
#ifndef __test__expfit__
#define __test__expfit__
#include <stdio.h>
#endif /* defined(__test__expfit__) */
计算拟合函数时,考虑特例t=0以避免log(0),但函数值没有区别:
if(t==0){
Yi = A + lambda * log(t) + b * pow(log(t),2);//function for t==0
}else{
Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
此外,您在计算导数时没有考虑这种情况。 因此,我将函数和导数更改如下:
int logb_f (const gsl_vector * x, void *data, gsl_vector * f){
size_t n = ((struct data *)data)->n;
double *y = ((struct data *)data)->y;
double *sigma = ((struct data *) data)->sigma;
double A = gsl_vector_get (x, 0);
double lambda = gsl_vector_get (x, 1);
double b = gsl_vector_get (x, 2);
double Yi;//will hold the value of the function to be stored into the vector set
double t;//time variable.
size_t i;//iterative variable
for (i = 0; i < n; i++){
/* Model Yi = A + lambda*log(i) + b*lambda*log(i)^2 */
t = i;
if(t==0){ //need if statement to bypass log(0) when t==0 since the value of log is undefined there
Yi = A ;
}else{
Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
//Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
gsl_vector_set (f, i, (Yi - y[i])/sigma[i]);
}
return GSL_SUCCESS;
}
int logb_df (const gsl_vector * x, void *data, gsl_matrix * J){
size_t n = ((struct data *)data)->n;
double *sigma = ((struct data *) data)->sigma;
//double A = gsl_vector_get (x, 0);
//double lambda = gsl_vector_get (x, 1);
//double b = gsl_vector_get(x,2);
size_t i;
for (i = 0; i < n; i++){
/* Jacobian matrix J(i,j) = dfi / dxj, */
/* where fi = (Yi - yi)/sigma[i], */
/* Yi = A + lambda*log(i) + b*log(i)^2 */
/* and the xj are the parameters (A,lambda,b) */
// d
double t = i;
double s = sigma[i];
if(i == 0)
{
gsl_matrix_set (J, i, 0, 0);
gsl_matrix_set (J, i, 1, 0);
gsl_matrix_set (J, i, 2, 0);
}
else
{
gsl_matrix_set (J, i, 0, 1/s);
gsl_matrix_set (J, i, 1, log(t)/s);
gsl_matrix_set (J, i, 2, pow(log(t),2)/s);
}
//Yi = A + lambda * log(t) + b * pow(log(t),2); // This is the function we used
}
return GSL_SUCCESS;
}
在这种情况下迭代收敛:
iter: 0 x = 0.00000000 -0.10000000 -0.10000000 |f(x)| = 470.77
status = success
iter: 1 x = 2.08763815 -0.60282892 -0.97819822 |f(x)| = 5.5047
status = success
iter: 2 x = 2.08763815 -0.60282892 -0.97819822 |f(x)| = 5.5047
chisq/dof = 0.818964
A = 2.08764 +/- 0.08245
lambda = -0.60283 +/- 0.07702
b = -0.97820 +/- 0.01722
status = success
但是,我建议验证拟合函数对 t -> 0 的可微性。
如果有疑问,您还可以通过仅考虑 t > 0 的值来限制上述函数中的拟合范围:
for (i = 1; i < n; i++) ...
而不是 for (i = 0; i < n; i++) ...