"""
Mechanism classes
Theses classes represent mechanisms in the model
"""
"""
Copyright (c) 2016-2020, EPFL/Blue Brain Project
This file is part of BluePyOpt <https://github.com/BlueBrain/BluePyOpt>
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License version 3.0 as published
by the Free Software Foundation.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
# pylint: disable=W0511
import logging
from . import base
from . import serializer
logger = logging.getLogger(__name__)
# TODO: use Location class to specify location
[docs]class Mechanism(base.BaseEPhys):
"""Base parameter class"""
pass
[docs]class NrnMODMechanism(Mechanism, serializer.DictMixin):
"""Neuron mechanism"""
SERIALIZED_FIELDS = (
'name',
'comment',
'mod_path',
'suffix',
'locations',
'preloaded',
)
def __init__(
self,
name,
mod_path=None,
suffix=None,
locations=None,
preloaded=True,
deterministic=True,
prefix=None,
comment=''):
"""Constructor
Args:
name (str): name of this object
mod_path (str): path to the MOD file (not used for the moment)
suffix (str): suffix of this mechanism in the MOD file
locations (list of Locations): a list of Location objects pointing
to where this mechanism should be added to.
preloaded (bool): should this mechanism be side-loaded by
BluePyOpt, or was it already loaded and compiled by the user ?
(not used for the moment)
prefix (str): Deprecated. Use suffix instead.
"""
super(NrnMODMechanism, self).__init__(name, comment)
self.mod_path = mod_path
self.suffix = suffix
self.locations = locations
self.preloaded = preloaded
self.cell_model = None
self.deterministic = deterministic
if prefix is not None and suffix is not None:
raise TypeError('NrnMODMechanism: it is not allowed to set both '
'prefix and suffix in constructor: %s %s' %
(self.prefix, self.suffix))
elif prefix is not None:
self.suffix = prefix
[docs] def instantiate(self, sim=None, icell=None):
"""Instantiate"""
for location in self.locations:
isec_list = location.instantiate(sim=sim, icell=icell)
for isec in isec_list:
try:
isec.insert(self.suffix)
except ValueError as e:
raise ValueError(str(e) + ': ' + self.suffix)
self.instantiate_determinism(
self.deterministic,
icell,
isec,
sim)
logger.debug(
'Inserted %s in %s', self.suffix, [
str(location) for location in self.locations])
[docs] def instantiate_determinism(self, deterministic, icell, isec, sim):
"""Instantiate enable/disable determinism"""
if 'Stoch' in self.suffix:
setattr(
isec,
'deterministic_%s' %
(self.suffix),
1 if deterministic else 0)
# Set the seeds even when deterministic,
# that way neuron's psection does not crash
# when encountering a stoch mech var that is not set (e.g. rng)
short_secname = sim.neuron.h.secname(sec=isec).split('.')[-1]
for iseg in isec:
seg_name = '%s.%.19g' % (short_secname, iseg.x)
getattr(sim.neuron.h,
"setdata_%s" % self.suffix)(iseg.x, sec=isec)
seed_id1 = icell.gid
seed_id2 = self.hash_py(seg_name)
getattr(
sim.neuron.h,
"setRNG_%s" % self.suffix)(seed_id1, seed_id2)
else:
if not deterministic:
# can't do this for non-Stoch channels
raise TypeError(
'Deterministic can only be set to False for '
'Stoch channel, not %s' %
self.suffix)
[docs] def destroy(self, sim=None):
"""Destroy mechanism instantiation"""
pass
def __str__(self):
"""String representation"""
return "%s: %s at %s" % (
self.name, self.suffix,
[str(location) for location in self.locations])
[docs] @staticmethod
def hash_hoc(string, sim):
"""Calculate hash value of string in Python"""
# Load hash function in hoc, only do this once
if not hasattr(sim.neuron.h, 'hash_str'):
sim.neuron.h(NrnMODMechanism.hash_hoc_string)
return sim.neuron.h.hash_str(string)
[docs] @staticmethod
def hash_py(string):
"""Calculate hash value of string in Python"""
hash_value = 0.0
for char in string:
# Multiplicative hash function using Mersenne prime close to 2^32
hash_value = (hash_value * 31.0 + ord(char)) % (2.0 ** 31.0 - 1.0)
return hash_value
[docs] def generate_reinitrng_hoc_block(self):
""""Create re_init_rng code blocks for this channel"""
reinitrng_hoc_block = ''
if 'Stoch' in self.suffix:
# TODO this is dangerous, implicitely assumes type of location
for location in self.locations:
if self.deterministic:
reinitrng_hoc_block += \
' forsec %(seclist_name)s { ' \
'deterministic_%(suffix)s = 1 }\n' % {
'seclist_name': location.seclist_name,
'suffix': self.suffix}
else:
reinitrng_hoc_block += \
' forsec %(seclist_name)s {%(mech_reinitrng)s' \
' }\n' % {
'seclist_name': location.seclist_name,
'mech_reinitrng':
self.mech_reinitrng_block_template % {
'suffix': self.suffix}}
return reinitrng_hoc_block
@property
def prefix(self):
"""Deprecated, prefix is now replaced by suffix"""
return self.suffix
@prefix.setter
def prefix(self, value):
"""Deprecated, prefix is now replaced by suffix"""
self.suffix = value
hash_hoc_string = \
"""
func hash_str() {localobj sf strdef right
sf = new StringFunctions()
right = $s1
n_of_c = sf.len(right)
hash = 0
char_int = 0
for i = 0, n_of_c - 1 {
sscanf(right, "%c", & char_int)
hash = (hash * 31 + char_int) % (2 ^ 31 - 1)
sf.right(right, 1)
}
return hash
}
"""
reinitrng_hoc_string = """
proc re_init_rng() {localobj sf
strdef full_str, name
sf = new StringFunctions()
if(numarg() == 1) {
// We received a third seed
channel_seed = $1
channel_seed_set = 1
} else {
channel_seed_set = 0
}
%(reinitrng_hoc_blocks)s
}
"""
mech_reinitrng_block_template = """
for (x, 0) {
setdata_%(suffix)s(x)
sf.tail(secname(), "\\\\.", name)
sprint(full_str, "%%s.%%.19g", name, x)
if (channel_seed_set) {
setRNG_%(suffix)s(gid, hash_str(full_str), channel_seed)
} else {
setRNG_%(suffix)s(gid, hash_str(full_str))
}
}
"""
[docs]class NrnMODPointProcessMechanism(Mechanism):
"""Neuron mechanism"""
def __init__(
self,
name,
mod_path=None,
suffix=None,
locations=None,
preloaded=True,
comment=''):
"""Constructor
Args:
name (str): name of this object
mod_path (str): path to the MOD file (not used for the moment)
suffix (str): suffix of this mechanism in the MOD file
locations (list of Locations): a list of Location objects pointing
to compartments where this mechanism should be added to.
preloaded (bool): should this mechanism be side-loaded by
BluePyOpt, or was it already loaded and compiled by the user ?
(not used for the moment)
"""
super(NrnMODPointProcessMechanism, self).__init__(name, comment)
self.mod_path = mod_path
self.suffix = suffix
self.locations = locations
self.preloaded = preloaded
self.cell_model = None
self.pprocesses = None
[docs] def instantiate(self, sim=None, icell=None):
"""Instantiate"""
self.pprocesses = []
for location in self.locations:
icomp = location.instantiate(sim=sim, icell=icell)
try:
iclass = getattr(sim.neuron.h, self.suffix)
self.pprocesses.append(iclass(icomp.x, sec=icomp.sec))
except AttributeError as e:
raise AttributeError(str(e) + ': ' + self.suffix)
logger.debug(
'Inserted %s at %s ', self.suffix, [
str(location) for location in self.locations])
[docs] def destroy(self, sim=None):
"""Destroy mechanism instantiation"""
self.pprocesses = None
def __str__(self):
"""String representation"""
return "%s: %s at %s" % (
self.name, self.suffix,
[str(location) for location in self.locations])