Matlab error with nlinfit - matlab

I'm attempting to get the nonlinear least squares fit of the following equation:
y = 1 / (1 + a (ln(duration)^b))
The data I wish to fit this to is presented below, as is my attempt to use the nlinfit function to solve it...
x = [1.99000000000000;3.01000000000000;4.01000000000000;5.09000000000000;5.77000000000000;6.85000000000000;7.72000000000000;8.87000000000000;9.56000000000000;];
y = [1;1;0.800000000000000;0.730000000000000;0.470000000000000;0.230000000000000;0.270000000000000;0.100000000000000;0.100000000000000;];
plot(x,y,'o','linestyle','none');
p(1) = 1;
p(2) = 1;
fun = #(p,x) 1 / (1 + p(1).*(log(x).^p(2)));
myfit = nlinfit(x,y,fun,p);
My problem seems to be with defining the fourth required input for the nlinfit function ('p' in the example above). The documentation doesn't give a clear explanation of what is needed for this to work, and I can't solve the problem based on the error messages I receive:
??? Error using ==> nlinfit at 128 MODELFUN should return a vector of fitted values the same length as Y.
Error in ==> fitting at 14 myfit = nlinfit(x,y,fun,p);
I've tried setting p to repmat(1,9,1) and repmat(1,1,9), but neither of these seem to solve my problem. Any help would be greatly appreciated!

I believe you forgot the dot to do element-wise deletion in the function:
fun = #(p,x) 1 ./ (1 + p(1).*(log(x).^p(2)));
^

Related

`fzero` function in matlab fails to find the root and keeps on generating error

I am using MATLAB and I want to find the root of an equation F(x)-u=0. Here u=0.2861 and
F=normcdf(sqrt(lambda/t)*(t/mu-1))+exp(2*lambda/mu)*normcdf(-sqrt(lambda/t)*(t/mu+1)).
The value of lambda and mu are both 1.
I typed the following code
[x,fval] = fzero(#(t) normcdf(sqrt(lambda/t)*(t/mu-1))+exp(2*lambda/mu)*normcdf(-sqrt(lambda/t)*(t/mu+1))-u, 10);
and hope this can help me find the root. I can show mathematically that this equation has unique root. However, I keep on getting the following error
Error using erfc Input must be real and full.
Error in normcdf>localnormcdf (line 128) p(todo) = 0.5 * erfc(-z ./
sqrt(2));
Error in normcdf (line 50) [varargout{1:max(1,nargout)}] =
localnormcdf(uflag,x,varargin{:});
Error in
Test>#(t)normcdf(sqrt(lambda/t)*(t/mu-1))+exp(2*lambda/mu)*normcdf(-sqrt(lambda/t)*(t/mu+1))-u
Error in fzero (line 363)
a = x - dx; fa = FunFcn(a,varargin{:});
Then I did a "brutal force" method.
t = [0:0.001:20];
F = normcdf(sqrt(lambda./t).*(t/mu-1))+exp(2*lambda/mu).*normcdf(-sqrt(lambda./t).*(t/mu+1))-u;
plot(t,F)
I can clearly eyeball that F(t)-u is increasing in t and the root is around 0.4. My question is why fzero does not work in this case and is there a way to make fzero work?
The problem is that the function does not change sign, which is required as the docs say:
x = fzero(fun,x0) tries to find a point x where fun(x) = 0. This
solution is where fun(x) changes sign — fzero cannot find a root of a
function such as x^2.
I broke up your code to make it a bit clearer (at least for me).
lambda = 1;
mu = 1;
u = 1;
% break up function code
arg1 = #(t) +sqrt(lambda./t).*(t./mu-1);
arg2 = #(t) -sqrt(lambda./t).*(t./mu+1);
fnc = #(t) normcdf(arg1(t))+exp(2*lambda/mu).*normcdf(arg2(t))-u;
% call fzero to find the root
% [x,fval] = fzero(fnc, 10);
% plot
x = 0:0.01:10;
plot(x,fnc(x))
The function is not defined for any input t < 0 due to the sqrt in my function handle arg. So if you plot it for values t > 0, you see that it never passes zero.
EDITED: sign mix-up in the arguments. Thx flxx for pointing this out. Plot & code updated. The argument still holds.

Using MATLAB plots to find linear equation constants

Finding m and c for an equation y = mx + c, with the help of math and plots.
y is data_model_1, x is time.
Avoid other MATLAB functions like fitlm as it defeats the purpose.
I am having trouble finding the constants m and c. I am trying to find both m and c by limiting them to a range (based on smart guess) and I need to deduce the m and c values based on the mean error range. The point where mean error range is closest to 0 should be my m and c values.
load(file)
figure
plot(time,data_model_1,'bo')
hold on
for a = 0.11:0.01:0.13
c = -13:0.1:-10
data_a = a * time + c ;
plot(time,data_a,'r');
end
figure
hold on
for a = 0.11:0.01:0.13
c = -13:0.1:-10
data_a = a * time + c ;
mean_range = mean(abs(data_a - data_model_1));
plot(a,mean_range,'b.')
end
A quick & dirty approach
You can quickly get m and c using fminsearch(). In the first example below, the error function is the sum of squared error (SSE). The second example uses the sum of absolute error. The key here is ensuring the error function is convex.
Note that c = Beta(1) and m = Beta(2).
Reproducible example (MATLAB code[1]):
% Generate some example data
N = 50;
X = 2 + 13*random(makedist('Beta',.7,.8),N,1);
Y = 5 + 1.5.*X + randn(N,1);
% Example 1
SSEh =#(Beta) sum((Y - (Beta(1) + (Beta(2).*X))).^2);
Beta0 = [0.5 0.5]; % Initial Guess
[Beta SSE] = fminsearch(SSEh,Beta0)
% Example 2
SAEh =#(Beta) sum(abs(Y-(Beta(1) + Beta(2).*X)));
[Beta SumAbsErr] = fminsearch(SAEh,Beta0)
This is a quick & dirty approach that can work for many applications.
#Wolfie's comment directs you to the analytical approach to solve a system of linear equations with the \ operator or mldivide(). This is the more correct approach (though it will get a similar answer). One caveat is this approach gets the SSE answer.
[1] Tested with MATLAB R2018a

Matlab, cannot turn sym to double

I am trying to solve a variable in an equation (syms x), I've simplified the equation. I am trying to store the value in P_9, a 1x1000 matrix by converting from a symbol to a double and am getting the error below. It is giving me a symbol of 0x0, which is where I think my error lies.
Please help me troubleshoot my code. Many thanks!
number = 1000;
P_9 = zeros(1,number);
A_t=0.67;
A_e = linspace(0,10,number);
for n=1:number
%% find p9
syms x
eqn = x + 5 == A_t/A_e(n);
solx = solve(eqn,x);
P_9(n) = double(solx);
end
Warning: Explicit solution could not be found.
In solve at 179
In HW4 at 74
In an assignment A(I) = B, the number of elements in B and I must be the same.
Error in HW4 (line 76)
P_9(n) = double(solx);
You certainly have an equation, where x can't be isolated.
For example it is impossible to isolate x in tan(x) + x == 1. So if you try to solve this equation analyticaly, matlab will tell you that x can't be isolated and therefore there is no explicit analytical solution.
So instead of using an analytical method to solve your equation, you need to use a numerical method, it's less "sexy" but this time you will be able to solve your equation.
Life is well done, matlab already integrate a numerical solver: vpasolve.
So your code will look like:
for n=1:number
%% find p9
syms x
eqn = x + 5 == A_t/A_e(n);
solx = vpasolve(eqn,x);
P_9(n) = double(solx);
end

How to use fminsearch to find local maximum?

I would like to use fminsearch in order to find the local maximum of a function.
Is it possible to find local maximum using fminsearch with "just" searching on the negative return value of the function.
for example:
function f = myfun(x,a)
f = x(1)^2 + a*x(2)^2;
a = 1.5;
x = fminsearch(#(x) -1 * myfun(x,a),[0,1]);
Is it possible?
Update1: In order to elaborate my question and making it clearer (following some comments below) - I'm adding this update:
By asking if it's possible to do so, I meant is it a proper use of fminsearch function - is it the intended use to find max using fminsearch?
Update2: for who ever concern with the same question - In addition to the correct answer below , here is the documentation from https://www.mathworks.com/help/matlab/math/optimizing-nonlinear-functions.html#bsgpq6p-10
Maximizing Functions
The fminbnd and fminsearch solvers attempt to minimize an objective function. If you have a maximization problem, that is, a problem of the form
max x f(x), then define g(x) = –f(x), and minimize g.
For example, to find the maximum of tan(cos(x)) near x = 5, evaluate:
[x fval] = fminbnd(#(x)-tan(cos(x)),3,8)
x = 6.2832
fval = -1.5574
The maximum is 1.5574 (the negative of the reported
fval), and occurs at x = 6.2832. This answer is correct since, to five
digits, the maximum is tan(1) = 1.5574, which occurs at x = 2π =
6.2832.
Yes you can, that's also why there is no fmaxsearch function:
For example:
func = #(x) sin(x);
sol = fminsearch(#(x) func(x),0)
% sol = pi/2
sol = fminsearch(#(x) func(x)*-1,0)
% sol = -pi/2

Fitting model to data in 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?