MATLAB: Lsqcurvefit problem. Error using snls (line 48) Objective function is returning undefined values at initial point. lsqcurvefit cannot continue.

lsqcurvefitnon linear curve fitsnls

Hello, I´ve been using matlab since a few years now for data manipulation and non linear fitting. So I´m feeling quite dumb not being able to solve this. Perhaps the problem arises from the fact that I´ve been using matlab 2017 and 2018 up to recently and I had to switch to R2012b recently.
Attached are the x and y data I´d been trying to fit with a simple exponential decay. The code I´m using is the following
fun=@(x,I) x(1)+x(2).*exp(-I/x(3));
ub=[1 100 1000];
lb=[1e-5 1e-5 10];
x0=[1.17e-4 0.00272 114.31287];
options=optimset('MaxIter',2000,'TolFun',1.e-5,'TolX',1.e-5,'Algorithm', ...
'trust-region-reflective',...
'MaxFunEvals',2000,'Display','iter');
x = lsqcurvefit(fun,x0,xdata,ydata,lb,ub,options);
The seed values are correct. I get the following error
Error using snls (line 48) Objective function is returning undefined values at initial point. lsqcurvefit cannot continue.
I don´t get whats wrong, the objective function looks defined at the initial point to me.
Thanks in advanced.
Luis

Best Answer

There are multiple issues here to deal with.
First of all, why is the objective returning undefined values? Probably because you have a NaN in your data.
find(isnan(ydata))
ans =
201
find(isnan(xdata))
ans =
1×0 empty double row vector
So ydata(201) is a NaN.
Next I'll check your start point, and the numerics in general.
x0=[1.17e-4 0.00272 114.31287];
plot(xdata,ydata,'o')
grid on
You are using this model
y = a(1) + a(2)*exp(-x*/a(3))
which at a glance, seems fine.
So lets see how good your start values are, just by use of some pencil and paper estimates.
For that model, the asymptote at x==inf is a(1). Looking at the data in that plot, I'd guess 1.5e-4 for the asymptote. So 1.17e-4 is not bad, within 50% of my guess. Since the curve has essentially bottomed out over the last 25 points or so, this should give me a better guess:
nanmean(ydata(175:end))
ans =
0.00015741
That seems to agree with my graphical estimate.
As far as the exponential rate parameter is concerned, a quick graphical estimate would have me guess a(3) should be roughly 100. So, it looks like you have chosen reasonably well.
Next, you have one NaN value in your data. The last point was a NaN in ydata. That won't really hurt anything as long as the tool you use for the fit excludes it. The problem is that lsqcurvefit does not seem to exclude NaNs in the data.
Next, I'll use my own fminspleas tool (found on the file exchange) to do the fit.
mdlterms = {1,@(C,x) exp(-x./C)};
[nlt,lint] = fminspleas(mdl,10,xdata(1:end-1),ydata(1:end-1))
nlt =
119.06
lint =
0.00019825
0.0088797
So it estimates the model as:
1.9825e-4 + 8.8797e-3*exp(-x/119.06)
That seems in line with the estimates I found myself, and within shooting range of what you used as starting values.
plot(xdata,ydata,'o')
grid on
hold on
yhat = 1.9825e-4 + 8.8797e-3*exp(-xdata/119.06);
plot(xdata,yhat,'r-')
which, as curve fits go, is not too bad. I'd guess there is a little lack of fit in the tail. Along with that, it seems like there is a larger amount of noise at the bottom end of the curve. It seems like non-uniformity in the error, so heteroscadisticity, but that could be just the high slope of the curve on the left end is quite high, so we miss seeing any noise one that end.
A regression spline fit will resolve that question. So my SLM toolbox:
mdl = slmengine(xdata,ydata,'plot','on','decreasing','on','concaveup','on');
Then the residuals:
So here we see the residuals for a good fit are roughly the same size along the entire curve. The high slope just hides the noise on that end.
Otherwise, what you did should work acceptably.
fun=@(x,I) x(1)+x(2).*exp(-I/x(3));
ub=[1 100 1000];
lb=[1e-5 1e-5 10];
x0=[1.17e-4 0.00272 114.31287];
options=optimset('MaxIter',2000,'TolFun',1.e-5,'TolX',1.e-5,'Algorithm', ...
'trust-region-reflective',...
'MaxFunEvals',2000,'Display','iter');
k = ~isnan(ydata) & ~isnan(xdata);
x = lsqcurvefit(fun,x0,xdata(k),ydata(k),lb,ub,options);
It now works fine.
Norm of First-order
Iteration Func-count f(x) step optimality
0 4 0.000582026 8.52
1 8 1.55855e-06 0.079033 1.63e-05
2 12 1.29161e-06 0.10269 3.47e-06
Local minimum found.
Optimization completed because the size of the gradient is less than
the selected value of the optimality tolerance.
What result came out?
x
x =
0.00020904 0.0089523 117.73
So quite reasonable. As I said, maybe a little lack of fit in the model at worst. I'd still use fminspleas for the fit here, because it will be more accurate, converge faster, and be more robust. For example, fminspleas found a better solution than did lsqcurvefit.
nanstd(ydata - (lint(1) + lint(2)*exp(-xdata/nlt)))
ans =
8.0291e-05
nanstd(ydata - (x(1) + x(2)*exp(-xdata/x(3))))
ans =
8.0552e-05
but not a huge difference.