I have a problem similar to this question:
How to solve an overdetermined set of equations using non-linear lest squares in Matlab
I have 9 equations with 2 unknowns x and y, as follows:
A11=(sin(x))*(cos(y))*(sin(x))*(cos(y));
A12=(sin(x))*(cos(y))*(sin(x))*(sin(y));
A13=(sin(x))*(cos(y))*(cos(x));
A21=(sin(x))*(sin(y))*(sin(x))*(cos(y));
A22=(sin(x))*(sin(y))*(sin(x))*(sin(y));
A23=(sin(x))*(sin(y))*(cos(x));
A31=(sin(x))*(cos(y))*(cos(x));
A32=(sin(x))*(sin(y))*(cos(x));
A33=(cos(x))*(cos(x));
I know the values for each A_ij and want to calculate x and y.
I tried to realize that by using lsqcurvefit like this:
ydata=[0, 0, 0, 0, 1, 0, 0, 0, 0]; % this is one set of A_ij
lb=[0,0];
ub=[pi,2*pi];
x0=[pi/2;pi];
p=zeros(2,1);
p = lsqcurvefit( myfun,x0,xdata,ydata,lb,ub)
I don't have any values for xdata, so is there any way to still make it run like this?
I defined the function myfun as:
function r = myfun(p)
x=p(1);
y=p(2);
The 9 equations;
r=[A11, A12, A13, A21, A22, A23, A31, A32, A33];
end
Now, whenever I run lsqcurvefit I get the error "Not enough input arguments." And the error occurs in the line of x=p(1);
I don't know what is missing or better, I don't know how to handle the fact that I don't have xdata input.
I hope somebody can help me getting this to work.
Thank you very much in advance.
Fabian
After some tinkering I got it to work with lsqcurvefit. It was basically a problem of how I addressed the xdata and ydata to the function handle.
This is how my code looked like in the end:
ydata = [0.12; 0.28; 0.16; 0.66; 0.38; 0.22];
xdata = [0; 0; 0; 0; 0; 0]; % just as a dummy, lsqcurvefit wants the input
x0=[0,0];
lb=[0,0];
ub=[180,180];
[x,resnorm,residual,exitflag,output] = lsqcurvefit(#fun,x0,xdata,ydata,lb,ub);
% only x is needed, but I used the other information for other parts of my code
% as a separate function.m file
function F = fun(x,xdata) % again, xdata is just a dummy and not used
p1=cosd(x(1));
p2=sind(x(1))*sind(x(2));
p3=sind(x(1))*cosd(x(2));
% I didn't need all 9 entries, since the 3x3 matrix is symmetric
F =[p1*p1;...
p1*p2;...
p1*p3;...
p2*p2;...
p2*p3;...
p3*p3];
end
I hope, somebody gets use from this information.
Related
I want to solve my differential equation and plot velocity vectors but I am having some trouble with that. I tried this:
syms y(x);
ode = (1+exp(x))*y*diff(y,x)-2*exp(x) == 0;
ySol = dsolve(ode)
[X,Y] = meshgrid(-2:.2:2);
Z = 2*exp(X)/((1+exp(X)).*Y);
[DX,DY] = gradient(Z,.2,.2);
figure
contour(X,Y,Z)
hold on
quiver(X,Y,DX,DY)
hold off
and I get this error:
Warning: Matrix is singular to working precision.
Warning: Contour not rendered for non-finite ZData
It is probably something simple that I do not see but I am just starting using Matlab and I cold not find a right way to do my task. Please help me...
EDIT
As bconrad suggested, I changed my Z function like this:
Z = 2*exp(X)/((1+exp(X)).*Y);
and the previous errors are fixed. However, my prime goal, to plot velocity vectors is not accomplished yet because I get a graph like this:
Don’t have the ability to check at the moment, but I reckon you want an element by element division in that line. You’re missing a dot on the division, try
Z = 2*exp(X)./((1+exp(X)).*Y);
I took a closer look once at my station. The zero-division mentioned by Pablo forces inf's in Z, so quiver get's confused when scaling the vectors (understandably) and just doesn't show them. Try this (with the ode part removed):
[X,Y] = meshgrid(-2 : .2 : 2);
Z = 2 * exp(X) ./ ((1 + exp(X)) .* Y);
Z(isinf(Z)) = nan; % To avoid 0-division problems
[DX, DY] = gradient(Z, .2, .2);
figure
contour(X, Y, Z, 30, 'k')
hold on
quiver(X, Y, DX, DY, 6)
hold off
I've done 3 things here:
Added the line Z(isinf(Z)) = nan; forcing infinite values to be essentially ignored by quiver
Added the arguments 30, 'k' to the contour function to show 30 lines, and make them black (a bit more visible)
Added the argument 6 to the quiver function. This overrides the automatic length-scaling of the vectors.
You'll want to play with the arguments in the contour and quiver functions to make your figure appear as you'd like.
PS: There is a handy arrow function on the file exchange that I find gives better control when creating vector field plots. See https://www.mathworks.com/matlabcentral/fileexchange/278-arrow - the ratings do it justice.
I made this code for ODE solutions using Runge-Kutta4:
function y=myODE(f,y0,x0,h,x_final)
x=x0;
y=y0;
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4)
x=x+h;
end
f is the function y' = f(x,y), y0 is initial value, x0 is where the function starts, h subinterval and x_final is where the function stops.
I tried my code and it solves ODEs for me correctly, but I also want to plot my function over a xy-axis on the interval x0 to x_final with h subintervals. When I try to plot it using plot(x0:h:x_final,y) I only get an empty graph. I understand (guessing) that I have to bind my y to several x in order to plot, but how can I do that without changing my code too much?
How can I plot the graph for y given y0, interval x0 to x_final and given h?
New to MATLAB, appreciate all help I can get!
Edit: To make clear what my code is for;
I need this ODE solver both for solution and graphing. I'm supposed to study the truncation error by looking at values of y on h compared to 2*h and the stability of Runge-Kutta4 by looking at graphs of y with different h.
This is not a very smart refactoring of your code (because it will slow down the solving, also will kill you graphics depending on how many steps you have in your ODE) but I'm sleepy so I go for the hack:
function y=myODE(f,y0,x0,h,x_final)
hold(axes('Parent',figure),'on');
x=x0;
y=y0;
plot(x,y, 'o');
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4);
x=x+h;
plot(x,y,'o');
end;
end
Maybe tomorrow I'll re-write this answer to something proper, but—for now—good-night! :-)
function y=myode(f,y0,x0,h,x_final)
x=x0;
y=y0;
plot(x0,y0,'.')
hold on
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4);
x=x+h;
plot(x,y,'.')
disp([x,y])
end
The comment box didn't let me to put my fixed-code in "code-format" so post it as an answer.
I would suggest you store the x and y values to vectors and plot them outside the loop. You may want to also output bigX and bigY in order to compare with the exact solution.
function y=myODE(f,y0,x0,h,x_final)
% Example:
% f = #(x,y) (x.^2+cos(y));
% y_final = myODE(f,0,0,0.1,2);
x=x0;
y=y0;
bigX = x0:h:x_final;
if bigX(end)<x_final
% Doesn't occur when x_final = n*h for some integer n
bigX = [bigX,x_final];
end
bigY = zeros(size(bigX));
count = 1;
bigY(1) = y0;
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4);
x=x+h;
count = count+1;
bigY(count) = y;
end
plot(bigX,bigY,'b-o')
xlabel('x')
ylabel('y')
I'm using fzero function to solve a non-linear equation depending on one parameter
and I'm not satisfied with my method. I have these issues:
1) Can for loop for the parameter be avoided ?
2) In order to avoid complex solutions I first have to pre-compute valid interval for fzero.
Is there is a better solution here ?
If I reduce the parameter step size the execution time becomes slow. If I don’t pre-compute
the interval I get an error "Function values at interval endpoints must be finite and real."
in Matlab and "fzero: not a valid initial bracketing" in Octave.
Here is the code
% solve y = 90-asind(n*(sind(90-asind(sind(a0)/n)-y)))
% set the equation paramaters
n=1.48; a0=0:0.1:60;
% loop over a0
for i = 1:size(a0,2)
% for each a0 find where the argument of outer asind()
% will not give complex solutions, i.e. argument is between 1 and -1
fun1 = #(y) n*(sind(90-asind(sind(a0(i))/n)-y))-0.999;
y1 = fzero(fun1,[0 90]);
fun2 = #(y) n*(sind(90-asind(sind(a0(i))/n)-y))+0.999;
y2 = fzero(fun2,[0 90]);
% use y1, y2 as limits in fzero interval
fun3 = #(y) 90-asind(n*(sind(90-asind(sind(a0(i))/n)-y)))-y;
y(i) = fzero(fun3, [y1 y2]);
end
% plot the result
figure; plot(y); grid minor;
xlabel('Incident ray angle [Deg]');
ylabel('Lens surface tangent angle');
With Matlab, I obtained the plot below with the following simplified loop.
for i = 1:size(a0,2)
fun3 = #(y) sind(90-y) - n*(sind(90-asind(sind(a0(i))/n)-y));
y(i) = fzero(fun3, [0,90]);
end
The difference is in the form of equation: I replaced 90-y = asind(something) with sin(90-y) = something. When "something" is greater than 1 in absolute value, the former version throws an error due to complex value of asind. The latter proceeds normally, recognizing that this is not a solution (sin(90-y) can't be equal to something that is greater than 1).
No precomputing of the bracket was necessary, [0,90] simply worked. Another change I made was in the plot: plot(a0,y) instead of plot(y), to get the correct horizontal axis.
And you can't avoid for loop here, nor should you worry about it. Vectorization means eliminating loops where the content is a low-level operation that can be done en masse by operating on some C array. But fzero is totally not that. If the code takes long to run, it's because solving a bunch of equations takes long, not because there's a for loop.
I have a set of odes written in matrix form as $X' = AX$; I also have a desired value of the states $X_des$. $X$ is a five dimensional vector. I want to stop the integration after all the states reach their desired values (or atleast close to them by $1e{-3}$). How do I use event function in matlab to do this? (All the help I have seen are about 1 dimensional states)
PS: I know for sure that all the states approach their desired values after long time. I just want to stop the integration once they are $1e{-3}$ within the desired values.
First, I presume that you're aware that you can use the matrix exponential (expm in Matlab) to solve your system of linear differential equations directly.
There are many ways to accomplish what you're trying to do. They all depend a bit on your system, how it behaves, and the particular event you want to capture. Here's a small example for a 2-by-2 system of linear differential equations:
function multipleeventsdemo
A = [-1 1;1 -2]; % Example A matrix
tspan = [0 50]; % Initial and final time
x0 = [1;1]; % Initial conditions
f = #(t,y)A*y; % ODE function
thresh = 0; % Threshold value
tol = 1e-3; % Tolerance on threshold
opts = odeset('Events',#(t,y)events(t,y,thresh,tol)); % Create events function
[t,y] = ode45(f,tspan,x0,opts); % Integrate with options
figure;
plot(t,y);
function [value,isterminal,direction] = events(t,y,thresh,tol)
value = y-thresh-tol;
isterminal = all(y-thresh-tol<=0)+zeros(size(y)); % Change termination condition
direction = -1;
Integration is stopped when both states are within tol of thresh. This is accomplished by adjusting the isterminal output of the events function. Note that separate tolerance and threshold variables isn't really necessary – you simply need to define the crossing value.
If your system oscillates as it approaches it's steady state (if A has complex eigenvalues), then you'll need to do more work. But you questions doesn't indicate this. And again, numerical integration may not be the easiest/best way to solve your problem which such a system. Here is how you could use expm in conjunction with a bit of symbolic math:
A = [-1 1;1 -2];
x0 = [1;1];
tol = 1e-3;
syms t_sym
y = simplify(expm(A*t_sym)*x0) % Y as a function of t
t0 = NaN(1,length(x0));
for i = 1:length(x0)
sol = double(solve(y(i)==tol,t_sym)) % Solve for t when y(i) equal to tol
if ~isempty(sol) % Could be no solution, then NaN
t0(i) = max(sol); % Or more than one solution, take largest
end
end
f = matlabFunction(y); % Create vectorized function of t
t_vec = linspace(0,max(t0),1e2); % Time vector
figure;
plot(t_vec,f(t_vec));
This will only work for fairly small A, however, because of the symbolic math. Numerical approaches using expm are also possible and likely more scalable.
In my experiment, I need to approximate or fitting a measurement y = f_m(x) with n linear segments. Value of n can be selected to be 1, 2, 3, 4, 5... and probably less than 10. For clarity, it's good if the errors from different cases can be compared to find the one with smallest error.
I've found one example using MATLAB (link):
% Random data...
xdata = linspace(-2,3,101);
ydata = log(abs(10./(10+1i*10.^xdata))) + 0.5*randn(size(xdata));
plot(xdata,ydata)
F = #(B,xdata) min(B(1),B(2)+B(3)*xdata); %Form of the equation
IC = [max(ydata) max(ydata) 0]; %Initial guess
B = lsqcurvefit( F,IC,xdata,ydata,[min(ydata) -inf -inf],[max(xdata) inf 0]);
hold all;
plot(xdata,F(B,xdata));
a = (B(1) - B(2)) / B(3)
cte = B(1)
c = B(2)
d = B(3)
This is similar to what I'm looking for in the case of 2 segments. I tried to modify this function to suit my need with changing the function handle:
F = #(B,xdata) min(B(1),B(2)+B(3)*xdata); %Form of the equation
to
F = #(B,xdata) min(B(1)+B(2)*xdata,B(3)+B(4)*xdata);
but it seems my modification results in 2 segments on the same line.
I don't know much about MATLAB function handle. Especially here, there is "min" function in it. Moreover, how should I do to extend this example to several linear segments?
Thank you in advance!!
Edit01:
Thanks!! your answer has made my code run as desired. But, may I ask a little on the question here. As previously mentioned, I originally want to extend the approximation to several linear segments. So, I go like:
F = #(B,xdata) min(B(1)+B(2)*xdata, B(3)+B(4)*xdata, B(5)+B(6)*xdata); %Form of the equation
IC = [max(ydata) max(ydata) max(ydata) max(ydata) max(ydata) 0]; %Initial guess
B = lsqcurvefit( F,IC,xdata,ydata,[min(ydata) -inf -inf -inf -inf -inf],[max(xdata) inf inf inf inf 0]);
but MATLAB response with I.C. error:
Failure in initial user-supplied objective function evaluation
Can you help me shortly with the I.C here? and what's with the "min" function in function handle?
I get the following error when running your code :
??? Error using ==> lsqncommon at 101
LSQCURVEFIT cannot continue because user supplied objective function failed with the following error:
Attempted to access B(4); index out of bounds because numel(B)=3.
Hence, this means that there is nothing in B(4). I would try to modify IC, lb and ub to have 4 elements.
So try to put these two lines in your code :
IC = [max(ydata) max(ydata) max(ydata) 0]; %Initial guess
B = lsqcurvefit( F,IC,xdata,ydata,[min(ydata) -inf -inf -inf],[max(xdata) inf inf 0]);