Can this algorithm be simplified (written cleaner)? - matlab

I have this algorithm, but I am not too keen on the many if-statements.
Can someone see if this function can be written in a cleaner way?
rand('twister',101)
n = 10;
f = A.^(0:n)./factorial(0:n);
f = f/sum(f);
n = 10000;
Xi = 2;
X = zeros(1,n);
for i =1:n,
dXi = (-1)^round(rand);
Yi = Xi + dXi;
if Yi > 0 & Yi <= length(f),
if f(Yi) >= f(Xi),
X(i) = Yi;
Xi = Yi;
else
if rand <= f(Yi)/f(Xi),
X(i) = Yi;
Xi = Yi;
else
X(i) = Xi;
end
end
end
X(i) = Xi;
end

I don't know Matlab syntax, but generally something like this:
if (cond1) then
mainAction
else if (cond2) then
mainAction
else
otherAction
can be simplified as:
if (cond1 OR cond2) then
mainAction
else
otherAction
The OR would have to be short-circuiting for exact equivalence, but if cond2 has no side-effects then it doesn't really matter.

This can be simplified by noting that whenever you do X(i) = Yi you also do Xi = Yi and therefore you could just assign X(i) once at the end of the loop. This allows a lot of the other logic to simplify.
Also note that the , at the end of an if-clause is really only necessary in one-line if statements, e.g.
if x < y, do_something, else do_something_else, end
Anyway I get this (you could simplify further into one if statement but perhaps that's less clear. Also having more than one if statement allows breakpoints on particular sections.):
for i =1:n,
dXi = (-1)^round(rand);
Yi = Xi + dXi;
if Yi > 0 & Yi <= length(f)
if f(Yi) >= f(Xi) || rand <= f(Yi)/f(Xi)
Xi = Yi;
end
end
X(i) = Xi;
end

Related

Jacobi method to solve linear systems in MATLAB

How would you code this in MATLAB?
This is what I've tried, but it doesn't work quite right.
function x = my_jacobi(A,b, tot_it)
%Inputs:
%A: Matrix
%b: Vector
%tot_it: Number of iterations
%Output:
%:x The solution after tot_it iterations
n = length(A);
x = zeros(n,1);
for k = 1:tot_it
for j = 1:n
for i = 1:n
if (j ~= i)
x(i) = -((A(i,j)/A(i,i)) * x(j) + (b(i)/A(i,i)));
else
continue;
end
end
end
end
end
j is an iterator of a sum over each i, so you need to change their order. Also the formula has a sum and in your code you're not adding anything so that's another thing to consider. The last thing I see that you're omitting is that you should save the previous state of xbecause the right side of the formula needs it. You should try something like this:
function x = my_jacobi(A,b, tot_it)
%Inputs:
%A: Matrix
%b: Vector
%tot_it: Number of iterations
%Output:
%:x The solution after tot_it iterations
n = length(A);
x = zeros(n,1);
s = 0; %Auxiliar var to store the sum.
xold = x
for k = 1:tot_it
for i = 1:n
for j = 1:n
if (j ~= i)
s = s + (A(i,j)/A(i,i)) * xold(j);
else
continue;
end
end
x(i) = -s + b(i)/A(i,i);
s = 0;
end
xold = x;
end
end

Poisson PDE solver on a disked shaped domain with finite difference method using matlab

For my studies I had to write a PDE solver for the Poisson equation on a disc shaped domain using the finite difference method.
I already passed the Lab exercise. There is one issue in my code I couldn't fix. Function fun1 with the boundary value problem gun2 is somehow oscillating at the boundary. When I use fun2 everything seems fine...
Both functions use at the boundary gun2. What is the problem?
function z = fun1(x,y)
r = sqrt(x.^2+y.^2);
z = zeros(size(x));
if( r < 0.25)
z = -10^8*exp(1./(r.^2-1/16));
end
end
function z = fun2(x,y)
z = 100*sin(2*pi*x).*sin(2*pi*y);
end
function z = gun2(x,y)
z = x.^2+y.^2;
end
function [u,A] = poisson2(funame,guname,M)
if nargin < 3
M = 50;
end
%Mesh Grid Generation
h = 2/(M + 1);
x = -1:h:1;
y = -1:h:1;
[X,Y] = meshgrid(x,y);
CI = ((X.^2 +Y.^2) < 1);
%Boundary Elements
Sum= zeros(size(CI));
%Sum over the neighbours
for i = -1:1
Sum = Sum + circshift(CI,[i,0]) + circshift(CI,[0,i]) ;
end
%if sum of neighbours larger 3 -> inner note!
CI = (Sum > 3);
%else boundary
CB = (Sum < 3 & Sum ~= 0);
Sum= zeros(size(CI));
%Sum over the boundary neighbour nodes....
for i = -1:1
Sum = Sum + circshift(CB,[i,0]) + circshift(CB,[0,i]);
end
%If the sum is equal 2 -> Diagonal boundary
CB = CB + (Sum == 2 & CB == 0 & CI == 0);
%Converting X Y to polar coordinates
Phi = atan(Y./X);
%Converting Phi R back to cartesian coordinates, only at the boundarys
for j = 1:M+2
for i = 1:M+2
if (CB(i,j)~=0)
if j > (M+2)/2
sig = 1;
else
sig = -1;
end
X(i,j) = sig*1*cos(Phi(i,j));
Y(i,j) = sig*1*sin(Phi(i,j));
end
end
end
%Numberize the internal notes u1,u2,......,un
CI = CI.*reshape(cumsum(CI(:)),size(CI));
%Number of internal notes
Ni = nnz(CI);
f = zeros(Ni,1);
k = 1;
A = spalloc(Ni,Ni,5*Ni);
%Create matix A!
for j=2:M+1
for i =2:M+1
if(CI(i,j) ~= 0)
hN = h;hS = h; hW = h; hE = h;
f(k) = fun(X(i,j),Y(i,j));
if(CB(i+1,j) ~= 0)
hN = abs(1-sqrt(X(i,j)^2+Y(i,j)^2));
f(k) = f(k) + gun(X(i,j),Y(i+1,j))*2/(hN^2+hN*h);
A(k,CI(i-1,j)) = -2/(h^2+h*hN);
else
if(CB(i-1,j) ~= 0) %in negative y is a boundry
hS = abs(1-sqrt(X(i,j)^2+Y(i,j)^2));
f(k) = f(k) + gun(X(i,j),Y(i-1,j))*2/(hS^2+h*hS);
A(k,CI(i+1,j)) = -2/(h^2+h*hS);
else
A(k,CI(i-1,j)) = -1/h^2;
A(k,CI(i+1,j)) = -1/h^2;
end
end
if(CB(i,j+1) ~= 0)
hE = abs(1-sqrt(X(i,j)^2+Y(i,j)^2));
f(k) = f(k) + gun(X(i,j+1),Y(i,j))*2/(hE^2+hE*h);
A(k,CI(i,j-1)) = -2/(h^2+h*hE);
else
if(CB(i,j-1) ~= 0)
hW = abs(1-sqrt(X(i,j)^2+Y(i,j)^2));
f(k) = f(k) + gun(X(i,j-1),Y(i,j))*2/(hW^2+h*hW);
A(k,CI(i,j+1)) = -2/(h^2+h*hW);
else
A(k,CI(i,j-1)) = -1/h^2;
A(k,CI(i,j+1)) = -1/h^2;
end
end
A(k,k) = (2/(hE*hW)+2/(hN*hS));
k = k + 1;
end
end
end
%Solve linear system
u = A\f;
U = zeros(M+2,M+2);
p = 1;
%re-arange u
for j = 1:M+2
for i = 1:M+2
if ( CI(i,j) ~= 0)
U(i,j) = u(p);
p = p+1;
else
if ( CB(i,j) ~= 0)
U(i,j) = gun(X(i,j),Y(i,j));
else
U(i,j) = NaN;
end
end
end
end
surf(X,Y,U);
end
I'm keeping this answer short for now, but may extend when the question contains more info.
My first guess is that what you are seeing is just numerical errors. Looking at the scales of the two graphs, the peaks in the first graph are relatively small compared to the signal in the second graph. Maybe there is a similar issue in the second that is just not visible because the signal is much bigger. You could try to increase the number of nodes and observe what happens with the result.
You should always expect to see numerical errors in such simulations. It's only a matter of trying to get their magnitude as small as possible (or as small as needed).

Matlab trapezoidal Rule

I created a fas.m script, but I'm getting wrong result.
FUNCTION
function [fnc2] = fas(x)
if x>=0 && x<1
fnc2 = x^3;
elseif x>=1 && x<2
fnc2 = 2-((x^2)/5);
elseif x>2
fnc2 = x^2+x;
elseif x<0
fprintf('x is smaller than 0, function is not defined');
end
TRAPEZOIDAL RULE SUM
clear
clc
h=0.05;
x=0.05;
x0=0;
xn=3;
while x<=2.95
fas(x);
I=0.025*(fas(x0)+2*fas(x)+fas(x0));
x=x+h;
end
Trapezoidal rule is,
so,
h = 0.05;
x = 0;
I = 0;
while x < 3
I = I + h * (fas(x) + fas(x + h)) / 2;
x = x + h;
end
disp(I);
you will get I = 11.3664 while the actual value of I is 10.3667.

Plotting bisect method in matlab

So I had a problem in which I needed to find roots using the bisect method:
Function:
function [ c,k ] = bisect(f,a,b,tol)
k=0;
while b-a > tol
c=(a+b)/2;
if sign(f(c)) == sign(f(b))
b=c;
else
a=c;
end
k=k+1;
end
Script:
f = #(x) (((1800).*log((160000)./(160000 - (x.*2600))) - (9.812).*x)./750) - 1;
a = 10;
b = 50;
tol = 1e-4;
[root, iter] = bisect(f,a,b,tol);
fprintf(' iterations = %i root = %15.10e ' ,iter, root);
This works perfectly, now I need to use an embedded function in Matlab to find the value of c for different values of q (in the above example q is a fixed number 2600) from 2000 to 3000 in increments of 10 and plot x vs q. In order to do this I have the following script:
function myFunction
h = 10;
a = 10;
b = 50;
tol = 1e-4;
function y = f(x)
y = (((1800).*log((160000)./(160000 - (x.*q))) - (9.812).*x)./750) - 1;
end
for q = (2000:h:3000)
k=0;
while b-a > tol
c=(a+b)/2;
if sign(f(c)) == sign(f(b))
b=c;
cArray(q) = c;
else
a=c;
cArray(q) = c;
end
k=k+1;
end
end
plot(q,cArray)
end
This code has no errors but when I run it there is no graph. Can someone please help me with this issue? I don't even know if my code to find c vs q is correct.
Major issues with your myFunction code:
The endpoints a,b should be reset within the for loop, so that the root search begins anew.
Using q as index in cArray(q) results in a too-large array filled with zeros for indices below 2000. You need an index variable to keep track of that array. (I used j below)
plot(q,cArray) should be plot(2000:h:3000,cArray), because q is not an array, it's a scalar.
Minor issues:
You never used k
You assign to cArray(q) within the while loop; this only needs to be done once the loop has ran.
Corrected function:
function myFunction
h = 10;
tol = 1e-4;
function y = f(x)
y = (((1800).*log((160000)./(160000 - (x.*q))) - (9.812).*x)./750) - 1;
end
j = 0;
for q = 2000:h:3000
a = 10;
b = 50;
while b-a > tol
c=(a+b)/2;
if sign(f(c)) == sign(f(b))
b=c;
else
a=c;
end
end
j=j+1;
cArray(j)=c;
end
plot(2000:h:3000,cArray)
end
and its output:

While loop Vectorization

I would like to know if there is some way to vectorize this code. I tried so hard to do it... but failed.
while (delta_F > e) && (i < maxLoop)
x1 = x0+d;
y0 = f(x0);
y1 = f(x1);
if y1 < y0
x0= x1;
d = a*d;
else
vF = [vF;x1];
d = -b*d;
end
i = i + 1;
if length(vF) > 1
ultm = vF(end);
pultm = vF(end-1);
delta_F = abs(ultm+pultm)/2;
end
end
It's a simple implementation of the Rosenbrock method for finding the min of a function.
In general, Matlab's vectorization works over fixed-size arrays/matrices. If you want to speed up this code in some other ways, the single biggest thing you could do is reuse your previous results in each iteration of the loop and get rid of the extraneous variables.
y0 = f(x0);
while (delta_F > e) && (i < maxLoop)
x1 = x0+d;
y1 = f(x1);
if (y1 < y0) %# new starting point, so swap points
x0 = x1;
y0 = y1;
d = a*d;
else %# same starting point, refine step and see if we're done
d = -b*d;
delta_F = abs(x1-x0)/2;
end
i = i+1;
end
This removes half the calls to f and the dynamic resizing of vF, which is horrendously slow, especially as vF gets big.