Nonlinear Regression & Optimisation - matlab

I need some help... I have got a model function which is :
function y = Surf(param,x);
global af1 af2 tData % A2 mER2
A1 = param(1); m1 = param(2); A2 = param(3); m2 = param(4);
m = param(5); n = param(6);
k1 = #(T) A1*exp(mER1/T);
k2 = #(T) A2*exp(mER2/T);
af = #(T) sech(af1*T+af2);
y = zeros(length(x),1);
for i = 1:length(x)
a = x(i,1); T = temperature(i,1);
y(i) = (k2(T)+k1(T)*(a.^m))*((af(T)-a).^n);
end
end
And I have got a set of Data giving Cure, Cure_rate, Temperature. Which are all in a single vertical column matrix.
Basically, I tried to use :
[output,R1] = lsqcurvefit(#Surf, initial_guess, Cure, Cure_rate)
[output2,R2] = nlinfit(Cure,Cure_rate,#Surf,initial_guess)
And they works pretty well, (my initial_guess are initial guess of parameters in the above model which is in : [1.1e+07 -7.8e+03 1.2e+06 -7.1e+03 2.2 0.72])
My main problem is, when I try to look into different methods which could do nonlinear regression such as fminsearch, fmincon, fsolve, fminunc, etc. They just dont work and I am quite confused about the input that I am considering. Mainly beacuse they dont work as same as nlinfit and lsqcurvefit (input of Cure, Cure_rate), most of them considered the model function and the initial guess only, The way I did the above:
output3 = fminsearch(#Surf,initial_guess)
output4 = fsolve(#Surf,initial_guess)
output5 = fmincon(#Surf,x0,A,b,Aeq,beq)
(Not sure what should I put for Linear Inequality Constraint:
A,b and Aeq,beq )
output6 = fminunc(#Surf,initial_guess)
The problem is Matlab keep saying either I have not enough input or too many input which I don't get it and how should I include my Dataset in the fitting function (Cure, Cure_rate) in the above functions, like in nlinfit and lsqcurvefit?

Related

Curve fitting with self-defined equation

I am trying to fit the experimental data with a self-defined equation with MATLAB. The parameters I am interested are y0, tau, D and C. But I keep getting an error saying matrix dimensions must agree with the the following codes:
t = [0.00468
0.01277
0.05987
0.10316
0.18595
0.29252
0.39529
0.52136
0.68313
0.88818
1.08182
1.28688
1.45625
1.57471
1.73267
1.84685]
y = [9.02766
7.53879
5.3679
4.28093
3.09349
2.11005
1.63202
1.10224
0.77341
0.54506
0.42022
0.24363
0.21623
0.11575
0.11575
0.06704]
a1 = 10.86;
a2 = 15.5;
b1 = 8.74;
E = 1e15;
F = #(x,xdata)y0.*exp(-xdata./tau-(4.*pi./3).*E.*(1.7725).*(C.*xdata).^(1/2).*((1.+a1.*(D.*C.^(-1/3).*xdata.^(2/3)...
)+a2.*(D.*C.^(-1/3).*xdata.^(2/3)).^2)./(1.+b1.*(D.*C.^(-1/3).*xdata.^(2/3))).^(3/4)));
X0 = [1 1e-3 1e-13 1e-30];
[x,resnorm,~,exitflag,output] = lsqcurvefit(F,X0,t,y)
The equation is described in the inserted graph and an example of using the functiong to fit the data is shown in another graph. Thank you for your help.

Input & Coefficients from time domain to frequency, add them together and back to time domain

I am currently studying computer science and i have a task to solve for my lab project. I have to transfer input's signal & coefficients' from time domain to frequency domain, add them together and transfer back to time domain. My results have to match filter function output. However i cannot seem to find what am doing wrong here. I think its something wrong when i add two frequency via conj function. Unfortunately neither my teacher nor my lab supervisor are interested in actually teaching anything so i have to find answers on my own. Hope you guys can help.
clc
clear
B = [0.2];
A = [1,-0.5];
xt = ones(1,20);
xt = padarray(xt,[0,100])
A1 = 1;
A2 = 1;
f1 = 1;
f2 = 25;
fs = 1000;
xd = fft(xt);
wd = freqz(B,A,length(xt));
y = filter(B,A,xt);
yd = conj((wd)').*xd;
yt = real(ifft(yd));
subplot(4,2,1);
plot(xt)
title('Input signal')
subplot(4,2,2);
plot(abs(xd))
title('Input in frequency domain')
subplot(4,2,4);
plot(abs(wd))
title('Coefficients in frequency domain')
subplot(4,2,7);
plot(y)
title('Output using FILTER function')
subplot(4,2,6);
plot(yd)
title('Adding input with coefficients in frequency domain')
subplot(4,2,8);
plot(yt)
title('Back to time domain using IFFT')
The matlab function freqz() can be a little misleading. The "FFT" domain of your coefficients needs to be generated differently. Replace your stuff with the following code, and it should give you what you want:
xt = xt.';
xd = fft(xt);
wd = freqz(B,A,length(xt),'whole');
y = filter(B,A,xt);
yd = wd.*xd;
yt = ifft(yd);
figure
plot(abs(xd))
hold on
plot(abs(wd))
figure
plot(y,'.k','markersize',20)
hold on
plot(yt,'k')
hold off
Also, a note on the ' operator with complex vectors: unless you use the .' operator (e.g., x = x.'), it will transpose the vector while taking the complex conjugate, i.e., (1+1i).' = (1+1i) while (1+1i)' = (1-1i)

separate 'entangled' vectors in Matlab

I have a set of three vectors (stored into a 3xN matrix) which are 'entangled' (e.g. some value in the second row should be in the third row and vice versa). This 'entanglement' is based on looking at the figure in which alpha2 is plotted. To separate the vector I use a difference based approach where I calculate the difference of one value with respect the three next values (e.g. comparing (1,i) with (:,i+1)). Then I take the minimum and store that. The method works to separate two of the three vectors, but not for the last.
I was wondering if you guys can share your ideas with me how to solve this problem (if possible). I have added my coded below.
Thanks in advance!
Problem in figures:
clear all; close all; clc;
%%
alpha2 = [-23.32 -23.05 -22.24 -20.91 -19.06 -16.70 -13.83 -10.49 -6.70;
-0.46 -0.33 0.19 2.38 5.44 9.36 14.15 19.80 26.32;
-1.58 -1.13 0.06 0.70 1.61 2.78 4.23 5.99 8.09];
%%% Original
figure()
hold on
plot(alpha2(1,:))
plot(alpha2(2,:))
plot(alpha2(3,:))
%%% Store start values
store1(1,1) = alpha2(1,1);
store2(1,1) = alpha2(2,1);
store3(1,1) = alpha2(3,1);
for i=1:size(alpha2,2)-1
for j=1:size(alpha2,1)
Alpha1(j,i) = abs(store1(1,i)-alpha2(j,i+1));
Alpha2(j,i) = abs(store2(1,i)-alpha2(j,i+1));
Alpha3(j,i) = abs(store3(1,i)-alpha2(j,i+1));
[~, I] = min(Alpha1(:,i));
store1(1,i+1) = alpha2(I,i+1);
[~, I] = min(Alpha2(:,i));
store2(1,i+1) = alpha2(I,i+1);
[~, I] = min(Alpha3(:,i));
store3(1,i+1) = alpha2(I,i+1);
end
end
%%% Plot to see if separation worked
figure()
hold on
plot(store1)
plot(store2)
plot(store3)
Solution using extrapolation via polyfit:
The idea is pretty simple: Iterate over all positions i and use polyfit to fit polynomials of degree d to the d+1 values from F(:,i-(d+1)) up to F(:,i). Use those polynomials to extrapolate the function values F(:,i+1). Then compute the permutation of the real values F(:,i+1) that fits those extrapolations best. This should work quite well, if there are only a few functions involved. There is certainly some room for improvement, but for your simple setting it should suffice.
function F = untangle(F, maxExtrapolationDegree)
%// UNTANGLE(F) untangles the functions F(i,:) via extrapolation.
if nargin<2
maxExtrapolationDegree = 4;
end
extrapolate = #(f) polyval(polyfit(1:length(f),f,length(f)-1),length(f)+1);
extrapolateAll = #(F) cellfun(extrapolate, num2cell(F,2));
fitCriterion = #(X,Y) norm(X(:)-Y(:),1);
nFuncs = size(F,1);
nPoints = size(F,2);
swaps = perms(1:nFuncs);
errorOfFit = zeros(1,size(swaps,1));
for i = 1:nPoints-1
nextValues = extrapolateAll(F(:,max(1,i-(maxExtrapolationDegree+1)):i));
for j = 1:size(swaps,1)
errorOfFit(j) = fitCriterion(nextValues, F(swaps(j,:),i+1));
end
[~,j_bestSwap] = min(errorOfFit);
F(:,i+1) = F(swaps(j_bestSwap,:),i+1);
end
Initial solution: (not that pretty - Skip this part)
This is a similar solution that tries to minimize the sum of the derivatives up to some degree of the vector valued function F = #(j) alpha2(:,j). It does so by stepping through the positions i and checks all possible permutations of the coordinates of i to get a minimal seminorm of the function F(1:i).
(I'm actually wondering right now if there is any canonical mathematical way to define the seminorm so we get our expected results... I initially was going for the H^1 and H^2 seminorms, but they didn't quite work...)
function F = untangle(F)
nFuncs = size(F,1);
nPoints = size(F,2);
seminorm = #(x,i) sum(sum(abs(diff(x(:,1:i),1,2)))) + ...
sum(sum(abs(diff(x(:,1:i),2,2)))) + ...
sum(sum(abs(diff(x(:,1:i),3,2)))) + ...
sum(sum(abs(diff(x(:,1:i),4,2))));
doSwap = #(x,swap,i) [x(:,1:i-1), x(swap,i:end)];
swaps = perms(1:nFuncs);
normOfSwap = zeros(1,size(swaps,1));
for i = 2:nPoints
for j = 1:size(swaps,1)
normOfSwap(j) = seminorm(doSwap(F,swaps(j,:),i),i);
end
[~,j_bestSwap] = min(normOfSwap);
F = doSwap(F,swaps(j_bestSwap,:),i);
end
Usage:
The command alpha2 = untangle(alpha2); will untangle your functions:
It should even work for more complicated data, like these shuffled sine-waves:
nPoints = 100;
nFuncs = 5;
t = linspace(0, 2*pi, nPoints);
F = bsxfun(#(a,b) sin(a*b), (1:nFuncs).', t);
for i = 1:nPoints
F(:,i) = F(randperm(nFuncs),i);
end
Remark: I guess if you already know that your functions will be quadratic or some other special form, RANSAC would be a better idea for larger number of functions. This could also be useful if the functions are not given with the same x-value spacing.

Changing parameters within function for use with ODE solver

Is is it possible to use an ODE solver, such as ode45, and still be able to 'change' values for the parameters within the called function?
For example, if I were to use the following function:
function y = thisode(t, Ic)
% example derivative function
% parameters
a = .05;
b = .005;
c = .0005;
d = .00005;
% state variables
R = Ic(1);
T = Ic(2);
y = [(R*a)-(T/b)+d; (d*R)-(c*T)];
with this script:
clear all
% Initial conditions
It = [0.06 0.010];
% time steps
time = 0:.5:10;
% ODE solver
[t,Ic1] = ode45(#thisode, time, It);
everything works as I would expect. However, I would like a way to easily change the parameter values, but still run multiple iterations of the ODE solver with just one function and one script. However, it does not seem like I can just add more terms to the ODE solver, for example:
function y = thisode(t, Ic, P)
% parameters
a = P(1);
b = P(2);
c = P(3);
d = P(4);
% state variables
R = Ic(1);
T = Ic(2);
y = [(R*a)-(T/b)+d; (d*R)-(c*T)];
with this script:
clear all
% Initial conditions
It = [0.06 0.010];
P1 = [.05 .005 .0005 .00005]
% time steps
time = 0:.5:10;
% ODE solver
[t,Ic1] = ode45(#thisode, time, It, [], P1);
does not work. I guess I know this shouldn't work, but I have been unable to come up with a solution. I was also considering an if statement within the function and then hard coding several sets of parameters based to be used (e.g use set 1 when P == 1, set 2 when P == 2, etc) but this also did not work as I don't where to call the set to be used with an ODE. Any tips or suggestion on how to use one function and one script with an ODE solver while being able to change parameter values would be much appreciated.
Thank you,
Mike
You'll have to call the function differently:
[t,Ic1] = ode45(#(t,y) thisode(t,y,P1), time, It);
The function ode45 assumes all functions passed to it accept only an t and a y. The above call is a standard trick to get Matlab to pass P1, while ode45 will pass it t and y on every call.

Non linear function parameter estimation - matlab, lsqnonlin, fzero

I'm having difficulty with a fitting problem. From the errors that I get I imagine that the boundaries are not defined correctly and I haven't managed to find a solution. Any help would be very much appreciated.
Alternative methods for the solution of the same problem are also accepted.
Description
I have to estimate the parameters of a non-linear function of the type:
A*y(x) + B*EXP(C*y(x)) + g(x,D) = 0
subjected to the parameters PAR = [A,B,C,D] being within the range
LB < PAR < UB
Code
To solve the problem I'm using the Matlab functions lsqnonlin and fzero. The simplified code used is reported below.
The problem is divided in four functions:
parameterEstimation - (a wrapper for the lsqnonlin function)
objectiveFunction_lsq - (the objective function for the param estimation)
yFun - (the function returing the value of the variable y)
objectiveFunction_zero - (the objective function of the non-linear equation used to calculate y)
Errors
Running the code on the data I get the this waring
Warning: Length of lower bounds is > length(x); ignoring extra bounds
and this error
Failure in initial user-supplied objective function evaluation.
LSQNONLIN cannot continue
This makes me to think that the boundaries are not correctly used or not correctly called, but maybe the problem is elsewhere.
function Done = parameterEstimation()
%read inputs
Xmeas = xlsread('filepath','worksheet','range');
Ymeas = xlsread('filepath','worksheet','range');
%inital values and boundary conditions
initialGuess = [1,1,1,1]; %model parameters initial guess
LB = [0,0,0,0]; %model parameters lower boundaries
UB = [2,2,2,2]; %model parameters upper boundaries
%parameter estimation
calcParam = lsqnonlin(#objectiveFunction_lsq_2,initialGuess,LB,UB,[],Xmeas,Ymeas);
Done = calcParam;
function diff = objectiveFunction_lsq_2(PAR,Xmeas,Ymeas)
y_calculated = yFun(PAR,Xmeas);
diff = y_calculated-Ymeas;
function result = yFun(PAR,X)
y_0 = 2;
val = fzero(#(y)objfun_y(y,PAR,X),y_0);
result = val;
function result = objfun_y(y,PAR,X)
A = PAR(1);
B = PAR(2);
A = PAR(3);
C = PAR(4);
D = PAR(5);
val = A*y+B*exp(y*C)+g(D,X);
result = val;
I don't have the optimization toolbox, but are you sure you can pass the constants like that?
I would do this instead:
calcParam = lsqnonlin(#(PAR) objectiveFunction_lsq_2(PAR,Xmeas,Ymeas),initialGuess,LB,UB);