#!/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 = 33
# point
class Point
attr_accessor :x, :y
def initialize(x = nil, y = nil)
@x, @y = x, y
end
end
# model: a*exp(-((x-x0)**2+(y-y0)**2)/2/sigma**2)
gauss_f = Proc.new { |x, t, y, s, f|
# x: parameters as a Vecor
# t: observed points as an Array
# y: observed data as a GSL::Vector
# s: errorbar
# f: result
a = x[0]
x0 = x[1]
y0 = x[2]
sigma2 = x[3]**2
y.size.times do |i|
f.set(i, (a*Math::exp(-((t[i].x - x0)**2+(t[i].y - y0)**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]
y0 = x[2]
sigma = x[3]
sigma2 = sigma**2
y.size.times do |i|
dx = t[i].x - x0; dx2 = dx**2
dy = t[i].y - y0; dy2 = dy**2
f = a*Math::exp(-(dx2+dy2)/2/sigma2)
df.set(i, 0, f/a/s[i])
df.set(i, 1, f*dx/sigma2/s[i])
df.set(i, 2, f*dy/sigma2/s[i])
df.set(i, 3, f*(dx2+dy2)/sigma2/sigma/s[i])
end
GSL::SUCCESS
}
# goal
xgoal = GSL::Vector.alloc([1, 0, 0, 1])
parname = %w( a x0 y0 si )
# data
t = Array.new
tmin = -10.0
tmax = 10.0
N.times do |j|
N.times do |i|
t << Point.new(tmin + (tmax - tmin)*i/(N-1), tmin + (tmax - tmin)*j/(N-1))
end
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, -0.1, 2.0]) # initial guess
fdf = GSL::MultiFit::Function_fdf.alloc(gauss_f, gauss_df, x.size)
fdf.set_data(t, y, s)
solver = GSL::MultiFit::FdfSolver.alloc(GSL::MultiFit::FdfSolver::LMSDER, t.size, x.size)
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 - xresult.size
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
# allow resulting parameters to differ two times than standard error
end
puts "a*exp(-((x-x0)**2+(y-y0)**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])
if (xresult[i] - xgoal[i]).abs > xsigma[i] then
puts "#{'%-2.2s' % parname[i]} is #{xresult[i]} +- #{xsigma[i]}"
exit 1
end
end
syntax highlighted by Code2HTML, v. 0.9.1