forked from ivanbrugere/matlab-networks-toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
louvainCommunityFinding.m
executable file
·129 lines (82 loc) · 3.51 KB
/
louvainCommunityFinding.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
function [modules,inmodule] = louvainCommunityFinding(A)
%LOUVAINCOMMUNITYFINDING Implementation of a community finding algorithm by Blondel et al
% @input A, NxN adjacency matrix
% @output modules, cell array of node groupings, each cell element contains a vector of node numbers
% @output inmodule, a 1xN cell array of the node's affiliation
% Source: "Fast unfolding of communities in large networks", July 2008
% https://sites.google.com/site/findcommunities/
% Note 1: This is just the first step of the Louvain community finding
% algorithm. To extract fewer communities, need to repeat with
% the resulting modules themselves.
% Note 2: This works for undirected graphs only.
% Note 3: Permuting randomly the node order at every step helps the
% algorithm performance. Unfortunately, node order in this
% algorithm affects the results.
%
%
% Other routines used: numEdges.m, kneighbors.m
% Updated: muted method.
% GB: last updated, Oct 17 2012
%##################################################################
m = numEdges(A);
n = numNodes(A);
inmodule = {}; % inmodule{node} = module
for mm=1:length(A); inmodule{mm} = mm; end;
modules = inmodule; % equal only for this step; modules{ind} = [nodes in module]
% for all nodes, visit and assess joining to their neighbors
% revisit until no improvement in dQ is available
converged = false;
while not(converged)
converged = true;
perm = randperm(n); % permute the order of the nodes randomly
% this helps the algorithm performance
for i=1:n
i = perm(i);
neigh = kneighbors(A,i,1);
dQ = zeros(1,length(neigh));
c0 = inmodule{i};
for nei=1:length(neigh)
% attempt to join i and neigh(nei)
% that is: move i from modules{i} to modules{neigh(nei)}
% if dQs balance is positive, do move; else: move on
c1 = inmodule{neigh(nei)};
dQ(nei) = dQi(i,c0,c1,A,modules,m);
end % loop across all neighbors
[maxdQ,maxnei] = max(dQ);
if maxdQ>0
modules{inmodule{i}} = setdiff(modules{inmodule{i}},i); % remove i from c0=inmodule{i}
inmodule{i}=inmodule{neigh(maxnei)}; % assign inmodule{i} to inmodule{neigh(maxnei)}
modules{inmodule{neigh(maxnei)}} = [modules{inmodule{neigh(maxnei)}} i]; % add i to modules{inmodule{neigh(maxnei)}}
converged = false;
end
end % loop across all nodes
end % convergence loop
% remove empty modules
new_modules={};
for mm=1:length(modules)
if length(modules{mm})>0; new_modules{length(new_modules)+1}=modules{mm}; end
end
modules=new_modules;
%fprintf('found %3i modules\n',length(modules));
function dqi = dQi(i,c0,c1,adj,modules,m)
% INPUTs:
% c0, c1: indices of the two modules
% i: node which changes membership
% modules: modules{c0} = [list of nodes] etc
% m: total number of nodes in graph
%
% OUTPUTs: dqi: modularity change if i moves from c0 to c1
if c0 == c1; dqi=0; return; end % same module - no change in modularity
% k_ic0: degree of i within c0
k_ic0 = sum(adj(i,modules{c0}));
% k_ic1: degree of i witnin c1
k_ic1 = sum(adj(i,modules{c1}));
% k_i: degree of i (in entire graph)
k_i = sum(adj(i,:));
% m_c0: number of edges in c0
m_c0 = numEdges(adj(modules{c0},modules{c0}));
% m_c1: number of edges in c1
m_c1 = numEdges(adj(modules{c1},modules{c1}));
dqi = (k_ic1-k_ic0)/(2*m) - k_i*(m_c1-m_c0)/(2*m^2);
function p = randperm(n)
[~,p] = sort(rand(n,1));