from __future__ import division as _, print_function as _
import sys as _sys
import os as _os
import numbers as _numbers
from collections import OrderedDict as _OrderedDict
from operator import itemgetter as _itemgetter, eq as _eq
import numpy as _np
import functools
import moose
from subprocess import check_output
def syn_name(synpath,headname):
if headname in synpath:
#try to strip out name of cell from branch name
headpath=moose.element(synpath).parent.path
parentname=moose.element(headpath).parent.name
postbranch=parentname+'/'+moose.element(headpath).name
else:
postbranch=moose.element(synpath).parent.name
return postbranch
def neurontypes(param_cond,override=None):
"Query or set names of neurontypes of each neurons to be created"
if override is None:
return param_cond.neurontypes if param_cond.neurontypes is not None else sorted(param_cond.Condset.keys())
else:
if any(key not in param_cond.Condset.keys() for key in override):
raise ValueError('unknown neuron types requested')
return override
def inclusive_range(start, stop=None, step=None):
if stop is None:
stop = start
if stop == start:
return _np.array([start])
if step is None:
step = stop - start
return _np.arange(start, stop + step/2, step)
def get_dist_name(comp):
name = comp.name
xloc = comp.x
yloc = comp.y
zloc = comp.z
dist = _np.sqrt(xloc*xloc+yloc*yloc+zloc*zloc)
return dist,name
class dist_dependent_cond_equation:
def __init__(self,cmin,cmax,dhalf,slope):
self.cmin = cmin
self.cmax = cmax
self.dhalf = dhalf
self.slope = slope
self.__repr__()
def __call__(self,distance):
return self.cmin+(self.cmax-self.cmin)/(1+_np.exp((distance-self.dhalf)/self.slope))
def __repr__(self):
return '{}+({}-{})/(1+np.exp((distance-{})/{})))'.format(self.cmin,self.cmax,self.cmin,self.dhalf,self.slope)
"""
def dist_dependent_cond_equation(cmin, cmax, dhalf, slope):
'''Returns a function only of distance that specifies a distance
dependent sigmoid equation for channel conductance.
cmin: minimal distance-dependent conductance
cmax: maximal distant-dependent conductance
dhalf: distance from soma where amplitude of sigmoid is one half max-min
slope: slope of sigmoid equation. If positive, somatic conductance is higher; If negative, somatic conductance is smaller
'''
def equation(distance):
return cmin+(cmax-cmin)/(1+_np.exp((distance-dhalf)/slope))
print('{}+({}-{})/(1+np.exp((distance-{})/{})))'.format(cmin,cmax,cmin,dhalf,slope))
return equation
"""
def distance_mapping(mapping, where):
#where is a location, either a compartment or string or moose.vec
if isinstance(where, (moose.Compartment, moose.ZombieCompartment)):
comp=where
elif isinstance(where,moose.vec):
#Needs to be tested. May need to loop over comps in moose.vec
comp = moose.element(where)
elif isinstance(where,str):
try:
comp = moose.element(where)
except ValueError:
print('No element ',where)
return 0
elif isinstance(where, _numbers.Number):
name = ''
dist = where
else:
print('Wrong distance/element passed in distance mapping ',where)
return 0
#calculate distance of compartment from soma
if isinstance(where, (moose.Compartment, moose.ZombieCompartment)):
dist,name = get_dist_name(where)
from collections import OrderedDict as od
ordered_map=od(sorted(mapping.items(),key=lambda x:len(x[0]),reverse=True))
result=None
for k, value in ordered_map.items():
#print('k,v',k,value)
if len(k) == 3:
min_dist, max_dist, description = k
elif len(k) == 2:
min_dist, max_dist = k
description=''
else:
continue
if min_dist <= dist < max_dist:
if description:
#name.startswith allows using swc files with _1 as soma, _2 as apical dend, _3 as basal dend and _4 as axon
if name.startswith(description) or name.endswith(description):
result = value
break
else:
result = value
break
#print('##########', comp.name,'at',dist,'=',result)
if not result:
return 0
if isinstance(result, _numbers.Number):
return result
elif isinstance(result, list):
return result
elif isinstance(result, dict): #Used for calcium buffer and pump dictionaries
return result
#otherwise, calculate distance dependent function.
return result(dist)
try:
from __builtin__ import execfile
except ImportError:
def execfile(fn):
exec(compile(open(fn).read(), fn, 'exec'))
def _itemsetter(index):
def helper(where, value):
where[index] = value
return helper
_class_template = '''\
class {typename}(list):
'{typename}({arg_list})'
__slots__ = ()
def __init__(self, {init_args}):
'Create new instance of {typename}({arg_list})'
return _list.__init__(self, ({arg_list}))
def __repr__(self):
'Return a nicely formatted representation string'
return '{typename}({repr_fmt})' % tuple(self)
{field_defs}
'''
_repr_template = '{name}=%r'
_field_template = '''\
{name} = _property(_itemgetter({index:d}), _itemsetter({index:d}),
doc='Alias for field number {index:d}')
'''
def NamedList(typename, field_names, verbose=False):
"Returns a new subclass of list with named fields."
# Validate the field names.
init_args = field_names.replace(',', ' ').split()
field_names = [name.partition('=')[0] for name in init_args]
if sorted(set(field_names)) != sorted(field_names):
raise ValueError('Duplicate field names')
# Fill-in the class template
class_definition = _class_template.format(
typename = typename,
num_fields = len(field_names),
init_args = ', '.join(init_args),
arg_list = ', '.join(field_names),
repr_fmt = ', '.join(_repr_template.format(name=name)
for name in field_names),
field_defs = '\n'.join(_field_template.format(index=index, name=name)
for index, name in enumerate(field_names))
)
if verbose:
print(class_definition)
# Execute the template string in a temporary namespace and support
# tracing utilities by setting a value for frame.f_globals['__name__']
namespace = dict(_itemgetter=_itemgetter, _itemsetter=_itemsetter,
__name__='NamedList_%s' % typename,
OrderedDict=_OrderedDict, _property=property, _list=list)
exec(class_definition, namespace)
result = namespace[typename]
# For pickling to work, the __module__ variable needs to be set to the frame
# where the named tuple is created. Bypass this step in enviroments where
# sys._getframe is not defined (Jython for example) or sys._getframe is not
# defined for arguments greater than 0 (IronPython).
try:
result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
return result
class NamedDict(dict):
"""Creates a python dict with a name and attribute access of keys.
Usage: mydict = NamedDict(name,**kwargs)
where **kwargs are used to create dictionary key/value pairs.
e.g.: params = NamedDict('modelParams',x=15,y=0)
dict keys can be accessed and written as keys or attributes:
myNamedDict['k'] is equivalent to myNamedDict.k, and
myNamedDict['k'] = newvalue is equivalent to myNamedDict.k=newvalue.
New entries/attributes can be created:
myNamedDict.newkey = newvalue OR myNamedDict['newkey']= newvalue.
Note: Dict ignores attributes beginning with underscore, so
myNamedDict.__name__ returns the NamedDict name, but there is no dict key
== "__name__"
Note: all dict keys must be valid attribute names: that is, strings with
first character in a-z/A-Z. This could be changed to allow all valid python
dict keys as keys, but these keys would not have attribute access.
"""
def __init__(self, name, **kwargs):
super(NamedDict, self).__init__(**kwargs)
self.__dict__ = dict(**kwargs)
self.__name__ = name
def __repr__(self):
items = ('{}={}'.format(k,v) for (k,v) in self.items())
l = len(self.__name__) + 1
sep = ',\n' + ' '*l
return '{}({})'.format(self.__name__, sep.join(items))
def __setitem__(self, k, v):
super(NamedDict, self).__setitem__(k,v)
setattr(self,k,v)
def __getattribute__(self, k):
# attributes have higher priority
try:
return super(NamedDict, self).__getattribute__(k)
except AttributeError:
return super(NamedDict, self).__getitem__(k)
def __setattr__(self, k, v):
super(NamedDict, self).__setattr__(k,v)
if not k.startswith('_'):
super(NamedDict, self).__setitem__(k,v)
def __dir__(self):
dirlist = super(NamedDict, self).__dir__()
return dirlist
def block_if_noninteractive():
if not any([hasattr(_sys, 'ps1'), _sys.flags.interactive]):
print('Simulation finished. Close all windows or press ^C to exit.')
import matplotlib.pyplot as plt
try:
plt.show(block=True)
except KeyboardInterrupt:
pass
def find_file(name, *paths):
if not _os.path.isabs(name):
for path in paths:
p = _os.path.join(path, name)
if _os.path.exists(p):
return p
return name
def find_model_file(model, name):
return find_file(name, _os.path.dirname(model.__file__))
def listize(func):
def wrapper(*args, **kwargs):
return list(func(*args, **kwargs))
return functools.update_wrapper(wrapper, func)
def call_counter(func):
'''Decorator to count number of times a function has been called'''
def wrapper(*args,**kwargs):
try:
wrapper.calls += 1
return func(*args,**kwargs)
except Exception as E: # Fixes count if there's an exception
wrapper.calls += -1
raise E
return func(*args,**kwargs)
wrapper.calls = 0
return functools.update_wrapper(wrapper, func)
def gitlog(model):
'''Log current git commit hash and any uncommitted differences of model
For computational reproducibility, logging the git commit hash plus any
uncomitted changes of currently checked out branch almost allows reproducing
any result.
Need to also know any command line arguments or optional kwargs passed to
model at setup.
'''
enc = _sys.stdout.encoding
modelpath = model.__path__[0]
gitRepoPath = check_output(['git', '-C', modelpath, 'rev-parse',
'--show-toplevel']).decode(enc).rstrip()
gitCurrentCommitHash = check_output(['git', '-C', gitRepoPath, 'rev-parse',
'HEAD']).decode(enc).rstrip()
gitUnstagedDiff = check_output(['git', '-C', gitRepoPath, 'diff-index',
'-p', 'HEAD', '--']).decode(enc)
gitStagedDiff = check_output(['git', '-C', gitRepoPath, 'diff-index', '-p',
'--cached', 'HEAD', '--']).decode(enc)
#print(gitRepoPath, gitCurrentCommitHash, gitUnstagedDiff, gitStagedDiff)
return '\n'.join(['Git Repo Path of model: '+gitRepoPath + '\n',
'Current Git Commit Hash: '+gitCurrentCommitHash + '\n',
'Unstaged Git Differences: \n' + gitUnstagedDiff +'\n',
'Staged Git Differences: \n' + gitStagedDiff])