GSL Shell provides support for nonlinear least squares fitting for user-defined data and functions. The data to fit can be either real or complex while the fitting parameters should be real numbers.
The problem of multidimensional nonlinear least-squares fitting requires the minimization of the squared residuals of n functions, fi, in p parameters, xi,
All algorithms proceed from an initial guess using the linearization,
where x is the initial point, p is the proposed step and J is the Jacobian matrix Jij = dfi / dxj. Additional strategies are used to enlarge the region of convergence. These include requiring a decrease in the norm ||F|| on each step or using a trust region to avoid steps which fall outside the linear regime.
To perform a weighted least-squares fit of a nonlinear model Y(x,t) to data (ti, yi) with independent gaussian errors σi, use function components of the following form
Note that the model parameters are denoted by x in this chapter since the non-linear least-squares algorithms are described geometrically (i.e. finding the minimum of a surface). The independent variable of any data to be fitted is denoted by t.
With the definition above the Jacobian is
, where
.
To perform a non-linear fit with GSL Shell there are two ways.
The simpler one use the function nlinfit() and can be used if the data depend on a single indipendent variable “x”. In this case you will supply two vectors, “x” and “y”, with the observations, a “model” function and the seeds for the fit. The seeds are the starting point for the search of the fit parameters.
The second method gives you more control and more flexibility over the non-linear fit. In this case you use directly the nlfsolver and you can advance in the fit step-by-step by using the method iterate().
In the following sections we give the details to use each method.
Here an example:
px = vector {1.55, -1.1, 12.5}
p0 = vector {2.5, -1.5, 5.3}
n = 50
xs = |i| (i-1)/n
r = rng()
f = function(p, x, J)
local e, s = exp(p[2] * x), sin(p[3] * x)
if J then
J:set(1,1, e * s)
J:set(1,2, x * p[1] * e * s)
J:set(1,3, x * p[1] * e * cos(p[3] * x))
end
return p[1] * e * s
end
y = new(n, 1, |i,j| f(px, xs(i)) * (1 + rnd.gaussian(r, 0.1)))
x = new(n, 1, |i,j| xs(i))
fit, pr = nlinfit(f, x, y, p0)
print('Fit result:', tr(pr))
pl = plot('Non-linear fit / A * exp(a t) sin(w t)')
pl:addline(xyline(x, y), 'blue', {{'marker', size= 5}})
pl:addline(fxline(|x| f(p0, x), 0, xs(n)), 'red', {{'dash', a=7, b=3}})
pl:addline(fxline(fit, 0, xs(n)), 'red')
pl:show()
The most basic method to perform a non linear fitting with GSL Shell does use the nlfsolver object. The logical steps to use a nonlinear fitting solver are:
We give an below the same example of the above section but by using directly the nlfsolver object. We first define some useful elements:
n = 50
px = vector {1.55, -1.1, 12.5}
p0 = vector {2.5, -1.5, 5.3}
xs = |i| (i-1)/n
Then we define the model function and the “fit function”. You can note that the “fit function” uses the model function to set the vector f and the vector J for each observation. Note also that for the “fit function” J is a n x p matrix while for the model J is a row matrix:
function fmodel(p, t, J)
local e, s = exp(p[2] * t), sin(p[3] * t)
if J then
J:set(1,1, e * s)
J:set(1,2, t * p[1] * e * s)
J:set(1,3, t * p[1] * e * cos(p[3] * t))
end
return p[1] * e * s
end
function expf(x, f, J)
for k=1, n do
local ym = fmodel(x, xs(k), J and J:row(k))
if f then f:set(k, 1, ym - y[k]) end
end
end
Then we create the data that we want to fit. We can use the model function itself by just adding some gaussian noise:
r = rng()
y = new(n, 1, |i,j| fmodel(px, xs(i)) * (1 + rnd.gaussian(r, 0.1)))
x = new(n, 1, |i,j| xs(i))
The we create a plot and iterate the non-linear solver as long as it is required to converge:
pl = plot('Non-linear fit / A * exp(a t) sin(w t)')
pl:addline(xyline(x, y), 'blue', {{'marker', size= 5}})
s = nlfsolver {fdf= expf, n= n, p0= p0}
pl:addline(fxline(|x| fmodel(s.p, x), 0, xs(n)), 'red', {{'dash', a=7, b=3}})
repeat
print('Chi square:', prod(s.f, s.f)[1], tr(s.p))
local status = s:iterate()
until status ~= 'continue'
print('Chi square:', prod(s.f, s.f)[1], tr(s.p))
pl:addline(fxline(|x| fmodel(s.p, x), 0, xs(n)), 'red')
pl:show()
The output you obtain is:
Non-linear fit of function A exp(a t) sin(w t) with gaussian noise
Nonlinear solver class for real numbers data.
Create a new solver for real data. The spec should be a table containing the following fields: