bmtool 0.4.2__py3-none-any.whl → 0.5.1__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/connectors.py CHANGED
@@ -288,9 +288,22 @@ def pr_2_rho(p0, p1, pr):
288
288
  return (pr - p0 * p1) / (p0 * (1 - p0) * p1 * (1 - p1)) ** .5
289
289
 
290
290
 
291
+ def rho_2_pr(p0, p1, rho):
292
+ """Calculate reciprocal probability pr given correlation coefficient rho"""
293
+ for p in (p0, p1):
294
+ assert(p > 0 and p < 1)
295
+ pr = p0 * p1 + rho * (p0 * (1 - p0) * p1 * (1 - p1)) ** .5
296
+ if not (pr >= 0 and pr <= p0 and pr <= p1 and pr >= p0 + p1 - 1):
297
+ pr0, pr = pr, np.max((0., p0 + p1 - 1, np.min((p0, p1, pr))))
298
+ rho0, rho = rho, (pr - p0 * p1) / (p0 * (1 - p0) * p1 * (1 - p1)) ** .5
299
+ print('rho changed from %.3f to %.3f; pr changed from %.3f to %.3f'
300
+ % (rho0, rho, pr0, pr))
301
+ return pr
302
+
303
+
291
304
  class ReciprocalConnector(AbstractConnector):
292
305
  """
293
- Object for buiilding connections in bmtk network model with reciprocal
306
+ Object for building connections in bmtk network model with reciprocal
294
307
  probability within a single population (or between two populations).
295
308
 
296
309
  Algorithm:
@@ -479,7 +492,7 @@ class ReciprocalConnector(AbstractConnector):
479
492
  """Must run this before building connections"""
480
493
  if self.stage:
481
494
  # check whether the correct populations
482
- if (source is None or target is None or
495
+ if (source is None or target is None or
483
496
  not self.is_same_pop(source, self.target) or
484
497
  not self.is_same_pop(target, self.source)):
485
498
  raise ValueError("Source or target population not consistent.")
@@ -701,13 +714,13 @@ class ReciprocalConnector(AbstractConnector):
701
714
  self.callable_set = callable_set
702
715
 
703
716
  # Make callable variables except a few, accept index input instead
704
- for name in callable_set - set(('p0', 'p1', 'pr')):
717
+ for name in callable_set - {'p0', 'p1', 'pr'}:
705
718
  var = self.vars[name]
706
719
  setattr(self, name, self.node_2_idx_input(var, '1' in name))
707
720
 
708
721
  def cache_variables(self):
709
722
  # Select cacheable attrilbutes
710
- cache_set = set(('p0', 'p0_arg', 'p1', 'p1_arg'))
723
+ cache_set = {'p0', 'p0_arg', 'p1', 'p1_arg'}
711
724
  if self.symmetric_p1:
712
725
  cache_set.remove('p1')
713
726
  if self.symmetric_p1_arg:
@@ -823,8 +836,9 @@ class ReciprocalConnector(AbstractConnector):
823
836
  if backward:
824
837
  n_backward = self.n_syn1(j, i)
825
838
  if self.recurrent:
826
- self.conn_mat[0, j, i] = n_backward
827
- self.add_conn_prop(j, i, p1_arg, 0)
839
+ if i != j:
840
+ self.conn_mat[0, j, i] = n_backward
841
+ self.add_conn_prop(j, i, p1_arg, 0)
828
842
  else:
829
843
  self.conn_mat[1, i, j] = n_backward
830
844
  self.add_conn_prop(i, j, p1_arg, 1)
@@ -901,9 +915,11 @@ class ReciprocalConnector(AbstractConnector):
901
915
  n_poss = np.array(self.possible_count)
902
916
  n_pair = conn_mat.size / 2
903
917
  if self.recurrent:
904
- n_recp = (np.count_nonzero(conn_mat[0] & conn_mat[0].T)
905
- - np.count_nonzero(np.diag(conn_mat[0]))) // 2
906
- n_conn = n_conn - n_recp
918
+ n_recp = np.count_nonzero(conn_mat[0] & conn_mat[0].T)
919
+ if self.autapses:
920
+ n_recp -= np.count_nonzero(np.diag(conn_mat[0]))
921
+ n_recp //= 2
922
+ n_conn -= n_recp
907
923
  n_poss = n_poss[None]
908
924
  n_pair += (1 if self.autapses else -1) * self.n_source / 2
909
925
  else:
@@ -933,7 +949,7 @@ class ReciprocalConnector(AbstractConnector):
933
949
 
934
950
  class UnidirectionConnector(AbstractConnector):
935
951
  """
936
- Object for buiilding unidirectional connections in bmtk network model with
952
+ Object for building unidirectional connections in bmtk network model with
937
953
  given probability within a single population (or between two populations).
938
954
 
939
955
  Parameters:
@@ -1067,7 +1083,7 @@ class UnidirectionConnector(AbstractConnector):
1067
1083
 
1068
1084
  class CorrelatedGapJunction(UnidirectionConnector):
1069
1085
  """
1070
- Object for buiilding gap junction connections in bmtk network model with
1086
+ Object for building gap junction connections in bmtk network model with
1071
1087
  given probabilities within a single population which could be correlated
1072
1088
  with the recurrent chemical synapses in this population.
1073
1089
 
@@ -1091,6 +1107,7 @@ class CorrelatedGapJunction(UnidirectionConnector):
1091
1107
  Important attributes:
1092
1108
  Similar to `UnidirectionConnector`.
1093
1109
  """
1110
+
1094
1111
  def __init__(self, p_non=1., p_uni=1., p_rec=1., p_arg=None,
1095
1112
  connector=None, verbose=True):
1096
1113
  super().__init__(p=p_non, p_arg=p_arg, verbose=verbose)
@@ -1105,9 +1122,10 @@ class CorrelatedGapJunction(UnidirectionConnector):
1105
1122
 
1106
1123
  def setup_nodes(self, source=None, target=None):
1107
1124
  super().setup_nodes(source=source, target=target)
1108
- if len(self.source) != len(self.source):
1125
+ if len(self.source) != len(self.target):
1109
1126
  raise ValueError("Source and target must be the same for "
1110
1127
  "gap junction.")
1128
+ self.n_source = len(self.source)
1111
1129
 
1112
1130
  def conn_exist(self, sid, tid):
1113
1131
  trg_dict = self.ref_conn_prop.get(sid)
@@ -1122,11 +1140,11 @@ class CorrelatedGapJunction(UnidirectionConnector):
1122
1140
  return conn0 + conn1, prop0 if conn0 else prop1
1123
1141
 
1124
1142
  def initialize(self):
1125
- super().initialize()
1126
1143
  self.has_p_arg = self.vars['p_arg'] is not None
1127
1144
  if not self.has_p_arg:
1128
1145
  var = self.connector.vars
1129
- self.p_arg = var['p_arg'] if 'p_arg' in var else var['p0_arg']
1146
+ self.vars['p_arg'] = var.get('p_arg', var.get('p0_arg', None))
1147
+ super().initialize()
1130
1148
  self.ps = [self.p_non, self.p_uni, self.p_rec]
1131
1149
 
1132
1150
  def make_connection(self, source, target, *args, **kwargs):
@@ -1140,7 +1158,7 @@ class CorrelatedGapJunction(UnidirectionConnector):
1140
1158
 
1141
1159
  # Consider each pair only once
1142
1160
  nsyns = 0
1143
- i, j = divmod(self.iter_count, self.n_pair)
1161
+ i, j = divmod(self.iter_count, self.n_source)
1144
1162
  if i < j:
1145
1163
  sid, tid = source.node_id, target.node_id
1146
1164
  conn_type, p_arg = self.connection_type(sid, tid)
@@ -1172,7 +1190,7 @@ class CorrelatedGapJunction(UnidirectionConnector):
1172
1190
 
1173
1191
 
1174
1192
  class OneToOneSequentialConnector(AbstractConnector):
1175
- """Object for buiilding one to one correspondence connections in bmtk
1193
+ """Object for building one to one correspondence connections in bmtk
1176
1194
  network model with between two populations. One of the population can
1177
1195
  consist of multiple sub-populations. These sub-populations need to be added
1178
1196
  sequentially using setup_nodes() and edge_params() methods followed by BMTK
@@ -1208,7 +1226,7 @@ class OneToOneSequentialConnector(AbstractConnector):
1208
1226
  source: NodePool object for the single population.
1209
1227
  targets: List of NodePool objects for the multiple sub-populations.
1210
1228
  """
1211
-
1229
+
1212
1230
  def __init__(self, n_syn=1, partition_source=False, verbose=True):
1213
1231
  self.n_syn = int(n_syn)
1214
1232
  self.partition_source = partition_source
@@ -1315,10 +1333,13 @@ class OneToOneSequentialConnector(AbstractConnector):
1315
1333
 
1316
1334
  SYN_MIN_DELAY = 0.8 # ms
1317
1335
  SYN_VELOCITY = 1000. # um/ms
1336
+ FLUC_STDEV = 0.2 # ms
1337
+ DELAY_LOWBOUND = 0.2 # ms must be greater than h.dt
1338
+ DELAY_UPBOUND = 2.0 # ms
1318
1339
 
1319
1340
  def syn_dist_delay_feng(source, target,
1320
1341
  min_delay=SYN_MIN_DELAY, velocity=SYN_VELOCITY,
1321
- fluc_stdev=0.05, connector=None):
1342
+ fluc_stdev=FLUC_STDEV, connector=None):
1322
1343
  """Synpase delay linearly dependent on distance.
1323
1344
  min_delay: minimum delay (ms)
1324
1345
  velocity: synapse conduction velocity (micron/ms)
@@ -1329,7 +1350,8 @@ def syn_dist_delay_feng(source, target,
1329
1350
  else:
1330
1351
  dist = connector.get_conn_prop(source.node_id, target.node_id)
1331
1352
  del_fluc = fluc_stdev * rng.normal()
1332
- delay = max(dist / SYN_VELOCITY + SYN_MIN_DELAY + del_fluc, 0.)
1353
+ delay = dist / SYN_VELOCITY + SYN_MIN_DELAY + del_fluc
1354
+ delay = min(max(delay, DELAY_LOWBOUND), DELAY_UPBOUND)
1333
1355
  return delay
1334
1356
 
1335
1357
 
@@ -1348,35 +1370,7 @@ def syn_dist_delay_feng_section_PN(source, target, p=0.9,
1348
1370
  s_id, s_x = syn_section_PN(source, target, p=p, sec_id=sec_id, sec_x=sec_x)
1349
1371
  return delay, s_id, s_x
1350
1372
 
1351
- # The function below is not necessary if sec_id is specified in edge parameters
1352
- # TODO: select based on cell types of a pair
1353
- TARGET_SEC_ID = {
1354
- 'CP': 1, 'CS': 1,
1355
- 'CTH': 1, 'CC': 1,
1356
- 'FSI': 0, 'LTS': 0
1357
- }
1358
-
1359
- def get_target_sec_id(source, target):
1360
- # 0 - Target soma, 1 - Target basal dendrite
1361
- sec_id = TARGET_SEC_ID.get(source['pop_name'], None)
1362
- if sec_id is None:
1363
- if source['model_type'] == 'virtual':
1364
- sec_id = 1
1365
- print('virtual cell') # Testing
1366
- else: # We really don't want a default case so we can catch errors
1367
- # return 0
1368
- import pdb; pdb.set_trace()
1369
- return sec_id
1370
-
1371
- def syn_dist_delay_feng_section(source, target, connector=None,
1372
- sec_id=None, sec_x=0.9):
1373
- if sec_id is None:
1374
- sec_id = get_target_sec_id(source, target)
1375
- return syn_dist_delay_feng(source, target, connector), sec_id, sec_x
1376
-
1377
-
1378
- def syn_uniform_delay_section(source, target, low=0.5, high=1,
1379
- sec_id=None, sec_x=0.9, **kwargs):
1380
- if sec_id is None:
1381
- sec_id = get_target_sec_id(source, target)
1382
- return rng.uniform(low, high), sec_id, sec_x
1373
+
1374
+ def syn_uniform_delay_section(source, target, low=DELAY_LOWBOUND,
1375
+ high=DELAY_UPBOUND, **kwargs):
1376
+ return rng.uniform(low, high)
bmtool/util/util.py CHANGED
@@ -559,6 +559,7 @@ def relation_matrix(config=None, nodes=None,edges=None,sources=[],targets=[],sid
559
559
  return stdev
560
560
 
561
561
  for s_type_ind,s_type in enumerate(source_uids[sm]):
562
+
562
563
  for t_type_ind,t_type in enumerate(target_uids[tm]):
563
564
  source_index = int(s_type_ind+sources_start[sm])
564
565
  target_index = int(t_type_ind+target_start[tm])
@@ -586,12 +587,12 @@ def relation_matrix(config=None, nodes=None,edges=None,sources=[],targets=[],sid
586
587
  syn_info[source_index,target_index] = ""
587
588
  else:
588
589
  syn_info[source_index,target_index] = syn_list
589
-
590
+
590
591
  e_matrix[source_index,target_index]=total
591
592
 
592
593
  return syn_info, e_matrix, source_pop_names, target_pop_names
593
594
 
594
- def connection_totals(config=None,nodes=None,edges=None,sources=[],targets=[],sids=[],tids=[],prepend_pop=True,synaptic_info='0'):
595
+ def connection_totals(config=None,nodes=None,edges=None,sources=[],targets=[],sids=[],tids=[],prepend_pop=True,synaptic_info='0',include_gap=True):
595
596
 
596
597
  def total_connection_relationship(**kwargs):
597
598
  edges = kwargs["edges"]
@@ -600,12 +601,60 @@ def connection_totals(config=None,nodes=None,edges=None,sources=[],targets=[],si
600
601
  source_id = kwargs["source_id"]
601
602
  target_id = kwargs["target_id"]
602
603
 
603
- total = edges[(edges[source_id_type] == source_id) & (edges[target_id_type]==target_id)].count()
604
+ total = edges[(edges[source_id_type] == source_id) & (edges[target_id_type]==target_id)]
605
+ if include_gap == False:
606
+ total = total[total['is_gap_junction'] != True]
607
+ total = total.count()
604
608
  total = total.source_node_id # may not be the best way to pick
605
609
  return total
606
610
  return relation_matrix(config,nodes,edges,sources,targets,sids,tids,prepend_pop,relation_func=total_connection_relationship,synaptic_info=synaptic_info)
607
611
 
608
- def connection_divergence(config=None,nodes=None,edges=None,sources=[],targets=[],sids=[],tids=[],prepend_pop=True,convergence=False,method='mean'):
612
+ def percent_connections(config=None,nodes=None,edges=None,sources=[],targets=[],sids=[],tids=[],prepend_pop=True,method=None,include_gap=True):
613
+
614
+ def precent_func(**kwargs):
615
+ edges = kwargs["edges"]
616
+ source_id_type = kwargs["sid"]
617
+ target_id_type = kwargs["tid"]
618
+ source_id = kwargs["source_id"]
619
+ target_id = kwargs["target_id"]
620
+ t_list = kwargs["target_nodes"]
621
+ s_list = kwargs["source_nodes"]
622
+
623
+ cons = edges[(edges[source_id_type] == source_id) & (edges[target_id_type]==target_id)]
624
+ if include_gap == False:
625
+ cons = cons[cons['is_gap_junction'] != True]
626
+ total_cons = cons.count().source_node_id
627
+ # to determine reciprocal connectivity
628
+ # create a copy and flip source/dest
629
+ cons_flip = edges[(edges[source_id_type] == target_id) & (edges[target_id_type]==source_id)]
630
+ cons_flip = cons_flip.rename(columns={'source_node_id':'target_node_id','target_node_id':'source_node_id'})
631
+ # append to original
632
+ cons_recip = pd.concat([cons, cons_flip])
633
+
634
+ # determine dropped duplicates (keep=False)
635
+ cons_recip_dedup = cons_recip.drop_duplicates(subset=['source_node_id','target_node_id'])
636
+
637
+ # note counts
638
+ num_bi = (cons_recip.count().source_node_id - cons_recip_dedup.count().source_node_id)
639
+ num_uni = total_cons - num_bi
640
+
641
+ num_sources = s_list.apply(pd.Series.value_counts)[source_id_type].dropna().sort_index().loc[source_id]
642
+ num_targets = t_list.apply(pd.Series.value_counts)[target_id_type].dropna().sort_index().loc[target_id]
643
+
644
+ total = round(total_cons / (num_sources*num_targets) * 100,2)
645
+ uni = round(num_uni / (num_sources*num_targets) * 100,2)
646
+ bi = round(num_bi / (num_sources*num_targets) * 100,2)
647
+ if method == 'total':
648
+ return total
649
+ if method == 'uni':
650
+ return uni
651
+ if method == 'bi':
652
+ return bi
653
+
654
+
655
+ return relation_matrix(config,nodes,edges,sources,targets,sids,tids,prepend_pop,relation_func=precent_func)
656
+
657
+ def connection_divergence(config=None,nodes=None,edges=None,sources=[],targets=[],sids=[],tids=[],prepend_pop=True,convergence=False,method='mean',include_gap=True):
609
658
 
610
659
  import pandas as pd
611
660
 
@@ -620,6 +669,8 @@ def connection_divergence(config=None,nodes=None,edges=None,sources=[],targets=[
620
669
  count = 1
621
670
 
622
671
  cons = edges[(edges[source_id_type] == source_id) & (edges[target_id_type]==target_id)]
672
+ if include_gap == False:
673
+ cons = cons[cons['is_gap_junction'] != True]
623
674
 
624
675
  if convergence:
625
676
  if method == 'min':
@@ -646,12 +697,16 @@ def connection_divergence(config=None,nodes=None,edges=None,sources=[],targets=[
646
697
  std = cons.apply(pd.Series.value_counts).source_node_id.dropna().std()
647
698
  return round(std,2)
648
699
  else: #mean
649
- vc = s_list.apply(pd.Series.value_counts)[source_id_type].dropna().sort_index().loc[source_id]
700
+ #vc = s_list.apply(pd.Series.value_counts)[source_id_type].dropna().sort_index().loc[source_id]
701
+ vc = s_list.apply(pd.Series.value_counts)
650
702
  vc = vc[source_id_type].dropna().sort_index()
651
703
  count = vc.loc[source_id]#count = s_list[s_list[source_id_type]==source_id]
652
704
 
653
705
  # Only executed when mean
654
- total = edges[(edges[source_id_type] == source_id) & (edges[target_id_type]==target_id)].count()
706
+ total = edges[(edges[source_id_type] == source_id) & (edges[target_id_type]==target_id)]
707
+ if include_gap == False:
708
+ total = total[total['is_gap_junction'] != True]
709
+ total = total.count()
655
710
  total = total.source_node_id # may not be the best way to pick
656
711
  ret = round(total/count,1)
657
712
  if ret == 0:
@@ -661,7 +716,7 @@ def connection_divergence(config=None,nodes=None,edges=None,sources=[],targets=[
661
716
  return relation_matrix(config,nodes,edges,sources,targets,sids,tids,prepend_pop,relation_func=total_connection_relationship)
662
717
 
663
718
  def connection_probabilities(config=None,nodes=None,edges=None,sources=[],
664
- targets=[],sids=[],tids=[],prepend_pop=True,dist_X=True,dist_Y=True,dist_Z=True,num_bins=10):
719
+ targets=[],sids=[],tids=[],prepend_pop=True,dist_X=True,dist_Y=True,dist_Z=True,num_bins=10,include_gap=True):
665
720
 
666
721
  import pandas as pd
667
722
  from scipy.spatial import distance
@@ -736,7 +791,9 @@ def connection_probabilities(config=None,nodes=None,edges=None,sources=[],
736
791
  return ret
737
792
 
738
793
  relevant_edges = edges[(edges[source_id_type] == source_id) & (edges[target_id_type]==target_id)]
739
- connected_distances = eudist(relevant_edges,dist_X,dist_Y,dist_Z).tolist()
794
+ if include_gap == False:
795
+ relevant_edges = relevant_edges[relevant_edges['is_gap_junction'] != True]
796
+ connected_distances = eudist(relevant_edges,dist_X,dist_Y,dist_Z).values.tolist()
740
797
  if len(connected_distances)>0:
741
798
  if connected_distances[0]==0:
742
799
  return -1