Source code for netpyne.network.subconn

"""
Module for distributing synapses at the subcellular level in networks

"""

import numpy as np
from neuron import h

# -----------------------------------------------------------------------------
# Calculate distance between 2 segments
# -----------------------------------------------------------------------------
[docs] def pathDistance(from_segment, to_segment): """ Compute the path distance between two points on a neuron based on h.distance() Parameters ---------- from_segment : <type> <Short description of origin_segment> **Default:** *required* to_segment : <type> <Short description of to_segment> **Default:** *required* """ return h.distance(from_segment, to_segment)
# ----------------------------------------------------------------------------- # Calculate position from segment location # -----------------------------------------------------------------------------
[docs] def posFromLoc(sec, x): """ Compute absolute 3D position of given section at location x Parameters ---------- sec : dict <Short description of origin_segment> **Default:** *required* x : float <Short description of to_segment> **Default:** *required* """ secObj = sec['hObj'] secObj.push() s = x * secObj.L numpts = int(h.n3d()) b = -1 for ii in range(numpts): if h.arc3d(ii) >= s: b = ii break if b == -1: print("an error occurred in pointFromLoc, SOMETHING IS NOT RIGHT") if h.arc3d(b) == s: # shortcut x, y, z = h.x3d(b), h.y3d(b), h.z3d(b) else: # need to interpolate a = b - 1 t = (s - h.arc3d(a)) / (h.arc3d(b) - h.arc3d(a)) x = h.x3d(a) + t * (h.x3d(b) - h.x3d(a)) y = h.y3d(a) + t * (h.y3d(b) - h.y3d(a)) z = h.z3d(a) + t * (h.z3d(b) - h.z3d(a)) h.pop_section() return x, y, z
# ----------------------------------------------------------------------------- # Calculate syn density for each segment from grid # ----------------------------------------------------------------------------- def _interpolateSegmentSigma(self, cell, secList, gridX, gridY, gridSigma): segNumSyn = {} # for secName in secList: sec = cell.secs[secName] segNumSyn[secName] = [] for seg in sec['hObj']: x, y, z = posFromLoc(sec, seg.x) if gridX and gridY: # 2D distX = [abs(gx - x) for gx in gridX] distY = [abs(gy - y) for gy in gridY] ixs = np.array(distX).argsort()[:2] jys = np.array(distY).argsort()[:2] i1, i2, j1, j2 = min(ixs), max(ixs), min(jys), max(jys) x1, x2, y1, y2 = gridX[i1], gridX[i2], gridY[j1], gridY[j2] sigma_x1_y1 = gridSigma[i1][j1] sigma_x1_y2 = gridSigma[i1][j2] sigma_x2_y1 = gridSigma[i2][j1] sigma_x2_y2 = gridSigma[i2][j2] if x1 == x2 or y1 == y2: print("ERROR in closest grid points: ", secName, x1, x2, y1, y2) else: # bilinear interpolation, see http://en.wikipedia.org/wiki/Bilinear_interpolation (fixed bug from Ben Suter's code) sigma = ( sigma_x1_y1 * (x2 - x) * (y2 - y) + sigma_x2_y1 * (x - x1) * (y2 - y) + sigma_x1_y2 * (x2 - x) * (y - y1) + sigma_x2_y2 * (x - x1) * (y - y1) ) / ((x2 - x1) * (y2 - y1)) elif gridY: # 1d = radial distY = [abs(gy - y) for gy in gridY] jys = np.array(distY).argsort()[:2] sigma = np.zeros((1, 2)) j1, j2 = min(jys), max(jys) y1, y2 = gridY[j1], gridY[j2] sigma_y1 = gridSigma[j1] sigma_y2 = gridSigma[j2] if y1 == y2: print("ERROR in closest grid points: ", secName, y1, y2) else: # linear interpolation, see http://en.wikipedia.org/wiki/Bilinear_interpolation sigma = (sigma_y1 * (y2 - y) + sigma_y2 * (y - y1)) / (y2 - y1) numSyn = sigma * sec['hObj'].L / sec['hObj'].nseg # return num syns segNumSyn[secName].append(numSyn) return segNumSyn # ----------------------------------------------------------------------------- # Subcellular connectivity (distribution of synapses) # -----------------------------------------------------------------------------
[docs] def subcellularConn(self, allCellTags, allPopTags): """ Function for/to <short description of `netpyne.network.subconn.subcellularConn`> Parameters ---------- self : <type> <Short description of self> **Default:** *required* allCellTags : <type> <Short description of allCellTags> **Default:** *required* allPopTags : <type> <Short description of allPopTags> **Default:** *required* """ from .. import sim sim.timing('start', 'subConnectTime') print(' Distributing synapses based on subcellular connectivity rules...') for subConnParamTemp in list(self.params.subConnParams.values()): # for each conn rule or parameter set subConnParam = subConnParamTemp.copy() # find list of pre and post cell preCellsTags, postCellsTags = self._findPrePostCellsCondition( allCellTags, subConnParam['preConds'], subConnParam['postConds'] ) if preCellsTags and postCellsTags: # iterate over postsyn cells to redistribute synapses for postCellGid in postCellsTags: # for each postsyn cell if postCellGid in self.gid2lid: postCell = self.cells[self.gid2lid[postCellGid]] allConns = [conn for conn in postCell.conns if conn['preGid'] in preCellsTags] if 'NetStim' in [ x['cellModel'] for x in list(preCellsTags.values()) if 'cellModel' in x.keys() ]: # temporary fix to include netstim conns allConns.extend([conn for conn in postCell.conns if conn['preGid'] == 'NetStim']) # group synMechs so they are not distributed separately if 'groupSynMechs' in subConnParam and len(subConnParam['groupSynMechs']) > 1: conns = [] connsGroup = {} # iConn = -1 for conn in allConns: if not conn['synMech'].startswith('__grouped__'): conns.append(conn) # iConn = iConn + 1 connGroupLabel = '%d_%s_%.4f' % (conn['preGid'], conn['sec'], conn['loc']) if conn['synMech'] in subConnParam['groupSynMechs']: for synMech in [s for s in subConnParam['groupSynMechs'] if s != conn['synMech']]: connGroup = next( ( c for c in allConns if c['synMech'] == synMech and c['sec'] == conn['sec'] and c['loc'] == conn['loc'] ), None, ) try: connGroup['synMech'] = '__grouped__' + connGroup['synMech'] connsGroup[connGroupLabel] = connGroup except: print(' Warning: Grouped synMechs %s not found' % (str(connGroup))) else: conns = allConns # sort conns so reproducible across different number of cores # use sec+preGid to avoid artificial distribution based on preGid (e.g. low gids = close to soma) conns = sorted(conns, key=lambda v: v['sec'] + str(v['loc']) + str(v['preGid'])) # set sections to be used secList = postCell._setConnSections(subConnParam) # Uniform distribution if subConnParam.get('density', None) == 'uniform': # calculate new syn positions newSecs, newLocs = postCell._distributeSynsUniformly(secList=secList, numSyns=len(conns)) # 2D map and 1D map (radial) elif isinstance(subConnParam.get('density', None), dict) and subConnParam['density']['type'] in [ '2Dmap', '1Dmap', ]: gridY = subConnParam['density']['gridY'] gridSigma = subConnParam['density']['gridValues'] #TODO jchen.6727@gmail.com 241247 'soma' is not always the name of the soma section try: somaX, somaY, _ = posFromLoc(postCell.secs['soma'], 0.5) # get cell pos move method to Cell! except KeyError: raise KeyError( "Error in subcellularConn: there is no section named soma in cell {}".format(postCell.gid) ) if 'fixedSomaY' in subConnParam['density']: # is fixed cell soma y, adjust y grid accordingly fixedSomaY = subConnParam['density'].get('fixedSomaY') gridY = [ y + (somaY - fixedSomaY) for y in gridY ] # adjust grid so cell soma is at fixedSomaY if subConnParam['density']['type'] == '2Dmap': # 2D gridX = [x - somaX for x in subConnParam['density']['gridX']] # center x at cell soma segNumSyn = self._interpolateSegmentSigma( postCell, secList, gridX, gridY, gridSigma ) # move method to Cell! elif subConnParam['density']['type'] == '1Dmap': # 1D segNumSyn = self._interpolateSegmentSigma( postCell, secList, None, gridY, gridSigma ) # move method to Cell! totSyn = sum([sum(nsyn) for nsyn in list(segNumSyn.values())]) # summed density scaleNumSyn = float(len(conns)) / float(totSyn) if totSyn > 0 else 0.0 diffList = [] for sec in segNumSyn: for seg, x in enumerate(segNumSyn[sec]): orig = float(x * scaleNumSyn) scaled = int(round(x * scaleNumSyn)) segNumSyn[sec][seg] = scaled diff = orig - scaled if diff > 0: diffList.append([diff, sec, seg]) totSynRescale = sum([sum(nsyn) for nsyn in list(segNumSyn.values())]) # if missing syns due to rescaling to 0, find top values which were rounded to 0 and make 1 if totSynRescale < len(conns): extraSyns = len(conns) - totSynRescale diffList = sorted(diffList, key=lambda l: l[0], reverse=True) for i in range(min(extraSyns, len(diffList))): sec = diffList[i][1] seg = diffList[i][2] segNumSyn[sec][seg] += 1 # convert to list so can serialize and save subConnParam['density']['gridY'] = list(subConnParam['density']['gridY']) subConnParam['density']['gridValues'] = list(subConnParam['density']['gridValues']) newSecs, newLocs = [], [] for sec, nsyns in segNumSyn.items(): for i, seg in enumerate(postCell.secs[sec]['hObj']): for isyn in range(nsyns[i]): newSecs.append(sec) newLocs.append(seg.x) # Distance-based elif ( isinstance(subConnParam.get('density', None), dict) and subConnParam['density']['type'] == 'distance' ): # find origin section # giving argument if 'ref_sec' in subConnParam['density']: if subConnParam['density']['ref_sec'] in list(postCell.secs.keys()): secOrigName = subConnParam['density']['ref_sec'] else: print( ' Warning: Redistributing synapses based on inexistent information for neuron %d - section %s not found' % (postCell.gid, subConnParam['density']['ref_sec']) ) # default if not secOrigName: secOrigName = postCell.originSecName() # find origin segment segOrig = 0.5 # default if 'ref_seg' in subConnParam['density']: segOrig = subConnParam['density']['ref_seg'] secOrig = postCell.secs[secOrigName] segOrigObj = secOrig['hObj'](segOrig) # target target_distance = 0.0 if 'target_distance' in subConnParam['density']: target_distance = subConnParam['density']['target_distance'] newSec, newLoc = secOrigName, segOrig min_dist = target_distance if 'coord' in subConnParam['density'] and subConnParam['density']['coord'] == 'cartesian': # calculate euclidean distance from reference x0, y0, z0 = posFromLoc(secOrig, segOrigObj.x) for secName in secList: sec = postCell.secs[secName] for seg in sec['hObj']: x, y, z = posFromLoc(sec, seg.x) dist = np.sqrt((x - x0) ** 2 + (y - y0) ** 2 + (z - z0) ** 2) if abs(dist - target_distance) <= min_dist: min_dist = abs(dist - target_distance) newSec, newLoc = secName, seg.x else: # (default) calculate distance based on the topology for secName in secList: sec = postCell.secs[secName] for seg in sec['hObj']: dist = pathDistance(segOrigObj, seg) if abs(dist - target_distance) <= min_dist: min_dist = abs(dist - target_distance) newSec, newLoc = secName, seg.x newSecs = [newSec] * len(conns) newLocs = [newLoc] * len(conns) for i, (conn, newSec, newLoc) in enumerate(zip(conns, newSecs, newLocs)): # get conn group label before updating params connGroupLabel = '%d_%s_%.4f' % (conn['preGid'], conn['sec'], conn['loc']) # update weight if weightNorm present newWeightNorm = None if 'weightNorm' in postCell.secs[conn['sec']] and isinstance( postCell.secs[conn['sec']]['weightNorm'], list ): oldNseg = postCell.secs[conn['sec']]['geom']['nseg'] oldWeightNorm = postCell.secs[conn['sec']]['weightNorm'][ int(round(conn['loc'] * oldNseg)) - 1 ] newNseg = postCell.secs[newSec]['geom']['nseg'] newWeightNorm = ( postCell.secs[newSec]['weightNorm'][int(round(newLoc * newNseg)) - 1] if 'weightNorm' in postCell.secs[newSec] else 1.0 ) conn['weight'] = conn['weight'] / oldWeightNorm * newWeightNorm # avoid locs at 0.0 or 1.0 - triggers hoc error if syn needs an ion (eg. ca_ion) if newLoc == 0.0: newLoc = 0.00001 elif newLoc == 1.0: newLoc = 0.99999 # updade sec and loc conn['sec'] = newSec conn['loc'] = newLoc # find grouped conns if ( subConnParam.get('groupSynMechs', None) and len(subConnParam['groupSynMechs']) > 1 and conn['synMech'] in subConnParam['groupSynMechs'] ): connGroup = connsGroup[connGroupLabel] # get grouped conn from previously stored dict connGroup['synMech'] = connGroup['synMech'].split('__grouped__')[ 1 ] # remove '__grouped__' label connGroup['sec'] = newSec connGroup['loc'] = newLoc if newWeightNorm: connGroup['weight'] = connGroup['weight'] / oldWeightNorm * newWeightNorm sim.pc.barrier()