-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgenerateNewRandomMatrix.m
executable file
·140 lines (130 loc) · 6.65 KB
/
generateNewRandomMatrix.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
130
131
132
133
134
135
136
137
138
139
140
function A = generateNewRandomMatrix(N, p, allowSelf, undirected, ensureConnected, ensureALinkForEachNode)
%%
% Generate a new Erdos-Renyi random matrix
%
% Inputs
% - N - network size
% - p - connection probability
% - allowSelf - whether to allow self-connections
% - undirected - whether to make the matrix directed or not
% - ensureConnected - only return a connected matrix.
% If this is set to true, then p must be >= 2/N(N-1) for undirected, 1/N(N-1) for directed, for the matrix to be connected.
% Note: if allowSelf==true, then (N-1) -> N here.
% - ensureALinkForEachNode - whether to make sure that each node connects to at least one other.
% The supplied value here is ignored if ensureConnected is set to true and an undirected graph is requested.
% The supplied value is ignored if a directed graph is requested (because I haven't thought about whether
% we would want it to mean all have connections in, out or bidirectional).
% Setting this to true will give a higher probability that the final graph is connected.
% I don't think we can grow the graph by adding each node one at a time, giving it a link to the existing cluster,
% then once all nodes are in adding enough extra links to get p right - I think the earlier nodes will have
% more links - this paper describes a similar process that gave different structures to truly random graphs:
% http://math.uchicago.edu/~shmuel/Network-course-readings/CHKNS.pdf
%
% Outputs
% - A - connectivity matrix (can be directed; A(i,j) means a link exists from i->j)
%
%% Linear Sync Toolkit (linsync)
% Copyright (C) 2023 Joseph T. Lizier
% Distributed under GNU General Public License v3
if (allowSelf)
numPossibleDirectedConnections = N * N;
withSelfSuffix = '';
else
numPossibleDirectedConnections = N * (N - 1);
withSelfSuffix = 'out';
end
if (nargin < 6)
% This will be overridden if ensureConnected == true
ensureALinkForEachNode = false;
end
if (ensureConnected)
% If we want a connected graph, then there must be a link for each node.
% This doesn't ensure it will be connected, but if it's not the case then the graph definitely won't be connected.
ensureALinkForEachNode = true;
% Now check that the user has requested enough links to make the sure the graph is connected.
% (Need at least N-1 links to make it undirectionally connected)
if (undirected && (p < 2.*(N-1)./numPossibleDirectedConnections))
error('Require p >= 2*%d/%d to have a connected undirected graph of %d nodes with%s self-connections (p=%.4f, 2*%d/%d = %.4f)\n', ...
(N-1), numPossibleDirectedConnections, N, withSelfSuffix, p, (N-1), ...
numPossibleDirectedConnections, 2.*(N-1)./numPossibleDirectedConnections);
end
if (not(undirected) && (p < 1.*(N-1)./numPossibleDirectedConnections))
error('Require p >= 1*%d/%d to have an (undirectionally) connected directed graph of %d nodes with%s self-connections (p=%.4f, 1*%d/%d = %.4f)\n', ...
(N-1), numPossibleDirectedConnections, N, withSelfSuffix, p, (N-1), ...
numPossibleDirectedConnections, (N-1)./numPossibleDirectedConnections);
end
end
while (true)
% Generate a new network structure with connectivity depth p
A = zeros(N);
if (ensureALinkForEachNode)
if (not(undirected))
% The supplied value is ignored if a directed graph is requested (because I haven't thought about whether
% we would want it to mean all have connections in, out or bidirectional).
fprintf('Argument ensureALinkForEachNode=true is currently ignored for directed graphs!\n');
pBetweenAll = p;
else
% Introduce one link for each node. In fact, this adds one more link that is strictly necessary (N-1 are necessary here)
% but we will reduce p for the rest of the network to take account of this.
% It would be foolish to call this function with the absolute minimum p anyway since it will be very difficult
% to randomly generate a connected network with that p.
if (not(allowSelf) || ensureConnected)
% Work out who to connect each node to, removing one option representing self-connections
nodesToConnectEachTo = ceil(rand(N, 1) .* (N - 1));
% Make the undirected connections
for n = 1 : N
if (nodesToConnectEachTo(n) >= n)
% This means we want to connect to a node with a higher index than ourselves, so
% adjust this node index upwards (since we removed one index to represent ourself).
nodesToConnectEachTo(n) = nodesToConnectEachTo(n) + 1;
end
A(n, nodesToConnectEachTo(n)) = 1;
A(nodesToConnectEachTo(n), n) = 1;
end
else
% We're allowing self-connections and not ensuring the network is connected.
% Work out who to connect each node to
nodesToConnectEachTo = ceil(rand(N, 1) .* N);
% Make the undirected connections
for n = 1 : N
A(n, nodesToConnectEachTo(n)) = 1;
A(nodesToConnectEachTo(n), n) = 1;
end
end
% Now adjust the connection probability in the rest of the network for the N undirected connections made here:
pBetweenAll = p - 2.*N./numPossibleDirectedConnections;
end
else
pBetweenAll = p;
end
% Assign directed connections at random, but allow any initial connections made above to come through without pBetweenAll applying:
A = A | (rand(N) < pBetweenAll);
if (not(allowSelf))
% Remove self-connections
A = A .* not(eye(N));
end
if (undirected)
% Just take the upper triangle and reflect it
A = triu(A);
A = A | A';
symmA = A;
else
symmA = A | A';
end
if (ensureConnected)
% Now check that the matrix is connected
visited = zeros(1,N);
visited = runDfs(symmA,N,1,visited);
if (sum(visited) ~= N)
% The matrix is not undirectionally connected
fprintf('Matrix is not unidirectionally connected, trying again ...\n');
continue;
end
end
% The matrix is fine to return:
% Debug print:
actualP = sum(sum(A)) ./ numPossibleDirectedConnections;
fprintf('Generated a graph for p=%.4f with p=%.4f\n', p, actualP);
return;
end
end