"""
Current source classes for the neuron module.
Classes:
DCSource -- a single pulse of current of constant amplitude.
StepCurrentSource -- a step-wise time-varying current.
NoisyCurrentSource -- a Gaussian whitish noise current.
ACSource -- a sine modulated current.
:copyright: Copyright 2006-2016 by the PyNN team, see AUTHORS.
:license: CeCILL, see LICENSE for details.
"""
from neuron import h
import numpy
from pyNN.standardmodels import electrodes, build_translations, StandardCurrentSource
from pyNN.parameters import ParameterSpace, Sequence
from pyNN.neuron import simulator
class NeuronCurrentSource(StandardCurrentSource):
"""Base class for a source of current to be injected into a neuron."""
def __init__(self, **parameters):
self._devices = []
self.cell_list = []
self._amplitudes = None
self._times = None
self._h_iclamps = {}
parameter_space = ParameterSpace(self.default_parameters,
self.get_schema(),
shape=(1,))
parameter_space.update(**parameters)
parameter_space = self.translate(parameter_space)
self.set_native_parameters(parameter_space)
simulator.state.current_sources.append(self)
@property
def _h_amplitudes(self):
if self._amplitudes is None:
if isinstance(self.amplitudes, Sequence):
self._amplitudes = h.Vector(self.amplitudes.value)
else:
self._amplitudes = h.Vector(self.amplitudes)
return self._amplitudes
@property
def _h_times(self):
if self._times is None:
if isinstance(self.times, Sequence):
self._times = h.Vector(self.times.value)
else:
self._times = h.Vector(self.times)
return self._times
def _reset(self):
if self._is_computed:
self._amplitudes = None
self._times = None
self._generate()
for iclamp in self._h_iclamps.values():
self._update_iclamp(iclamp)
def _update_iclamp(self, iclamp):
if not self._is_playable:
iclamp.delay = max(0, self.start - simulator.state.t)
iclamp.dur = self.stop - self.start
iclamp.amp = self.amplitude
if self._is_playable:
iclamp.delay = 0.0
iclamp.dur = 1e12
iclamp.amp = 0.0
self._h_amplitudes.play(iclamp._ref_amp, self._h_times)
def set_native_parameters(self, parameters):
parameters.evaluate(simplify=True)
for name, value in parameters.items():
if isinstance(value, Sequence): # this shouldn't be necessary, but seems to prevent a segfault
value = value.value
object.__setattr__(self, name, value)
self._reset()
def get_native_parameters(self):
return ParameterSpace(dict((k, self.__getattribute__(k)) for k in self.get_native_names()))
def inject_into(self, cells):
__doc__ = StandardCurrentSource.inject_into.__doc__
for id in cells:
if id.local:
if not id.celltype.injectable:
raise TypeError("Can't inject current into a spike source.")
if not (id in self._h_iclamps):
self.cell_list += [id]
self._h_iclamps[id] = h.IClamp(0.5, sec=id._cell.source_section)
self._devices.append(self._h_iclamps[id])
def _record(self):
self.itrace = h.Vector()
self.itrace.record(self._devices[0]._ref_i)
self.record_times = h.Vector()
self.record_times.record(h._ref_t)
def _get_data(self):
return numpy.array((self.record_times, self.itrace))
[docs]class DCSource(NeuronCurrentSource, electrodes.DCSource):
__doc__ = electrodes.DCSource.__doc__
translations = build_translations(
('amplitude', 'amplitude'),
('start', 'start'),
('stop', 'stop')
)
_is_playable = False
_is_computed = False
[docs]class StepCurrentSource(NeuronCurrentSource, electrodes.StepCurrentSource):
__doc__ = electrodes.StepCurrentSource.__doc__
translations = build_translations(
('amplitudes', 'amplitudes'),
('times', 'times')
)
_is_playable = True
_is_computed = False
def _generate(self):
pass
[docs]class ACSource(NeuronCurrentSource, electrodes.ACSource):
__doc__ = electrodes.ACSource.__doc__
translations = build_translations(
('amplitude', 'amplitude'),
('start', 'start'),
('stop', 'stop'),
('frequency', 'frequency'),
('offset', 'offset'),
('phase', 'phase')
)
_is_playable = True
_is_computed = True
def __init__(self, **parameters):
NeuronCurrentSource.__init__(self, **parameters)
self._generate()
def _generate(self):
# Not efficient at all... Is there a way to have those vectors computed on the fly ?
# Otherwise should have a buffer mechanism
self.times = numpy.arange(self.start, self.stop + simulator.state.dt, simulator.state.dt)
tmp = numpy.arange(0, self.stop - self.start, simulator.state.dt)
self.amplitudes = self.offset + self.amplitude * numpy.sin(tmp * 2 * numpy.pi * self.frequency / 1000. + 2 * numpy.pi * self.phase / 360)
self.amplitudes[-1] = 0.0
[docs]class NoisyCurrentSource(NeuronCurrentSource, electrodes.NoisyCurrentSource):
__doc__ = electrodes.NoisyCurrentSource.__doc__
translations = build_translations(
('mean', 'mean'),
('start', 'start'),
('stop', 'stop'),
('stdev', 'stdev'),
('dt', 'dt')
)
_is_playable = True
_is_computed = True
def __init__(self, **parameters):
NeuronCurrentSource.__init__(self, **parameters)
self._generate()
def _generate(self):
## Not efficient at all... Is there a way to have those vectors computed on the fly ?
## Otherwise should have a buffer mechanism
self.times = numpy.arange(self.start, self.stop + simulator.state.dt, simulator.state.dt)
tmp = numpy.arange(0, self.stop - self.start, simulator.state.dt)
self.amplitudes = self.mean + (self.stdev * self.dt) * numpy.random.randn(len(tmp))
self.amplitudes[-1] = 0.0