-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconnection_matrix_gen.py
More file actions
278 lines (233 loc) · 11.7 KB
/
Copy pathconnection_matrix_gen.py
File metadata and controls
278 lines (233 loc) · 11.7 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
import numpy as np
import networkx as nx
from networkx.algorithms import bipartite, community
import matplotlib.pyplot as plt
def pr_full_to_pr_sparse(pr_full, Ntot, cluster_size):
#account the average num of connections per neuron with the full pr
avg_num_connections = np.mean([(np.random.rand(1)<pr_full)*Ntot for i in np.arange(Ntot)])
#adjust the pr to account for the cluster size
pr_sparse = avg_num_connections/cluster_size
#clip the pr_sparse to 1
pr_sparse = np.clip(pr_sparse, 0, 1)
#assert pr_sparse <= 1, "pr_sparse is greater than 1"
#also assert it results in the same number of average connections
#avg_num_connections_2 = np.mean([(np.random.rand(1)<pr_sparse)*cluster_size for i in np.arange(Ntot)])
#assert np.isclose(avg_num_connections, avg_num_connections_2, atol=5), "pr_sparse does not result in the same number of average connections"
return pr_sparse
def gaussian_clustered(Ntot=1000, NE=500, NI=500, cluster_size=10, variance=5, p_in=0.25, p_out=0.25, no_EE=True, no_II=True, no_IE=False, no_EI=False):
if NE+NI != Ntot:
raise ValueError("NE and NI must sum to Ntot")
#generate the
directed_graph = nx.gaussian_random_partition_graph(Ntot, cluster_size, variance, p_in, p_out)
#bipartite_graph = community.kernighan_lin_bisection(directed_graph, max_iter=1000)
#get the edges
nodes = {}
E_count = 0
I_count = 0
for graph in directed_graph.graph['partition']:
num_nodes = len(graph)
for i, node in enumerate(graph):
if i < num_nodes//2 and E_count < NE:
#name the node in the directed graph, these will be E_{i}
directed_graph.nodes[node]['name'] = 'E_'+str(E_count)
nodes['E_'+str(E_count)] = node
E_count += 1
elif I_count < NI:
directed_graph.nodes[node]['name'] = 'I_'+str(I_count)
nodes['I_'+str(I_count)] = node
I_count += 1
#invert the dict
inv_nodes = {v: k for k, v in nodes.items()}
#get the e -> I edges
#get the I -> E edges
IE_edges = []
EI_edges = []
EE_edges = []
II_edges = []
known_edges = inv_nodes.keys()
for node in inv_nodes:
#get the neighbors of the node
neighbors = list(directed_graph.neighbors(node))
#get the type of the node
node_type = inv_nodes[node][0]
node_number = int(inv_nodes[node][2:])
#loop through the neighbors
for neighbor in neighbors:
if neighbor not in known_edges:
continue
#get the type of the neighbor
neighbor_type = inv_nodes[neighbor][0]
neighbor_number = int(inv_nodes[neighbor][2:])
#if the neighbor is an E
if neighbor_type == 'E':
#if the node is an I
if node_type == 'I':
#add the node to the IE list
IE_edges.append([node_number, neighbor_number])
#if the node is an E
else:
#add the node to the EE list
EE_edges.append([node_number, neighbor_number])
#if the neighbor is an I
else:
#if the node is an I
if node_type == 'I':
#add the node to the II list
II_edges.append([node_number, neighbor_number])
#if the node is an E
else:
#add the node to the EI list
EI_edges.append([node_number, neighbor_number])
#order the connection matrix
IE_connection_array = np.array(IE_edges)
EI_connection_array = np.array(EI_edges)
EE_connection_array = np.array(EE_edges)
II_connection_array = np.array(II_edges)
return EI_connection_array, IE_connection_array
def stochastic_block(Ntot=1000, NE=500, NI=500, NUM_CLUSTER=15, p_EE=0.00, p_II=0.00, p_IE=0.04, p_EI=0.02):
#make a list of nodes, E and I by splitting the total number of nodes
nodes = np.arange(Ntot)
clusters_tot = NUM_CLUSTER*2
ne_per_cluster = NE//NUM_CLUSTER
ni_per_cluster = NI//NUM_CLUSTER
#make a list of clusters sizes
ne_cluster_sizes = np.full(NUM_CLUSTER, ne_per_cluster)
ni_cluster_sizes = np.full(NUM_CLUSTER, ni_per_cluster)
cluster_sizes = np.hstack((ne_cluster_sizes, ni_cluster_sizes))
#make a square matrix of the probabilities
prob_matrix = np.zeros((clusters_tot, clusters_tot))
#fill the matrix with the probabilities
for row in np.arange(clusters_tot):
#if row is an E
row_type = 'E' if row < NUM_CLUSTER else 'I'
if row_type == 'E':
prob_matrix[row, :] = np.array([p_EE if col < NUM_CLUSTER else p_EI for col in np.arange(clusters_tot)])
else:
prob_matrix[row, :] = np.array([p_EI if col < NUM_CLUSTER else p_II for col in np.arange(clusters_tot)])
#multiply the matrix by uniform random numbers
#prob_matrix = np.multiply(prob_matrix, np.random.rand(clusters_tot, clusters_tot))
#feed to the stochastic block model
directed_graph = nx.stochastic_block_model(cluster_sizes, prob_matrix, directed=True)
num_computed_ne = np.sum([len(graph) for i, graph in enumerate(directed_graph.graph['partition']) if i < NUM_CLUSTER])
num_computed_ni = np.sum([len(graph) for i, graph in enumerate(directed_graph.graph['partition']) if i >= NUM_CLUSTER])
#get the edges
IE_edges = []
EI_edges = []
EE_edges = []
II_edges = []
for i, graph in enumerate(directed_graph.graph['partition']):
if i < NUM_CLUSTER:
node_type = 'E'
else:
node_type = 'I'
for node in graph:
neighbors = list(directed_graph.neighbors(node))
for neighbor in neighbors:
if neighbor in graph:
if node_type == 'E':
EE_edges.append([node, neighbor - NE])
else:
II_edges.append([node - NE, neighbor])
else:
if node_type == 'E':
EI_edges.append([node, neighbor - num_computed_ne])
else:
IE_edges.append([node - num_computed_ne, neighbor])
#order the connection matrix
IE_connection_array = np.array(IE_edges)
EI_connection_array = np.array(EI_edges)
EE_connection_array = np.array(EE_edges)
II_connection_array = np.array(II_edges)
#drop some of the connections larger than NE
IE_connection_array = IE_connection_array[IE_connection_array[:,0] < NE, :]
EI_connection_array = EI_connection_array[EI_connection_array[:,0] < NE, :]
print(IE_connection_array.min(axis=0))
print(EI_connection_array.min(axis=0))
return EI_connection_array, IE_connection_array
def custom_circ_graph(Ntot=1000, EI_ratio=0.5,NUM_CLUSTERS=5, p_ei=0.02, p_ie=0.02, inter_cluster_num=2, return_clust_labels=False):
N = int(Ntot*EI_ratio)
DIV = int((N/NUM_CLUSTERS))
number_idx = np.arange(NUM_CLUSTERS)
number_idx = np.ravel(np.repeat(number_idx, DIV+1))
#if the len of number_idx is not N, add some more, or drop random rows
if len(number_idx) < N:
print("adding more")
number_idx = np.append(number_idx, np.random.choice(number_idx, size=N-len(number_idx), replace=True))
elif len(number_idx) > N:
print("dropping some")
number_idx = np.delete(number_idx, np.random.choice(np.arange(len(number_idx)), size=len(number_idx)-N, replace=False))
if NUM_CLUSTERS == 1:
#if the user wants a single cluster, just return a random connection matrix
return random_connect_matrix(Ntot=1000, EI_ratio=0.5, p_ei=0.02, p_ie=0.02)
#decide which nodes to connect
unique_idx = np.unique(number_idx)
unique_idx_pairs = [np.random.choice(unique_idx[unique_idx!=i], size=(len(unique_idx))) for i in np.arange(inter_cluster_num)]
unique_idx_pairs = np.hstack((unique_idx.reshape(-1,1), np.array(unique_idx_pairs).T))
#adjust the probability of connections
p_ei = pr_full_to_pr_sparse(p_ei, Ntot//2, DIV*inter_cluster_num)
p_ie = pr_full_to_pr_sparse(p_ie, Ntot//2, DIV*inter_cluster_num)
EI_cluster_labels = np.copy(number_idx)
#connect ids with idxs with same number
connection_array = [0, 0]
for i in np.arange(N):
paired_idx = unique_idx_pairs[unique_idx_pairs[:,0] == number_idx[i], :][0]
EI_cluster_labels[i] = np.where(unique_idx==number_idx[i])[0][0]
for pair in paired_idx:
downstream = np.where(number_idx==pair)[0]
#Each connection only has a 50% chance of being made, so drop half to none of them
#possible prob of p_EI
count = np.count_nonzero(np.where(np.random.rand(N)<=p_ei, 1, 0))
downstream = np.random.choice(downstream, size=count, replace=True)
connection = np.vstack((np.full(len(downstream), i), downstream)).T
connection_array = np.vstack((connection_array, connection))
connection_array = np.array(connection_array).reshape(-1,2)[1:, :]
EI_connection_array = np.copy(connection_array)
#create a new connection array for the other way around
unique_idx_pairs = [np.random.choice(unique_idx, size=(len(unique_idx))) for i in np.arange(inter_cluster_num)]
unique_idx_pairs = np.hstack((unique_idx.reshape(-1,1), np.array(unique_idx_pairs).T))
#print(unique_idx_pairs)
IE_cluster_labels = np.copy(number_idx) + NUM_CLUSTERS
#connect ids with idxs with same number
connection_array = [0, 0]
for i in np.arange(N):
paired_idx = unique_idx_pairs[unique_idx_pairs[:,0] == number_idx[i], :][0]
for pair in paired_idx:
downstream = np.where(number_idx==pair)[0]
#Each connection only has a 50% chance of being made, so drop half to none of them
#possible prob of p_EI
count = np.count_nonzero(np.where(np.random.rand(N)<=p_ie, 1, 0))
downstream = np.random.choice(downstream, size=count, replace=True)
connection = np.vstack((np.full(len(downstream), i), downstream)).T
connection_array = np.vstack((connection_array, connection))
connection_array = np.array(connection_array).reshape(-1,2)[1:, :]
IE_connection_array = np.copy(connection_array)
#if the IE_connection_array or EI_connection_array is empty, just return two zeros
if len(IE_connection_array) == 0:
IE_connection_array = np.zeros((1,2)).astype(int)
if len(EI_connection_array) == 0:
EI_connection_array = np.zeros((1,2)).astype(int)
if return_clust_labels:
return EI_connection_array, IE_connection_array, {'EI_clust': EI_cluster_labels, 'IE_clust': IE_cluster_labels}
return EI_connection_array, IE_connection_array
def random_connect_matrix(Ntot=1000, EI_ratio=0.5, p_ei=0.02, p_ie=0.02):
N = int(Ntot*EI_ratio)
#each neuron has a p_ei chance of connecting to another neuron etc
#make a matrix of random numbers
EI_matrix = []
IE_matrix = []
for i in np.arange(N):
downstream = np.where(np.random.rand(N)<=p_ei, 1, 0)
connection = np.vstack((np.full(len(downstream), i), np.where(downstream==1)[0])).T
EI_matrix.append(connection)
EI_matrix = np.vstack(EI_matrix)
IE_matrix = []
for i in np.arange(N):
downstream = np.where(np.random.rand(N)<=p_ie, 1, 0)
connection = np.vstack((np.full(len(downstream), i), np.where(downstream==1)[0])).T
IE_matrix.append(connection)
IE_matrix = np.vstack(IE_matrix)
EI_connection_array = EI_matrix
IE_connection_array = IE_matrix
return EI_connection_array, IE_connection_array
if __name__=="__main__":
custom_circ_graph(NUM_CLUSTERS=27, inter_cluster_num=16, p_ei=0.04, p_ie=0.0001)