I want to fit a Lorentzian to my data, so first I want to test my fitting procedure to simulated data:
X = linspace(0,100,200);
Y = 20./((X-30).^2+20)+0.08*randn(size(X));
starting parameters
a3 = ((max(X)-min(X))/10)^2;
a2 = (max(X)+min(X))/2;
a1 = max(Y)*a3;
a0 = [a1,a2,a3];
find minimum for fit
afinal = fminsearch(@devsum,a0);
afinal is vector with parameters for my fit. If I test my function as follows
d= devsum(a0)
then d= 0, but if I do exactly what's in my function
a=a0;
d = sum((Y - a(1)./((X-a(2)).^2+a(3))).^2)
then d is not equal to zero. How is this possible? My function is super simple so I don't know what's going wrong. my function:
%devsum.m
function d = devsum(a)
global X Y
d = sum((Y - a(1)./((X-a(2)).^2+a(3))).^2);
end
Basically I'm just implementing stuff I found here http://www.home.uni-osnabrueck.de/kbetzler/notes/fitp.pdf page 7
It is usually better to avoid using global variables. The way I usually solve these problems is to first define a function which evaluates the curve you want to fit as a function of x
and the parameters:
% lorentz.m
function y = lorentz(param, x)
y = param(1) ./ ((x-param(2)).^2 + param(3))
In this way, you can reuse the function later for plotting the result of the fit.
Then, you define a small anonymous function with the property you want to minimize, with only a single parameter as input, since that is the format that fminsearch
needs. Instead of using global variables, the measured X
and Y
are 'captured' (technical term is doing a closure over these variables) in the definition of the anonymous function:
fit_error = @(param) sum((y_meas - lorentz(param, x_meas)).^2)
And finally you fit your parameters by minimizing the error with fminsearch
:
fitted_param = fminsearch(fit_error, starting_param);
Quick demonstration:
% simulate some data
X = linspace(0,100,200);
Y = 20./((X-30).^2+20)+0.08*randn(size(X));
% rough guess of initial parameters
a3 = ((max(X)-min(X))/10)^2;
a2 = (max(X)+min(X))/2;
a1 = max(Y)*a3;
a0 = [a1,a2,a3];
% define lorentz inline, instead of in a separate file
lorentz = @(param, x) param(1) ./ ((x-param(2)).^2 + param(3));
% define objective function, this captures X and Y
fit_error = @(param) sum((Y - lorentz(param, X)).^2);
% do the fit
a_fit = fminsearch(fit_error, a0);
% quick plot
x_grid = linspace(min(X), max(X), 1000); % fine grid for interpolation
plot(X, Y, '.', x_grid, lorentz(a_fit, x_grid), 'r')
legend('Measurement', 'Fit')
title(sprintf('a1_fit = %g, a2_fit = %g, a3_fit = %g', ...
a_fit(1), a_fit(2), a_fit(3)), 'interpreter', 'none')
Result: