matlabcurve-fitting

Matlab function for lorentzian fit with global variables


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


Solution

  • 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: enter image description here