#!/usr/bin/env ruby
# vim: set ts=8 sw=2 sts=2:
#require("gsl")
#require("../gsl_test2.rb")
require("./test_multifit.rb")
include GSL::Test
include Math
Maxiter = 10
N = 1000
# model: a*exp(-(x-x0)**2/2/sigma**2)
gauss_p = 3
gauss_f = Proc.new { |x, t, y, s, f|
# x: parameters as a Vecor
# t: observed points as a GSL::Vector
# y: observed data as a GSL::Vector
# s: errorbar
# f: result
a = x[0]
x0 = x[1]
sigma2 = x[2]**2
y.size.times do |i|
f.set(i, (a*Math::exp(-(t[i] - x0)**2/2/sigma2) - y[i])/s[i])
end
GSL::SUCCESS
}
gauss_df = Proc.new { |x, t, y, s, df|
a = x[0]
x0 = x[1]
sigma = x[2]
sigma2 = sigma**2
y.size.times do |i|
dx = t[i] - x0
dx2 = dx**2
f = a*Math::exp(-dx2/2/sigma2)
df.set(i, 0, f/a/s[i])
df.set(i, 1, f*dx/sigma2/s[i])
df.set(i, 2, f*dx2/sigma2/sigma/s[i])
end
GSL::SUCCESS
}
# goal
xgoal = GSL::Vector.alloc([1, 0, 1])
parname = %w( a x0 si )
# data
t = GSL::Vector.alloc(N) # positions of data
tmin = -10.0
tmax = 10.0
t.size.times do |i|
t[i] = tmin + (tmax - tmin)*i/(N-1)
end
stdev = xgoal[0]*0.1
s = GSL::Vector.alloc(Array.new(t.size, stdev)) # error bar of each datum
r = Rng.alloc
e = GSL::Vector.alloc(t.size)
t.size.times do |i|
e[i] = -r.gaussian(stdev) # perturbation to data
end
y = GSL::Vector.alloc(t.size)
n = GSL::Vector.alloc(Array.new(t.size, 1.0))
gauss_f.call(xgoal, t, e, n, y) # data: y = model - e
# fitting
x = GSL::Vector.alloc([0.5, 0.1, 2]) # initial guess
fdf = GSL::MultiFit::Function_fdf.alloc(gauss_f, gauss_df, gauss_p)
fdf.set_data(t, y, s)
solver = GSL::MultiFit::FdfSolver.alloc(GSL::MultiFit::FdfSolver::LMSDER, t.size, gauss_p)
solver.set(fdf, x)
solver.print_state(0)
Maxiter.times do |i|
solver.iterate
status = solver.test_delta(1e-6, 1e-6)
solver.print_state(i+1)
break unless GSL::CONTINUE == status
end
# results
covar = solver.covar(0.0)
xresult = solver.position
dof = t.size - gauss_p
chi2 = pow_2(solver.f.dnrm2)
xsigma = GSL::Vector.alloc(xresult.size)
xresult.size.times do |i|
xsigma[i] = Math::sqrt(chi2/dof*covar[i][i]) * 2.0
# resulting parameters to differ two times than standard error
end
puts("a*exp(-(x-x0)**2/2/si**2), chi2/N:%.3g" % (chi2/t.size))
xresult.size.times do |i|
test_rel(xresult[i], xgoal[i], xsigma[i], "%-2.2s" % parname[i])
exit 1 if (xresult[i] - xgoal[i]).abs > xsigma[i]
end
syntax highlighted by Code2HTML, v. 0.9.1