Related
I am currently working on a project in MATLAB that compares two different numerical methods (direct and iterative), each for two different matrix sizes, to the actual analytic solution of the system. The first .m file included below provides an implementation of a tridiagonal matrix solver, where the main function plots a solution for two different sizes of tridiagonal matrices: a 2500x2500 matrix and 50x50 matrix.
function main
n=50;
for i=1:2
di=2*ones(n,1);
up=-ones(n,1);
lo=-ones(n,1);
b=ones(n,1)/(n*n);
[subl,du,supu]=tridiag_factor(lo,di,up);
usol = tridiag_solve(subl,du,supu,b);
%figure(i)
plot(usol);
hold on;
title(['tridiag ' num2str(n) ''])
n=2500;
end
function [subl,du,supu]=tridiag_factor(subd,d,supd)
n=length(d);
for i=1:n-1
subd(i)=subd(i)/d(i);
d(i+1)=d(i+1)-subd(i)*supd(i);
end
subl=subd; supu=supd; du=d;
function x = tridiag_solve(subl,du,supu,b)
% forward solve
n=length(b); z=b;
z(1)=b(1);
for i=2:n
z(i)=b(i)-subl(i)*z(i-1);
end
% back solve
x(n)=z(n);
for i=n-1:-1:1
x(i)=(z(i)-supu(i)*x(i+1))/du(i);
end
I have included a hold on statement after the first usol is plotted so I can capture the second usol on the same figure during the second iteration of the for loop. The next piece of code is a Jacobi method that iteratively solves a system for a tridiagonal matrix. In my case, I have constructed two tridiagonal matrices of different sizes which I will provide after this piece of code:
function [xf,r] = jacobi(A,b,x,tol,K)
%
% The function jacobi applies Jacobi's method to solve A*x = b.
% On entry:
% A coefficient matrix of the linear system;
% b right-hand side vector of the linear system;
% x initial approximation;
% eps accuracy requirement: sx=top when norm(dx) < eps;
% K maximal number of steps allowed.
% On return:
% x approximation for the solution of A*x = b;
% r residual vector last used to update x,
% if success, then norm(r) < eps.[
%
n = size(A,1);
fprintf('Running the method of Jacobi...\n');
for k = 1:K
% r = b - A*x;
for i=1:n
sum=0;
for j = 1:n
if j~=i
sum=sum+A(i,j)*x(j);
end
end
x(i)=(b(i)-sum)/(A(i,i));
end;
k=k+1;
r = b - A*x;
% fprintf(' norm(r) = %.4e\n', norm(r));
if (norm(r) < tol)
fprintf('Succeeded in %d steps\n', k);
return;
end;
end
fprintf('Failed to reached accuracy requirement in %d steps.\n', K);
xf=x;
%r(j) = r(j)/A(j,j);
%x(j) = x(j) + r(j);
Now, lastly, here is my code for the two tridiagonal matrices (and other related information for each system corresponding to the appropriate matrices) I wish to use in my two jacobi function calls:
% First tridiagonal matrix
n=50;
A_50=full(gallery('tridiag', n, -1, 2, -1));
% Second tridiagonal matrix
n=2500;
A_2500=full(gallery('tridiag', n, -1, 2, -1));
K=10000;
tol=1e-6;
b_A50=ones(length(A_50), 1);
for i=1:length(A_50)
b_A50(i,1)=0.0004;
end
x_A50=ones(length(A_50),1);
b_A2500=ones(length(A_2500), 1);
for i=1:length(A_2500)
b_A2500(i,1)= 1.6e-7;
end
x_A25000=ones(length(A_2500),1);
As stated in the question header, I need to plot the vectors produced from jacobi(A_50,b_A50,x_A50,tol,K), jacobi(A_2500,b_A2500,x_A25000,tol,K) and a mathematical function y=#(x) -0.5*(x-1)*x all on the same figure along with the two usol (for n=50 and n=2500) produced by the main function shown in the first piece of code, but I have had no luck due to the different lengths of vectors. I understand this is probably an easy fix, but of course my novice MATLAB skills are not sufficing.
Note: I understand x_A50 and x_A2500 are trivial choices for x, but I figured while I ask for help I better keep it simple for now and not create any more issues in my code.
In MATLAB traces sent to same plot must have same length.
I have allocated just one variable containing all traces, for the respective tridiagonals and resulting traces out of your jacobi function.
I have shortened from 2500 to 250, and the reason is that with 2500, compared to 50, the tridiag traces, and the short jacobi are so flat compared to the last trace than one has to recur to dB scale to find them on same graph window as you asked.
1st generate all data, and then plot all data.
So here you have the script plotting all traces in same graph :
clear all;close all;clc
n=[50 250];
us_ol=zeros(numel(n),max(n));
% generate data
for k=[1:1:numel(n)]
di=2*ones(n(k),1);
up=-ones(n(k),1);
lo=-ones(n(k),1);
b=ones(n(k),1)/(n(k)*n(k));
[subl,du,supu]=tridiag_factor(lo,di,up);
us_ol0 = tridiag_solve(subl,du,supu,b);
us_ol(k,[1:numel(us_ol0)])=us_ol0;
end
n_us_ol=[1:1:size(us_ol,2)];
str1=['tridiag ' num2str(n(1))];
str2=['tridiag ' num2str(n(2))];
legend(str1,str2);
grid on
% the jacobi traces
nstp=1e3;
tol=1e-6;
A1=zeros(max(n),max(n),numel(n));
for k=1:1:numel(n)
A0=full(gallery('tridiag', n(k), -1, 2, -1));
A1([1:1:size(A0,1)],[1:1:size(A0,2)],k)=A0;
end
b_A1=ones(max(n),max(n),numel(n));
for k=1:1:numel(n)
for i=1:n(k)
b_A1(i,1,k)=0.0004;
end
end
n_A1=[1:1:size(A1,1)];
jkb=zeros(numel(n),max(n));
for k=1:1:numel(n)
A0=A1([1:n(k)],[1:n(k)],k);
b_A0=b_A1([1:n(k)],[1:n(k)],k);
n0=[1:1:n(k)];
jkb0=jacobi(A0,b_A0,n0',tol,nstp)
jkb(k,[1:numel(jkb0)])=jkb0';
end
% plot data
figure(1)
ax1=gca
plot(ax1,n_us_ol,us_ol(1,:),n_us_ol,us_ol(2,:));
hold(ax1,'on')
plot(ax1,n_A1,jkb(1,:),n_A1,jkb(2,:))
grid on
legend('3/1','3/2','jkb1','jkb2')
title('3diags and jakobians graph')
As mentioned above, one has to zoom to find some of the traces
One way to combine really small traces with large traces is to use Y log scale
figure(2)
ax2=gca
plot(ax2,n_us_ol,10*log10(us_ol(1,:)),n_us_ol,10*log10(us_ol(2,:)));
hold(ax2,'on')
plot(ax2,n_A1,10*log10(jkb(1,:)),n_A1,10*log10(jkb(2,:)))
grid on
legend('3/1[dB]','3/2[dB]','jkb1[dB]','jkb2[dB]')
title('3diags and jakobians graph in dB')
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 want to plot the error in the composite simpson's rule
... Here is my code
for r=1:100
n=600;
a=0;
b=5;
err=[];
x=zeros(1,n);
f=#(x)cos(x)+x.^2;
h=(b-a)/n;
xexact=integral(f,a,b);
p=0;
q=0;
for i=1:n
x(i)=a+(i-1)*h;
end
for i=1:n-1
p=p+2*(f(x(i)))+4*(f(x(i)+h/2));
end
x=(h/6)*(f(a)+f(a+h/2)+p+f(b))
err(end+1)=x-xexact;
plot(r,x,'*')
end
When I run the code I get one point in the plot .. I want to have all the points ploted in the plot , How to do that ?
thanks
The simplest fix is to put hold on; before your code here and hold off; after it. That way, each of your calls to plot will add a point to an existing plot, rather than creating a new one. (What is happening at the moment is that you are plotting a point with r=1, and then replacing that plot with one for r=2, etc. So what you end up with is a plot showing just what happens for r=100.)
You might do better, though, to build up an array containing your xs as your loop runs, and then do a single plot(1:100, xs, '*'); at the end.
There is a conceptual problem in your code: you want to plot the x variable which is not function of r and that will never change for each r value. So you will plot a row of r dots with a fixed x value.
Moreover I think that, if possible, is good to avoid for loops in Matlab because there are more powerful and optimized ways to do things that you do with them. For example I will write this code in this form:
function [x, err] = compSimp(n, a, b)
err = [];
x=zeros(1,n+1);
f=#(x)cos(x)+x.^2;
h=(b-a)/n;
xexact=integral(f,a,b);
% x_0 = a
x(1)=a;
% x_n = b
x(n+1)=b;
p=0;
% x_j = a + j*h with j = 1, 2, ... , n-2, n-1 (no need to set again x_0 and x_n)
x(2:n)= a + (1:(n-1))*h;
% summations
for i=2:2:n
p = p + 2*(f(x(i+1))) + 4*(f(x(i)));
end
x=(h/3)*(f(a) + p + f(b));
err(end+1)=x-xexact;
end
As you can see i've changed some code logic, following wikipedia for theory. If you want, you can also change code so that you can pass a custom f function to the compSimp function.
I have a system of coupled differential equations. One of the parameters is changing over time and I would like to track the change of said parameter (and overlay it with my final graph).
I tried to write all generated values of v into a separate vector. However, since not all function calls result in a successful integration I end up with many values for v than of my ode-solver return vector.
Can someone point out how I could implement this feature into my code?
Thanks so much, been trying this for a couple of hours now. To no avail, unfortunately.
Cheers,
dahlai
Find my code below:
Coupled differential equations + attempt of writing all values of v to vector:
%chemostat model, based on:
%DCc=-v0*Cc/V + umax*Cs*Cc/(Ks+Cs)-rd
%Dcs=(v0/V)*(Cs0-Cs) - Cc*(Ys*umax*Cs/(Ks+Cs)-m)
function dydt=systemEquationsRibose(t,y,funV0Ribose,V,umax,Ks,rd,Cs0,Ys,m)
v=funV0Ribose(t,y); %funV0Ribose determines v dependent on y(1)
y(2) = max(0, y(2)); %minimum value of y(2), therefore Cs, is 0
dydt=[-(v/V)*y(1)+(umax*y(1)*y(2))/(Ks+y(2))-rd;
(v/V)*(Cs0-y(2))-((1/Ys)*(umax*y(2)*y(1))/(Ks+y(2)));
];
% persistent k
% persistent vel
%
% if isempty(vel)
% vel=0
% end
%
% if isempty(k)
% k=1
% end
%
% if v>= vel(k)
% vel(k+1)=v %stores all v values, for plotting and analysis of v0 behaviour
% end
% assignin('base','vel',vel)
% k=k+1
end
ode23-solver call:
[t,y]=ode23(#systemEquationsRibose, [t0 tx],[Cc0 Cs0],[],#funV0Ribose,V,umax,Ks,rd,Cs0,Ys,m);
v is given by a separate function called funV0Ribose.The value of funV0Ribose is dependent on y(1) at every given time point.
Doing some more digging (and putting the right words into google) I found a solution:
Second answer of together with reading through the documentation (which is still very hard for me, since I am pretty new to MATLAB):
Saving derivative values in ode45 in Matlab
My implementation:
Initializing the output function:
options=odeset('OutputFcn', #recordFun)
global v
if isempty(v)
v=0
end
Calling ode-solver:
[t,y]=ode23(#systemEquationsRibose, [t0 tx],[Cc0 Cs0],options,#funV0Ribose,V,umax,Ks,rd,Cs0,Ys,m);
recordFun:
function status=recordFun(t,y,flag,funV0Ribose,V,umax,Ks,rd,Cs0,Ys,m)
status = 0; %=0 to keep ode-solver running, if =1 ode-solver stops
%counter for growing vectors
persistent k
if isempty(k)
k=0
end
%recording of every v value
persistent vel %to store vel for multiple calls of recordFun
global v %to access v from #systemEquationsRibose
vel(k+1)=v
assignin('base','vel',vel)
k=k+1 %counter increase for next elements of growing vectors
end
I'm trying to filter a short audio file using a Butter worth filter and then a function to recursively solve the difference equation using the coefficients given by Butter.m.
My code is as follows.
[x,Fs]=wavread('bugsbunny1');
wn=2500/(Fs/2);
n=10;
[B,A]=butter(n,wn);
C=zeros(1,10);
for n=2:10
C(n-1)=-A(n); %rearrange A for the recur function
end
A=C;
x=x'; % make x a row vector
n=11:length(x);
y0=zeros(1,11);
x0=zeros(1,11);
y1=recur(A,B,n,x,x0,y0); %calculate approximation recursively
y1=[y0 y1]; %add initial conditions to vector
doing this results in y1 being a matrix of invalid data given by 'NaN', any help is appreciated thanks.
Code for the recur function:
function [ y ] = recur(a,b,n,x,x0,y0)
%Use Recursion to approximate solution to first order differential equation
% Detailed explanation goes here
N=length(a);
M=length(b)-1;
y=[y0 zeros(1,length(n))];
x=[x0 x];
a1=a(length(a):-1:1);
b1=b(length(b):-1:1);
for i=N+1:N+length(n),
y(i)=-a1*y(i-N:i-1)'+b1*x(i-N:i-N+M)';
end
y=y(N+1:N+length(n));