Nodes that form a loop in adjacency matrix (Matlab) - matlab

I have an adjacency matrix with directed edges. Computing A^3, would help me detect if there are any cycles of length 3 (triangle) in the matrix. But, I want to know which nodes form the triangle. How can I achieve this in Matlab?
Thanks

The problem with matrix multiplication is that it adds up all the rows. When you multiply the first row of matrix P by the first column of matrix Q, it does an element-wise multiplication and then generates the sum of the resulting vector, throwing away all of the data about the intermediate nodes. Well, we want that vector, so let's stop it from adding them up.
Say we have our adjacency matrix A:
A =
0 1 0 0 0
0 0 1 0 0
1 0 0 1 0
0 0 0 0 0
0 0 0 1 0
and we want to find out if there are any paths (not cycles, yet) from node x to node z passing through node y. Row x tells us what nodes have edges from x to y, and column z tells us what nodes have edges from y to z. If we do an element-wise AND of row x and column z, we should get a vector of all of the nodes y that are connected to both x and z.
For example, if we AND row 1 and column 3 for this adjacency matrix:
A =
0 1 0 0 0
x x 1 x x
x x 0 x x
x x 0 x x
x x 0 x x
>> A(1,:) & A(:,3).' %// remember to transpose the column first...
ans =
0 1 0 0 0
We see that they're connected by node 2. Awesome, now we know that for this case we have a path 1->2->3. In general though, there could be multiple paths from 1 to 3, so we're going to use the whole vector.
Now all we have to do is make sure that there's a path from node 3 back to node 1. Well, that's row 3, column 1 in our adjacency matrix. If we AND A(3,1) with our vector, we should the same vector back if there's an edge from 3 to 1 and get zeros back if there isn't (and thus, no cycle).
>> (A(1,:) & A(:,3).') & A(3,1)
ans =
0 1 0 0 0
Generalizing this, the vector for each path from x to z is
C(x,:,z) = (A(x,:) & A(:,z).') & A(z,x);
Unfortunately I have been unable to find a way to vectorize this, so a double for loop will have to suffice for now:
for x = 1:size(A,1)
for z = 1:size(A,2)
C(x,:,z) = (A(x,:) & A(:,z).') & A(z,x);
end
end
The resulting matrix will have C(x,y,z) = 1 if there is a cycle from x to y to z (and back), and 0 otherwise. Note that each cycle will be listed 3 times:
C(x,y,z) == C(y,z,x) == C(z,x,y)

Related

How does Y = eye(K)(y, :); replace a "for" loop? Coursera

Working on an assignment from Coursera Machine Learning. I'm curious how this works... From an example, this much simpler code:
% K is the number of classes.
K = num_labels;
Y = eye(K)(y, :);
seems to be a substitute for the following:
I = eye(num_labels);
Y = zeros(m, num_labels);
for i=1:m
Y(i, :)= I(y(i), :);
end
and I have no idea how. I'm having some difficulty Googling this info as well.
Thanks!
Your variable y in this case must be an m-element vector containing integers in the range of 1 to num_labels. The goal of the code is to create a matrix Y that is m-by-num_labels where each row k will contain all zeros except for a 1 in column y(k).
A way to generate Y is to first create an identity matrix using the function eye. This is a square matrix of all zeroes except for ones along the main diagonal. Row k of the identity matrix will therefore have one non-zero element in column k. We can therefore build matrix Y out of rows indexed from the identity matrix, using y as the row index. We could do this with a for loop (as in your second code sample), but that's not as simple and efficient as using a single indexing operation (as in your first code sample).
Let's look at an example (in MATLAB):
>> num_labels = 5;
>> y = [2 3 3 1 5 4 4 4]; % The columns where the ones will be for each row
>> I = eye(num_labels)
I =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
>> Y = I(y, :)
Y =
% 1 in column ...
0 1 0 0 0 % 2
0 0 1 0 0 % 3
0 0 1 0 0 % 3
1 0 0 0 0 % 1
0 0 0 0 1 % 5
0 0 0 1 0 % 4
0 0 0 1 0 % 4
0 0 0 1 0 % 4
NOTE: Octave allows you to index function return arguments without first placing them in a variable, but MATLAB does not (at least, not very easily). Therefore, the syntax:
Y = eye(num_labels)(y, :);
only works in Octave. In MATLAB, you have to do it as in my example above, or use one of the other options here.
The first set of code is Octave, which has some additional indexing functionality that MATLAB does not have. The second set of code is how the operation would be performed in MATLAB.
In both cases Y is a matrix generated by re-arranging the rows of an identity matrix. In both cases it may also be posible to calculate Y = T*y for a suitable linear transformation matrix T.
(The above assumes that y is a vector of integers that are being used as an indexing variables for the rows. If that's not the case then the code most likely throws an error.)

How to find number of occurrences of a subset of elements in a vector without using loops in MATLAB?

Say X is the given vector:
X=[1
2
4
2
3
1
4
5
2
4
5];
And Y is the given subset of elements from X:
Y=[3
4
5];
The required output is the number of times the elements in Y occur in X:
out=[1
3
2];
My solution to do this would be to use for loop:
for i=1:size(X,1)
temp = X(X(:,1)==Y(i,1),:);
out(i,1) = size(temp,1);
end
But when X and Y are large, this is inefficient. So, how to do it faster making use of vectorization? I know about hist and histc, but I can't think of how to use them in this case to get the desired output.
A Fast Option
You could use bsxfun combined with sum to compute this
sum(bsxfun(#eq, Y, X.'), 2)
Explanation
In this example, bsxfun performs a given operation on every combination of elements in X and Y. The operation we're gonig to use is eq (a check for equality). The result is a matrix that has a row for each element in Y and a column for each element in X. It will have a 1 value if the element in X equals the element in Y that corresponds to a given row.
bsxfun(#eq, Y, X.')
% 0 0 0 0 1 0 0 0 0 0 0
% 0 0 1 0 0 0 1 0 0 1 0
% 0 0 0 0 0 0 0 1 0 0 1
We can then sum across the columns to count the number of elements in X that were equal to a given value in Y.
sum(bsxfun(#eq, Y, X.'), 2)
% 1
% 3
% 2
On newer versions of MATLAB (since R2016b), you can omit the bsxfun since the equality operation will automatically broadcast.
sum(Y - X.', 2)
A Memory-Efficient Option
The first option isn't the most efficient since it requires creating a matrix that is [numel(Y), numel(X)] elements large. Another way which may be more memory efficient may be to use the second output of ismember combined with accumarray
[tf, ind] = ismember(X, Y);
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], #numel);
Explanation
ismember is used to determine if the values in one array are in another. The first input tells us if each element of the first input is in the second input and the second output tells you where in the second input each element of the first input was found.
[tf, ind] = ismember(X, Y);
% 0 0 1 0 1 0 1 1 0 1 1
% 0 0 2 0 1 0 2 3 0 2 3
We can use the second input to "group" the same values together. The accumarray function does exactly this, it uses the ind variable above to determine groups and then applies a given operation to each group. In our case, we want to simply determine the number of element within each group. So to do that we can pass a second input the size of the ind input (minus the ones that didn't match) of ones, and then use numel as the operation (counts the number in each group)
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], #numel);
% 1
% 3
% 2

how to convert edge list to adjacency matrix

I have this code
function adj=edgeL2adjj(e)
Av = [e; fliplr(e)];
nodes = unique(Av(:, 1:2)); % get all nodes, sorted
adj = zeros(numel(nodes)); % initialize adjacency matrix
% across all edges
for i=1:size(Av,1)
adj(nodes==Av(i,1),(nodes==Av(i,2))) = 1;
end
end
to convert an edge list to an adjacency matrix but if I input u=[8 5;1 4;3 5;6 7]
and then I divide u into two set [8 5;1 4], [3 5,6 7] and apply previous code on [3 5;6 7] I will get a 7 x 7 matrix.
but I want a 8 x 8 matrix to any input.
You have a 7x7 matrix because numel(nodes)=7. Indeed node #2 is not present in u. I suggest you to give this function a second input, that is the maximum number of nodes in your network (in this case, 8) and preallocate the adjacency matrix with such input parameter instead of numel(nodes). Or, alternatively, you can preallocate a zeros() square matrix by giving in input not numel(nodes), but the maximum value in input u. The first option will make your code more robust, the second option will make your code robust as long as the 8-th node is in input u.
Also, there is no need for the fliplr(): if your graph is undirected (that is, the adjacency matrix is symmetric) you can rely on such structure inside the for-loop, without concatenation in Av.
Such function can indeed be simplified as follows:
function adj=edgeL2adjj(e)
adj=zeros(max(max(e))); % initialize adjacency matrix
% across all edges
for i=1:size(e,1)
adj(e(i,1),e(i,2))=1;
adj(e(i,2),e(i,1))=1;
end
end
If the input e is your matrix:
e= [8 5;1 4;3 5;6 7];
such code returns
adj =
0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1
0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0
0 0 0 0 1 0 0 0
As you can see you simply exploit the input e. The maximum value is 8, so you build a square 8x8 matrix. Also inside the for-loop by swapping the column indices 1 and 2 in e, you automatically take care of the symmetric structure of the adjacency matrix. Finally, this code automatically sort you out in case of missing nodes: indeed, as you can see, the second row and the second column in adj are all-zeros because node #2 is not present in the edge list e.
Note: I do not recommend to split your input edge list. In this manner, you will loose all the global informations regarding your network. Indeed you will have (let's say) two "sub-networks" which you later have to concatenate (in terms of adjacency matrix). That is, if you split e in two sub-matrices, you'll have two adjacency matrices. By taking into account the code as it is now in my answer, the first matrix will be 8x8 whereas the second will be 7x7 because max(max([3 5;6 7]))=7.
If I may, I'd like to offer an altogether different solution to your problem, which avoids looping over all edges (always a good idea to avoid loops in MATLAB!).
Since you have all your row and column indices, your edge list basically already specifies a (albeit sparse) matrix. You can simply allocate the matrix like this:
function adj = edgeL2adjj(e)
r = e(:,1);
c = e(:,2);
vals = ones(size(r));
% maximum node index determines matrix dimensions
n_nodes = max(e(:));
% create sparse matrix
adj = sparse(r, c, vals, n_nodes, n_nodes);
% if really necessary, you can just convert it to a full matrix
adj = full(adj);
end
If you know you always want the graph to have the same dimensions, even if you only use part of your edges as an input to the function, you could just pass n_nodes as an input instead of determining it from e:
function adj = edgeL2adjj(e, n_nodes)

Locate touching pixels of two objects

Suppose I have matrix, where each cell of this matrix describes a location (e.g. a bin of a histogram) in a two dimensional space. Lets say, some of these cells contain a '1' and some a '2', indicating where object number 1 and 2 are located, respectively.
I now want to find those cells that describe the "touching points" between the two objects. How do I do that efficiently?
Here is a naive solution:
X = locations of object number 1 (x,y)
Y = locations of object number 2 (x,y)
distances = pdist2(X,Y,'cityblock');
Locations (x,y) and (u,v) touch, iff the respective entry in distances is 1. I believe that should work, however does not seem very clever and efficient.
Does anyone have a better solution? :)
Thank you!
Use morphological operations.
Let M be your matrix with zeros (no object) ones and twos indicating the locations of different objects.
M1 = M == 1; % create a logical mask of the first object
M2 = M == 2; % logical mask of second object
dM1 = imdilate( M1, [0 1 0; 1 1 1; 0 1 0] ); % "expand" the mask to the neighboring pixels
[touchesY touchesX] =...
find( dM1 & M2 ); % locations where the expansion of first object overlap with second one
Code
%%// Label matrix
L = [
0 0 2 0 0;
2 2 2 1 1;
2 2 1 1 0
0 1 1 1 1]
[X_row,X_col] = find(L==1);
[Y_row,Y_col] = find(L==2);
X = [X_row X_col];
Y = [Y_row Y_col];
%%// You code works till this point to get X and Y
%%// Peform subtractions so that later on could be used to detect
%%// where Y has any index that touches X
%%// Subtract all Y from all X. This can be done by getting one
%%//of them and in this case Y into the third dimension and then subtracting
%%// from all X using bsxfun. The output would be used to index into Y.
Y_touch = abs(bsxfun(#minus,X,permute(Y,[3 2 1])));
%%// Perform similar subtractions, but this time subtracting all X from Y
%%// by putting X into the third dimension. The idea this time is to index
%%// into X.
X_touch = abs(bsxfun(#minus,Y,permute(X,[3 2 1]))); %%// for X too
%%// Find all touching indices for X, which would be [1 1] from X_touch.
%%// Thus, their row-sum would be 2, which can then detected and using `all`
%%// command. The output from that can be "squeezed" into a 2D matrix using
%%// `squeeze` command and then the touching indices would be any `ones`
%%// columnwise.
ind_X = any(squeeze(all(X_touch==1,2)),1)
%%// Similarly for Y
ind_Y = any(squeeze(all(Y_touch==1,2)),1)
%%// Get the touching locations for X and Y
touching_loc = [X(ind_X,:) ; Y(ind_Y,:)]
%%// To verify, let us make the touching indices 10
L(sub2ind(size(L),touching_loc(:,1),touching_loc(:,2)))=10
Output
L =
0 0 2 0 0
2 2 2 1 1
2 2 1 1 0
0 1 1 1 1
L =
0 0 10 0 0
2 10 10 10 1
10 10 10 10 0
0 10 10 1 1

How to display coordinates within a matrix?

I would like to display the coordinates of a matrix in terms of X and Y. For example
if matrix = [ 0 0 5 0; 0 0 1 0; 0 0 0 1; 0 0 0 0]
Say, i want the coordinate for 5...how can i write a code that says 5 = 1x and 3 y.
I don't want to display the element in the matrix, just the coordinates of that element.
use find
[y x] = find( matrix ~= 0 ); % gives you the x y coordinates of all non-zero elements
Note the order of y and x, since Matlab is indexing using row-column.