Fitting model to data in matlab - matlab

i have some experimental data and a theoretical model which i would like to try and fit. i have made a function file with the model - the code is shown below
function [ Q,P ] = RodFit(k,C )
% Function file for the theoretical scattering from a Rod
% R = radius, L = length
R = 10; % radius in Å
L = 1000; % length in Å
Q = 0.001:0.0001:0.5;
fun = #(x) ( (2.*besselj(1,Q.*R.*sin(x)))./...
(Q.*R.*sin(x)).*...
(sin(Q.*L.*cos(x)./2))./...
(Q.*L.*cos(x)./2)...
).^2.*sin(x);
P = (integral(fun,0,pi/2,'ArrayValued',true))*k+C;
end
with Q being the x-values and P being the y-values. I can call the function fine from the matlab command line and it works fine e.g. [Q,P] = RodFit(1,0.001) gives me a result i can plot using plot(Q,P)
But i cannot figure how to best find the fit to some experimental data. Ideally, i would like to use the optimization toolbox and lsqcurvefit since i would then also be able to optimize the R and L parameters. but i do not know how to pass (x,y) data to lsqcurvefit. i have attempted it with the code below but it does not work
File = 30; % the specific observation you want to fit the model to
ydata = DataFiles{1,File}.data(:,2)';
% RAdius = linspace(10,1000,length(ydata));
% LEngth = linspace(100,10000,length(ydata));
Multiplier = linspace(1e-3,1e3,length(ydata));
Constant = linspace(0,1,length(ydata));
xdata = [Multiplier; Constant]; % RAdius; LEngth;
L = lsqcurvefit(#RodFit,[1;0],xdata,ydata);
it gives me the error message:
Error using *
Inner matrix dimensions must agree.
Error in RodFit (line 15)
P = (integral(fun,0,pi/2,'ArrayValued',true))*k+C;
Error in lsqcurvefit (line 199)
initVals.F = feval(funfcn_x_xdata{3},xCurrent,XDATA,varargin{:});
Caused by:
Failure in initial user-supplied objective function evaluation. LSQCURVEFIT cannot continue.
i have tried i) making all vectors/matrices the same length and ii) tried using .* instead. nothing works and i am giving the same error message
Any kind of help would be greatly appreciated, whether it is suggestion regading what method is should use, suggestions to my code or something third.
EDIT TO ANSWER Osmoses:
A really good point but i do not think that is the problem. just checked the size of the all the vectors/matrices and they should be alright
>> size(Q)
ans =
1 1780
>> size(P)
ans =
1 1780
>> size(xdata)
ans =
2 1780
>> size([1;0.001]) - the initial guess/start point for xdata (x0)
ans =
2 1
>> size(ydata)
ans =
1 1780
UPDATE
I think i have identified the problem. the function RodFit works fine when i specify the input directly e.g. [Q,P] = RodFit(1,0.001);.
however, if i define x0 as x0 = [1,0.001] i cannot pass x0 to the function
>> x0 = [1;0.001]
x0 =
1.0000
0.0010
>> RodFit(x0);
Error using *
Inner matrix dimensions must agree.
Error in RodFit (line 15)
P = (integral(fun,0,pi/2,'ArrayValued',true))*k+C;
The same happens if i use x0 = [1,0.001]
clearly, matlab is interpreting x0 as input for k only and attempts to multiplay a vector of length(ydata) and a vector of length(x0) which obviously fails.
So my problem is that i need to code so that lsqcurvefit understands that the first column of xdata and x0 is the k variable and the second column of xdata and x0 is the C variable. According to the documentation - Passing Matrix Arguments - i should be able to pass x0 as a matrix to the solver. The solver should then also pass the xdata in the same format as x0.

Have you tried (that's sometimes the mistake) looking at the orientation of your input data (e.g. if xdata & ydata are both row/column vectors?). Other than that your code looks like it should work.

I have been able to solve some of the problems. One mistake in my code was that the objective function did not use of vector a variables but instead took in two variables - k and C. changing the code to accept a vector solved this problem
function [ Q,P ] = RodFit(X)
% Function file for the theoretical scattering from a Rod
% R = radius, L = length
% Q = 0.001:0.0001:0.5;
Q = linspace(0.11198,4.46904,1780);
fun = #(x) ( (2.*besselj(1,Q.*R.*sin(x)))./...
(Q.*R.*sin(x)).*...
(sin(Q.*L.*cos(x)./2))./...
(Q.*L.*cos(x)./2)...
).^2.*sin(x);
P = (integral(fun,0,pi/2,'ArrayValued',true))*X(1)+X(2);
with the code above, i can define x0 as x0 = [1 0.001];, and pass that into RodFit and get a result. i can also pass xdata into the function and get a result e.g. [Q,P] = RodFit(xdata(2,:));
Notice i have changed the orientation of all vectors so that they are now row-vectors and xdata has size size(xdata) = 1780 2
so i thought i had solved the problem completely but i still run into problems when i run lsqcurvefit. i get the error message
Error using RodFit
Too many input arguments.
Error in lsqcurvefit (line 199)
initVals.F = feval(funfcn_x_xdata{3},xCurrent,XDATA,varargin{:});
Caused by:
Failure in initial user-supplied objective function evaluation. LSQCURVEFIT cannot continue.
i have no idea why - does anyone have any idea about why Rodfit recieves to many input arguments when i call lsqcurvefit but not when i run the function manual using xdata?

Related

Having difficulties in finding error: Matrix dimensions must agree

My aim is to call this function into the pso code for minimization.
actually im calling this code into another program(mfile),here
v=0.1*x0; % initial velocity
for i=1:n
f0(i,1)=ofun(x0(i,:));
end
so what should i do, could anyone plz write me a code, so i can remove this problem. my aim is to minimize error using ITEA code, which im trying to do, im trying to find a that every time code runs, e_t have last updated value, not e_t=0.001 .
I don't have e_t. If I'm going to initialize it, it will remain constant, but I need to change its value in the code.
Second, I'm getting this error
Error using .* Matrix dimensions must agree.
Error in ofun (line 10), f = sum(t'.*abs(e_t)*dt);
function f=ofun(x)
Kp= x(1);
Ki= x(2);
e_t;
d=0.001;
I_ref=-1.1:d:1;
dt = 0.01;
t = 0:dt:1;
e_t= I_ref - (Kp.*e_t +Ki.*sum(t'.*abs(e_t)*dt));
f = sum(t'.*abs(e_t)*dt); % line 10
I want to write code for following equations
error= I_ref - (kp * error + ki*(integration of error));
I want to set I-ref=-1.1-1.1;
Here e_t get's the same size as I_ref
e_t= I_ref - (Kp.*e_t +Ki.*sum(t'.*abs(e_t)*dt));
Then you want to multiply it with t
f = sum(t'.*abs(e_t)*dt);
But t is of a different size as I_ref. t has length 101, I_ref has length 2101.
"Matrix dimensions must agree" is an error message that appears in this case in line 10 because matrix t cannot be multiplied elementwise with the e_t. Whichever matrix is smaller, I would probably add ones to the rest of it so that the result matrix is still able tone populated. Why are you transposing matrix t though? Good luck with your project!
function f=ofun(x)
%Define variables and constants- when you define e_t, why are you not multiplying dt elementwise? Also why do you transpose matrix t?
Kp = x(1); Ki = x(2); d = 0.001; I_ref = [-1.1 : d : 1]; dt = 0.01; t = [0 : dt : 1]; e_t = I_ref - (Kp.*e_t + Ki.*sum(t'.*abs(e_t)*dt));
f = sum(t'.*abs(e_t)*dt);% line 10
end

Unable to fit nonlinear curve to data in Matlab

I am trying to fit some data in Matlab to a Hill function of the form y = r^n/(r^n+K^n). I have data for r,y and I need to find K,n.
I tried two different approaches after reading the docs extensively - one uses fit from the CurveFitting Toolbox and the other uses lsqcurvefit from the Optimization Toolbox. I haven't had any success with either. What am I missing?
Here is my xdata and ydata:
xdata = logspace(-2,2,101);
ydata = [0.0981 0.1074 0.1177 0.1289 0.1411 0.1545 0.1692 0.1852 0.2027 ...
0.2219 0.2428 0.2656 0.2905 0.3176 0.3472 0.3795 0.4146 0.4528 ...
0.4944 0.5395 0.5886 0.6418 0.6994 0.7618 0.8293 0.9022 0.9808 ...
1.0655 1.1566 1.2544 1.3592 1.4713 1.5909 1.7183 1.8537 1.9972 ...
2.1490 2.3089 2.4770 2.6532 2.8371 3.0286 3.2272 3.4324 3.6437 ...
3.8603 4.0815 4.3065 4.5344 4.7642 4.9950 5.2258 5.4556 5.6833 ...
5.9082 6.1292 6.3457 6.5567 6.7616 6.9599 7.1511 7.3347 7.5105 ...
7.6783 7.8379 7.9893 8.1324 8.2675 8.3946 8.5139 8.6257 8.7301 ...
8.8276 8.9184 9.0029 9.0812 9.1539 9.2212 9.2834 9.3408 9.3939 ...
9.4427 9.4877 9.5291 9.5672 9.6022 9.6343 9.6638 9.6909 9.7157 ...
9.7384 9.7592 9.7783 9.7957 9.8117 9.8263 9.8397 9.8519 9.8630 ...
9.8732 9.8826];
'Fit' code:
HillEqn = '#(x,xdata)xdata.^x(1)./(xdata.^x(1)+x(2).^x(1))';
startPoints = [1 1];
fit(xdata',ydata',HillEqn,'Start',startPoints)
Error message:
Error using fittype>iTestCustomModelEvaluation (line 726)
Expression #(x,xdata)xdata.^x(1)./(xdata.^x(1)+x(2).^x(1)) is not a valid MATLAB
expression, has non-scalar coefficients, or cannot be evaluated:
Undefined function 'imag' for input arguments of type 'function_handle'.
'lsqcurvefit' code:
fun = #(x,xdata) xdata.^x(1)./(xdata.^x(1)+x(2).^x(1));
x0 = [1,1]; % Initial Parameter Estimates
x = lsqcurvefit(fun,x0,xdata,ydata);
Error message:
Error using snls (line 47)
Objective function is returning undefined values at initial point. lsqcurvefit cannot
continue.
First I think you need 3 variables to start from, because the hill function will be max of 1, and your data it is maxed at 10. So either normalize your data by doing ydata=ydata./max(ydata), or add a 3rd variable (which I did just for the demonstration). This is how I did it:
startPoints = [1 1 1];
s = fitoptions('Method','NonlinearLeastSquares',... %
'Lower',[0 0 0 ],...
'Upper',[inf inf inf],...
'Startpoint',startPoints);
HillEqn = fittype( 'x.^a1./(x.^a1+a2.^a1)*a3','options',s);
[ffun,gofrr] = fit(xdata(:),ydata(:),HillEqn);
yfit=feval(ffun,xdata(:)); %Fitted function
plot(xdata,ydata,'-bx',xdata,yfit,'-ro');
ffun =
General model:
ffun(x) = x.^a1./(x.^a1+a2.^a1)*a3
Coefficients (with 95% confidence bounds):
a1 = 1.004 (1.004, 1.004)
a2 = 0.9977 (0.9975, 0.9979)
a3 = 9.979 (9.978, 9.979)
Side note:
In your case what you really want to do is to transform you data by looking at Y=1./ydata instead, then fit, and then take another 1./Y to get the answer in the previous "hill" function representation. This is because your problem is bilinear in nature , so by going 1./ydata you get a bilinear relation, for which a polyfit of order 1 will do:
Y=1./ydata;
X = 1./xdata;
p=polyfit(X,Y,1);
plot(X,Y,'-bx',X,polyval(p,X),'-ro')

Nonlinear parameters search

need to find a set of optimal parameters P of the system y = P(1)*exp(-P(2)*x) - P(3)*x where x and y are experimental values. I defined my function
f = #(P) P(1)*exp(-P(2)*x) - P(3)*x
and
guess = [1, 1, 1]
and tried
P = fminsearch(f,guess)
according to Help. I get an error
Subscripted assignment dimension mismatch.
Error in fminsearch (line 191)
fv(:,1) = funfcn(x,varargin{:});
I don't quite understand where my y values would fall in, as well as where the function takes P from. I unfortunately have no access to nlinfit or optimization toolboxes.
You should try the matlab function lsqnonlin(#testfun,[1;1;1])
But first make a function and save in an m-file that includes all the data points, lets say your y is A and x is x like here below:
function F = testfun(P)
A = [1;2;3;7;30;100];
x = [1;2;3;4;5;6];
F = A-P(1)*exp(-P(2)*x) - P(3)*x;
This minimized the 2-norm end gives you the best parameters.

Matlab fit function error : Function value and YDATA sizes are not equal

I'm trying to use the fit function to estimate a 4 parameters model(P B A R) and meet the error following message and I dont know what does it mean.
Error using fit>iFit (line 367)
Function value and YDATA sizes are not equal.
Error in fit (line 108)
[fitobj, goodness, output, convmsg] = iFit( xdatain, ydatain, fittypeobj, ...
The basic function is
function c1 = c1(x,T,P,B,A,R)
if T == 0
c1=0;
else
G = #(t) 0.5*erfc((P./(4*B*R*t)).^0.5.*(B*R*x-t))...
-1/2*(1+P*x+P*t/(B*R))*exp(P*x).*erfc((P./(4*B*R*t)).^0.5.*(B*R*x+t))...
+(P*t/(pi*B*R)).^0.5.*exp(-P*(B*R*x-t).^2./(4*B*R*t)); %first term in the solution
u = #(t) A*t/(B*R);%.
v = #(t) A*(T-t)/(1-B)/R; %.
e = #(t) 2*(u(t.*v(t))).^0.5; %.
H1 = #(t) exp(-u(t)-v(t)).*(besseli(0,e(t))/B+besseli(1,e(t)).*((u(t)./v(t)).^0.5)/(1-B));
GH = #(t) G(t).*H1(t);
c1 = G(T).*exp(-A*T/(B*R))+A/R*integral(GH,0,T); %int((g*H1),0,T);
end
and another function that based on the foregoing function c1 is
function cm = cm(x,time,P,B,A,R,T1)
for i=1:length(time);
if time(i)<T1
cm(i)=c1(x,time(i),P,B,A,R);
else
cm(i)=c1(x,time(i),P,B,A,R)-c1(x,time(i)-T1,P,B,A,R);
end
end
This function mainly divide the data into two parts for different calculation.
I tried to give a reasonable arbitrary four parameters to run cm to obtain a set of time-c data, use the following code
x=2;
time=0.1:0.1:10;
T1=2;
c=cm(x,time,0.8,0.8,0.8,0.8,T1);
and it works well
after that I tried to use fit function to fit the set of data to obtain the four parameters, using the following code
ft = fittype('cm(x,time,P,B,A,R,T1)','independent','time','problem','x'); % independent variable is time, fixed parameter x
>> [f, gof] = fit( time', c', ft, 'Lower', [0, 0, 0, 1,2], 'Upper', [1, 1, 1, 1,2],'problem',x);
thats when I met the error
Error using fit>iFit (line 367)
Function value and YDATA sizes are not equal.
I checked the input time-c data that obtained from function cm, they have the same size, so I don'k see anything wrong with the input data. I suspect it is the problem with the function that fit function does not work.
Can anyone help me with this problem? Besides, what does it mean by YDATA?
Thank you in advance !

correct function handle for integral2 in matlab

I created a function in matlab that returns a vector like
function w = W_1D(x,pos,h)
w=zeros(1,length(x));
if (h~=0)
xmpos = x-pos;
inds1 = (-h <= xmpos) & (xmpos < 0);
w(inds1) = xmpos(inds1)./h + 1;
inds2 = (0 <= xmpos) & (xmpos <= h);
w(inds2) = -xmpos(inds2)./h + 1;
else
error('h shouldn't be 0')
end
end
Thus, in the end, there is a vector w of size length(x).
Now i created a second function like
function f = W_2D(x,y,pos_1,pos_2,h)
w_x = W_1D(x,pos_1,h);
w_y = W_1D(y,pos_2,h);
f = w_x'*w_y;
end
where length(x)=length(y). Thus, the function W_2D obviously returns a matrix.
But when I now try to evaluate the integral over a rectangular domain like e.g.
V = integral2(#(x,y) W_2D(x,y,2,3,h),0,10,0,10);
matlab returns some errors:
Error using integral2Calc>integral2t/tensor (line 242)
Integrand output size does not match the input size.
Error in integral2Calc>integral2t (line 56)
[Qsub,esub] = tensor(thetaL,thetaR,phiB,phiT);
Error in integral2Calc (line 10)
[q,errbnd] = integral2t(fun,xmin,xmax,ymin,ymax,optionstruct);
Error in integral2 (line 107)
Q = integral2Calc(fun,xmin,xmax,yminfun,ymaxfun,opstruct);
I also tried to vary something in the W_2D-function: instead of f = w_x'*w_y;
I tried f = w_x.'*w_y;
or w_y = transpose(w_y); f = kron(w_x,w_y);, but there is always this error with the Integrand output size-stuff.
Can anyone explain, where my fault is?
EDIT: After Werner's hint with the keyboard debugging method, I can tell you the following.
The first step returns w_x of type <1x154 double>, w_y is <1x192 double>, x and y are both <14x14 double>. In the next step, f appears with a value of <154x192 double>. Then everything disappears, except x and y and the matlab-function integral2Calc.m appears in the editor and it jumps to the Function Call Stack integral2t/tensor and after some more steps, the error occurs here
Z = FUN(X,Y); NFE = NFE + 1;
if FIRSTFUNEVAL
if ~isfloat(Z)
error(message('MATLAB:integral2:UnsupportedClass',class(Z)));
end
% Check that FUN is properly vectorized. This is important here
% because we (otherwise) always pass in square matrices, which
% reduces the probability of the user generating an error by
% using matrix functions instead of elementwise functions.
Z1 = FUN(X(VTSTIDX),Y(VTSTIDX)); NFE = NFE + 1;
if ~isequal(size(Z),size(X)) || ~isequal(size(Z1),size(VTSTIDX))
% Example:
% integral2(#(x,y)1,0,1,0,1)
error(message('MATLAB:integral2:funSizeMismatch'));
end
Hope that information is detailed enough...I have no idea what happenes, because my example is exact as it is given on the mathworks site about integral2, isn't it?
Maybe I should precise a bit more, what I wanna do: since W_2D gives me a surface w(x,y) of a compactly supported 2-dimensional hat-function, stored in a matrix w, I want to calculate the volume between the (x,y)-plane and the surface z=w(x,y)...
EDIT2: I still do not understand how to handle the problem, that integral2 creates matrices as inputs for my W_1D-functions, which are called in W_2D and intended to have a <1xn double>-valued input and return a <1xn double> output, but at least I can simply use the following to solve the integration over the tensor product by using two one-dimensional integral-calls, that is
V = integral(#(x)integral(#(y)W_1D(y,3,h),0,10).*W_1D(x,2,h),0,10);
This first function is quite wrong. You are not indexing the array positions while you are doing w = x inside for.
Besides, if that would work, you are returning a line vector, that is, size 1xlength(x) and when you do w_x'*w_y you are doing length(x)x1 times 1xlength(y), which would give you a matrix length(x)*length(y).
Consider correcting your function:
function w = W_1D(x,pos)
w = zeros(length(x),1); % Allocate w as column vector, so that the product gives a scalar (as I suppose that it is what you want.
for ii=1:length(x) % Here, so that is indexes w and x elements as you need
w(ii)=x(ii) - pos; % I changed your code to something that makes sense, but I don't know if that is what you want to do, you have to adapt it to work correctly.
end
end
You may also want to debug your functions, consider adding keyboard before your operations and check what they are returning using dbstep. I.e:
function f = W_2D(x,y,pos_1,pos_2)
w_x = W_1D(x,pos_1);
w_y = W_1D(y,pos_2);
keyboard
f = w_x'*w_y;
end
Execution will stop at keyboard, then you can check w_x size, w_y size, and do dbstep to go after f = w_x'*w_y and see what it returned. After you finish debug, you can do dbcont so that it will continue execution.
This answer is a draft as it is quite difficult to help you with the information you have provided. But I think you can start working the things out with this. If you have more doubts feel free to ask.