How do I vectorize this code? - matlab

I have written a recursive function, however, it takes a lot of time. Hence I vectorized it, but it does not yield the same result as the recursive function. This is my non-vectorized code:
function visited = procedure_explore( u, adj_mat, visited )
visited(u) = 1;
neighbours = find(adj_mat(u,:));
for ii = 1:length(neighbours)
if (visited(neighbours(ii)) == 0)
visited = procedure_explore( neighbours(ii), adj_mat, visited );
end
end
end
This is my vectorized code:
function visited = procedure_explore_vec( u, adj_mat, visited )
visited(u) = 1;
neighbours = find(adj_mat(u,:));
len_neighbours=length(neighbours);
visited_neighbours_zero=visited(neighbours(1:len_neighbours)) == 0;
if(~isempty(visited_neighbours_zero))
visited = procedure_explore_vec( neighbours(visited_neighbours_zero), adj_mat, visited );
end
end
This is the test code
function main
adj_mat=[0 0 0 0;
1 0 1 1;
1 0 0 0;
1 0 0 1];
u=2;
visited=zeros(size(adj_mat,1));
tic
visited = procedure_explore( u, adj_mat, visited )
toc
visited=zeros(size(adj_mat,1));
tic
visited = procedure_explore_vec( u, adj_mat, visited )
toc
end
This is the algorithm I'm trying to implement:
If vectorization is impossible, a mex solution would also be good.
Update benchmark: This benchmark is based on MATLAB 2017a. It shows that the original code is faster than other methods
Speed up between original and logical methods is 0.39672
Speed up between original and nearest methods is 0.0042583
Full code
function main_recersive
adj_mat=[0 0 0 0;
1 0 1 1;
1 0 0 0;
1 0 0 1];
u=2;
visited=zeros(size(adj_mat,1));
f_original=#()(procedure_explore( u, adj_mat, visited ));
t_original=timeit(f_original);
f_logical=#()(procedure_explore_logical( u, adj_mat ));
t_logical=timeit(f_logical);
f_nearest=#()(procedure_explore_nearest( u, adj_mat,visited ));
t_nearest=timeit(f_nearest);
disp(['Speed up between original and logical methods is ',num2str(t_original/t_logical)])
disp(['Speed up between original and nearest methods is ',num2str(t_original/t_nearest)])
end
function visited = procedure_explore( u, adj_mat, visited )
visited(u) = 1;
neighbours = find(adj_mat(u,:));
for ii = 1:length(neighbours)
if (visited(neighbours(ii)) == 0)
visited = procedure_explore( neighbours(ii), adj_mat, visited );
end
end
end
function visited = procedure_explore_nearest( u, adj_mat, visited )
% add u since your function also includes it.
nodeIDs = [nearest(digraph(adj_mat),u,inf) ; u];
% transform to output format of your function
visited = zeros(size(adj_mat,1));
visited(nodeIDs) = 1;
end
function visited = procedure_explore_logical( u, adj_mat )
visited = false(1, size(adj_mat, 1));
visited(u) = true;
new_visited = visited;
while any(new_visited)
visited = any([visited; new_visited], 1);
new_visited = any(adj_mat(new_visited, :), 1);
new_visited = and(new_visited, ~visited);
end
end

Here's a fun little function that does a non-recursive breadth-first search on the graph.
function visited = procedure_explore_logical( u, adj_mat )
visited = false(1, size(adj_mat, 1));
visited(u) = true;
new_visited = visited;
while any(new_visited)
visited = any([visited; new_visited], 1);
new_visited = any(adj_mat(new_visited, :), 1);
new_visited = and(new_visited, ~visited);
end
end
In Octave, this runs about 50 times faster than your recursive version on a 100x100 adjacency matrix. You'll have to benchmark it on MATLAB to see what you get.

You can think of your adjacency matrix as a list of paths of length exactly one. You can generate paths of other lengths n by taking it to the n-th power up to the rank of your matrix. (adj_mat^0 is the identity matrix)
In a graph with n nodes, the longest path cannot be longer than n-1, therefore you can sum all the powers together for a reachability analysis:
adj_mat + adj_mat^2 + adj_mat^3
ans =
0 0 0 0
4 0 1 3
1 0 0 0
3 0 0 3
This is the number of (different) ways that you can use to go from one node to another. For simple reachablitity, check whether this value is greater than zero:
visited(v) = ans(v, :) > 0;
Depending on your definition, you may have to change columns and rows in the result (i.e. take ans(:, v)).
For performance, you can use the lower powers to make higher ones. For example something like A + A^2 + A^3 + A^4 + A^5 would be efficiently calculated:
A2 = A^2;
A3 = A2*A
A4 = A2^2;
A5 = A4*A;
allWalks= A + A2 + A3 + A4 + A5;
Note: If you want to include the original node as being reachable from itself, you should include the identity matrix in the sum.
This minimizes the number of matrix multiplications, also MATLAB will likely execute a matrix square faster than a regular multiplication.
In my experience, matrix multiplication is relatively fast in MATLAB and this will yield the result (reachability) vector for all the nodes in the graph at once. If you are only interested in a small subset of a large graph, this is probably not the best solution.
See also this answer: https://stackoverflow.com/a/7276595/1974021

I don't think you can properly vectorise your function: Your original function never reaches the same node multiple times. By vectorising it you would pass all directly connected nodes at the same time to the next function. Therefore it then would be possible that in the following instances the same node gets reached multiple times. E.g. in your example node 1 would be reached 3 times. So while you would no longer have a loop, the function might, depending on your network, be called more times recursively which would increase the computational time.
That being said, it is not generally impossible to find all reachable nodes without loops or recursive calls. For example you could check all (valid or invalid) paths. But that would work very different from your function and, depending on the number of nodes, might result in a performance loss due to the staggering amount of paths to be checked. Your current function isn't too bad and will scale well with large networks.
A bit offtopic, but since Matlab 2016a you can use nearest() to find all reachable nodes (without the starting node). It invokes a breadth-first algorithm in contrast to your depth-first algorithm:
% add u since your function also includes it.
nodeIDs = [nearest(digraph(adj_mat),u,inf) ; u];
% transform to output format of your function
visited = zeros(size(adj_mat,1));
visited(nodeIDs) = 1;
If this is for a students project, you could argue that while your function works you used the built-in function for performance reasons.

The problem with the recursive function is related to visited(u) = 1;. That is because MATLAB uses copy-on-write technique to pass/assign variables. If you don't change visited in the body of function no copy of it is made but when it is modified a copy of it is created and modification is applied on a copy of it. To prevent that you can use a handle object to be passed by reference to the function.
Define a handle class (save it to visited_class.m):
classdef visited_class < handle
properties
visited
end
methods
function obj = visited_class(adj_mat)
obj.visited = zeros(1, size(adj_mat,1));
end
end
end
The recursive function:
function procedure_explore_handle( u, adj_mat,visited_handle )
visited_handle.visited(u) = 1;
neighbours = find(adj_mat(u,:));
for n = neighbours
if (visited_handle.visited(n) == 0)
procedure_explore_handle( n, adj_mat , visited_handle );
end
end
end
Initialize variables:
adj_mat=[0 0 0 0;
1 0 1 1;
1 0 0 0;
1 0 0 1];
visited_handle = visited_class(adj_mat);
u = 2;
Call it as :
procedure_explore_handle( u, adj_mat,visited_handle );
The result is saved into visited_handle:
disp(visited_handle.visited)

If you want to go from one point in the graph to another, the most efficient way to find it in terms of resources is Dijkstra's algorithm. The Floyd-Warshall algorithm computes all the distances between all points and can be parallellised (by starting from multiple points).
Why the need to vectorise (or use mex programming)? If you just want to make the most use of Matlab's fast matrix multiplication routines then using products of A should get you there quickly:
adj_mat2=adj_mat^2; % allowed to use 2 steps
while (adj_mat2 ~= adj_mat) % check if new points were reached
adj_mat=adj_mat2; % current set of reachable points
adj_mat2=(adj_mat^2)>0; % allowed all steps again: power method
end

This answer just gives an explicit, vectorized implementation of the suggestion from DasKrümelmonster's answer, which I think is faster than the code in the question (at least if the dimensions of the matrix are not too big). It uses the polyvalm function to evaluate the sum of powers of the adjacency matrix.
function visited = procedure_explore_vec(u, adj_mat)
connectivity_matrix = polyvalm(ones(size(adj_mat,1),1),adj_mat)>0;
visited = connectivity_matrix(u,:);
end

Related

Metropolis-Hastings in matlab

I am trying to use the Metropolis Hastings algorithm with a random walk sampler to simulate samples from a function $$ in matlab, but something is wrong with my code. The proposal density is the uniform PDF on the ellipse 2s^2 + 3t^2 ≤ 1/4. Can I use the acceptance rejection method to sample from the proposal density?
N=5000;
alpha = #(x1,x2,y1,y2) (min(1,f(y1,y2)/f(x1,x2)));
X = zeros(2,N);
accept = false;
n = 0;
while n < 5000
accept = false;
while ~accept
s = 1-rand*(2);
t = 1-rand*(2);
val = 2*s^2 + 3*t^2;
% check acceptance
accept = val <= 1/4;
end
% and then draw uniformly distributed points checking that u< alpha?
u = rand();
c = u < alpha(X(1,i-1),X(2,i-1),X(1,i-1)+s,X(2,i-1)+t);
X(1,i) = c*s + X(1,i-1);
X(2,i) = c*t + X(2,i-1);
n = n+1;
end
figure;
plot(X(1,:), X(2,:), 'r+');
You may just want to use the native implementation of matlab mhsample.
Regarding your code, there are a few things missing:
- function alpha,
- loop variable i (it might be just n but it is not suited for indexing since it starts at zero).
And you should always allocate memory in matlab if you want to fill it dynamically, i.e. X in your case.
To expand on the suggestions by #max, the code appears to work if you change the i indices to n and replace
n = 0;
with
n = 2;
X(:,1) = [.1,.1];
It would probably be better to assign X(:,1) to random values within your accept region (using the same code you use later), and/or include a burn-in period.
Depending upon what you are going to do with this, it may also make things cleaner to evaluate the argument to sin in the f function to keep it within 0 to 2 pi (likely by shifting the value by 2 pi if it exceeds those bounds)

How to setup equation that involves a sum from x=1 to infinity and loops?

I am getting confused on how to properly set up this equation. To find a value of V(i,j). The end result would be plotting V over time. I understand that there needs to be loops to allow this equation to work, however I am lost when it comes to setting it up. Basically I am trying to take the sum from n=1 to infinity of (1-(-1)^n)/(n^4 *pi^4)*sin((n*pi*c*j)/L)*sin((n*pi*i)/L)
I originally thought that I should make it a while loop to increment n by 1 until I reach say 10 or so just to get an idea of what the output would look like. All of the variables were unknown and values were added again to see what the plot would look like.
I have down another code where the equation is just dependent on i and j. However with this n term, I am thrown off. Any advice would be great as to setting up the equation. Thank you.
L=10;
x=linspace(0,L,30);
t1= 50;
X=30;
p=1
c=t1/1000;
V=zeros(X,t1);
V(1,:)=0;
V(30,:)=0;
R=((4*p*L^3)/c);
n=1;
t=1:50;
while n < 10
for i=1:31
for j=1:50
V(i,j)=R*sum((1-(-1)^n)/(n^4 *pi^4)*sin((n*pi*c*j)/L)*sin((n*pi*i)/L));
end
end
n=n+1;
end
figure(1)
plot(V(i,j),t)
Various ways of doing so:
1) Computing the sum up to one Nmax in one shot:
Nmax = 30;
Vijn = #(i,j,n) R*((1-(-1)^n)/(n^4 *pi^4)*sin((n*pi*c*j)/L)*sin((n*pi*i)/L));
i = 1:31;
j = 1:50;
n = 1:Nmax;
[I,J,N] = ndgrid(i,j,n);
V = arrayfun(Vijn,I,J,N);
Vc = cumsum(V,3);
% now Vc(:,:,k) is sum_n=1^{k+1} V(i,j,n)
figure(1);clf;imagesc(Vc(:,:,end));
2) Looping indefinitely
n = 1;
V = 0;
i = 1:31;
j = 1:50;
[I,J] = meshgrid(i,j);
while true
V = V + R*((1-(-1)^n)/(n^4 *pi^4)*sin((n*pi*c*J)/L).*sin((n*pi*I)/L));
n = n + 1;
figure(1);clf;
imagesc(V);
title(sprintf('N = %d',n))
drawnow;
pause(0.25);
end
Note that in your example you won't need many terms, since:
Every second term is zero (for even n, the term 1-(-1)^n is zero).
The terms decay with 1/n^4. In norms: n=1 contributes ~2e4, n=3 contributes ~4e2, n=5 contributes 5e1, n=7 contributes ~14, etc. Visually, there is a small difference between n=1 and n=1+n=3 but barely a noticeable one for n=1+n=3+n=5.
Given that so few terms are needed, the first approach is probably the better one. Also, skip the even indices, as you don't need them.

Saving derivative values in ode45 in Matlab

I'm simulating equations of motion for a (somewhat odd) system with mass-springs and double pendulum, for which I have a mass matrix and function f(x), and call ode45 to solve
M*x' = f(x,t);
I have 5 state variables, q= [ QDot, phi, phiDot, r, rDot]'; (removed Q because nothing depends on it, QDot is current.)
Now, to calculate some forces, I would like to also save the calculated values of rDotDot, which ode45 calculates for each integration step, however ode45 doesn't give this back. I've searched around a bit, but the only two solutions I've found are to
a) turn this into a 3rd order problem and add phiDotDot and rDotDot to the state vector. I would like to avoid this as much as possible, as it's already non-linear and this really makes matters a lot worse and blows up computation time.
b) augment the state to directly calculate the function, as described here. However, in the example he says to make add a line of zeros in the mass matrix. It makes sense, since otherwise it will integrate the derivative and not just evaluate it at the one point, but on the other hand it makes the mass matrix singular. Doesn't seem to work for me...
This seems like such a basic thing (to want the derivative values of the state vector), is there something quite obvious that I'm not thinking of? (or something not so obvious would be ok too....)
Oh, and global variables are not so great because ode45 calls the f() function several time while refining it's step, so the sizes of the global variable and the returned state vector q don't match at all.
In case someone needs it, here's the code:
%(Initialization of parameters are above this line)
options = odeset('Mass',#massMatrix);
[T,q] = ode45(#f, tspan,q0,options);
function dqdt = f(t,q,p)
% q = [qDot phi phiDot r rDot]';
dqdt = zeros(size(q));
dqdt(1) = -R/L*q(1) - kb/L*q(3) +vs/L;
dqdt(2) = q(3);
dqdt(3) = kt*q(1) + mp*sin(q(2))*lp*g;
dqdt(4) = q(5);
dqdt(5) = mp*lp*cos(q(2))*q(3)^2 - ks*q(4) - (mb+mp)*g;
end
function M = massMatrix(~,q)
M = [
1 0 0 0 0;
0 1 0 0 0;
0 0 mp*lp^2 0 -mp*lp*sin(q(2));
0 0 0 1 0;
0 0 mp*lp*sin(q(2)) 0 (mb+mp)
];
end
The easiest solution is to just re-run your function on each of the values returned by ode45.
The hard solution is to try to log your DotDots to some other place (a pre-allocated matrix or even an external file). The problem is that you might end up with unwanted data points if ode45 secretly does evaluations in weird places.
Since you are using nested functions, you can use their scoping rules to mimic the behavior of global variables.
It's easiest to (ab)use an output function for this purpose:
function solveODE
% ....
%(Initialization of parameters are above this line)
% initialize "global" variable
rDotDot = [];
% Specify output function
options = odeset(...
'Mass', #massMatrix,...
'OutputFcn', #outputFcn);
% solve ODE
[T,q] = ode45(#f, tspan,q0,options);
% show the rDotDots
rDotDot
% derivative
function dqdt = f(~,q)
% q = [qDot phi phiDot r rDot]';
dqdt = [...
-R/L*q(1) - kb/L*q(3) + vs/L
q(3)
kt*q(1) + mp*sin(q(2))*lp*g
q(5)
mp*lp*cos(q(2))*q(3)^2 - ks*q(4) - (mb+mp)*g
];
end % q-dot function
% mass matrix
function M = massMatrix(~,q)
M = [
1 0 0 0 0;
0 1 0 0 0;
0 0 mp*lp^2 0 -mp*lp*sin(q(2));
0 0 0 1 0;
0 0 mp*lp*sin(q(2)) 0 (mb+mp)
];
end % mass matrix function
% the output function collects values for rDotDot at the initial step
% and each sucessful step
function status = outputFcn(t,q,flag)
status = 0;
% at initialization, and after each succesful step
if isempty(flag) || strcmp(flag, 'init')
deriv = f(t,q);
rDotDot(end+1) = deriv(end);
end
end % output function
end
The output function only computes the derivatives at the initial and all successful steps, so it's basically doing the same as what Adrian Ratnapala suggested; re-run the derivative at each of the outputs of ode45; I think that would even be more elegant (+1 for Adrian).
The output function approach has the advantage that you can plot the rDotDot values while the integration is being run (don't forget a drawnow!), which can be very useful for long-running integrations.

push relabel algorithm

I have done a MATLAB version of the push-relabel FIFO code (exactly like the one on wikipedia and tried it. The discharge function is exactly like wikipedia.
It works perfectly for small graphs (e.g. number of Nodes = 7). However, when I increase my graph size (i.e. number of nodes/vertices > 3500 or more) the "relabel" function runs very slowly, which is called in the "discharge" function. My graphs are huge (i.e. > 3000nodes) so I need to optimize my code.
I tried to optimize the code according to WIKIPEDIA suggestions for global relabeling/gap relabeling:
1) Make neighbour lists for each node, and let the index seen[u] be an iterator over this, instead of the range .
2) Use a gap heuristic.
I'm stuck at the first one , I don't understand what exactly I have to do since it seems there's details left out. (I made neighbor lists such that for vertex u, any connected nodes v(1..n) to u is in the neighbor list already, just not sure how to iterate with the seen[u] index).
[r,c] = find(C);
uc = unique(c);
s = struct;
for i=1:length(uc)
ind = find(c == uc(i));
s(uc(i)).n = [r(ind)];
end
AND the discharge function uses the 's' neighborhood struct list:
while (excess(u) > 0) %test if excess of current node is >0, if so...
if (seen(u) <= length(s(u).n)) %check next neighbor
v = s(u).n(seen(u));
resC = C(u,v) - F(u,v);
if ((resC > 0) && (height(u) == height(v) + 1)) %and if not all neighbours have been tried since last relabel
[C, F, excess] = push(C, F, excess, u, v); %push into untried neighbour
else
seen(u) = seen(u) + 1;
height = relabel(C, F, height, u, N);
end
else
height = relabel(C, F, height, u, N);
seen(u) = 1; %relabel start of queue
end
end
Can someone direct, show or help me please?

how to get back to the first index of matrices

I have to implement a single layer perceptron using Matlab.
The problem which I am facing is that when I ran my program it gave me output for every input (it shows results 4 times), but I want to go back to the first index of matrix, after when it reached to the fourth, and I can't find out that how I get back to the first index of matrix.
I want to train my program so that it yields the same result as in b by iterating the matrix in every loop.
This is my current code:
a = [ 1 1
1 -1
-1 1
-1 -1 ];
b = [1
-1
-1
-1];
disp(a);
disp(b);
x = a(:,1);
disp(x);
y = a(:,2);
disp(y)
learningrate = 0.1;
maxiteration = 10;
weight(1)=0.1;
weight(2)=0.1;
weight(3)=0.1;
count = length(x);
for p = 1:count
s = (x(p) * weight(1))+ (y(p) * weight(2))+ weight(3);
if s >= 0
result = 1;
if result ~= b(p)
weight(1) = weight(1)+learningrate*(b(p)-result)*x(p);
weight(2) = weight(2)+learningrate*(b(p)-result)*y(p);
weight(3) = weight(3)+learningrate*(b(p)-result);
disp(result);
disp(x(p));
disp(y(p));
disp(weight(1));
disp(weight(2));
disp(weight(3));
end
else
if s <= 0
result = -1;
disp(result);
if result ~= b(p)
weight(1)=weight(1)+learningrate*(b(p)-result)*x(p);
weight(2)=weight(2)+learningrate*(b(p)-result)*y(p);
weight(3)=weight(3)+learningrate*(b(p)-result);
disp(x(p));
disp(y(p));
disp(weight(1));
disp(weight(2));
disp(weight(3));
end
end
end
end
#Amro has posted an elaborate answer on implementing a single layer perceptron with MATLAB. His post is not only valuable in terms of 'getting some code', but on how a technical problem shall be solved. It starts with a graphical representation of the perceptron showing the signal flow, the problem description. And goes on with excellent comments in the code, as part of the solution.
Just replacing variables a and b in your code with meaningful names could make a big difference.