For my computing course, I am given the following function:
y'(x) = -8y(x) + 0.5x + 1/16 and an initial value of y(0)=2.
Now, I am asked to solve this equation by using Euler's method, in MATLAB.
My code should give an output of 2 arrays: xar and yar, in which I see the x-values vs. the y-values, however, if I run my code, it says: "undefined variable x". Here's my code:
function [xar,yar] = Euler(a,b,ybouco,N)
% a is the lower limit
% b is the upper limit
% ybouco is the initial value
% N is the number of intervals
a=0;
b=3;
ybouco=2;
N=10;
h=(b-a)/N;
T=a:h:b;
y(1)=ybouco;
f = #(x) -8*y(x) + 0.5x + (1/16);
y(x) = 2*exp(-8*x)+(1/16)*x;
for i = 1:N
y (i+1) = y(i)+h*f(T(i));
end
end
Can someone explain what is wrong with my code??
First of all, note that assigning argument parameters in the function block is wrong! (i.e. a,b,ybouco and N) should be passed through argument by calling the function. There is no use in writing arguments to be assigned by user beside assigning them in the script manually.
One way is to call the function and assign the value in the command window like below:
[x,y]=Euler(0,3,2,10)
where a=0, b=3, ybouco=2 and N=10 was passed to the function as an input and x and y returned by the function as output.
Plus, when you are solving an ODE numerically it means you do not know y analytically.
So you should omit the assigning part of the code and do a little change like below:
function [xar,yar] = Euler(a,b,ybouco,N)
h=(b-a)/N;
T=a:h:b;
y(1)=ybouco;
for i = 1:N
f(i) = -8*y(i) + 0.5*T(i) + (1/16);
y(i+1) = y(i)+h*f(i);
end
xar=T;
yar=y;
end
Then by calling the function in the command window, you will get the following results:
x =
Columns 1 through 8
0 0.3000 0.6000 0.9000 1.2000 1.5000 1.8000 2.1000
Columns 9 through 11
2.4000 2.7000 3.0000
y =
Columns 1 through 8
2.0000 -2.7813 3.9575 -5.4317 7.7582 -10.6627 15.1716 -20.9515
Columns 9 through 11
29.6658 -41.1533 58.0384
You can also plot the result and get the following graph:
If you increase N from 10 to 100 you will have more accurate results and a smooth graph like below:
The error message is because you have an assignment
y(x) = 2*exp(-8*x)+(1/16)*x;
where x is not defined. The x in y(x) indexes into the array y.
Maybe you intended to write
y = #(x) 2*exp(-8*x)+(1/16)*x;
to define an anonymous function. But that would clash with the array y you have already defined. Maybe just delete this line?
Also,
h=(b-a)/N;
T=a:h:b;
can be better written as
T = linspace(a,b,N);
Related
I have a problem about matlab code. I have a specific one variable function, and i want to assign a vector or array in this function as a x value. But i didn't reach success.
A part of my code is at below:
a=-5; b=10; n=20; %[a,b] interval range and n is number of interval
sz = (b-a)/n; %interval size
t=1; %iteration number
for i=1:n
I(i,:,t) = [a+(i-1)*sz a+i*sz]; %interval
x(i,:,t) = a+(i-0.5)*sz; %midpoint of interval
end
f= x.^2-3.*x+5; %my sample function
for i=1:n
if i==1
j=i+1;
neigbor(i,:,t) = I(j,:,t); %neigbor of interval I1's
h_f(i,:,t) =abs(f(x(i,:,t))-f(x(j,:,t))); %heuristic value
prob(i,:,t)=(ph(j,:,t).*h_f(i,:,t))./(ph(j,:,t).*h_f(i,:,t)); %probability
...
Other if conditions are following this code, but i check this below portion with sample i and j value, it gives error like this : "Subscript indices must either be real positive integers or logicals."
h_f(i,:,t) =abs(f(x(i,:,t))-f(x(j,:,t)));
What don't i know? What is my mistake? Can you suggest anything? If you need complete code, i can post.
Edit : Actually this function f returns value by using itself. But it doesn't return value in comment h_f(i,:,t)= abs((f(x(i,:,t)-x(j,:,t))
Solution Edit: After creating separate function m file, and calling in main function. Don't need to write x array in f.
In Matlab, you need to declare functions in a separate .m file. I created a separate file "f.m" and inserted the following code:
function return_val = f(x)
return_val = x.^2-3.*x+5; %my sample function
end
Then, in your main file, you can call this function as follows:
a=-5; b=10; n=20; %[a,b] interval range and n is number of interval
sz = (b-a)/n; %interval size
t=1; %iteration number
for i=1:n
I(i,:,t) = [a+(i-1)*sz a+i*sz]; %interval
x(i,:,t) = a+(i-0.5)*sz; %midpoint of interval
end
f = f(x)
Hope this helps.
As was posted you can create f as a function in a file but you can also inline the function with a handle. This is done as follows:
f = #(x) x.^2 - 3.*x + 5;
The f is the function handle with x as an input, see more information here: http://www.mathworks.com/help/matlab/matlab_prog/creating-a-function-handle.html.
This new f is then used as you would expect.
>> f(2)
ans =
3
>> f(5)
ans =
15
>> f(1:3)
ans =
3 3 5
>> f(4:10)
ans =
9 15 23 33 45 59 75
So I have been trying to develop a secant method program that can be used for finding the root of
f(x) = tanh(x) - (x / 3)
However the answer output is nowhere close. Every solution I have found seems a more complex way to solve it.
x = 2;
prevx = x;
for i = 1:20
x = x - (tanh(x)-(x/3))*((x-(prevx))/((tanh(x)-(x/3))-(tanh(prevx))-((prevx/3))));
prevx = prevx + x;
x
end
The answer should be 2.987. I am getting a negative number though for some reason.
You suppose to add up terms so replace minus in this line:
x = x - (tanh(x)-(x/3))*((x-(prevx))/((tanh(x)-(x/3))-(tanh(prevx))-((prevx/3))));
To a plus:
x = x + (tanh(x)-(x/3))*((x-(prevx))/((tanh(x)-(x/3))-(tanh(prevx))-((prevx/3))));
To get a desired result of 2.987 for x. Also remove x before the end of the loop.
Here is an alternative way to do it (#madbitloman is right on spot as for the error in your code).
The secant method can be illustrated as follows (from Wikipedia):
Now if we translate this into MATLAB code, that would look like this:
x(k) = x(k-1) - (f(x(k-1)))*((x(k-1) - x(k-2))/(f(x(k-1)) - f(x(k-2))));
What is left to be done is provide the algorithm with 2 initial estimates and some tolerance value to tell it when to stop searching for a root, i.e. when 2 consecutive entries in x are very close to each other.
Let's choose 2 and 4. The closer the estimates to the real root, the faster the convergence.
To avoid using tanh(x)-(x/3) inside the for loop, which could get messy, let's define an anonymous function that takes x as an argument:
f = #(x) tanh(x)-(x/3);
So that would be the f in the line of code above.
Therefore, the whole code would look like the following, with a tolerance of say 0.001:
clear
clc
%// Initial values and tolerance
x(1) = 2;
x(2) = 4;
f = #(x) tanh(x)-(x/3);
tolerance = 0.001;
%// Let's try from 3 to 15.
for k=3:15
x(k) = x(k-1) - (f(x(k-1)))*((x(k-1) - x(k-2))/(f(x(k-1)) - f(x(k-2))));
if abs(x(k)-x(k-1)) < tolerance
break
end
end
After running the code, x looks like this:
x =
2.0000 4.0000 2.9420 2.9839 2.9847
so it took 5 iterations to reach the desired tolerance. Note that due to floating arithmetic you might want to use a smaller tolerance because Matlab stores numbers with much more digits than 4.
Finally you can get the negative root as well using appropriate initial estimates like -2 and -4:
x =
-2.0000 -4.0000 -2.9420 -2.9839 -2.9847
Hope that helps somehow!
I am trying to numerically simulate a system using ODE45. I cannot seem to figure out why i'm getting the following error:
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
[t,x] = ode45(#NL_hw3c, [0,20], [1, 2, 3]);
function sys = NL_hw3c(t,x)
sys = [x(2)+ x(1)^2;
x(3)+log10(x(2)^2 + 1);
-3*x(1)-5*x(2)-3*x(3)+4*cos(t)-5*x(1)^2 -3*log10(x(2)^2 + 1) -6*x(1)*x(2) -6*x(1)^3 -(2*x(2)*(x(3)+ log10(x(2)^2 + 1)))/((x(2)^2 + 1)*log(10)) -2*x(1)*x(3) -2*x(1)*log10(x(2)^2 + 1) -2*x(2)^2 -8*x(1)^2*x(2) -6*x(1)^4];
end
Googled and couldn't find a similar solution. Any help would be appreciated.
Thanks
I had to separate each of the variables in your array for it to work:
function s = NL_hw3c(t,x)
s1 = x(2)+ x(1)^2;
s2 = x(3)+log10(x(2)^2 + 1);
s3 = -3*x(1)-5*x(2)-3*x(3)+4*cos(t)-5*x(1)^2 -3*log10(x(2)^2 + 1) -6*x(1)*x(2) -6*x(1)^3 -(2*x(2)*(x(3)+ log10(x(2)^2 + 1)))/((x(2)^2 + 1)*log(10)) -2*x(1)*x(3) -2*x(1)*log10(x(2)^2 + 1) -2*x(2)^2 -8*x(1)^2*x(2) -6*x(1)^4;
s = [s1;s2;s3];
end
I got the following output:
t =
0
0.0018
0.0037
0.0055
0.0074
0.0166
...
...
19.7647
19.8431
19.9216
20.0000
x =
1.0000 2.0000 3.0000
1.0055 2.0067 2.8493
1.0111 2.0131 2.6987
1.0167 2.0192 2.5481
1.0224 2.0251 2.3975
...
...
0.7926 -0.0187 -1.7587
0.8380 -0.1567 -1.7624
0.8781 -0.2928 -1.7534
0.9129 -0.4253 -1.7299
The reason why your function didn't work was because in the last value of your array, the spaces between each part of the expression are interpreted as going into a separate column. Essentially, the first two rows of your matrix consist of 1 element and if you use the last expression exactly as it is, you would be trying to place 9 elements in the last row. I'm assuming you want a 3 x 1 matrix and the last element would thus violate the size of this matrix that you want to create and this is why it's giving you an error.
I'm assuming you want the last value as an entire expression, so to do this you will need to place this as a separate expression then place it into your array.
To make the code more readable, I've placed all of the entries as separate variables before making the array.
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?
I'm using ode45() in matlab for some work in dynamics, using this procedure to calculate the largest Lyaponov exponent of the Lorenz system.
The process involves solving a system of differential equations starting from x0, and comparing this with a trajectory starting very close to x0.
At each time step, the second trajectory needs to be re-adjusted before advancing the time step, so I would like to be able to call ode45() just once - is this possible?
A starting attempt for an alternative solution is presented here, but it doesn't work; as far as I can see, the matrices r and R obtained below should be similar:
% Time
ti = 0; tf = 1; res = 10;
T = linspace(ti, tf, res);
% Solve the system first time
[~,R] = ode45('tbmLorenz',T,x0);
% Alternate trajectory
r = zeros(size(R));
% Temporary cordinates
temp = zeros(3,3);
% Solve for second trajectory
for i = 2:(res-1)
% Time step
ts = T((i-1):(i+1));
% Solve three steps
[~,temp] = ode45('tbmLorenz',ts,r(i-1,:));
r_i = temp(2,:)
% Code to alter r_i goes here
% Save this
r(i,:) = r_i;
end
... but they aren't:
r =
1.0000 3.0000 4.0000
9.7011 20.6113 7.4741
29.9265 16.4290 79.0449
-5.7096 -15.2075 49.2946
-12.4917 -13.6448 44.7003
-13.6131 -13.8826 45.0346
-13.5061 -13.1897 45.4782
-13.0538 -13.0119 44.5473
-13.4463 -13.8155 44.4783
0 0 0
>> R
R =
1.0000 3.0000 4.0000
9.7011 20.6139 7.4701
29.9663 16.5049 79.1628
-5.7596 -15.2745 49.3982
-12.4738 -13.5598 44.7800
-13.5440 -13.8432 44.9084
-13.5564 -13.3049 45.4568
-13.1016 -12.9980 44.6882
-13.3746 -13.7095 44.4364
-13.7486 -13.6991 45.4092
That the last line of r is zero is not a problem.
Any ideas? Cheers!
\T
Ode45 is an adaptive algorithm with varying step size by definition. You can fix the step size if you want, but then you are not using ode45, you're using something else.
If you want to solve this type of problem, I suggest you use a fixed time step. This link has code for what you need.
If you insist on using ode45, then you must adjust the simulation file itself with time-interpolated inputs.