Use lsqnonlin to optimize parameters to fit the property of a function

4 views (last 30 days)
Andrea Rogolino on 6 Jul 2022
Edited: Torsten on 7 Jul 2022
Hi everyone,
I would like to use lsqnonline to optimize the parameters of a function with lsqnonlin. I already use it in the past, but now I am trying to adjust only one property of a parametrized function rather than the whole function itself.
For example, I am trying this trivial example: I have a sin function f=sin(x*q) and I want to optimize that q so to have a specific period of the sinus. Here is my script:
x=[0:0.1:1000]
q = 2
f = sind(x*q)
%find peaks and then make the difference between two consecutive peaks to
%find the period of the sinus
[pks, locs] = findpeaks(f,x)
p = (locs(2)-locs(1))
lb = [0];
ub = [1000];
param = q;
options = optimset('MaxFunEvals',30000,'MaxIter',10000,'Display','iter'); % specify options
[param_fitted, resnorm] = lsqnonlin(@fun,param,lb,ub);
q_fit=param_fitted
f_fit = sind(x*q_fit)
[pks, locs] = findpeaks(f_fit,x)
p_fit = (locs(3)-locs(2))
figure
plot(x,f,'b',x,f_fit,'r')
And the function fun wants to calculate the difference between the period with the parameter q and an arbitrary period=10.
function differences=fun(param)
q=param;
x=[0:0.1:1000]
f = sind(x*q)
[pks, locs] = findpeaks(f,x)
p = (locs(2)-locs(1))
differences = p-10;
I should expect to find the q that gives a sinus function with period 10, but the parameter is not optimized at all. What am I doing wrong?

Torsten on 6 Jul 2022
lsqnonlin slightly changes the input to "fun" from the first to the second call, but does not recognize any change of the output "differences". The solver concludes that a decrease in "differences" is not possible and that the initial point already is a local minimum.
format long
x=[0:0.1:1000];
q = 2;
f = sind(x*q);
%find peaks and then make the difference between two consecutive peaks to
%find the period of the sinus
[pks, locs] = findpeaks(f,x);
p = (locs(2)-locs(1));
lb = [0];
ub = [1000];
param = q;
options = optimset('MaxFunEvals',30000,'MaxIter',10000,'Display','iter'); % specify options
[param_fitted, resnorm] = lsqnonlin(@fun,param,lb,ub);
p =
180
differences =
170
p =
180
differences =
170
Initial point is a local minimum. Optimization completed because the size of the gradient at the initial point is less than the value of the optimality tolerance.
fun(param_fitted)
p =
180
differences =
170
ans =
170
q_fit=param_fitted;
f_fit = sind(x*q_fit)
f_fit = 1×10001
0 0.003490651415224 0.006981260297962 0.010471784116246 0.013962180339145 0.017452406437284 0.020942419883357 0.024432178152653 0.027921638723569 0.031410759078128 0.034899496702501 0.038387809087520 0.041875653729200 0.045362988129254 0.048849769795613 0.052335956242944 0.055821504993164 0.059306373575962 0.062790519529313 0.066273900400000 0.069756473744125 0.073238197127632 0.076719028126819 0.080198924328859 0.083677843332315 0.087155742747658 0.090632580197780 0.094108313318514 0.097582899759149 0.101056297182946
[pks, locs] = findpeaks(f_fit,x);
p_fit = (locs(3)-locs(2));
figure
plot(x,f,'b',x,f_fit,'r')
function differences=fun(param)
q=param;
x=[0:0.1:1000];
f = sind(x*q);
[pks, locs] = findpeaks(f,x);
p = (locs(2)-locs(1))
differences = p-10
end
Torsten on 7 Jul 2022
Edited: Torsten on 7 Jul 2022
No, the reason is that the x-array
x=[0:0.1:1000]
is too coarse for findpeaks to find the peaks with sufficient accuracy. Try
x = [0:1e-6:1000]
or something similar and set the options parameter "FinDiffRelStep" to a higher value than the default.
Or just compute the peaks analytically by differentiating sin(x*q) and setting the result to 0.