Non Linear Fit help?
Show older comments
I have been trying to fit some data with a model for some time now and am getting fairly close. In origin pro the fit seems to work ok and I get reasonable parameters back albeit the residuals are a little funny. Unfortunately in Matlab the fit is not so good. Could I send some one my code my function and some data to take a look at?
Oh and also there are imaginary numbers!
clear all;
close all
close all hidden
A = uiimport;
clear xdata;
clear ydata;
xdata = A.data(:,1);
ydata = A.data(:,2);
Vg = xdata;
Id = abs(ydata);
x0 = [8.6E-10;1.7;0.8;5E6];
options = optimset('Display','iter',...
'TolFun',1E-100,'TolX',1E-100,'MaxIter',1000);
[beta,resid,J,COVB,mse] = nlinfit(Vg,Id,@RcFun,[x0],options);
[ci se] = nlparci(real(beta),resid,'covar',COVB);
Id_new = ((((real(beta(1))*((Vg-real(beta(2))).^(real(beta(3))+1))).^(-1))+real(beta(4))).^(-1));
plot(Vg,Id_new,'r',Vg,Id,'o');
hold on;
plot(Vg,resid,'+');
function F = Rcfun(a,Vg)
K = real(a(1));
V = real(a(2));
b = real(a(3));
R = real(a(4));
F = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1);
end
Data
A B
0 3.03E-12
1 1.5E-13
2 1.58E-12
3 2.81E-12
4 2.55E-12
5 2.31E-12
6 4.13E-12
7 2.89E-12
8 4.99E-12
9 6.38E-12
10 1.068E-11
11 1.96E-11
12 5.343E-11
13 5.405E-11
14 5.347E-11
15 5.142E-11
16 2.4139E-10
17 7.4428E-10
18 1.5752E-9
19 2.7328E-9
20 4.3347E-9
21 6.5506E-9
22 9.5258E-9
23 1.31356E-8
24 1.72672E-8
25 2.17876E-8
26 2.66302E-8
27 3.18252E-8
28 3.7101E-8
29 4.23594E-8
30 4.78078E-8
31 5.32604E-8
32 5.86136E-8
33 6.39262E-8
34 6.93234E-8
35 7.47466E-8
36 8.01152E-8
37 8.54398E-8
38 9.08214E-8
39 9.62598E-8
40 1.0184E-7
41 1.074E-7
42 1.1322E-7
43 1.1876E-7
44 1.2432E-7
45 1.299E-7
46 1.3534E-7
47 1.4062E-7
48 1.4596E-7
49 1.5096E-7
50 1.558E-7
51 1.6118E-7
52 1.6616E-7
53 1.7064E-7
54 1.7546E-7
55 1.7946E-7
56 1.8402E-7
57 1.8776E-7
58 1.9138E-7
59 1.9584E-7
60 1.9992E-7
7 Comments
Star Strider
on 20 Jul 2012
Edited: Star Strider
on 20 Jul 2012
Please post your code, objective function, and some data. (Adding it by ‘Edit’ to your original post would be best.)
What do you mean by the residuals being ‘a little funny’? What was the MSE? If Origin Pro gave you covariances or confidence limits on the parameters, please post them, too.
Where do the imaginary numbers appear — data, parameter estimates, or somewhere else?
Adam Parry
on 21 Jul 2012
Adam Parry
on 21 Jul 2012
Star Strider
on 21 Jul 2012
You added your code correctly. To add some of your data (that seems to be an [n x 2] array), append it (by editing) to the code you already posted. I suggest for clarity something like:
DATA:
12.475 0.156
14.330 1.271
etc.
Please format you data as ‘code’ as I did here. That makes it easier to read.
Also, I see ‘myfun’ but I don't see ‘RcFun’. It would help if you posted it. Is there a particular reason you're taking the absolute value of ‘ydata’? Please post the original ‘xdata’ and ‘ydata’ rather than ‘Vg’ and ‘Id’.
Adam Parry
on 22 Jul 2012
Teja Muppirala
on 24 Jul 2012
Just curious, but, what are the good values of K,V,b,R that you obtained through origin?
Teja Muppirala
on 24 Jul 2012
Ah. Sorry. You had it down below and I missed it. k = 8.6E-10 V = 17.3 b = 0.618 R = 2.3E6
Accepted Answer
More Answers (6)
Adam Parry
on 23 Jul 2012
Edited: Walter Roberson
on 25 Jul 2012
0 votes
19 Comments
Adam Parry
on 23 Jul 2012
Adam Parry
on 23 Jul 2012
Star Strider
on 23 Jul 2012
When I plugged those parameters into the function I've been using and removed the negative from ‘Vg’ and ‘RcFun’, it behaved similarly to when I used ‘abs(Vg-V)’. The values MATLAB gave using this ‘RcFun’:
RcFun = @(a,Vg) (1./(1./(a(1).*(Vg-a(2)).^(a(3)+1)) + a(4)));
for the range 0V to 20V was:
Vg Id_new
0.0000e+000 41.0816e-009 - 68.1918e-009i
1.0000e+000 36.7197e-009 - 63.0046e-009i
2.0000e+000 32.5968e-009 - 57.8053e-009i
3.0000e+000 28.7182e-009 - 52.6210e-009i
4.0000e+000 25.0870e-009 - 47.4801e-009i
5.0000e+000 21.7052e-009 - 42.4118e-009i
6.0000e+000 18.5731e-009 - 37.4463e-009i
7.0000e+000 15.6895e-009 - 32.6150e-009i
8.0000e+000 13.0521e-009 - 27.9500e-009i
9.0000e+000 10.6578e-009 - 23.4849e-009i
10.0000e+000 8.5028e-009 - 19.2546e-009i
11.0000e+000 6.5831e-009 - 15.2962e-009i
12.0000e+000 4.8952e-009 - 11.6496e-009i
13.0000e+000 3.4370e-009 - 8.3593e-009i
14.0000e+000 2.2090e-009 - 5.4771e-009i
15.0000e+000 1.2174e-009 - 3.0677e-009i
16.0000e+000 479.2402e-012 - 1.2228e-009i
17.0000e+000 44.4394e-012 -114.2460e-012i
18.0000e+000 482.3752e-012
19.0000e+000 2.0200e-009
20.0000e+000 4.2480e-009
It's possible that the model isn't appropriate for ‘Vg’ < ‘V’.
There nevertheless must be some significant difference between the Origin and MATLAB nonlinear regression functions, because even when I restrict ‘Vg’ >= 18V I'm not getting as good a fit with either ‘nlinfit’ or ‘lsqcurvefit’ as you are with Origin. (I find this extremely disconcerting!) I'm still getting complex parameters for ‘K’, ‘V’, and ‘b’. Those latest (with ‘nlinfit’) are:
beta_est =
3.3672e-009 - 11.3930e-012i
19.2532e+000 -682.8867e-015i
141.3304e-003 +987.7662e-006i
554.7623e+003
absbeta_est =
3.3672e-009
19.2532e+000
141.3339e-003
554.7623e+003
I also get different results from ‘nlinfit’ (better) and ‘lsqcurvefit’. (I'm going to look into Origin. I'd like to know why it comes up with better parameter estimates than MATLAB does.) I wonder if it would be worthwhile to bring this up with TMW Tech Support?
I have the Symbolic Math Toolbox, so I'm going to see if calculating the Jacobian with it and giving that analytically to the functions could solve the problem. That may take a day or so, assuming the Symbolic Math Toolbox can do that. (It too has been having problems of late in some situations.)
Sorry also for the delay. I ran a number of test fits while I was writing this comment.
Adam Parry
on 23 Jul 2012
Star Strider
on 23 Jul 2012
The vectorized Jacobian (Symbolic Math Toolbox) of:
F = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1)
w.r.t: K, V, b, R
respectively a(1), a(2), a(3), a(4)
is:
J = [1./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)) - (a(1).*a(4))./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2;
-(a(1).*(a(3) + 1))./((Vg - a(2)).^(a(3) + 2).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2);
(a(1).*log(Vg - a(2)))./((Vg - a(2)).^(a(3) + 1).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2);
-a(1).^2./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2];
MATLAB wants it as a row vector, I listed it as a column vector for readability. I'll work on it in a few minutes. Add to ‘optimset’ the argument pair 'Jacobian', 'on' to use it, add it to ‘RcFun’, and change ‘RcFun’ to
function [F, J] = RcFun(a,Vg)
I'm posting it now so you can work on it in parallel.
I'm not at all happy that MATLAB can't produce as good a fit to your function as Origin obviously can, even when ‘Vg’ > ‘V’. If an analytic Jacobian doesn't work to fit your function with precision similar to what Origin is producing, I'm going to contact TMW Tech Support to see if they have a solution. At the very least they need to know about the problem.
Star Strider
on 23 Jul 2012
I haven't tried lm.m and LMFsolve.m. I wasn't aware of them. I've been pursuing the Jacobian hypothesis, so our doing those in parallel seems an effective strategy.
I'm taking a short break, then will be back to see if the calculated Jacobian can improve the fit. That's the only possibility I can think of that we haven't collectively already tried.
It's my pleasure to help! For my part, I'm learning about organic FETS (new and interesting) and more about curve fitting and parameter estimation in MATLAB. I probably learn more than I contribute.
Adam Parry
on 23 Jul 2012
Star Strider
on 23 Jul 2012
Well ... unfortunately, the analytic Jacobian with ‘nlinfit’ didn't improve anything. I'm still getting complex parameter estimates and a complex fit, although the imaginary parts of the parameters are much less (by 1E-3) than the real parts. Those parameter estimates are:
beta_est =
3.1982e-009 + 2.7072e-012i
19.2149e+000 + 3.0356e-015i
162.7345e-003 -229.5976e-006i
685.9447e+003
absbeta_est =
3.1982e-009
19.2149e+000
162.7347e-003
685.9447e+003
I think it's time we contacted TMW Tech Support with this. If Origin can fit the function, MATLAB should be able to as well.
As for stopping the fit with ‘V’ > ‘Vg’, the only way I can think of is to add a statement at the beginning of ‘RcFun’:
if a(2) > Vg
F = 0;
return
end
That I also incorporated the latest run. The ‘return’ breaks out of the objective function without calling the Jacobian, and returns control to the calling program. It might throw an error if it wants the Jacobian at that step, but we can deal with that later if necessary.
Star Strider
on 23 Jul 2012
Edited: Star Strider
on 23 Jul 2012
Add this instead of your current ‘options’ statement before you call ‘nlinfit’:
options = statset('Display','final', 'TolFun',1E-100,'TolX',1E-100,'MaxIter',7500, 'MaxFunEvals', 5000, 'Jacobian', 'on', 'RobustWgtFun','bisquare');
It seems to improve the fit and parameter estimates significantly, approaching those Origin is producing. It might be interesting to experiment with various types of 'RobustWgtFun' options and 'Tune' tuning constants. I need to learn more about these.
The parameter estimates with these options are:
Parameter Estimates:
K = 4.013845660779272E-09 2.971810901348672E-11j
V = 1.973624752002914E+01 1.566502377201617E-02j
b = 8.384772342971474E-02 -2.018805502904597E-03j
R = 3.163204974971465E+05 0.000000000000000E+00j
Parameter Estimates:
|K| = 4.013955674214642E-09
|V| = 1.973625373683734E+01
|b| = 8.387202334512081E-02
|R| = 3.163204974971465E+05
The Jacobian is the multidimensional directional derivative that the gradient-descent algorithms use to determine the direction the algorithm proceeds to find the multivariate least-squares minimum. The default is to use a finite-difference numerical approximation of the Jacobian, but the analytic functions are always superior, and especially so when the response surface slope is shallow.
Adam Parry
on 23 Jul 2012
Adam Parry
on 23 Jul 2012
Edited: Adam Parry
on 23 Jul 2012
Adam Parry
on 23 Jul 2012
Adam Parry
on 23 Jul 2012
Adam Parry
on 23 Jul 2012
Star Strider
on 23 Jul 2012
Catching up ...
I believe 'RobustWgtFun' only works for ‘nlinfit’. I can't find any mention of it in the ‘optimset’ function that ‘lsqcurvefit’ uses. Note that ‘nlinfit’ is in the Statistics Toolbox and ‘lsqcurvefit’ in the Optimization Toolbox. The two Toolboxes and their functions don't seem to have much in common.
I got the same errors with ‘nlinfit’ as you did. I believe it's simply the nature of the function.
I haven't tried ‘lsqnonlin’ specifically. It uses the same algorithm and option structure as as ‘lsqcurvefit’, so I doubt there would be much difference in performance.
Trying to get some insight into the Jacobian, I created these plots:
Jcbn = @(a) ([1./((Vg - a(2)).^(a(3) + 1).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2), -(a(1).*(a(3) + 1))./((Vg - a(2)).^(a(3) + 2).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2), (a(1).*log(Vg - a(2)))./((Vg - a(2)).^(a(3) + 1).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2), -a(1).^2./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2]);
Fit_J = Jcbn(beta);
Fit_J_a = abs(Fit_J);
figure(4099)
semilogy(Vg,Fit_J_a,'-')
% hold on
% hold off
legend('K','V','B','R', 4)
title('Jacobian Absolute Value')
xlabel('Vg')
ylabel('log_{e} |J|')
grid
figure(5000)
mesh(Fit_J_a)
set(gca, 'XTickLabel', 'K|V|B|R');
xlabel('Parameters')
grid on
I have the feeling that we may be approaching the point at which we've exhausted our options. The one idea I want to consider is the possibility of using a weighting vector to set the weights below 18V to zero and the rest to Vg or something else. That way it will ignore those with zero weight and weigh appropriately those we want it to. What parts of the function are most important for you to fit most closely?
We will solve this! I don't want to be responsible for your not being selected for FRS.
Adam Parry
on 24 Jul 2012
Adam Parry
on 24 Jul 2012
Adam Parry
on 24 Jul 2012
Star Strider
on 24 Jul 2012
I gave it some thought too overnight. (However I'm reluctant to change nlinfit or the others.) I thought about the weighting vector, and am considering:
WgtV = sqrt(Id);
thus forcing the routine to ignore the region where (Id = 0). I've done this successfully in the past with other functions and data, but in those situations using the usual inverse-variance weighting vectors, (clearly inappropriate here since there are no variances on the measurements).
The other thing I thought of is that since I have the Global Optimization Toolbox and a fair amount of experience with genetic algorithms (GA), I'm going to give that a go. We already have a good start on the fitness function (the objective function you derived), and setting up a weighted metric [to avoid the (Vg < V) and (Id = 0) problems]. An Euclidean distance measure is probably the easiest, although we can get creative and use something more robust if necessary. The problem is that GAs usually take a while to converge, but have the significant benefit that they are essentially immune to the response surface because they completely ignore it.
If we're not happy with the GA results, we'll give GlobalSearch a go, even though that may take longer than GA to converge. It is the most likely to produce the best solution. With luck, we'll have its results today or tomorrow.
These are problems I'll have to solve for my own applications in the not distant future, so I'm interested in learning about their solutions.
Teja Muppirala
on 24 Jul 2012
This problem is clearly difficult. It has multiple minima, and a strong dependence on initial conditions. Instead of NLINFIT, I tried two different approaches which both give me good fits for values of Vg > 18.
In both cases, since it seems this equation can't really model the Vg < 18 region, I weighted that region to be zero. Also, just to scale things better, I am working with the log10's of K and R.
Option 1: Pattern Search Algorithm from the Global Optimization Toolbox
This gives a solution that is almost identical to the one that you found from your other software.
G = @(X) (((10.^(X(1)).*(Vg-X(2)).^(X(3)+1)).^(-1))+10.^(X(4))).^(-1);
E = @(X) ( sum(((real(G(X)) - Id)).^2 .* (Vg >= 18)) );
opts = psoptimset;
opts.Display = 'iter';
opts.MaxIter = realmax;
opts.MaxFunEvals = realmax;
X0 = [log10(8.6E-10);1.7;0.8;log10(5E6)];
X = patternsearch(E,X0,[],[],[],[],[],[],[],opts);
% Your previous answer
K = 8.6E-10; V = 17.3; b = 0.618; R = 2.3E6;
F0 = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1);
% Comparing results: They are virtually identical
plot(Vg, Id);hold all; plot(Vg, G(X)); plot(Vg,F0);
Option 2: Simply using FMINSEARCH from various starting points
Again, identical to the previous results.
G = @(X) (((10.^(X(1)).*(Vg-X(2)).^(X(3)+1)).^(-1))+10.^(X(4))).^(-1);
E = @(X) ( sum(((real(G(X)) - Id)).^2 .* (Vg >= 18)) );
opts = optimset('fminsearch');
opts.MaxIter = realmax;
opts.MaxFunEvals = realmax;
options.TolFun = realmin;
options.TolX = realmin;
X0 = [log10(8.6E-10);1.7;0.8;log10(5E6)];
Emin = Inf;
for n = 1:100
disp(n)
[Xn, err(n)] = fminsearch(E,X0.*(1+0.01*randn(size(X0))),opts);
if err(n) < Emin
Emin = err(n);
X = Xn;
end
end
% Your previous answer
K = 8.6E-10; V = 17.3; b = 0.618; R = 2.3E6;
F0 = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1);
% Comparing results: They are virtually identical
plot(Vg, Id);hold all; plot(Vg, G(X)); plot(Vg,F0);
2 Comments
Adam Parry
on 24 Jul 2012
Teja Muppirala
on 24 Jul 2012
Edited: Teja Muppirala
on 24 Jul 2012
Are you using MATLAB R2012a?
Teja Muppirala
on 24 Jul 2012
Ah! Sorry sorry sorry. There's actually a MUCH easier way to make all of this work. The important point is to set the model equal to zero whenever it goes imaginary. I bet your other software probably was doing this automatically; MATLAB does not. See how I modified rcfun below to include this line:
F = F.*(~imag(F));
This sets F to be identically zero whenever it has an imaginary component.
Anyways, copy the entire function below into a file and run it from the command line:
>> [BETA,Rout,J,COVB,MSE] = domyfit
and it will work perfectly (and it should work for other data in general). The standard errors on your coefficients are given by diag(COVB).
function [BETA,Rout,J,COVB,MSE] = domyfit
A = struct;
A.data = [0 3.03E-12
1 1.5E-13
2 1.58E-12
3 2.81E-12
4 2.55E-12
5 2.31E-12
6 4.13E-12
7 2.89E-12
8 4.99E-12
9 6.38E-12
10 1.068E-11
11 1.96E-11
12 5.343E-11
13 5.405E-11
14 5.347E-11
15 5.142E-11
16 2.4139E-10
17 7.4428E-10
18 1.5752E-9
19 2.7328E-9
20 4.3347E-9
21 6.5506E-9
22 9.5258E-9
23 1.31356E-8
24 1.72672E-8
25 2.17876E-8
26 2.66302E-8
27 3.18252E-8
28 3.7101E-8
29 4.23594E-8
30 4.78078E-8
31 5.32604E-8
32 5.86136E-8
33 6.39262E-8
34 6.93234E-8
35 7.47466E-8
36 8.01152E-8
37 8.54398E-8
38 9.08214E-8
39 9.62598E-8
40 1.0184E-7
41 1.074E-7
42 1.1322E-7
43 1.1876E-7
44 1.2432E-7
45 1.299E-7
46 1.3534E-7
47 1.4062E-7
48 1.4596E-7
49 1.5096E-7
50 1.558E-7
51 1.6118E-7
52 1.6616E-7
53 1.7064E-7
54 1.7546E-7
55 1.7946E-7
56 1.8402E-7
57 1.8776E-7
58 1.9138E-7
59 1.9584E-7
60 1.9992E-7];
xdata = A.data(:,1);
ydata = A.data(:,2);
Vg = xdata;
Id = abs(ydata);
X0 = [log10(8.6E-10);1.7;0.8;log10(5E6)];
[BETA,Rout,J,COVB,MSE] = nlinfit(Vg,Id,@rcfun,X0);
K = 8.6E-10; V = 17.3; b = 0.618; R = 2.3E6;
F0 = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1);
plot(Vg, Id);hold all; plot(Vg,rcfun(BETA,Vg)); plot(Vg,F0);
function F = rcfun(X,Vg)
F = (((10.^(X(1)).*(Vg-X(2)).^(X(3)+1)).^(-1))+10.^(X(4))).^(-1);
F = F.*(~imag(F));
9 Comments
Adam Parry
on 24 Jul 2012
Star Strider
on 24 Jul 2012
From what I understand, the log transformations normalize the parameters. I honestly have problems with that, although linear normalizing and scaling of the data — and not the parameters — are routine in most statistical applications.
I haven't run or plotted the posted code, but oscillating residuals — a problem I encountered earlier — indicates unmodeled behaviour in the fitted objective function. Any residuals with a non-random trend indicates a suboptimal fit.
I had problems with ‘lsqcurvefit’ miscalculating the Jacobian. I submitted a bug report to TMW Tech Support about it, since the same function and Jacobian worked perfectly in ‘nlinfit’. That won't have a significant effect on your problem since curve fitting doesn't seem to be the solution, but is an issue I want to resolve with TMW.
I have a couple errands to run, but will continue working on this with GlobalSearch later.
Teja Muppirala
on 24 Jul 2012
You could probably do without the logs, I'm sure it will still work just fine. But it just makes the numbers "nicer" and keeps all of the variables more or less of the same magnitude, which sometimes makes things easier numerically.
Instead of having to deal with very different numbers such as 1e-9 and 17 and 5000000, now the fitting routine can work with -9 and 17 and 6.7.
Teja Muppirala
on 24 Jul 2012
The residuals have an oscillating pattern because those oscillations were present in your original data, it's just hard to see them.
You can see this by plotting the gradient of your data, and comparing that with the gradient of the fitted function:
figure;
plot(diff(Id));
hold on;
plot(rcfun(BETA,Vg))
Adam Parry
on 24 Jul 2012
Adam Parry
on 24 Jul 2012
Star Strider
on 24 Jul 2012
I'm going to continue experimenting with GlobalSearch, since your problem is likely the best test for it that I've encountered. The Origin fit didn't give much oscillation in the residuals if I remember correctly.
As for the fit being good enough to use — that's definitely your call! I'm going to see what GlobalSearch comes up with. That's likely as close to a definitive set of parameter estimates as is possible. (I'm not happy with the fits the curve-fitting routines have come up with so far.) They may be the set that Origin estimates, but at least we'll know.
I suggest continuing to use RobustWgtFun and the Jacobian. If you use the Jacobian with ‘lsqcurvefit’, insert this line before you calculate the Jacobian in RcFun:
Vg = Vg';
The MATLAB convention is column-major order, and nlinfit passes column vectors (as I would expect) to its objective functions, so I have no idea why lsqcurvefit passes row vectors. (I figured this out myself by experimenting with it.)
I've successfully estimated equivalent circuit models (to physiological problems) with pF capacitances, mH inductances, and MOhm resistances with MATLAB curve-fitting routines without having problems (although it usually took several guesses of initial parameters to get them to fit), so I doubt parameter magnitude ranges are the problem. My data oscillated so were not as challenging to fit as yours are.
Adam Parry
on 24 Jul 2012
Star Strider
on 25 Jul 2012
SUCCESS!
After all that, it came down to the initial conditions, although I also made some changes to ‘RcFun’ to make it work seamlessly with both ‘nlinfit’ and ‘lsqcurvefit’.
The new initial conditions:
x0 = rand(4,1).*[1E-13; 1; 1; 1E+7];
and the new objective function:
function [F,J] = RdFun(a,Vg)
% Adam Parry’s Organic FET Function
% DATE: 2012 07 23
%
%
%
% F = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1)
% w.r.t: K, V, b, R
% respectively a(1), a(2), a(3), a(4)
% x0 = [ 8.6E-10 ;1.7 ;0.8 ;5E6];
if a(2) > Vg
F = 0;
return
end
F = a(1)./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1));
Jfcn = @(a,Vg) [1./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)) - (a(1).*a(4))./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2; -(a(1).*(a(3) + 1))./((Vg - a(2)).^(a(3) + 2).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2); (a(1).*log(Vg - a(2)))./((Vg - a(2)).^(a(3) + 1).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2); -a(1).^2./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2]';
if nargout > 1
J = Jfcn(a,Vg);
if any(size(J) == 1)
J = Jfcn(a,Vg');
elseif ~any(size(J) == 1)
return
end
end
% ========================= END: RcFun.m =========================
produces the parameters:
Parameter Estimates:
K = 3.254448861356919E-10 0.000000000000000E+00j
V = 1.559046341560319E+01 0.000000000000000E+00j
b = 9.096959820500091E-01 0.000000000000000E+00j
R = 2.828879053496115E+06 0.000000000000000E+00j
Parameter Estimates:
|K| = 3.254448861356919E-10
|V| = 1.559046341560319E+01
|b| = 9.096959820500091E-01
|R| = 2.828879053496115E+06
and it only took ‘nlinfit’ 2.075 seconds to converge.
However they do not appear to be unique because these produce and equivalently close fit:
Parameter Estimates:
K = 6.692378493392830E-10 0.000000000000000E+00j
V = 1.685567986182001E+01 0.000000000000000E+00j
b = 6.960550385747625E-01 0.000000000000000E+00j
R = 2.476857099346900E+06 0.000000000000000E+00j
Parameter Estimates:
|K| = 6.692378493392830E-10
|V| = 1.685567986182001E+01
|b| = 6.960550385747625E-01
|R| = 2.476857099346900E+06
as do these:
Parameter Estimates:
K = 3.054487171664253E-10 0.000000000000000E+00j
V = 1.547909799526304E+01 0.000000000000000E+00j
b = 9.280342803415647E-01 0.000000000000000E+00j
R = 2.854955008915018E+06 0.000000000000000E+00j
Parameter Estimates:
|K| = 3.054487171664253E-10
|V| = 1.547909799526304E+01
|b| = 9.280342803415647E-01
|R| = 2.854955008915018E+06
that produced these 95% confidence intervals:
137.6613e-012 473.2361e-012
14.4356e+000 16.5226e+000
772.1086e-003 1.0840e+000
2.6426e+006 3.0673e+006
and this covariance matrix:
8.2089e-021 49.8600e-012 -7.6134e-012 -9.6597e-006
49.8600e-012 317.5260e-003 -45.6541e-003 -54.6273e+003
-7.6134e-012 -45.6541e-003 7.0893e-003 9.1732e+003
-9.6597e-006 -54.6273e+003 9.1732e+003 13.1525e+009
although sometimes it still had a bit of a problem converging. The residuals were reasonably flat with no significant trend, at least in my opinion. I guess that's the result of the ‘rand’ component of the initial parameter guess vector. I'm still going to work on the GlobalSearch, but I feel reasonably confident about these, considering the fit and the residuals. Unfortunately, ‘lsqcurvefit’ consistently failed to find a fit with the same initial parameter guesses. I'll let you know what GlobalSearch comes up with. It may take a few days for me to learn about and successfully run it.
You might want to experiment a bit with it yourself to see what the various parameter estimates look like. That will give you a better idea than even the confidence intervals will about the region of fit.
Teja Muppirala
on 25 Jul 2012
0 votes
"How do you go from log10 parameters to estimating the error in the parameter itself using COVB??"
It's very easy, you just use the chain rule.
My change of coordinates was y = 10^x
So then dy/dx = 10^x * log(10)
Let A = Covariance in regular coordinates (this is what you want) and B = Covariance in log coordinates (this is what you have)
Then you can find A as
A = 10.^(B) * log(10)
In hindsight, I think this was all a bad idea, and you should just do it without any rescaling, so you don't have to worry about these kinds of things.
17 Comments
Adam Parry
on 25 Jul 2012
Adam Parry
on 25 Jul 2012
Star Strider
on 25 Jul 2012
Edited: Star Strider
on 25 Jul 2012
The Jacobian is the directional derivative matrix that tells the gradient descent algorithm the direction in which to proceed to minimise the objective function with respect to each parameter. The gradient-descent algorithms all use the Jacobian for every problem, but usuallly use a finite-difference approximation to the Jacobian. The problem with this is that the approximation is only accurate to about 1E-10 or so. (I've read MATLAB documentation on this but I can't find it just now.) In situations such as yours, an analytic Jacobian is more accurate and more precise and as a bonus is also faster. If you want, you can experiment with that idea with this snippet with various values of ‘deltaw’:
w1 = [0:0.1:2*pi]';
deltaw = 1E-13;
cosapp = (sin(w1+deltaw)-sin(w1))/deltaw;
errs = [cosapp cos(w1) cos(w1)-cosapp]
It has nothing directly to do with your current problem but illustrates the difference between the accuracy and precision of finite-difference calculations and analytic calculations of derivatives.
The code bit you quoted has to do with changes I had to make to the objective function work with both the Statistics and Optimization Toolboxes.
First, I inserted the anonymous function ‘Jcbn’ because it's easier than duplicating the code for the Jacobian matrix in the ‘if’ block. I then call it with ‘J = Jcbn(a,Vg)’ or ‘J = Jcbn(a,Vg')’ later. The code is more efficient that way.
Second, the Optimization Toolbox does two things differently from the Statistics Toolbox. The Optimization Toolbox does not always call the Jacobian when it calls the objective function, so according to the documentation it's necessary to test to see if the calling routine wants the Jacobian or only wants the function. The Statistics Toolbox passes the data (in this case, Vg) to the objective function as a column, while the Optimization Toolbox passes it as a row, but both want a Jacobian matrix returned. When the Optimization Toolbox passes row-vector data to the Jacobian set up for the Statistics Toolbox, it returns a vector. In order for the objective function to return the Jacobian matrix to the Optimization Toolbox, it's necessary to transpose the data vector and calculate the Jacobian with it. This is what I spent a few hours yesterday figuring out.
The code I sent you was exactly the code that worked for me for both ‘nlinfit’ and ‘lsqcurvefit’. I copy-pasted it from the objective function I'm using. I have no idea why it didn't work for you, but I'll troubleshoot it with you. What about it doesn't work? Does it throw an error?
There is probably a ‘best’ solution, a problem I intend to use GlobalSearch to find, but the response surface is shallow enough that numerical methods can only approximate it. That's the reason I suggested you run the fit several times to get an idea of the range of acceptable parameter estimates. I then suggest doing some basic statistics on a set of 10-20 good fits to get an idea of how the parameters behave and how they are related.
The code and fits I quoted give real numbers for the parameter estimates and the function approximation, so no imaginary numbers appear. The residuals with the parameter estimates I quoted are very small and have no discernable oscillation.
I'm not sure what you mean by ‘change the boundary condition to include the drain voltage’. We're regressing drain current against gate voltage as I understand it. If we're going to add Vd as a parameter to be estimated, this would probably require rewriting the objective function and the Jacobian. Is there any way we could get around that by calculating it from ‘Id’ and ‘R’? It would be indirect rather than a specific parameter estimate, but considering the nature of the function we're fitting, might be preferable.
No worries with your questions. The ‘best’ method to fit the data may be GlobalSearch. That's the reason I'm going to give it a shot as soon as I teach myself to use it. In your particular situation, gradient-descent algorithms are likely not the best. By the same token, there may be no one ‘best’ way to approximate your or other problems with difficult response surfaces. We simply have to live with the uncertainties in the estimates. I note that the 95% confidence intervals calculated for some of the parameter estimate sets do not include parameters estimated in other runs (that appear to be as valid) so the parameter estimates may not be normally distributed. This is the reason I suggest you do several (perhaps 20 or so) runs, save the parameter estimates that are real and give good fits, and then explore their statistical distributions. (See ‘kstest’ for details on how to do this.) I may do that as well, but not until after I give GlobalSearch a go.
Adam Parry
on 25 Jul 2012
Adam Parry
on 25 Jul 2012
Star Strider
on 25 Jul 2012
I believe we may be close to a saddle point or some other discontinuity in the response surface. I had to do the fit several times each to get the parameter estimates I posted. It won't always converge to the optimal parameter set, but the optimal sets give quite good fits. There aren't any discernable or repeatable oscillations in those residuals, and the residuals are very small in both absolute and relative magnitude.
If you're only going to use ‘nlinfit’ or other Statistics Toolbox functions, then use the function you quoted in your last comment. It's only necessary to use mine (with the ‘if’ blocks) if you want to use it with Optimization Toolbox functions.
This is a sort of heuristic effort. Just now, it took me six attempts to get one set of decent parameter estimates:
ci =
73.5905e-012 385.7190e-012
13.6982e+000 16.2602e+000
819.6940e-003 1.2000e+000
2.7308e+006 3.2005e+006
Parameter Estimates:
K = 2.296547485965766E-10 0.000000000000000E+00j
V = 1.497918790161365E+01 0.000000000000000E+00j
b = 1.009832375543948E+00 0.000000000000000E+00j
R = 2.965617775329871E+06 0.000000000000000E+00j
Parameter Estimates:
|K| = 2.296547485965766E-10
|V| = 1.497918790161365E+01
|b| = 1.009832375543948E+00
|R| = 2.965617775329871E+06
This is the rule more than the exception in nonlinear parameter estimation, especially for difficult problems such as yours. That's the reason I'm going to give GlobalSearch a go. I suspect (and I sincerely hope) that it will be able to go better than I can in finding the optimal set of parameter estimates.
Adam Parry
on 26 Jul 2012
Star Strider
on 26 Jul 2012
I have GlobalSearch running in the background. After running it a few times over a small region to be sure my code for it worked, I started it using:
x0 = [1E-15; 1; 1; 1E+9];
about 13 hours ago. It's still running now and hasn't found all the minima! On a smaller scale, it found 113 local minima, some with complex parameters, but none of those with complex parameters was a preferred fit.
The ‘lsqcurvefit’ function wants its Jacobian to be the transpose of the one ‘nlinfit’ wants. (That was the reason I had to insert the second ‘if’ block inside the first that ‘lsqcurvefit’ required.) I didn't get the error of ‘Inf or NaN values’, in my parameter estimation attempts, at least that I recall. The ‘lsqcurvefit’ function has the option of using a ‘trust-region-reflective’ algorithm that has advantages over the Levenberg-Marquardt algorithm, but it has some peculiarities and specific requriements I haven't studied. (I opted to learn about GlobalSearch instead. MATLAB is still running your and my GlobalSearch problem, so until it finishes, I can't do anything else with it.) You can specify the algorithm ‘lsqcurvefit’ uses.
While I'm thinking about it, the code I'm using for GlobalSearch is:
GSRcFun = @(a) a(1)./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1));
GlblSrchRcFun = @(a) sum([GSRcFun(a) - Id].^2);
problem = createOptimProblem('fmincon','objective',GlblSrchRcFun, 'x0',x1, 'lb',ones(4,1)*eps);
gs = GlobalSearch;
[xg fg flg og sl] = run(gs,problem)
I'm including this for your information so that if you find a computer with the Global Optimization Toolbox and want to run it yourself, you can use it rather than having to start from scratch.
I'll definitely let you know what GlobalSearch comes up with. I have no idea how long it will take for GlobalSearch to explore the response surface, discover all the local minima, and ideally identify the global minimum.
Adam Parry
on 26 Jul 2012
Star Strider
on 26 Jul 2012
I'm running 2012a with all the latest Toolbox versions (of the ones I subscribe to). Note that 2012b is still in beta test.
‘GlobalSearch’ is still running 13½ hours after it started ...
Adam Parry
on 26 Jul 2012
Adam Parry
on 26 Jul 2012
Star Strider
on 26 Jul 2012
If you're going to have to analyse hundreds of such data, it definitely seems to be worthwhile to consider a more efficient strategy. (I thought this was a one off.) Is there any way to measure any of your parameters directly, or do a load line analysis or something similar?
Just an idea. I haven't need to do a load line in a very long time.
T+14 hours and counting ...
Adam Parry
on 26 Jul 2012
Star Strider
on 26 Jul 2012
Do you mean ‘other method’ of device fabrication or parameter estimation?
I would be interested in other data to fit, yes.
I finally gave up on GlobalSearch, at least for now. I've since set a two-hour time limit on it. I would have done that earlier but I had no idea it would still be running after 18 hours!
Out of sheer desperation, I asked the Symbolic Math Toolbox to calculate a second-order Taylor series approximation of your function to see if that would make it easier to estimate the parameters, at least initially. The problem is that the Taylor series wants to calculate (-V).^b, guaranteeing a complex result. Here's the Taylor series, formatted by the Symbolic Math Toolbox as an anonymous function:
Taylr_Id = @(K,R,V,Vg,b)K.*(-V).^b.*1.0./(K.*R.*V.*(-V).^b-1.0).^2.*(-V+Vg+Vg.*b+K.*R.*V.^2.*(-V).^b)
that after this substitution: {K V b R}, {'a(1)' 'a(2)' 'a(3)' 'a(4)'} (and apparently some rearranging) becomes:
Taylr_Id = @(a,Vg) (a(1).*(-a(2)).^a(3).*(Vg - a(2) + Vg.*a(3) + a(1).*(-a(2)).^(a(3) + 2).*a(4)))./(a(1).*(-a(2)).^(a(3) + 1).*a(4) + 1).^2
I doubt if it contributes anything of significance to the effort, but so long as I have it I'll send it along for you to experiment with.
Adam Parry
on 27 Jul 2012
Star Strider
on 27 Jul 2012
The nice thing is that fitting straight lines only require two parameters. However you have four in your model, so I don't see what the advantage is in that unless you can ignore two of them. When I did this just now, I got estimates for R of 195.8839E+006 and for V of 19.9989E+000.
I believe the nature of your data and the nature of your model make fitting your data difficult. The model doesn't really account for the sharp discontinuity where Vg=V, or for the very small oscillations in the Id data, that are likely real although quantitatively unimportant. (When I looked at them on the model I estimated from the parameter sets I posted, the magnitude of the oscillations with the best parameter estimates was about 1% of the value of the function at that point.)
The problem is that taking the logs of the parameters and then reconsituting them in the model changes the model. It's easier to see if instead of using 10.^a(1) and 10.^a(4) you substitute exp(a(1)) and exp(a(4)). That's not the model you specified and it doesn't make any sense in the context of the papers you referred me to, so I am not using the parameter transformation.
A Taylor series approximation might still be the way to go initially to get a good start on the parameter estimates, but you will have to change your data and model to avoid the possiblity of ‘(-V).^p’. Changing ‘(Vg-V)’ to ‘(V-Vg)’ does this, and likely only requires changing the Vg data to -Vg. In the Taylor series I sent along, to make that transformation simply change the signs of ‘V’ (a(2)) and ‘Vg’. I suggest that only for the Taylor approximation in order to get a good initial set of stable parameter estimates.
The parameter estimates I posted earlier are among the best I got. You can plug them in directly and calculate the relevant statistics. There is a small oscillation in the residuals, but it is barely discernable. This set took less than three seconds to converge and produced an MSE = 1.3182E-018:
Parameter Estimates:
|K| = 2.627883069159599E-10
|V| = 1.521501224663270E+01
|b| = 9.713005068703799E-01
|R| = 2.914483559512664E+06
I gave up on GlobalSearch because GlobalSearch either didn't find a global minimum or got caught in a loop that required me to either stop or completely exit MATLAB, and so about which it gave me no informaton. It seemed to have the most trouble fitting ‘R’ (it seemed to persisstently underestimate it), but didn't seem to have trouble with the other parameters.
I suggest you go with the parameter estimates that make the most sense and that you are the most comfortable with. I know nothing about Origin, how it decides the region to fit, or how it calculates its parameters, so I can't advise you on what parameter set is best.
I'll be glad to continue to provide what help I can.
Adam Parry
on 30 Jul 2012
0 votes
24 Comments
Adam Parry
on 30 Jul 2012
Star Strider
on 30 Jul 2012
There's no statistical magic about my code or what I'm doing. I'm getting the same errors you are most of the time. It's simply that you have a difficult problem and a difficult data set, and the usual gradient-descent (and I suspect many other different) algorithms are going to have trouble with it. (I came late to statistics, by the way. My undergraduate concentration was Chemistry, and one professor remarked that ‘with Avogadro's number of molecules, you don't need statistics’! I learned differently later when I started physiological system modeling and parameter estimation and other fun activities.)
The ‘lsqcurvefit’ function wants ‘optimset’. I suggest you change the code you quoted above to something like:
options = optimset('Display','final', 'TolFun',1E-100,'TolX',1E-100,'MaxIter',10000, 'MaxFunEvals', 10000, 'Jacobian','on');
I suggest something like this for the call to ‘statset’ for ‘nlinfit’:
options = statset('Display','final', 'TolFun',1E-100,'TolX',1E-100,'MaxIter',7500, 'MaxFunEvals', 5000, 'Jacobian', 'on', 'RobustWgtFun','bisquare');
I'm not sure that the options structure for one would translate to the other.
I estimated R from the linear region of the fit by doing the linear regression:
B = polyfit(Vg, Id, 1);
and taking advantage of Ohm's law. In that region (that I would consider best to be bounded by 20V to 60V to avoid the discontinuity), we can take advantage of the relation that:
Id = Vg*B(1) + B(2), where: R = 1/B(1) and V = -B(2)/B(1)
Setting R ( i.e. a(4) ) to a constant using something like a linear regression to calculate it, and estimating only the other three parameters, would definitely improve the fit. It might also be worthwhile to consider restricting:
Vg > ceil(-B(2)/B(1))
(That's not valid MATLAB code. I simply formatted it that way.)
Let me know what you want to do with R and if and how you want to incorporate it as a constant. If it's going to be the same for all data sets, then it can be incorporated as a constant in the model, or it could simply be edited into ‘RcFun’ for each data set. Otherwise, there may be a way to pass it as an extra parameter. I'm considering these possibilities with respect to ‘nlinfit’. It would be easy with ‘lsqcurvefit’ since that would require simply setting a narrow bound for it.
I'm simply thinking out loud here as to the possibilities I'd consider. This is definitely your call.
Adam Parry
on 30 Jul 2012
Adam Parry
on 30 Jul 2012
Star Strider
on 30 Jul 2012
First, the other thread was an entirely different (and much easier) problem. I've done a fair amount of nonlinear parameter estimation and have made about every mistake possible by now (I hope), so I at least look at many of them and answer the ones I believe I can help with. Yours is the most difficult nonlinear parameter estimation problem I've ever encountered.
In some regions of your response surface, and for reasons I don't understand (I haven't analyzed them), some local solutions to your problem produce complex parameters and complex function estimates even when Vg (and Id) are restricted to the region where Vg > V. The GlobalSearch function found several local minima (I believe at one run it discovered nearly 200) and many with complex coefficients but rejected all of the complex coefficients as suboptimal fits.
Please send me the equation you referred to where you fitted the data from 0V to -60V and the constant that was actually -1. (I'm lost.)
I used the MATLAB Symbolic Math Toolbox to calculate the Jacobian and Taylor equations. It's easier than doing it by hand, and most importantly it avoids the inevitable algebra errors.
Adam Parry
on 30 Jul 2012
Star Strider
on 31 Jul 2012
I believe we did that before as well, without much difference. I may give it a go again to see what the result is. If I do, I'll likely recalculate the Jacobian to be sure it reflects the changes.
If we switched the polarities of ‘Id’ and ‘Vg’, negated the equation, and changed (Vg - V) to (V - Vg), I'm not certain we'd be any closer to a definitive solution unless the parameter values we're now estimating don't make sense ( i.e. agree with literature values except perhaps for sign ). I'm not sure that negating everything would get us anywhere.
I've designed biomedical instrumentation with transistors, but I've never done what you are doing with them. (Truth be told, I prefer FET op-amps.)
The statement (that I disagree with) that sets F = 0 for imaginary results:
F = F.*(~imag(F));
will introduce a significant discontinuity in the estimation procedure because the function isn't actually zero with imaginary parameters, and neither the analytic nor the finite-difference Jacobians would reflect the function arbitrarily being set to zero if it produced complex parameters or a complex fit to the real-valued data. (I don't know how the gradient-descent algorithm handles those problems.) However, restricting the fit region to Vg > 20 avoids the nonlinearity for V > Vg that the model fails to account for.
I may experiment with estimating R uniquely and fitting only the remaining three parameters. The easiest way to do that is likely to use ‘lsqcurvefit’ and restrict the upper and lower bounds for R to a narrow range around the estimated value. That would be easy to do in code with the current scripts.
Adam Parry
on 31 Jul 2012
Star Strider
on 31 Jul 2012
Edited: Star Strider
on 31 Jul 2012
Yes. It seems R varies least, and in the best-fitting parameter estimates I've recorded R varies from a bit more than 2E+6 to a bit less than 3E+6 Ohms. The latest (and best fit yet) parameter estimates are:
Parameter Estimates:
|K| = 1.102954516033334E-09
|V| = 1.776473601749216E+01
|b| = 5.426049408039866E-01
|R| = 2.160189159613943E+06
95%CI =
843.3331e-012 1.3626e-009
17.2964e+000 18.2331e+000
470.0186e-003 615.1913e-003
1.9953e+006 2.3251e+006
MSE = 4.873345E-019
Those with nlinfit. Other low-MSE nlinfit values for R I've recorded are: 2.594274479774597E+06, 2.589905421589671E+06, and 2.914483559512664E+06. For some reason, lsqcurvefit doesn't seem to do nearly as well as nlinfit with your problem.
Adam Parry
on 31 Jul 2012
Adam Parry
on 31 Jul 2012
Edited: Adam Parry
on 31 Jul 2012
Star Strider
on 31 Jul 2012
Edited: Star Strider
on 31 Jul 2012
I haven't but I will. Good point. I was initially keeping lsqcurvefit with the trust-region-reflective option to compare it, but the Levenberg-Marquardt option now seems best.
I suggest bounding R in that situation to the range I quoted earlier: 2E+6 to 3E+6.
Adam Parry
on 31 Jul 2012
Star Strider
on 31 Jul 2012
After reading your last comment and re-reading the documentation for lsqcurvefit, I decided to experiment. First, I haven't been able to independently estimate R with regression or any other techniques. Second, the Taylor Series attempt to estimate the best initial parameter set failed.
Actually, nlinfit does reasonably well at estimating the parameters. It simply takes it a few attempts to get there.
If you're certain R isn't going to change in value between your organic FETs, it might simply be easier to set it as a constant and estimate the other three parameters.
Adam Parry
on 31 Jul 2012
Edited: Adam Parry
on 31 Jul 2012
Adam Parry
on 1 Aug 2012
Star Strider
on 1 Aug 2012
Edited: Star Strider
on 1 Aug 2012
Errands this morning. Late start here.
First, I believe I have a perfectly legitimate solution that will work for you now and in the future: linearly scale ‘Id’. I have the feeling that with ‘Id’ on the order of 1E-7, we're encountering Jacobian matrix condition and other numeric problems, as the errors have described. The disparate magnitudes of ‘Id’ and ‘Vg’ force ‘K’ and ‘R’ to the limits of numeric precision. Scaling ‘Id’ to be closer to ‘Vg’ avoids these extremes. Then to recover the actual magnitudes of ‘K’ and ‘R’, re-scale them by the same factor after the fit.
I experimented using ‘lsqcurvefit’ with this small change:
IdS = Id/median(Id); % ‘IdS’ = ‘Id Scaled’ -- median(Id) = 1E-7
then with Lobnd and Upbnd = [ ]:
LQFopts = optimset('Display','final', 'TolFun',1E-12, 'TolX',1E-12, 'MaxIter',10000, 'MaxFunEvals', 10000, 'Jacobian','on');
[beta,resnrm,resid,xitflg,outpt,lmda,J] = lsqcurvefit(@RcFun,x0,Vg,IdS,Lobnd,Upbnd,LQFopts);
yields very quickly and with an excellent fit to the data:
ElpTimeSec = 1.0250e+000
MSE = 34.9104e-006 % NOT RE-SCALED
Parameter Estimates:
K = 9.334400477356170E-03
V = 1.744670226837812E+01
b = 5.977271970075951E-01
R = 2.262890264338273E-01
To re-scale ‘K’ and ‘R’:
K = K * median(Id);
R = R / median(Id);
that here are:
K = 9.2457143384208E-010
R = 2.2845962129575E+006
Parameters ‘V’ and ‘b’ don't have to be re-scaled, because the scaling of ‘Id’ doesn't affect them. Only scaling ‘Vg’ would, and there's no need to scale it here.
NOTE that this is not ‘scaling the parameters’. I'm scaling the data with a very basic linear transformation — this is done commonly — then rescaling the parameters affected by the scaling of ‘Id’, using the same scaling factor I used to scale ‘Id’ initially.
Second, with respect to calculating the Jacobian with the Symbolic Math Toolbox, here's what I did:
F = (((K.*(V-Vg).^(b+1)).^(-1))+R).^(-1)
J_MOSFET = jacobian(F, [K V b R])
J_MOSFET = simplify(collect(J_MOSFET))
J_MOSFET_a = subs(J_MOSFET, {K V b R}, {'a(1)' 'a(2)' 'a(3)' 'a(4)'})
vectorize(J_MOSFET_a)
that yields:
ans =
matrix([[1./((a(2) - Vg).^(a(3) + 1).*(a(1).*a(4) + 1./(a(2) - Vg).^(a(3) + 1)).^2), (a(1).*(a(3) + 1))./((a(2) - Vg).^(a(3) + 2).*(a(1).*a(4) + 1./(a(2) - Vg).^(a(3) + 1)).^2), (a(1).*log(a(2) - Vg))./((a(2) - Vg).^(a(3) + 1).*(a(1).*a(4) + 1./(a(2) - Vg).^(a(3) + 1)).^2), -a(1).^2./(a(1).*a(4) + 1./(a(2) - Vg).^(a(3) + 1)).^2]])
From here it's easy to convert it to an anonymous function such as the one I sent along earlier for it.
After more than a week of exploring various options and experimenting, this may be the solution you're looking for. Please let me know your thoughts.
Adam Parry
on 2 Aug 2012
Star Strider
on 2 Aug 2012
Edited: Star Strider
on 2 Aug 2012
I'm getting excellent fits, no condition problems with the Jacobian, and no imaginary numbers with the scaled Id. I sent you my calling routine lines for lsqcurvefit. (I'm sure nlinfit would work as well but I wanted to see what lsqcurvefit did considering that it was having more problems with the unscaled Id.) I consider the problem solved.
I described my new approach as fully as I could. The only thing I left out is that I am now using the generic:
x0 = rand(4,1);
and am still getting excellent fits with scaled Id and no errors.
My full ‘RcFun.m’ m-file script is:
------------------------------------------------------------------------------------------------------------------------
function [F,J] = RcFun(a,Vg)
% Adam Parry’s Organic FET Function
% DATE: 2012 07 23
%
%
%
% F = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1)
% w.r.t: K, V, b, R
% respectively a(1), a(2), a(3), a(4)
% x0 = [ 8.6E-10 ;1.7 ;0.8 ;5E6];
if a(2) > Vg
F = 0;
return
end
F = a(1)./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1));
Jfcn = @(a,Vg) [1./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)) - (a(1).*a(4))./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2; -(a(1).*(a(3) + 1))./((Vg - a(2)).^(a(3) + 2).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2); (a(1).*log(Vg - a(2)))./((Vg - a(2)).^(a(3) + 1).*(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2); -a(1).^2./(a(1).*a(4) + 1./(Vg - a(2)).^(a(3) + 1)).^2]';
if nargout > 1
J = Jfcn(a,Vg);
if any(size(J) == 1)
J = Jfcn(a,Vg');
elseif ~any(size(J) == 1)
return
end
end
% ========================= END: RcFun.m =========================
------------------------------------------------------------------------------------------------------------------------
It's been interesting for me, too. I've learned a considerable amount about organic FETs, as well as data scaling as a solution to situations where the parameters assume extreme values. However I fail to see how it wasn't the most productive use of your time if you want to learn how to do parameter estimation in MATLAB and if this is an important part of your research.
If you're interested in learning more, I suggest an older but still very relevant text:
Beck, J.V.;Arnold, Kenneth J. Parameter Estimation in Engineering and Science (ISBN: 9780471061182).
It's pre-MATLAB but will tell you everything you need to know about nonlinear parameter estimation. There is also a lot on the Internet.
Adam Parry
on 2 Aug 2012
Star Strider
on 2 Aug 2012
Edited: Star Strider
on 2 Aug 2012
It's my pleasure to help! I remember spending more time than I wanted to when I was first learning MATLAB (and being a bit frustrated in the process) because I also had other course work to do at the time. The benefit is that being proficient in MATLAB makes nearly everything else you do in engineering and maths significantly easier. It's definitely worth the effort.
I'm using:
Vg = Data(19:end,1);
Id = Data(19:end,2);
LinScl = median(Id);
IdS = Id/LinScl;
I'm avoiding the region of the discontinuity because the function doesn't account for behaviour in that region. (I get real parameter estimates for all values of Vg >= 18V but not for any less than that.) This is also entirely legitimate because there is no reason to believe the parameters are complex, and the function is not defined for (Vg < V) anyway. It is not legitimate to force complex parameters to be real simply because we want them to be real! Also, it is never appropriate to extrapolate outside the region of fit in any regression. So while my choice of data allows us to estimate ‘V’ [defined as a(4) = 17.29V in my latest attempt] as being outside the region of fit (that in my code starts with Vg = 18V), it does not allow us to estimate the function for Vg < 18V.
Adam Parry
on 3 Aug 2012
Adam Parry
on 3 Aug 2012
Star Strider
on 3 Aug 2012
Edited: Star Strider
on 3 Aug 2012
I don't know how MATLAB routines could decide for themselves what part of the data to use. I have no idea how Origin managed to fit your function as well as it did and only over the linear region of fit that produced real parameters, assuming you gave it and MATLAB the same function and data.
I don't know the data you're commenting on. Since I know only the data from the present problem, I suggest you start the fit with the greatest ‘Id’ data, take a range of those data, do the fit, and estimate ‘V’. Then take the data greater than the estimated ‘V’ and fit all of it. If your parameters are linear functions of your data (as ‘K’ and ‘R’ were here), and if your ‘Id’ data are again on the order of 1E-7, you can scale the ‘Id’ data and then ‘un-scale’ ‘K’ and ‘R’ by the same factor. Since ‘V’ and ‘b’ are not linear functions of either variable, you have to fit them as they are. So you can't scale ‘Vg’.
Your Jacobian looks fine to me. The Optimization and Statistics Toolboxes use different conventions — Statistics uses column-major, Optimization row-major — so to get your Jacobian to work with both, you have to transpose your ‘VgX’ vector to use the Optimization functions. If your data are column vectors, the Jacobian as listed should work with Statistics Toolbox functions, but you'll have to transpose the ‘VgX’ vector to make it work with Optimization Toolbox functions. Take the ‘RcFun.m’ I sent you, rename it to your present problem, then paste your ‘F’ and ‘J’ functions in it in place of the ones I provided. It should work fine. Note that my variable ‘Jcbn’ is an anonymous function, so be sure to paste your ‘J’ expression there and change the argument from ‘Vg’ to ‘VgX’.
That should work.
Alex Sha
on 26 Apr 2019
0 votes
For the fitting function: F = (((K.*(Vg-V).^(b+1)).^(-1))+R).^(-1);
Obviously,should be: V < min(Vg) = 0, the reuslt may be:
Root of Mean Square Error (RMSE): 2.54378147359793E-9
Sum of Squared Residual: 3.94720275310625E-16
Correlation Coef. (R): 0.999399159124327
R-Square: 0.998798679258411
Parameter Best Estimate
---------- -------------
k 6.10183988149707E-14
v -5.67776067411581E-15
b 3.02503323118395
r 3951289.1816463

Categories
Find more on Get Started with Curve Fitting Toolbox in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!