"""
Module for creating network connections
"""
try:
basestring
except NameError:
basestring = str
import numpy as np
from array import array as arrayFast
from numbers import Number
from tqdm import tqdm
# -----------------------------------------------------------------------------
# Connect Cells
# -----------------------------------------------------------------------------
[docs]
def connectCells(self):
"""
Function for/to <short description of `netpyne.network.conn.connectCells`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
"""
from .. import sim
# Instantiate network connections based on the connectivity rules defined in params
sim.timing('start', 'connectTime')
if sim.rank == 0:
print('Making connections...')
if sim.nhosts > 1: # Gather tags from all cells
allCellTags = sim._gatherAllCellTags()
else:
allCellTags = {cell.gid: cell.tags for cell in self.cells}
allPopTags = {
-i: pop.tags for i, pop in enumerate(self.pops.values())
} # gather tags from pops so can connect NetStim pops
if self.params.subConnParams: # do not create NEURON objs until synapses are distributed based on subConnParams
origCreateNEURONObj = bool(sim.cfg.createNEURONObj)
origAddSynMechs = bool(sim.cfg.addSynMechs)
sim.cfg.createNEURONObj = False
sim.cfg.addSynMechs = False
hasPointerConns = self.params.synMechParams.hasPointerConns()
for connParamLabel, connParamTemp in self.params.connParams.items(): # for each conn rule or parameter set
connParam = connParamTemp.copy()
connParam['label'] = connParamLabel
# find pre and post cells that match conditions
preCellsTags, postCellsTags = self._findPrePostCellsCondition(
allCellTags, connParam['preConds'], connParam['postConds']
)
# if conn function not specified, select based on params
if 'connFunc' not in connParam:
if 'probability' in connParam:
connParam['connFunc'] = 'probConn' # probability based func
elif 'convergence' in connParam:
connParam['connFunc'] = 'convConn' # convergence function
elif 'divergence' in connParam:
connParam['connFunc'] = 'divConn' # divergence function
elif 'connList' in connParam:
connParam['connFunc'] = 'fromListConn' # from list function
else:
connParam['connFunc'] = 'fullConn' # convergence function
connFunc = getattr(self, connParam['connFunc']) # get function name from params
# process string-based funcs and call conn function
if preCellsTags and postCellsTags:
# initialize randomizer in case used in string-based function (see issue #89 for more details)
self.rand.Random123(
sim.hashStr('conn_' + connParam['connFunc']),
sim.hashList(sorted(preCellsTags) + sorted(postCellsTags)),
sim.cfg.seeds['conn'],
)
self._connStrToFunc(
preCellsTags, postCellsTags, connParam
) # convert strings to functions (for the delay, and probability params)
connFunc(preCellsTags, postCellsTags, connParam) # call specific conn function
# check if gap junctions in any of the conn rules (deprecated)
if 'gapJunction' in connParam:
hasPointerConns = True
if sim.cfg.printSynsAfterRule:
nodeSynapses = sum([len(cell.conns) for cell in sim.net.cells])
print(
(
' Number of synaptic contacts on node %i after conn rule %s: %i '
% (sim.rank, connParamLabel, nodeSynapses)
)
)
# add pointer conns targeting presynaptic cells
if hasPointerConns:
# distribute info across nodes
if not getattr(sim.net, 'preCellPointerConns', False):
sim.net.preCellPointerConns = []
data = [sim.net.preCellPointerConns] * sim.nhosts # send cells data to other nodes
data[sim.rank] = None
gather = sim.pc.py_alltoall(data) # collect cells data from other nodes (required to generate connections)
sim.pc.barrier()
for dataNode in gather:
if dataNode:
sim.net.preCellPointerConns.extend(dataNode)
# add connections (need to do separately because could be in different ranks)
for preGapParams in getattr(sim.net, 'preCellPointerConns', []):
if preGapParams['gid'] in self.gid2lid: # only cells in this rank
cell = self.cells[self.gid2lid[preGapParams['gid']]]
cell.addConn(preGapParams)
# apply subcellular connectivity params (distribution of synaspes)
if self.params.subConnParams:
self.subcellularConn(allCellTags, allPopTags)
sim.cfg.createNEURONObj = origCreateNEURONObj # set to original value
sim.cfg.addSynMechs = origAddSynMechs # set to original value
cellsUpdate = [c for c in sim.net.cells if c.tags.get('cellModel', None) not in ['NetStim', 'VecStim']]
if sim.cfg.createNEURONObj:
for cell in cellsUpdate:
# Add synMechs, stim and conn NEURON objects
cell.addStimsNEURONObj()
# cell.addSynMechsNEURONObj()
cell.addConnsNEURONObj()
nodeSynapses = sum([len(cell.conns) for cell in sim.net.cells])
if sim.cfg.createPyStruct:
nodeConnections = sum([len(set([conn['preGid'] for conn in cell.conns])) for cell in sim.net.cells])
else:
nodeConnections = nodeSynapses
print((' Number of connections on node %i: %i ' % (sim.rank, nodeConnections)))
if nodeSynapses != nodeConnections:
print((' Number of synaptic contacts on node %i: %i ' % (sim.rank, nodeSynapses)))
sim.pc.barrier()
sim.timing('stop', 'connectTime')
if sim.rank == 0 and sim.cfg.timing:
print((' Done; cell connection time = %0.2f s.' % sim.timingData['connectTime']))
return [cell.conns for cell in self.cells]
# -----------------------------------------------------------------------------
# Find pre and post cells matching conditions
# -----------------------------------------------------------------------------
def _findPrePostCellsCondition(self, allCellTags, preConds, postConds):
# try:
preCellsTags = dict(allCellTags) # initialize with all presyn cells (make copy)
postCellsTags = None
for condKey, condValue in preConds.items(): # Find subset of cells that match presyn criteria
if condKey in ['x', 'y', 'z', 'xnorm', 'ynorm', 'znorm']:
preCellsTags = {
gid: tags
for (gid, tags) in preCellsTags.items()
if condValue[0] <= tags.get(condKey, None) < condValue[1]
} # dict with pre cell tags
else:
if isinstance(condValue, list):
preCellsTags = {
gid: tags for (gid, tags) in preCellsTags.items() if tags.get(condKey, None) in condValue
} # dict with pre cell tags
else:
preCellsTags = {
gid: tags for (gid, tags) in preCellsTags.items() if tags.get(condKey, None) == condValue
} # dict with pre cell tags
if preCellsTags: # only check post if there are pre
postCellsTags = allCellTags
for condKey, condValue in postConds.items(): # Find subset of cells that match postsyn criteria
if condKey in ['x', 'y', 'z', 'xnorm', 'ynorm', 'znorm']:
postCellsTags = {
gid: tags
for (gid, tags) in postCellsTags.items()
if condValue[0] <= tags.get(condKey, None) < condValue[1]
} # dict with post Cell objects} # dict with pre cell tags
elif isinstance(condValue, list):
postCellsTags = {
gid: tags for (gid, tags) in postCellsTags.items() if tags.get(condKey, None) in condValue
} # dict with post Cell objects
else:
postCellsTags = {
gid: tags for (gid, tags) in postCellsTags.items() if tags.get(condKey, None) == condValue
} # dict with post Cell objects
# except:
# return None, None
return preCellsTags, postCellsTags
# -----------------------------------------------------------------------------
# Convert connection param string to function
# -----------------------------------------------------------------------------
def _connStrToFunc(self, preCellsTags, postCellsTags, connParam):
# list of params that have a function passed in as a string
paramsStrFunc = [
param
for param in self.connStringFuncParams + ['probability', 'convergence', 'divergence']
if param in connParam and isinstance(connParam[param], basestring)
]
# dict to store correspondence between string and actual variable
dictVars = {}
dictVars['pre_x'] = lambda preConds, postConds: preConds['x']
dictVars['pre_y'] = lambda preConds, postConds: preConds['y']
dictVars['pre_z'] = lambda preConds, postConds: preConds['z']
dictVars['pre_xnorm'] = lambda preConds, postConds: preConds['xnorm']
dictVars['pre_ynorm'] = lambda preConds, postConds: preConds['ynorm']
dictVars['pre_znorm'] = lambda preConds, postConds: preConds['znorm']
dictVars['post_x'] = lambda preConds, postConds: postConds['x']
dictVars['post_y'] = lambda preConds, postConds: postConds['y']
dictVars['post_z'] = lambda preConds, postConds: postConds['z']
dictVars['post_xnorm'] = lambda preConds, postConds: postConds['xnorm']
dictVars['post_ynorm'] = lambda preConds, postConds: postConds['ynorm']
dictVars['post_znorm'] = lambda preConds, postConds: postConds['znorm']
dictVars['dist_x'] = lambda preConds, postConds: abs(preConds['x'] - postConds['x'])
dictVars['dist_y'] = lambda preConds, postConds: abs(preConds['y'] - postConds['y'])
dictVars['dist_z'] = lambda preConds, postConds: abs(preConds['z'] - postConds['z'])
dictVars['dist_3D'] = lambda preConds, postConds: np.sqrt(
(preConds['x'] - postConds['x']) ** 2
+ (preConds['y'] - postConds['y']) ** 2
+ (preConds['z'] - postConds['z']) ** 2
)
dictVars['dist_3D_border'] = lambda preConds, postConds: np.sqrt(
(abs(preConds['x'] - postConds['x']) - postConds['borderCorrect'][0]) ** 2
+ (abs(preConds['y'] - postConds['y']) - postConds['borderCorrect'][1]) ** 2
+ (abs(preConds['z'] - postConds['z']) - postConds['borderCorrect'][2]) ** 2
)
dictVars['dist_2D'] = lambda preConds, postConds: np.sqrt(
(preConds['x'] - postConds['x']) ** 2 + (preConds['z'] - postConds['z']) ** 2
)
dictVars['dist_xnorm'] = lambda preConds, postConds: abs(preConds['xnorm'] - postConds['xnorm'])
dictVars['dist_ynorm'] = lambda preConds, postConds: abs(preConds['ynorm'] - postConds['ynorm'])
dictVars['dist_znorm'] = lambda preConds, postConds: abs(preConds['znorm'] - postConds['znorm'])
dictVars['dist_norm3D'] = lambda preConds, postConds: np.sqrt(
(preConds['xnorm'] - postConds['xnorm']) ** 2
+ np.sqrt(preConds['ynorm'] - postConds['ynorm'])
+ np.sqrt(preConds['znorm'] - postConds['znorm'])
)
dictVars['dist_norm2D'] = lambda preConds, postConds: np.sqrt(
(preConds['xnorm'] - postConds['xnorm']) ** 2 + np.sqrt(preConds['znorm'] - postConds['znorm'])
)
dictVars['rand'] = lambda unused1, unused2: self.rand
# add netParams variables
for k, v in self.params.__dict__.items():
if isinstance(v, Number):
dictVars[k] = v
# for each parameter containing a function, calculate lambda function and arguments
from netpyne.specs.utils import generateStringFunction
for paramStrFunc in paramsStrFunc:
strFunc = connParam[paramStrFunc] # string containing function
lambdaFunc, strVars = generateStringFunction(strFunc, list(dictVars.keys()))
if paramStrFunc in ['probability']:
# replace function with dict of values derived from function (one per pre+post cell)
connParam[paramStrFunc + 'Func'] = {
(preGid, postGid): lambdaFunc(
**{
strVar: dictVars[strVar]
if isinstance(dictVars[strVar], Number)
else dictVars[strVar](preCellTags, postCellTags)
for strVar in strVars
}
)
for preGid, preCellTags in preCellsTags.items()
for postGid, postCellTags in postCellsTags.items()
}
elif paramStrFunc in ['convergence']:
# replace function with dict of values derived from function (one per post cell)
connParam[paramStrFunc + 'Func'] = {
postGid: lambdaFunc(
**{
strVar: dictVars[strVar]
if isinstance(dictVars[strVar], Number)
else dictVars[strVar](None, postCellTags)
for strVar in strVars
}
)
for postGid, postCellTags in postCellsTags.items()
}
elif paramStrFunc in ['divergence']:
# replace function with dict of values derived from function (one per post cell)
connParam[paramStrFunc + 'Func'] = {
preGid: lambdaFunc(
**{
strVar: dictVars[strVar]
if isinstance(dictVars[strVar], Number)
else dictVars[strVar](preCellTags, None)
for strVar in strVars
}
)
for preGid, preCellTags in preCellsTags.items()
}
else:
# store lambda function and func vars in connParam (for weight, delay and synsPerConn since only calculated for certain conns)
connParam[paramStrFunc + 'Func'] = lambdaFunc
connParam[paramStrFunc + 'FuncVars'] = {strVar: dictVars[strVar] for strVar in strVars}
# -----------------------------------------------------------------------------
# Disynaptic bias for probability
# -----------------------------------------------------------------------------
def _disynapticBiasProb(self, origProbability, bias, prePreGids, postPreGids, disynCounter, maxImbalance=10):
probability = float(origProbability)
factor = bias / origProbability
# bias = min(bias, origProbability) # don't modify more than orig, so can compensate
if not set(prePreGids).isdisjoint(postPreGids) and disynCounter < maxImbalance:
probability = min(origProbability + bias, 1.0)
disynCounter += factor # 1
elif disynCounter > -maxImbalance:
probability = max(origProbability - (min(origProbability + bias, 1.0) - origProbability), 0.0)
disynCounter -= 1
# print disynCounter, origProbability, probability
return probability, disynCounter
# -----------------------------------------------------------------------------
# Disynaptic bias for probability (version 2)
# bis = min fraction of conns that will be disynaptic
# -----------------------------------------------------------------------------
def _disynapticBiasProb2(self, probMatrix, allRands, bias, prePreGids, postPreGids):
connGids = []
# calculate which conns are disyn vs
disynMatrix = {
(preGid, postGid): not set(prePreGids[preGid]).isdisjoint(postPreGids[postGid])
for preGid, postGid in list(probMatrix.keys())
}
# calculate which conns are going to be created
connCreate = {
(preGid, postGid): probMatrix[(preGid, postGid)] >= allRands[(preGid, postGid)]
for preGid, postGid in list(probMatrix.keys())
}
numConns = len([c for c in list(connCreate.values()) if c])
# change % bias of conns from non-disyn to disyn (start with low, high probs respectively)
disynConn, nonDisynConn, disynNotConn = [], [], []
for (pre, post), disyn in disynMatrix.items():
if disyn and connCreate[(pre, post)]:
disynConn.append((pre, post))
elif not disyn and connCreate[(pre, post)]:
nonDisynConn.append((pre, post))
elif disyn and not connCreate[(pre, post)]:
disynNotConn.append((pre, post))
disynNumNew = int(float(bias) * numConns)
disynAdd = disynNumNew - len(disynConn)
if disynAdd > 0:
# sort by low/high probs
nonDisynConn = sorted(nonDisynConn, key=lambda k: probMatrix[k])
disynNotConn = sorted(disynNotConn, key=lambda k: probMatrix[k], reverse=True)
# replaced nonDisynConn with disynNotConn
for i in range(min(disynAdd, nonDisynConn, disynNotConn)):
connCreate[nonDisynConn[i]] = False
connCreate[disynNotConn[i]] = True
connGids = [(pre, post) for (pre, post), c in connCreate.items() if c]
return connGids
# -----------------------------------------------------------------------------
# Full connectivity
# -----------------------------------------------------------------------------
[docs]
def fullConn(self, preCellsTags, postCellsTags, connParam):
"""
Function for/to <short description of `netpyne.network.conn.fullConn`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
preCellsTags : <type>
<Short description of preCellsTags>
**Default:** *required*
postCellsTags : <type>
<Short description of postCellsTags>
**Default:** *required*
connParam : <type>
<Short description of connParam>
**Default:** *required*
"""
from .. import sim
if sim.cfg.verbose:
print('Generating set of all-to-all connections (rule: %s) ...' % (connParam['label']))
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar:
pbar = tqdm(total=len(postCellsTags.items()), ascii=True,
desc=' ' + connParam['label'], position=0, leave=True,
bar_format= '{l_bar}{bar}| Creating synaptic connections for {n_fmt}/{total_fmt} postsynaptic cells on node %i (all-to-all connectivity)' % sim.rank)
# get list of params that have a lambda function
paramsStrFunc = [param for param in [p + 'Func' for p in self.connStringFuncParams] if param in connParam]
for paramStrFunc in paramsStrFunc:
# replace lambda function (with args as dict of lambda funcs) with list of values
connParam[paramStrFunc[:-4] + 'List'] = {
(preGid, postGid): connParam[paramStrFunc](
**{
k: v if isinstance(v, Number) else v(preCellTags, postCellTags)
for k, v in connParam[paramStrFunc + 'Vars'].items()
}
)
for preGid, preCellTags in preCellsTags.items()
for postGid, postCellTags in postCellsTags.items()
}
for postCellGid in postCellsTags: # for each postsyn cell
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1)
if postCellGid in self.gid2lid: # check if postsyn is in this node's list of gids
for preCellGid, preCellTags in preCellsTags.items(): # for each presyn cell
self._addCellConn(connParam, preCellGid, postCellGid, preCellsTags) # add connection
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.close()
# -----------------------------------------------------------------------------
# Generate random values for all pre and post cells (to use in prob conn)
# -----------------------------------------------------------------------------
[docs]
def generateRandsPrePost(self, pre, post):
"""
Function for/to <short description of `netpyne.network.conn.generateRandsPrePost`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
pre : <type>
<Short description of pre>
**Default:** *required*
post : <type>
<Short description of post>
**Default:** *required*
"""
from .. import sim
sortedPre = sorted(pre)
sortedPost = sorted(post)
# initialize randomizer using unique hash of pre and post gids and global conn seed
self.rand.Random123(sim.hashList(sortedPre), sim.hashList(sortedPost), sim.cfg.seeds['conn']) #
# obtain rand value for pre,post pairs
lenPre = len(pre)
lenPost = len(post)
vec = sim.h.Vector(lenPre * lenPost) # create Vector
self.rand.uniform(0, 1) # set unfiform distribution
vecList = list(vec.setrand(self.rand)) # fill in vector
allRands = {
(preGid, postGid): vecList[(ipre * lenPost) + ipost]
for ipre, preGid in enumerate(sortedPre)
for ipost, postGid in enumerate(sortedPost)
} # convert to dict
return allRands
# -----------------------------------------------------------------------------
# Probabilistic connectivity
# -----------------------------------------------------------------------------
[docs]
def probConn(self, preCellsTags, postCellsTags, connParam):
"""
Function for/to <short description of `netpyne.network.conn.probConn`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
preCellsTags : <type>
<Short description of preCellsTags>
**Default:** *required*
postCellsTags : <type>
<Short description of postCellsTags>
**Default:** *required*
connParam : <type>
<Short description of connParam>
**Default:** *required*
"""
from .. import sim
if sim.cfg.verbose:
print('Generating set of probabilistic connections (rule: %s) ...' % (connParam['label']))
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar:
pbar = tqdm(total=len(postCellsTags.items()), ascii=True,
desc=' ' + str(connParam['label']), position=0, leave=(sim.cfg.progressBar == 2),
bar_format= '{l_bar}{bar}| Creating synaptic connections for {n_fmt}/{total_fmt} postsynaptic cells on node %i (probabilistic connectivity)' % sim.rank)
allRands = self.generateRandsPrePost(preCellsTags, postCellsTags)
# get list of params that have a lambda function
paramsStrFunc = [param for param in [p + 'Func' for p in self.connStringFuncParams] if param in connParam]
# copy the vars into args immediately and work out which keys are associated with lambda functions only once per method
funcKeys = {}
for paramStrFunc in paramsStrFunc:
connParam[paramStrFunc + 'Args'] = connParam[paramStrFunc + 'Vars'].copy()
funcKeys[paramStrFunc] = [
key for key in connParam[paramStrFunc + 'Vars'] if callable(connParam[paramStrFunc + 'Vars'][key])
]
# probabilistic connections with disynapticBias (deprecated)
if isinstance(connParam.get('disynapticBias', None), Number):
allPreGids = sim._gatherAllCellConnPreGids()
prePreGids = {gid: allPreGids[gid] for gid in preCellsTags}
postPreGids = {gid: allPreGids[gid] for gid in postCellsTags}
probMatrix = {
(preCellGid, postCellGid): connParam['probabilityFunc'][preCellGid, postCellGid]
if 'probabilityFunc' in connParam
else connParam['probability']
for postCellGid, postCellTags in postCellsTags.items() # for each postsyn cell
for preCellGid, preCellTags in preCellsTags.items() # for each presyn cell
if postCellGid in self.gid2lid
} # check if postsyn is in this node
connGids = self._disynapticBiasProb2(
probMatrix, allRands, connParam['disynapticBias'], prePreGids, postPreGids
)
for preCellGid, postCellGid in connGids:
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1)
for paramStrFunc in paramsStrFunc: # call lambda functions to get weight func args
connParam[paramStrFunc + 'Args'] = {
k: v if isinstance(v, Number) else v(preCellsTags[preCellGid], postCellsTags[postCellGid])
for k, v in connParam[paramStrFunc + 'Vars'].items()
}
self._addCellConn(connParam, preCellGid, postCellGid, preCellsTags) # add connection
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.close()
# standard probabilistic conenctions
else:
# print('rank %d'%(sim.rank))
# print(connParam)
# calculate the conn preGids of the each pre and post cell
# for postCellGid,postCellTags in sorted(postCellsTags.items()): # for each postsyn cell
for postCellGid, postCellTags in postCellsTags.items(): # for each postsyn cell # for each postsyn cell
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1)
if postCellGid in self.gid2lid: # check if postsyn is in this node
for preCellGid, preCellTags in preCellsTags.items(): # for each presyn cell
probability = (
connParam['probabilityFunc'][preCellGid, postCellGid]
if 'probabilityFunc' in connParam
else connParam['probability']
)
if probability >= allRands[preCellGid, postCellGid]:
for paramStrFunc in paramsStrFunc: # call lambda functions to get weight func args
# update the relevant FuncArgs dict where lambda functions are known to exist in the corresponding FuncVars dict
for funcKey in funcKeys[paramStrFunc]:
connParam[paramStrFunc + 'Args'][funcKey] = connParam[paramStrFunc + 'Vars'][funcKey](
preCellTags, postCellTags
)
# connParam[paramStrFunc+'Args'] = {k:v if isinstance(v, Number) else v(preCellTags,postCellTags) for k,v in connParam[paramStrFunc+'Vars'].items()}
self._addCellConn(connParam, preCellGid, postCellGid, preCellsTags) # add connection
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.close()
# -----------------------------------------------------------------------------
# Generate random integers
# -----------------------------------------------------------------------------
[docs]
def randInt(r, N, vmin, vmax, unique=False):
if unique:
return randUniqueInt(r, N, vmin, vmax)
else:
from neuron import h
r.discunif(vmin, vmax)
v = h.Vector(N)
v.setrand(r)
return np.array(v, dtype=int)
[docs]
def randUniqueInt(r, N, vmin, vmax):
"""
Function for/to <short description of `netpyne.network.conn.randUniqueInt`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
r : <type>
<Short description of r>
**Default:** *required*
N : <type>
<Short description of N>
**Default:** *required*
vmin : <type>
<Short description of vmin>
**Default:** *required*
vmax : <type>
<Short description of vmax>
**Default:** *required*
"""
assert N <= (vmax - vmin) + 1, \
f"Error in randUniqueInt: impossible to generate {N} unique integers from [{vmin}, {vmax}]"
r.discunif(vmin, vmax)
out = []
while len(out) < N:
x = int(r.repick())
if x not in out:
out.append(x)
return out
# -----------------------------------------------------------------------------
# Convergent connectivity
# -----------------------------------------------------------------------------
[docs]
def convConn(self, preCellsTags, postCellsTags, connParam):
"""
Function for/to <short description of `netpyne.network.conn.convConn`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
preCellsTags : <type>
<Short description of preCellsTags>
**Default:** *required*
postCellsTags : <type>
<Short description of postCellsTags>
**Default:** *required*
connParam : <type>
<Short description of connParam>
**Default:** *required*
"""
from .. import sim
if sim.cfg.verbose:
print('Generating set of convergent connections (rule: %s) ...' % (connParam['label']))
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar:
pbar = tqdm(total=len(postCellsTags.items()), ascii=True,
desc=' ' + connParam['label'], position=0, leave=(sim.cfg.progressBar == 2),
bar_format= '{l_bar}{bar}| Creating synaptic connections for {n_fmt}/{total_fmt} postsynaptic cells on node %i (convergent connectivity)' % sim.rank)
# get list of params that have a lambda function
paramsStrFunc = [param for param in [p + 'Func' for p in self.connStringFuncParams] if param in connParam]
# copy the vars into args immediately and work out which keys are associated with lambda functions only once per method
funcKeys = {}
for paramStrFunc in paramsStrFunc:
connParam[paramStrFunc + 'Args'] = connParam[paramStrFunc + 'Vars'].copy()
funcKeys[paramStrFunc] = [
key for key in connParam[paramStrFunc + 'Vars'] if callable(connParam[paramStrFunc + 'Vars'][key])
]
# converted to list only once
preCellsTagsKeys = sorted(preCellsTags)
# calculate hash for post cell gids
hashPreCells = sim.hashList(preCellsTagsKeys)
for postCellGid, postCellTags in postCellsTags.items(): # for each postsyn cell
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1)
if postCellGid in self.gid2lid: # check if postsyn is in this node
convergence = (
connParam['convergenceFunc'][postCellGid]
if 'convergenceFunc' in connParam
else connParam['convergence']
) # num of presyn conns / postsyn cell
convergence = max(min(int(round(convergence)), len(preCellsTags) - 1), 0)
self.rand.Random123(hashPreCells, postCellGid, sim.cfg.seeds['conn']) # init randomizer
randSample = randUniqueInt(self.rand, convergence + 1, 0, len(preCellsTags) - 1)
# note: randSample[divergence] is an extra value used only if one of the random postGids coincided with the preGid
preCellsSample = {
preCellsTagsKeys[randSample[convergence]]
if preCellsTagsKeys[i] == postCellGid
else preCellsTagsKeys[i]: 0
for i in randSample[0:convergence]
} # dict of selected gids of postsyn cells with removed post gid
preCellsConv = {
k: v for k, v in preCellsTags.items() if k in preCellsSample
} # dict of selected presyn cells tags
for preCellGid, preCellTags in preCellsConv.items(): # for each presyn cell
for paramStrFunc in paramsStrFunc: # call lambda functions to get weight func args
# update the relevant FuncArgs dict where lambda functions are known to exist in the corresponding FuncVars dict
for funcKey in funcKeys[paramStrFunc]:
connParam[paramStrFunc + 'Args'][funcKey] = connParam[paramStrFunc + 'Vars'][funcKey](
preCellTags, postCellTags
)
if preCellGid != postCellGid: # if not self-connection
self._addCellConn(connParam, preCellGid, postCellGid, preCellsTags) # add connection
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.close()
# -----------------------------------------------------------------------------
# Divergent connectivity
# -----------------------------------------------------------------------------
[docs]
def divConn(self, preCellsTags, postCellsTags, connParam):
"""
Function for/to <short description of `netpyne.network.conn.divConn`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
preCellsTags : <type>
<Short description of preCellsTags>
**Default:** *required*
postCellsTags : <type>
<Short description of postCellsTags>
**Default:** *required*
connParam : <type>
<Short description of connParam>
**Default:** *required*
"""
from .. import sim
if sim.cfg.verbose:
print('Generating set of divergent connections (rule: %s) ...' % (connParam['label']))
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar:
pbar = tqdm(total=len(preCellsTags.items()), ascii=True,
desc=' ' + connParam['label'], position=0, leave=(sim.cfg.progressBar == 2),
bar_format= '{l_bar}{bar}| Creating synaptic connections for {n_fmt}/{total_fmt} presynaptic cells on node %i (divergent connectivity)' % sim.rank)
# get list of params that have a lambda function
paramsStrFunc = [param for param in [p + 'Func' for p in self.connStringFuncParams] if param in connParam]
# copy the vars into args immediately and work out which keys are associated with lambda functions only once per method
funcKeys = {}
for paramStrFunc in paramsStrFunc:
connParam[paramStrFunc + 'Args'] = connParam[paramStrFunc + 'Vars'].copy()
funcKeys[paramStrFunc] = [
key for key in connParam[paramStrFunc + 'Vars'] if callable(connParam[paramStrFunc + 'Vars'][key])
]
# converted to list only once
postCellsTagsKeys = sorted(postCellsTags)
# calculate hash for post cell gids
hashPostCells = sim.hashList(postCellsTagsKeys)
for preCellGid, preCellTags in preCellsTags.items(): # for each presyn cell
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1)
divergence = (
connParam['divergenceFunc'][preCellGid] if 'divergenceFunc' in connParam else connParam['divergence']
) # num of presyn conns / postsyn cell
divergence = max(min(int(round(divergence)), len(postCellsTags) - 1), 0)
self.rand.Random123(hashPostCells, preCellGid, sim.cfg.seeds['conn']) # init randomizer
randSample = randUniqueInt(self.rand, divergence + 1, 0, len(postCellsTags) - 1)
# note: randSample[divergence] is an extra value used only if one of the random postGids coincided with the preGid
postCellsSample = {
postCellsTagsKeys[randSample[divergence]]
if postCellsTagsKeys[i] == preCellGid
else postCellsTagsKeys[i]: 0
for i in randSample[0:divergence]
} # dict of selected gids of postsyn cells with removed pre gid
for postCellGid in [c for c in postCellsSample if c in self.gid2lid]:
postCellTags = postCellsTags[postCellGid]
for paramStrFunc in paramsStrFunc: # call lambda functions to get weight func args
# update the relevant FuncArgs dict where lambda functions are known to exist in the corresponding FuncVars dict
for funcKey in funcKeys[paramStrFunc]:
connParam[paramStrFunc + 'Args'][funcKey] = connParam[paramStrFunc + 'Vars'][funcKey](
preCellTags, postCellTags
)
if preCellGid != postCellGid: # if not self-connection
self._addCellConn(connParam, preCellGid, postCellGid, preCellsTags) # add connection
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.close()
# -----------------------------------------------------------------------------
# From list connectivity
# -----------------------------------------------------------------------------
[docs]
def fromListConn(self, preCellsTags, postCellsTags, connParam):
"""
Function for/to <short description of `netpyne.network.conn.fromListConn`>
Parameters
----------
self : <type>
<Short description of self>
**Default:** *required*
preCellsTags : <type>
<Short description of preCellsTags>
**Default:** *required*
postCellsTags : <type>
<Short description of postCellsTags>
**Default:** *required*
connParam : <type>
<Short description of connParam>
**Default:** *required*
"""
from .. import sim
if sim.cfg.verbose:
print('Generating set of connections from list (rule: %s) ...' % (connParam['label']))
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar:
pbar = tqdm(total=len(connParam['connList']), ascii=True,
desc=' ' + connParam['label'], position=0, leave=(sim.cfg.progressBar == 2),
bar_format= '{l_bar}{bar}| Creating synaptic connections for {n_fmt}/{total_fmt} pairs of neurons on node %i (from list)' % sim.rank)
orderedPreGids = sorted(preCellsTags)
orderedPostGids = sorted(postCellsTags)
# list of params that can have a lambda function
paramsStrFunc = [param for param in [p + 'Func' for p in self.connStringFuncParams] if param in connParam]
for paramStrFunc in paramsStrFunc:
# replace lambda function (with args as dict of lambda funcs) with list of values
connParam[paramStrFunc[:-4] + 'List'] = {
(orderedPreGids[preId], orderedPostGids[postId]): connParam[paramStrFunc](
**{
k: v
if isinstance(v, Number)
else v(preCellsTags[orderedPreGids[preId]], postCellsTags[orderedPostGids[postId]])
for k, v in connParam[paramStrFunc + 'Vars'].items()
}
)
for preId, postId in connParam['connList']
}
numConns = len(connParam['connList'])
weights, delays, secs, locs = [], [], [], []
preLocs, preSecs = [], [] # for pointer connections (e.g. gap junctions) only
for paramName, paramValList in zip(['weight', 'delay', 'sec', 'loc', 'preLocs', 'preSecs'],
[weights, delays, secs, locs, preLocs, preSecs]):
paramVal = connParam.get(paramName)
if not paramVal:
continue
if type(paramVal) in (list, tuple):
_ensure(len(paramVal) == numConns, connParam['label'], f"'{paramName}' has to be of the exact same length as 'connList'. Aborting..")
paramValList.extend(paramVal)
else:
paramValList.append(paramVal)
connList = connParam.pop('connList')
for iconn, (relativePreId, relativePostId) in enumerate(connList): # for each postsyn cell
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.update(1)
_ensure(relativePreId < len(orderedPreGids), connParam['label'], f"cell index in pre population in connection {iconn} is out of range. Aborting..")
_ensure(relativePostId < len(orderedPostGids), connParam['label'], f"cell index in post population in connection {iconn} is out of range. Aborting..")
preCellGid = orderedPreGids[relativePreId]
postCellGid = orderedPostGids[relativePostId]
if postCellGid in self.gid2lid: # check if postsyn is in this node's list of gids
def setConnValFromList(lst, iConn, connKey):
if not lst: return # this param not specified, skip it to use default value later
if len(lst) == 1: # this means the same value is used for all connections
connParam[connKey] = lst[0]
else:
connParam[connKey] = lst[iConn]
setConnValFromList(weights, iconn, 'weight')
setConnValFromList(delays, iconn, 'delay')
setConnValFromList(locs, iconn, 'loc')
setConnValFromList(secs, iconn, 'sec')
setConnValFromList(preLocs, iconn, 'preLoc')
setConnValFromList(preSecs, iconn, 'preSec')
# TODO: consider cfg.allowSelfConns?
if preCellGid != postCellGid: # if not self-connection
self._addCellConn(connParam, preCellGid, postCellGid, preCellsTags) # add connection
if sim.rank == 0 and not sim.cfg.verbose and sim.cfg.progressBar: pbar.close()
# -----------------------------------------------------------------------------
# Set parameters and create connection
# -----------------------------------------------------------------------------
def _addCellConn(self, connParam, preCellGid, postCellGid, preCellsTags={}):
from .. import sim
from ..specs.netParams import SynMechParams
# set final param values
paramStrFunc = self.connStringFuncParams # [weight, delay, synsPerConn, loc]
finalParam = {}
# Set final parameter values; initialize randomizer for string-based funcs that use rand to ensue replicability
# Note: could potentially speed up by generating list of values for all rand funcs used (e.g. rand.uniform())
# and then selecting a value from list based of pre- and post- gid -- that way only seed once at beginning in connectCells()
# Howeve, not clear if faster in all cases since need to generate values for len(pre)*len(post), whereas here only a subset
randSeeded = False
for param in paramStrFunc:
if param + 'List' in connParam:
finalParam[param] = connParam[param + 'List'][preCellGid, postCellGid]
elif param + 'Func' in connParam:
if not randSeeded and 'rand' in connParam[param + 'FuncArgs']:
self.rand.Random123(preCellGid, postCellGid, sim.cfg.seeds['conn'])
randSeeded = True
finalParam[param] = connParam[param + 'Func'](**connParam[param + 'FuncArgs'])
else:
finalParam[param] = connParam.get(param)
# get Cell object
postCell = self.cells[self.gid2lid[postCellGid]]
# convert synMech param to list (if not already)
if not isinstance(connParam.get('synMech'), list):
connParam['synMech'] = [connParam.get('synMech')]
synMechs = connParam['synMech']
numSynMechs = len(synMechs)
for i, synMech in enumerate(synMechs):
# weight, delay and loc (and also sec - if corresp. flag is set) are either single value or list of values. If single value, use it for all synMechs. If it is a list, use the value at index i
if connParam.get('distinctSecsPerSynMech', False):
paramNames = ['weight', 'delay', 'loc', 'synsPerConn', 'sec']
else:
paramNames = ['weight', 'delay', 'loc', 'synsPerConn']
# keep sec as is to be handled later in CompartCell.addConn()
sec = connParam.get('sec')
for param in paramNames:
if numSynMechs == 1:
finalParamVal = finalParam.get(param)
else:
if isinstance(finalParam.get(param), list): # get weight/delay/loc from list for each synMech
_ensure(len(finalParam[param]) == numSynMechs, connParam['label'], f"{param} should be {numSynMechs}-element list or a single value")
finalParamVal = finalParam[param][i]
elif (f'synMech{param}Factor' in connParam) and (param is not 'sec'): # adapt weight/delay/loc for each synMech
factors = connParam[f'synMech{param}Factor']
_ensure(len(factors) == numSynMechs, connParam['label'], f"{f'synMech{param}Factor'} should be {numSynMechs}-element list")
_ensure((type(finalParam[param]) in int, float), connParam['label'], f"{params} should be list of numbers")
finalParamVal = finalParam[param] * factors[i]
else:
finalParamVal = finalParam.get(param)
finalParam[param + 'SynMech'] = finalParamVal
params = {
'preGid': preCellGid,
'sec': sec, # TODO: will not work with `distinctSecsPerSynMech`?
'loc': finalParam['locSynMech'],
'synMech': synMech,
'weight': finalParam['weightSynMech'],
'delay': finalParam['delaySynMech'],
'synsPerConn': finalParam['synsPerConnSynMech'],
}
# if 'threshold' in connParam: params['threshold'] = connParam.get('threshold') # deprecated, use threshold in preSyn cell sec
if 'shape' in connParam:
params['shape'] = connParam.get('shape')
if 'plast' in connParam:
params['plast'] = connParam.get('plast')
if 'weightIndex' in connParam:
params['weightIndex'] = connParam.get('weightIndex')
if 'distributeSynsUniformly' in connParam:
params['distributeSynsUniformly'] = connParam['distributeSynsUniformly']
if 'connRandomSecFromList' in connParam:
params['connRandomSecFromList'] = connParam['connRandomSecFromList']
isGapJunction = 'gapJunction' in connParam # deprecated way of defining gap junction
if self.params.synMechParams.isPointerConn(params['synMech']) or isGapJunction:
params['preLoc'] = connParam.get('preLoc')
params['preSec'] = connParam.get('preSec', 'soma')
if isGapJunction:
params['gapJunction'] = connParam['gapJunction']
# TODO: synMech can be None here (meaning 'use default'). Then need to use default label while checking below
elif SynMechParams.stringFuncsReferPreLoc(synMech):
# save synapse pre-cell location to be used in stringFunc for synMech.
# for optimization purpose, do it only if preLoc is referenced for a given synMech
cellType = preCellsTags[preCellGid].get('cellType')
if cellType:
from ..cell import CompartCell
secs = self.params.cellParams[cellType].secs
loc, _ = CompartCell.spikeGenLocAndSec(secs)
params['preLoc'] = loc
if sim.cfg.includeParamsLabel:
params['label'] = connParam.get('label')
postCell.addConn(params=params)
def _ensure(condition, connLabel, message):
connLabelStr = f'[{connLabel}]' if connLabel else ''
assert condition, f" Error in connParams{connLabelStr}: {message}"