1

I have a matrix 2000x5, in the first column the point number, and in columns 2-5 the 4 neighbours (0s if there isnt a neighbour). Is there an efficient way to create an adjacency matrix out of this ?

1   129 0   65  0
2   130 0   66  85
3   131 169 67  0
4   132 170 68  87
5   133 0   69  81
6   134 0   70  82
7   135 173 71  83
8   136 174 72  84
9   137 161 73  0
10  138 162 74  93
11  139 163 75  0
12  140 164 76  95
13  141 165 77  89
14  142 166 78  90
15  143 167 79  91
16  144 168 80  92
17  145 0   81  65
18  146 0   82  66
....

I found the following thread, where it is explained for just one neighbour, but I am not sure how to use it for multiple neighbours. matlab adjacency list to adjacency matrix

I would very much appreciate any help.

Community
  • 1
  • 1
KiW
  • 593
  • 3
  • 20
  • So to follow how your matrix is formatted, given a point number delineated in the first column, the columns 2 - 5 tell you the points that are neighbours to the said point? – rayryeng Jun 10 '16 at 13:53
  • 1
    Okay, you need to stop duplicating the same question over and over. You've currently got 5 questions about the same exact problem. If you need to clarify your question, edit it rather than opening a new one. – beaker Jun 10 '16 at 14:11
  • I agree that the questions are similar ( because I still work on the problem) but they are in no way duplicates of each other. But I will try to avoid it in the future – KiW Jun 11 '16 at 11:08

2 Answers2

2

Firstly, I'm going to assume that the adjacency list is undirected. In any case, it's not that far of a stretch to go to multiple neighbours. What you need to do first is detect the total number of non-zero elements per row from columns 2 to 5. Once you do this, for the rows of the adjacency matrix, you would copy the point number for as many times as there are non-zero elements per that row. The function repelem is perfectly suitable to do that for you. The column indices would simply be the second to fifth columns removing all of the zero elements. How you can do this is first transpose the matrix resulting in indexing the second to fifth columns, then using a logical indexing matrix to remove out the zero entries. Doing this will unroll your vector in a column-major fashion, which is why transposing is required before doing this operation. Once you do this, you can create row and column access indices so that these can be input into sparse much like that post you linked.

Supposing that your matrix was stored in A, you would do something like this. This also assumes that each of the weights connecting the nodes are 1:

% Find total number of non-zero elements per row, skipping first column
non_zero = sum(A(:,2:end) ~= 0, 2);

% Create row indices
rows = repelem(A(:,1), non_zero);

% Create column indices
cols = A(:,2:end).';    
cols = cols(cols ~= 0);

% Create adjacency matrix
adj = sparse([rows; cols],[cols; rows], 1);

The above representation is in sparse. If you want the full numeric version, cast the output using full:

adj = full(adj);

If your graph is directed

If you have a directed graph instead of an undirected graph, the above call to sparse duplicates edges so that you are creating links to and from each of the neighbours. If your graph is actually directed, then you simply have to only use the row and column indices once instead of twice as seen in the above code:

% Create adjacency matrix
adj = sparse(rows, cols , 1);

Test Case

Here's a small test case to show you that this works. Supposing my adjacency list looked like the following:

>> A = [1 0 2 3; 2 4 0 0; 3 0 0 4]

A =

     1     0     2     3
     2     4     0     0
     3     0     0     4

The adjacency matrix is now:

>> full(adj)

ans =

     0     1     1     0
     1     0     0     1
     1     0     0     1
     0     1     1     0

Taking a look at the list above and how the matrix is populated, we can verify that this is correct.


Note about repelem

repelem assumes you have MATLAB R2015a or later. If you don't have this, you can consult this answer by user Divakar on a custom implementation of repelem here: Repeat copies of array elements: Run-length decoding in MATLAB

Community
  • 1
  • 1
rayryeng
  • 102,964
  • 22
  • 184
  • 193
2

A quick and simple technique:

adjMat = zeros(size(A,1));
for ind = 1:size(A,1)
    % Flag 1 on each row 'ind' at the indices mentioned in col 2-5
    adjMat(ind, nonzeros(A(ind,2:end))) = 1;
end

Since you have mentioned using the nearest neighbour search, it is likely that the adjacency list should be completely filled to result in a undirected graph, in the sense that if row 1 has 20 as a neighbour, row 20 very likely has 1 as a neighbour.

However technically speaking, this will produce an adjacency matrix exactly equivalent to the adjacency list, assuming nothing by itself.

Example:

For an adjacency list

A = [1 2 3; 2 0 1; 3 1 4; 4 5 3; 5 4 0]

A =

 1     2     3
 2     0     1
 3     1     4
 4     5     3
 5     4     0

The result is:

adjMat =

 0     1     1     0     0
 1     0     0     0     0
 1     0     0     1     0
 0     0     1     0     1
 0     0     0     1     0

P.S. To force undirected-ness, you can simply add another statement in the for loop body:

adjMat(nonzeros(A(ind,2:end)),ind) = 1;

This will ensure that the adjacency matrix will be symmetric, which is a characteristic of undirected graphs.

crazyGamer
  • 1,119
  • 9
  • 16
  • Good canonical approach. You can remove the square braces when you're indexing with `2:end`. It's superfluous here. Also, put a semi-colon at the end of your `zeros` call so you don't echo that allocation to the command window. – rayryeng Jun 10 '16 at 14:40
  • 1
    @rayryeng: Yes, don't know how I missed that. I've made the changes. Thank you for your suggestions :) – crazyGamer Jun 10 '16 at 14:47
  • Thank you for that answer. I am still working in the same area and just read the post again - what exactly do you mean with your P.S. note ? in which way symetrical ? I would be happy if you could explain it in a sentence! – KiW Jun 17 '16 at 08:38
  • @KiW, I've updated the P.S. note. Sorry about the misleading statement. To put it short, if your graph was directed, then it is possible that (say) node A is connected to node B, but B is not connected to A. If your adjacency list is of this sort, but you want to forcefully make your graph undirected (if A connects B, B should connect A as well), you will need to make the adjacency matrix symmetric. Just think about what the adjacency matrix describes, you should get it. – crazyGamer Jun 18 '16 at 10:39