bmtool 0.5.1__py3-none-any.whl → 0.5.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bmtool/bmplot.py +84 -9
- bmtool/connectors.py +216 -59
- bmtool/graphs.py +170 -0
- bmtool/singlecell.py +4 -4
- bmtool/util/util.py +110 -43
- {bmtool-0.5.1.dist-info → bmtool-0.5.3.dist-info}/METADATA +420 -23
- {bmtool-0.5.1.dist-info → bmtool-0.5.3.dist-info}/RECORD +11 -10
- {bmtool-0.5.1.dist-info → bmtool-0.5.3.dist-info}/WHEEL +1 -1
- {bmtool-0.5.1.dist-info → bmtool-0.5.3.dist-info}/LICENSE +0 -0
- {bmtool-0.5.1.dist-info → bmtool-0.5.3.dist-info}/entry_points.txt +0 -0
- {bmtool-0.5.1.dist-info → bmtool-0.5.3.dist-info}/top_level.txt +0 -0
bmtool/bmplot.py
CHANGED
@@ -3,7 +3,6 @@ Want to be able to take multiple plot names in and plot them all at the same tim
|
|
3
3
|
https://stackoverflow.com/questions/458209/is-there-a-way-to-detach-matplotlib-plots-so-that-the-computation-can-continue
|
4
4
|
"""
|
5
5
|
from .util import util
|
6
|
-
|
7
6
|
import argparse,os,sys
|
8
7
|
|
9
8
|
import numpy as np
|
@@ -82,7 +81,6 @@ def total_connection_matrix(config=None,title=None,sources=None, targets=None, s
|
|
82
81
|
title = "All Synapse .mod Files Used"
|
83
82
|
if synaptic_info=='3':
|
84
83
|
title = "All Synapse .json Files Used"
|
85
|
-
|
86
84
|
plot_connection_info(text,num,source_labels,target_labels,title, syn_info=synaptic_info, save_file=save_file)
|
87
85
|
return
|
88
86
|
|
@@ -198,7 +196,7 @@ def probability_connection_matrix(config=None,nodes=None,edges=None,title=None,s
|
|
198
196
|
|
199
197
|
return
|
200
198
|
|
201
|
-
def convergence_connection_matrix(config=None,title=None,sources=None, targets=None, sids=None, tids=None, no_prepend_pop=False,save_file=None,convergence=True,method='mean',include_gap=True):
|
199
|
+
def convergence_connection_matrix(config=None,title=None,sources=None, targets=None, sids=None, tids=None, no_prepend_pop=False,save_file=None,convergence=True,method='mean+std',include_gap=True):
|
202
200
|
"""
|
203
201
|
Generates connection plot displaying convergence data
|
204
202
|
config: A BMTK simulation config
|
@@ -208,7 +206,7 @@ def convergence_connection_matrix(config=None,title=None,sources=None, targets=N
|
|
208
206
|
tids: target node identifier
|
209
207
|
no_prepend_pop: dictates if population name is displayed before sid or tid when displaying graph
|
210
208
|
save_file: If plot should be saved
|
211
|
-
method: 'mean','min','max','stdev'
|
209
|
+
method: 'mean','min','max','stdev' or 'mean+std' connvergence plot
|
212
210
|
"""
|
213
211
|
if not config:
|
214
212
|
raise Exception("config not defined")
|
@@ -216,7 +214,7 @@ def convergence_connection_matrix(config=None,title=None,sources=None, targets=N
|
|
216
214
|
raise Exception("Sources or targets not defined")
|
217
215
|
return divergence_connection_matrix(config,title ,sources, targets, sids, tids, no_prepend_pop, save_file ,convergence, method,include_gap=include_gap)
|
218
216
|
|
219
|
-
def divergence_connection_matrix(config=None,title=None,sources=None, targets=None, sids=None, tids=None, no_prepend_pop=False,save_file=None,convergence=False,method='mean',include_gap=True):
|
217
|
+
def divergence_connection_matrix(config=None,title=None,sources=None, targets=None, sids=None, tids=None, no_prepend_pop=False,save_file=None,convergence=False,method='mean+std',include_gap=True):
|
220
218
|
"""
|
221
219
|
Generates connection plot displaying divergence data
|
222
220
|
config: A BMTK simulation config
|
@@ -226,7 +224,7 @@ def divergence_connection_matrix(config=None,title=None,sources=None, targets=No
|
|
226
224
|
tids: target node identifier
|
227
225
|
no_prepend_pop: dictates if population name is displayed before sid or tid when displaying graph
|
228
226
|
save_file: If plot should be saved
|
229
|
-
method: 'mean','min','max','stdev' for
|
227
|
+
method: 'mean','min','max','stdev', and 'mean+std' for divergence plot
|
230
228
|
"""
|
231
229
|
if not config:
|
232
230
|
raise Exception("config not defined")
|
@@ -256,17 +254,94 @@ def divergence_connection_matrix(config=None,title=None,sources=None, targets=No
|
|
256
254
|
title = "Maximum "
|
257
255
|
elif method == 'std':
|
258
256
|
title = "Standard Deviation "
|
259
|
-
|
257
|
+
elif method == 'mean':
|
260
258
|
title = "Mean "
|
259
|
+
else:
|
260
|
+
title = 'Mean + Std '
|
261
261
|
|
262
262
|
if convergence:
|
263
263
|
title = title + "Synaptic Convergence"
|
264
264
|
else:
|
265
265
|
title = title + "Synaptic Divergence"
|
266
|
-
|
267
|
-
plot_connection_info(data,data,source_labels,target_labels,title, save_file=save_file)
|
266
|
+
plot_connection_info(syn_info,data,source_labels,target_labels,title, save_file=save_file)
|
268
267
|
return
|
269
268
|
|
269
|
+
def gap_junction_matrix(config=None,title=None,sources=None, targets=None, sids=None,tids=None, no_prepend_pop=False,save_file=None,type='convergence'):
|
270
|
+
"""
|
271
|
+
Generates connection plot displaying gap junction data.
|
272
|
+
config: A BMTK simulation config
|
273
|
+
sources: network name(s) to plot
|
274
|
+
targets: network name(s) to plot
|
275
|
+
sids: source node identifier
|
276
|
+
tids: target node identifier
|
277
|
+
no_prepend_pop: dictates if population name is displayed before sid or tid when displaying graph
|
278
|
+
save_file: If plot should be saved
|
279
|
+
type:'convergence' or 'percent' connections
|
280
|
+
"""
|
281
|
+
if not config:
|
282
|
+
raise Exception("config not defined")
|
283
|
+
if not sources or not targets:
|
284
|
+
raise Exception("Sources or targets not defined")
|
285
|
+
if type !='convergence' and type!='percent':
|
286
|
+
raise Exception("type must be 'convergence' or 'percent'")
|
287
|
+
sources = sources.split(",")
|
288
|
+
targets = targets.split(",")
|
289
|
+
if sids:
|
290
|
+
sids = sids.split(",")
|
291
|
+
else:
|
292
|
+
sids = []
|
293
|
+
if tids:
|
294
|
+
tids = tids.split(",")
|
295
|
+
else:
|
296
|
+
tids = []
|
297
|
+
syn_info, data, source_labels, target_labels = util.gap_junction_connections(config=config,nodes=None,edges=None,sources=sources,targets=targets,sids=sids,tids=tids,prepend_pop=not no_prepend_pop,type=type)
|
298
|
+
|
299
|
+
|
300
|
+
def filter_rows(syn_info, data, source_labels, target_labels):
|
301
|
+
new_syn_info = syn_info
|
302
|
+
new_data = data
|
303
|
+
new_source_labels = source_labels
|
304
|
+
new_target_labels = target_labels
|
305
|
+
for row in new_data:
|
306
|
+
row_index = -1
|
307
|
+
try:
|
308
|
+
if((np.isnan(row).all())): #checks if all of a row is nan
|
309
|
+
row_index = np.where(np.isnan(new_data)==np.isnan(row))[0][0]
|
310
|
+
except:
|
311
|
+
row_index = -1
|
312
|
+
finally:
|
313
|
+
if(all(x==0 for x in row)): #checks if all of a row is zeroes
|
314
|
+
row_index = np.where(new_data==row)[0][0]
|
315
|
+
if row_index!=-1: #deletes corresponding row accordingly in all relevant variables.
|
316
|
+
new_syn_info = np.delete(new_syn_info,row_index,0)
|
317
|
+
new_data = np.delete(new_data,row_index,0)
|
318
|
+
new_source_labels = np.delete(new_source_labels,row_index)
|
319
|
+
return new_syn_info, new_data,new_source_labels,new_target_labels
|
320
|
+
|
321
|
+
def filter_rows_and_columns(syn_info,data,source_labels,target_labels):
|
322
|
+
syn_info, data, source_labels, target_labels = filter_rows(syn_info, data, source_labels, target_labels)
|
323
|
+
transposed_syn_info = np.transpose(syn_info) #transpose everything and put it in to make sure columns get filtered
|
324
|
+
transposed_data = np.transpose(data)
|
325
|
+
transposed_source_labels = target_labels
|
326
|
+
transposed_target_labels = source_labels
|
327
|
+
syn_info, data, source_labels, target_labels = filter_rows(transposed_syn_info, transposed_data, transposed_source_labels, transposed_target_labels)
|
328
|
+
filtered_syn_info = np.transpose(syn_info) #transpose everything back to original order after filtering.
|
329
|
+
filtered_data = np.transpose(data)
|
330
|
+
filtered_source_labels = target_labels
|
331
|
+
filtered_target_labels = source_labels
|
332
|
+
return filtered_syn_info,filtered_data,filtered_source_labels,filtered_target_labels
|
333
|
+
|
334
|
+
syn_info, data, source_labels, target_labels = filter_rows_and_columns(syn_info, data, source_labels, target_labels)
|
335
|
+
|
336
|
+
if title == None or title=="":
|
337
|
+
title = 'Gap Junction'
|
338
|
+
if type == 'convergence':
|
339
|
+
title+=' Syn Convergence'
|
340
|
+
elif type == 'percent':
|
341
|
+
title+=' Percent Connectivity'
|
342
|
+
plot_connection_info(syn_info,data,source_labels,target_labels,title, save_file=save_file)
|
343
|
+
return
|
344
|
+
|
270
345
|
def connection_histogram(config=None,nodes=None,edges=None,sources=[],targets=[],sids=[],tids=[],prepend_pop=True,synaptic_info='0',
|
271
346
|
source_cell = None,target_cell = None,include_gap=True):
|
272
347
|
"""
|
bmtool/connectors.py
CHANGED
@@ -4,6 +4,7 @@ from scipy.special import erf
|
|
4
4
|
from scipy.optimize import minimize_scalar
|
5
5
|
from functools import partial
|
6
6
|
import time
|
7
|
+
import types
|
7
8
|
|
8
9
|
rng = np.random.default_rng()
|
9
10
|
|
@@ -11,6 +12,13 @@ rng = np.random.default_rng()
|
|
11
12
|
############################## CONNECT CELLS #################################
|
12
13
|
|
13
14
|
# Utility Functions
|
15
|
+
def num_prop(ratio, N):
|
16
|
+
"""Calculate numbers of total N in proportion to ratio"""
|
17
|
+
ratio = np.asarray(ratio)
|
18
|
+
p = np.cumsum(np.insert(ratio.ravel(), 0, 0)) # cumulative proportion
|
19
|
+
return np.diff(np.round(N / p[-1] * p).astype(int)).reshape(ratio.shape)
|
20
|
+
|
21
|
+
|
14
22
|
def decision(prob, size=None):
|
15
23
|
"""
|
16
24
|
Make single random decision based on input probability.
|
@@ -76,10 +84,10 @@ class DistantDependentProbability(ProbabilityFunction):
|
|
76
84
|
assert(min_dist >= 0 and min_dist < max_dist)
|
77
85
|
self.min_dist, self.max_dist = min_dist, max_dist
|
78
86
|
|
79
|
-
def __call__(self, dist):
|
87
|
+
def __call__(self, dist, *arg, **kwargs):
|
80
88
|
"""Return probability for single distance input"""
|
81
89
|
if dist >= self.min_dist and dist <= self.max_dist:
|
82
|
-
return self.probability(dist)
|
90
|
+
return self.probability(dist)
|
83
91
|
else:
|
84
92
|
return 0.
|
85
93
|
|
@@ -88,7 +96,10 @@ class DistantDependentProbability(ProbabilityFunction):
|
|
88
96
|
dist = np.asarray(dist)
|
89
97
|
dec = np.zeros(dist.shape, dtype=bool)
|
90
98
|
mask = (dist >= self.min_dist) & (dist <= self.max_dist)
|
91
|
-
|
99
|
+
dist = dist[mask]
|
100
|
+
prob = np.empty(dist.shape)
|
101
|
+
prob[:] = self.probability(dist)
|
102
|
+
dec[mask] = decisions(prob)
|
92
103
|
return dec
|
93
104
|
|
94
105
|
|
@@ -99,7 +110,7 @@ class UniformInRange(DistantDependentProbability):
|
|
99
110
|
super().__init__(min_dist=min_dist, max_dist=max_dist)
|
100
111
|
self.p = np.array(p)
|
101
112
|
assert(self.p.size == 1)
|
102
|
-
assert(
|
113
|
+
assert(p >= 0. and p <= 1.)
|
103
114
|
|
104
115
|
def probability(self, dist):
|
105
116
|
return self.p
|
@@ -127,7 +138,7 @@ class GaussianDropoff(DistantDependentProbability):
|
|
127
138
|
input pmax, and calculate pmax. See calc_pmax_from_ptotal() method.
|
128
139
|
ptotal_dist_range: Distance range for calculating pmax when ptotal is
|
129
140
|
specified. If not specified, set to range (min_dist, max_dist).
|
130
|
-
dist_type: spherical or cylindrical for distance metric.
|
141
|
+
dist_type: 'spherical' or 'cylindrical' for distance metric.
|
131
142
|
Used when ptotal is specified.
|
132
143
|
|
133
144
|
Returns:
|
@@ -215,6 +226,51 @@ class GaussianDropoff(DistantDependentProbability):
|
|
215
226
|
self.probability = probability
|
216
227
|
|
217
228
|
|
229
|
+
class NormalizedReciprocalRate(ProbabilityFunction):
|
230
|
+
"""Reciprocal connection probability given normalized reciprocal rate.
|
231
|
+
Normalized reciprocal rate is defined as the ratio between the reciprocal
|
232
|
+
connection probability and the connection probability for a randomly
|
233
|
+
connected network where the two unidirectional connections between any pair
|
234
|
+
of neurons are independent. NRR = pr / (p0 * p1)
|
235
|
+
|
236
|
+
Parameters:
|
237
|
+
NRR: a constant or distance dependent function for normalized reciprocal
|
238
|
+
rate. When being a function, it should be accept vectorized input.
|
239
|
+
Returns:
|
240
|
+
A callable object that returns the probability value.
|
241
|
+
"""
|
242
|
+
|
243
|
+
def __init__(self, NRR=1.):
|
244
|
+
self.NRR = NRR if callable(NRR) else lambda *x: NRR
|
245
|
+
|
246
|
+
def probability(self, dist, p0, p1):
|
247
|
+
"""Allow numpy array input and return probability in numpy array"""
|
248
|
+
return p0 * p1 * self.NRR(dist)
|
249
|
+
|
250
|
+
def __call__(self, dist, p0, p1, *arg, **kwargs):
|
251
|
+
"""Return probability for single distance input"""
|
252
|
+
return self.probability(dist, p0, p1)
|
253
|
+
|
254
|
+
def decisions(self, dist, p0, p1, cond=None):
|
255
|
+
"""Return bool array of decisions
|
256
|
+
dist: distance (scalar or array). Will be ignored if NRR is constant.
|
257
|
+
p0, p1: forward and backward probability (scalar or array)
|
258
|
+
cond: A tuple (direction, array of outcomes) representing the condition.
|
259
|
+
Conditional probability will be returned if specified. The condition
|
260
|
+
event is determined by connection direction (0 for forward, or 1 for
|
261
|
+
backward) and outcomes (bool array of whether connection exists).
|
262
|
+
"""
|
263
|
+
dist, p0, p1 = map(np.asarray, (dist, p0, p1))
|
264
|
+
pr = np.empty(dist.shape)
|
265
|
+
pr[:] = self.probability(dist, p0, p1)
|
266
|
+
pr = np.clip(pr, a_min=np.fmax(p0 + p1 - 1., 0.), a_max=np.fmin(p0, p1))
|
267
|
+
if cond is not None:
|
268
|
+
mask = np.asarray(cond[1])
|
269
|
+
pr[mask] /= p1 if cond[0] else p0
|
270
|
+
pr[~mask] = 0.
|
271
|
+
return decisions(pr)
|
272
|
+
|
273
|
+
|
218
274
|
# Connector Classes
|
219
275
|
class AbstractConnector(ABC):
|
220
276
|
"""Abstract base class for connectors"""
|
@@ -231,22 +287,6 @@ class AbstractConnector(ABC):
|
|
231
287
|
`connection_rule` method."""
|
232
288
|
return NotImplemented
|
233
289
|
|
234
|
-
@staticmethod
|
235
|
-
def is_same_pop(source, target, quick=True):
|
236
|
-
"""Whether two NodePool objects direct to the same population"""
|
237
|
-
if quick:
|
238
|
-
# Quick check (compare filter conditions)
|
239
|
-
same = (source.network_name == target.network_name and
|
240
|
-
source._NodePool__properties ==
|
241
|
-
target._NodePool__properties)
|
242
|
-
else:
|
243
|
-
# Strict check (compare all nodes)
|
244
|
-
same = (source.network_name == target.network_name and
|
245
|
-
len(source) == len(target) and
|
246
|
-
all([s.node_id == t.node_id
|
247
|
-
for s, t in zip(source, target)]))
|
248
|
-
return same
|
249
|
-
|
250
290
|
@staticmethod
|
251
291
|
def constant_function(val):
|
252
292
|
"""Convert a constant to a constant function"""
|
@@ -255,7 +295,23 @@ class AbstractConnector(ABC):
|
|
255
295
|
return constant
|
256
296
|
|
257
297
|
|
258
|
-
# Helper
|
298
|
+
# Helper functions
|
299
|
+
def is_same_pop(source, target, quick=False):
|
300
|
+
"""Check whether two NodePool objects direct to the same population"""
|
301
|
+
if quick:
|
302
|
+
# Quick check (compare filter conditions)
|
303
|
+
same = (source.network_name == target.network_name and
|
304
|
+
source._NodePool__properties ==
|
305
|
+
target._NodePool__properties)
|
306
|
+
else:
|
307
|
+
# Strict check (compare all nodes)
|
308
|
+
same = (source.network_name == target.network_name and
|
309
|
+
len(source) == len(target) and
|
310
|
+
all([s.node_id == t.node_id
|
311
|
+
for s, t in zip(source, target)]))
|
312
|
+
return same
|
313
|
+
|
314
|
+
|
259
315
|
class Timer(object):
|
260
316
|
def __init__(self, unit='sec'):
|
261
317
|
if unit == 'ms':
|
@@ -303,7 +359,7 @@ def rho_2_pr(p0, p1, rho):
|
|
303
359
|
|
304
360
|
class ReciprocalConnector(AbstractConnector):
|
305
361
|
"""
|
306
|
-
Object for
|
362
|
+
Object for buiilding connections in bmtk network model with reciprocal
|
307
363
|
probability within a single population (or between two populations).
|
308
364
|
|
309
365
|
Algorithm:
|
@@ -388,10 +444,17 @@ class ReciprocalConnector(AbstractConnector):
|
|
388
444
|
symmetric_p1_arg: Whether p0_arg and p1_arg are identical. If this is
|
389
445
|
set to True, argument p1_arg will be ignored. This is forced to be
|
390
446
|
True when the population is recurrent.
|
391
|
-
pr, pr_arg: Probability of reciprocal connection and its input
|
392
|
-
when it is a function, similar to p0, p0_arg, p1, p1_arg.
|
393
|
-
a function when it has an explicit relation with some node
|
394
|
-
properties such as distance.
|
447
|
+
pr, pr_arg: Probability of reciprocal connection and its first input
|
448
|
+
argument when it is a function, similar to p0, p0_arg, p1, p1_arg.
|
449
|
+
It can be a function when it has an explicit relation with some node
|
450
|
+
properties such as distance. A function pr requires two additional
|
451
|
+
positional arguments p0 and p1 even if they are not used, i.e.,
|
452
|
+
pr(pr_arg, p0, p1), just in case pr is dependent on p0 and p1, e.g.,
|
453
|
+
when normalized reciprocal rate NRR = pr/(p0*p1) is given.
|
454
|
+
When pr_arg is a string, the same value as p1_arg will be used for
|
455
|
+
pr_arg if the string contains '1', e.g., '1', 'p1'. Otherwise, e.g.,
|
456
|
+
'', '0', 'p0', p0_arg will be used for pr_arg. Specifying this can
|
457
|
+
avoid recomputing pr_arg when it's given by p0_arg or p1_arg.
|
395
458
|
estimate_rho: Whether estimate rho that result in an overall pr. This
|
396
459
|
is forced to be False if pr is a function or if rho is specified.
|
397
460
|
To estimate rho, all the pairs with possible connections, meaning
|
@@ -425,6 +488,10 @@ class ReciprocalConnector(AbstractConnector):
|
|
425
488
|
into the connection matrix to reduce memory consumption.
|
426
489
|
autapses: Whether to allow connecting a cell to itself. Default: False.
|
427
490
|
This is ignored when the population is not recurrent.
|
491
|
+
quick_pop_check: Whether to use quick method to check if source and
|
492
|
+
target populations are the same. Default: False.
|
493
|
+
Quick method checks only whether filter conditions match.
|
494
|
+
Strict method checks whether all node id's match considering order.
|
428
495
|
cache_data: Whether to cache the values of p0, p0_arg, p1, p1_arg
|
429
496
|
during estimation of rho. This improves performance when
|
430
497
|
estimate_rho is True while not creating a significant overhead in
|
@@ -465,7 +532,7 @@ class ReciprocalConnector(AbstractConnector):
|
|
465
532
|
pr=0., pr_arg=None, estimate_rho=True, rho=None,
|
466
533
|
dist_range_forward=None, dist_range_backward=None,
|
467
534
|
n_syn0=1, n_syn1=1, autapses=False,
|
468
|
-
cache_data=True, verbose=True):
|
535
|
+
quick_pop_check=False, cache_data=True, verbose=True):
|
469
536
|
args = locals()
|
470
537
|
var_set = ('p0', 'p0_arg', 'p1', 'p1_arg',
|
471
538
|
'pr', 'pr_arg', 'n_syn0', 'n_syn1')
|
@@ -480,6 +547,7 @@ class ReciprocalConnector(AbstractConnector):
|
|
480
547
|
self.rho = rho
|
481
548
|
|
482
549
|
self.autapses = autapses
|
550
|
+
self.quick = quick_pop_check
|
483
551
|
self.cache = self.ConnectorCache(cache_data and self.estimate_rho)
|
484
552
|
self.verbose = verbose
|
485
553
|
|
@@ -493,8 +561,8 @@ class ReciprocalConnector(AbstractConnector):
|
|
493
561
|
if self.stage:
|
494
562
|
# check whether the correct populations
|
495
563
|
if (source is None or target is None or
|
496
|
-
not
|
497
|
-
not
|
564
|
+
not is_same_pop(source, self.target, quick=self.quick) or
|
565
|
+
not is_same_pop(target, self.source, quick=self.quick)):
|
498
566
|
raise ValueError("Source or target population not consistent.")
|
499
567
|
# Skip adding nodes for the backward stage.
|
500
568
|
return
|
@@ -508,7 +576,7 @@ class ReciprocalConnector(AbstractConnector):
|
|
508
576
|
raise ValueError("Target nodes do not exists")
|
509
577
|
|
510
578
|
# Setup nodes
|
511
|
-
self.recurrent =
|
579
|
+
self.recurrent = is_same_pop(self.source, self.target, quick=self.quick)
|
512
580
|
self.source_ids = [s.node_id for s in self.source]
|
513
581
|
self.n_source = len(self.source_ids)
|
514
582
|
self.source_list = list(self.source)
|
@@ -551,6 +619,7 @@ class ReciprocalConnector(AbstractConnector):
|
|
551
619
|
self.enable = enable
|
552
620
|
self._output = {}
|
553
621
|
self.cache_dict = {}
|
622
|
+
self.set_next_it()
|
554
623
|
self.write_mode()
|
555
624
|
|
556
625
|
def cache_output(self, func, func_name, cache=True):
|
@@ -606,9 +675,14 @@ class ReciprocalConnector(AbstractConnector):
|
|
606
675
|
self.enable = False
|
607
676
|
self.mode = 'read'
|
608
677
|
|
609
|
-
def
|
678
|
+
def set_next_it(self):
|
610
679
|
if self.enable:
|
611
|
-
|
680
|
+
def next_it():
|
681
|
+
self.iter_count += 1
|
682
|
+
else:
|
683
|
+
def next_it():
|
684
|
+
pass
|
685
|
+
self.next_it = next_it
|
612
686
|
|
613
687
|
def node_2_idx_input(self, var_func, reverse=False):
|
614
688
|
"""Convert a function that accept nodes as input
|
@@ -703,6 +777,13 @@ class ReciprocalConnector(AbstractConnector):
|
|
703
777
|
|
704
778
|
# *** A sequence of major methods executed during build ***
|
705
779
|
def setup_variables(self):
|
780
|
+
# If pr_arg is string, use the same value as p0_arg or p1_arg
|
781
|
+
if isinstance(self.vars['pr_arg'], str):
|
782
|
+
pr_arg_func = 'p1_arg' if '1' in self.vars['pr_arg'] else 'p0_arg'
|
783
|
+
self.vars['pr_arg'] = self.vars[pr_arg_func]
|
784
|
+
else:
|
785
|
+
pr_arg_func = None
|
786
|
+
|
706
787
|
callable_set = set()
|
707
788
|
# Make constant variables constant functions
|
708
789
|
for name, var in self.vars.items():
|
@@ -718,6 +799,21 @@ class ReciprocalConnector(AbstractConnector):
|
|
718
799
|
var = self.vars[name]
|
719
800
|
setattr(self, name, self.node_2_idx_input(var, '1' in name))
|
720
801
|
|
802
|
+
# Set up function for pr_arg if use value from p0_arg or p1_arg
|
803
|
+
if pr_arg_func is None:
|
804
|
+
self._pr_arg = self.pr_arg # use specified pr_arg
|
805
|
+
else:
|
806
|
+
self._pr_arg_val = 0. # storing current value from p_arg
|
807
|
+
p_arg = getattr(self, pr_arg_func)
|
808
|
+
def p_arg_4_pr(*args, **kwargs):
|
809
|
+
val = p_arg(*args, **kwargs)
|
810
|
+
self._pr_arg_val = val
|
811
|
+
return val
|
812
|
+
setattr(self, pr_arg_func, p_arg_4_pr)
|
813
|
+
def pr_arg(self, *arg):
|
814
|
+
return self._pr_arg_val
|
815
|
+
self._pr_arg = types.MethodType(pr_arg, self)
|
816
|
+
|
721
817
|
def cache_variables(self):
|
722
818
|
# Select cacheable attrilbutes
|
723
819
|
cache_set = {'p0', 'p0_arg', 'p1', 'p1_arg'}
|
@@ -825,7 +921,7 @@ class ReciprocalConnector(AbstractConnector):
|
|
825
921
|
if forward:
|
826
922
|
forward = decision(p0)
|
827
923
|
if backward:
|
828
|
-
pr = self.pr(self.
|
924
|
+
pr = self.pr(self._pr_arg(i, j), p0, p1)
|
829
925
|
backward = decision(self.cond_backward(forward, p0, p1, pr))
|
830
926
|
|
831
927
|
# Make connection
|
@@ -849,7 +945,7 @@ class ReciprocalConnector(AbstractConnector):
|
|
849
945
|
if self.verbose:
|
850
946
|
self.timer.report('Total time for creating connection matrix')
|
851
947
|
if self.wrong_pr:
|
852
|
-
print("
|
948
|
+
print("Warning: Value of 'pr' outside the bounds occurred.\n")
|
853
949
|
self.connection_number_info()
|
854
950
|
|
855
951
|
def make_connection(self):
|
@@ -949,7 +1045,7 @@ class ReciprocalConnector(AbstractConnector):
|
|
949
1045
|
|
950
1046
|
class UnidirectionConnector(AbstractConnector):
|
951
1047
|
"""
|
952
|
-
Object for
|
1048
|
+
Object for buiilding unidirectional connections in bmtk network model with
|
953
1049
|
given probability within a single population (or between two populations).
|
954
1050
|
|
955
1051
|
Parameters:
|
@@ -1081,9 +1177,80 @@ class UnidirectionConnector(AbstractConnector):
|
|
1081
1177
|
% (100. * self.n_conn / self.n_pair))
|
1082
1178
|
|
1083
1179
|
|
1084
|
-
class
|
1180
|
+
class GapJunction(UnidirectionConnector):
|
1181
|
+
"""
|
1182
|
+
Object for buiilding gap junction connections in bmtk network model with
|
1183
|
+
given probabilities within a single population which is uncorrelated with
|
1184
|
+
the recurrent chemical synapses in this population.
|
1185
|
+
|
1186
|
+
Parameters:
|
1187
|
+
p, p_arg: Probability of forward connection and its input argument when
|
1188
|
+
it is a function, similar to p0, p0_arg in ReciprocalConnector. It
|
1189
|
+
can be a constant or a deterministic function whose value must be
|
1190
|
+
within range [0, 1]. When p is constant, the connection is
|
1191
|
+
homogenous.
|
1192
|
+
verbose: Whether show verbose information in console.
|
1193
|
+
|
1194
|
+
Returns:
|
1195
|
+
An object that works with BMTK to build edges in a network.
|
1196
|
+
|
1197
|
+
Important attributes:
|
1198
|
+
Similar to `UnidirectionConnector`.
|
1085
1199
|
"""
|
1086
|
-
|
1200
|
+
|
1201
|
+
def __init__(self, p=1., p_arg=None, verbose=True):
|
1202
|
+
super().__init__(p=p, p_arg=p_arg, verbose=verbose)
|
1203
|
+
|
1204
|
+
def setup_nodes(self, source=None, target=None):
|
1205
|
+
super().setup_nodes(source=source, target=target)
|
1206
|
+
if len(self.source) != len(self.target):
|
1207
|
+
raise ValueError("Source and target must be the same for "
|
1208
|
+
"gap junction.")
|
1209
|
+
self.n_source = len(self.source)
|
1210
|
+
|
1211
|
+
def make_connection(self, source, target, *args, **kwargs):
|
1212
|
+
"""Assign gap junction per iteration using one_to_one iterator"""
|
1213
|
+
# Initialize in the first iteration
|
1214
|
+
if self.iter_count == 0:
|
1215
|
+
self.initialize()
|
1216
|
+
if self.verbose:
|
1217
|
+
src_str, _ = self.get_nodes_info()
|
1218
|
+
print("\nStart building gap junction \n in " + src_str)
|
1219
|
+
|
1220
|
+
# Consider each pair only once
|
1221
|
+
nsyns = 0
|
1222
|
+
i, j = divmod(self.iter_count, self.n_source)
|
1223
|
+
if i < j:
|
1224
|
+
p_arg = self.p_arg(source, target)
|
1225
|
+
p = self.p(p_arg)
|
1226
|
+
possible = p > 0
|
1227
|
+
self.n_poss += possible
|
1228
|
+
if possible and decision(p):
|
1229
|
+
nsyns = 1
|
1230
|
+
sid, tid = source.node_id, target.node_id
|
1231
|
+
self.add_conn_prop(sid, tid, p_arg)
|
1232
|
+
self.add_conn_prop(tid, sid, p_arg)
|
1233
|
+
self.n_conn += 1
|
1234
|
+
|
1235
|
+
self.iter_count += 1
|
1236
|
+
|
1237
|
+
# Detect end of iteration
|
1238
|
+
if self.iter_count == self.n_pair:
|
1239
|
+
if self.verbose:
|
1240
|
+
self.connection_number_info()
|
1241
|
+
self.timer.report('Done! \nTime for building connections')
|
1242
|
+
return nsyns
|
1243
|
+
|
1244
|
+
def connection_number_info(self):
|
1245
|
+
n_pair = self.n_pair
|
1246
|
+
self.n_pair = (n_pair - len(self.source)) // 2
|
1247
|
+
super().connection_number_info()
|
1248
|
+
self.n_pair = n_pair
|
1249
|
+
|
1250
|
+
|
1251
|
+
class CorrelatedGapJunction(GapJunction):
|
1252
|
+
"""
|
1253
|
+
Object for buiilding gap junction connections in bmtk network model with
|
1087
1254
|
given probabilities within a single population which could be correlated
|
1088
1255
|
with the recurrent chemical synapses in this population.
|
1089
1256
|
|
@@ -1120,13 +1287,6 @@ class CorrelatedGapJunction(UnidirectionConnector):
|
|
1120
1287
|
conn_prop = conn_prop[0]
|
1121
1288
|
self.ref_conn_prop = conn_prop
|
1122
1289
|
|
1123
|
-
def setup_nodes(self, source=None, target=None):
|
1124
|
-
super().setup_nodes(source=source, target=target)
|
1125
|
-
if len(self.source) != len(self.target):
|
1126
|
-
raise ValueError("Source and target must be the same for "
|
1127
|
-
"gap junction.")
|
1128
|
-
self.n_source = len(self.source)
|
1129
|
-
|
1130
1290
|
def conn_exist(self, sid, tid):
|
1131
1291
|
trg_dict = self.ref_conn_prop.get(sid)
|
1132
1292
|
if trg_dict is not None and tid in trg_dict:
|
@@ -1153,7 +1313,7 @@ class CorrelatedGapJunction(UnidirectionConnector):
|
|
1153
1313
|
if self.iter_count == 0:
|
1154
1314
|
self.initialize()
|
1155
1315
|
if self.verbose:
|
1156
|
-
src_str,
|
1316
|
+
src_str, _ = self.get_nodes_info()
|
1157
1317
|
print("\nStart building gap junction \n in " + src_str)
|
1158
1318
|
|
1159
1319
|
# Consider each pair only once
|
@@ -1182,15 +1342,9 @@ class CorrelatedGapJunction(UnidirectionConnector):
|
|
1182
1342
|
self.timer.report('Done! \nTime for building connections')
|
1183
1343
|
return nsyns
|
1184
1344
|
|
1185
|
-
def connection_number_info(self):
|
1186
|
-
n_pair = self.n_pair
|
1187
|
-
self.n_pair = (n_pair - len(self.source)) // 2
|
1188
|
-
super().connection_number_info()
|
1189
|
-
self.n_pair = n_pair
|
1190
|
-
|
1191
1345
|
|
1192
1346
|
class OneToOneSequentialConnector(AbstractConnector):
|
1193
|
-
"""Object for
|
1347
|
+
"""Object for buiilding one to one correspondence connections in bmtk
|
1194
1348
|
network model with between two populations. One of the population can
|
1195
1349
|
consist of multiple sub-populations. These sub-populations need to be added
|
1196
1350
|
sequentially using setup_nodes() and edge_params() methods followed by BMTK
|
@@ -1337,21 +1491,24 @@ FLUC_STDEV = 0.2 # ms
|
|
1337
1491
|
DELAY_LOWBOUND = 0.2 # ms must be greater than h.dt
|
1338
1492
|
DELAY_UPBOUND = 2.0 # ms
|
1339
1493
|
|
1340
|
-
def syn_dist_delay_feng(source, target,
|
1341
|
-
|
1342
|
-
|
1494
|
+
def syn_dist_delay_feng(source, target, min_delay=SYN_MIN_DELAY,
|
1495
|
+
velocity=SYN_VELOCITY, fluc_stdev=FLUC_STDEV,
|
1496
|
+
delay_bound=(DELAY_LOWBOUND, DELAY_UPBOUND),
|
1497
|
+
connector=None):
|
1343
1498
|
"""Synpase delay linearly dependent on distance.
|
1344
1499
|
min_delay: minimum delay (ms)
|
1345
1500
|
velocity: synapse conduction velocity (micron/ms)
|
1346
1501
|
fluc_stdev: standard deviation of random Gaussian fluctuation (ms)
|
1502
|
+
delay_bound: (lower, upper) bounds of delay (ms)
|
1503
|
+
connector: connector object from which to read distance
|
1347
1504
|
"""
|
1348
1505
|
if connector is None:
|
1349
1506
|
dist = euclid_dist(target['positions'], source['positions'])
|
1350
1507
|
else:
|
1351
1508
|
dist = connector.get_conn_prop(source.node_id, target.node_id)
|
1352
1509
|
del_fluc = fluc_stdev * rng.normal()
|
1353
|
-
delay = dist /
|
1354
|
-
delay = min(max(delay,
|
1510
|
+
delay = dist / velocity + min_delay + del_fluc
|
1511
|
+
delay = min(max(delay, delay_bound[0]), delay_bound[1])
|
1355
1512
|
return delay
|
1356
1513
|
|
1357
1514
|
|
@@ -1373,4 +1530,4 @@ def syn_dist_delay_feng_section_PN(source, target, p=0.9,
|
|
1373
1530
|
|
1374
1531
|
def syn_uniform_delay_section(source, target, low=DELAY_LOWBOUND,
|
1375
1532
|
high=DELAY_UPBOUND, **kwargs):
|
1376
|
-
return rng.uniform(low, high)
|
1533
|
+
return rng.uniform(low, high)
|