Hi, I'm trying to replicate the Levenberg-Marquardt optimization on an exponential curve from here: www .csse.uwa.edu.au/programming/gsl-1.0/gsl-ref_35.html in alglib with F#.
When I run what I have, it calculates the Jacobian and then terminates saying it met the gradient condition(4).
I figure I'm either calculating the Jacobian incorrectly or I'm using some other part of the library incorrectly. I've been stuck on this for a day now, and I figure it should be something obvious about how to setup the end conditions or a fundamental misunderstanding of the optimizer.
Here's the alglib code in F# I'm using now:
Code:
let expbF (expData : float[]) (x : float[]) (fi : float[]) myobj =
for i in 0..fi.Length-1 do
fi.[i] <- ((x.[0] * System.Math.Exp(-x.[1] * float i) + x.[2]) - expData.[i]) / 0.1
let expbDf (x : float[]) (fi : double[]) (jac : double[,]) myobj =
let a = x.[0]
let lambda = x.[1]
for i in 0..fi.Length-1 do
let t = float i
let e = System.Math.Exp(-lambda * t)
jac.[i, 0] <- e / 0.1
jac.[i, 1] <- -t * a * e / 0.1
jac.[i, 2] <- 1.0 / 0.1
let testDV rawExpData =
let mutable fitExp : float[] = [|1.0; 0.0; 0.0; |]
let mutable state : alglib.minlmstate = (alglib.minlmstate())
let mutable rep : alglib.minlmreport = alglib.minlmreport()
alglib.minlmcreatevj(Array.length rawExpData, fitExp, &state)
alglib.minlmsetcond(state, 0.0, 0.0, 0.0, 0)
alglib.minlmoptimize(state, alglib.ndimensional_fvec(expbF rawExpData), expbDf, null, null )
alglib.minlmresults(state, &fitExp, &rep)
rep.terminationtype, alglib.ap.format(fitExp, 2)
I'm calling testDV with an array with the same values for the data that I'm generating in C++, but it doesn't matter because it never even calls expbF. I tried to follow the source, but I got really lost in the many different cases/gotos of minlmiteration.
And I'll in line my slightly modified GSL C++ code that I'm running to compare as well (VS2015):
Code:
namespace GSL {
#include <gsl/gsl_blas.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_multifit_nlin.h>
#include <gsl/gsl_fit.h>
}
struct data {
size_t n;
const double * y;
};
int expb_f(const GSL::gsl_vector * x, void *data, GSL::gsl_vector * f)
{
size_t n = ((struct data *)data)->n;
const double *y = ((struct data *)data)->y;
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++)
{
/* Model Yi = A * exp(-lambda * i) + b */
double t = i;
double Yi = A * exp(-lambda * t) + b;
gsl_vector_set(f, i, (Yi - y[i]) / 0.1);
}
return GSL::GSL_SUCCESS;
}
int expb_df(const GSL::gsl_vector * x, void *data, GSL::gsl_matrix * J)
{
size_t n = ((struct data *)data)->n;
double A = GSL::gsl_vector_get(x, 0);
double lambda = GSL::gsl_vector_get(x, 1);
size_t i;
for (i = 0; i < n; i++)
{
/* Jacobian matrix J(i,j) = dfi / dxj, */
/* where fi = (Yi - yi)/sigma[i], */
/* Yi = A * exp(-lambda * i) + b */
/* and the xj are the parameters (A,lambda,b) */
double t = i;
double s = 0.1;
double e = exp(-lambda * t);
gsl_matrix_set(J, i, 0, e / s);
gsl_matrix_set(J, i, 1, -t * A * e / s);
gsl_matrix_set(J, i, 2, 1 / s);
}
return GSL::GSL_SUCCESS;
}
int expb_fdf(const GSL::gsl_vector * x, void *data, GSL::gsl_vector * f, GSL::gsl_matrix * J)
{
expb_f(x, data, f);
expb_df(x, data, J);
return GSL::GSL_SUCCESS;
}
double* getTestData() {
const size_t n = 40;
auto y = new double[n];
GSL::gsl_rng_env_setup();
auto type = GSL::gsl_rng_default;
GSL::gsl_rng * r;
r = GSL::gsl_rng_alloc(type);
for (size_t i = 0; i < n; i++)
{
double t = i;
y[i] = 1.0 + 5 * exp(-0.1 * t) + gsl_ran_gaussian(r, 0.1);
};
return y;
}
int main(void)
{
double* const y = ExpFit::getTestData();
const GSL::gsl_multifit_fdfsolver_type * T = GSL::gsl_multifit_fdfsolver_lmsder;
GSL::gsl_multifit_fdfsolver * s = gsl_multifit_fdfsolver_alloc(T, 40, 3);
struct data d = { 40, y };
GSL::gsl_multifit_function_fdf f;
f.f = &expb_f;
f.df = &expb_df;
f.fdf = &expb_fdf;
f.n = 40;
f.p = 3;
f.params = &d;
double x_init[3] = { 1.0, 0.0, 0.0 };
GSL::gsl_vector_view x = GSL::gsl_vector_view_array(x_init, 3);
gsl_multifit_fdfsolver_set(s, &f, &x.vector);
int status;
unsigned int iter = 0;
do
{
iter++;
status = gsl_multifit_fdfsolver_iterate(s);
if (status)
{
break;
}
status = gsl_multifit_test_delta(s->dx, s->x, 1e-4, 1e-4);
} while (status == GSL::GSL_CONTINUE && iter < 500);
gsl_multifit_fdfsolver_free(s);
delete[](y)
return 0;
}
Thoughts?
At the very least this code should be a good example of this for others when it's done. I assumed this sort of thing would be more common.