Source code for nengo.builder.neurons

import numpy as np

from nengo.builder import Builder, Operator, Signal
from nengo.exceptions import BuildError
from nengo.neurons import NeuronType
from nengo.utils.numpy import is_array_like


[docs]class SimNeurons(Operator): """Set a neuron model output for the given input current. Implements ``neurons.step(dt, J, **state)``. Parameters ---------- neurons : NeuronType The `.NeuronType`, which defines a ``step`` function. J : Signal The input current. output : Signal The neuron output signal that will be set. state : list, optional A list of additional neuron state signals set by ``step``. tag : str, optional A label associated with the operator, for debugging purposes. Attributes ---------- J : Signal The input current. neurons : NeuronType The `.NeuronType`, which defines a ``step`` function. output : Signal The neuron output signal that will be set. state : list A list of additional neuron state signals set by ``step``. tag : str or None A label associated with the operator, for debugging purposes. Notes ----- 1. sets ``[output] + state`` 2. incs ``[]`` 3. reads ``[J]`` 4. updates ``[]`` """ def __init__(self, neurons, J, state=None, tag=None): super().__init__(tag=tag) self.neurons = neurons self.sets = [] self.incs = [] self.reads = [J] self.updates = [] self.state_sigs = {} self.state_extra = {} if state is not None: for name, obj in state.items(): if isinstance(obj, Signal): # The signals actually stored in `self.sets` can be modified by the # optimizer. To allow this possibility, we store the index of the # signal in the sets list instead of storing the signal itself. self.state_sigs[name] = len(self.sets) self.sets.append(obj) else: # The only supported extra is a RandomState right now assert isinstance(obj, np.random.RandomState) self.state_extra[name] = obj @property def J(self): return self.reads[0] @property def _descstr(self): return "%s, %s" % (self.neurons, self.J)
[docs] def make_step(self, signals, dt, rng): J = signals[self.J] state = {name: signals[self.sets[idx]] for name, idx in self.state_sigs.items()} state.update(self.state_extra) def step_simneurons(): self.neurons.step(dt, J, **state) return step_simneurons
[docs]@Builder.register(NeuronType) def build_neurons(model, neurontype, neurons): """Builds a `.NeuronType` object into a model. This function adds a `.SimNeurons` operator connecting the input current to the neural output signals, and handles any additional state variables defined within the neuron type. Parameters ---------- model : Model The model to build into. neurontype : NeuronType Neuron type to build. neuron : Neurons The neuron population object corresponding to the neuron type. Notes ----- Does not modify ``model.params[]`` and can therefore be called more than once with the same `.NeuronType` instance. """ dtype = model.sig[neurons]["in"].dtype n_neurons = neurons.size_in rng = np.random.RandomState(model.seeds[neurons.ensemble] + 1) state_init = neurontype.make_state(n_neurons, rng=rng, dtype=dtype) state = {} for key, init in state_init.items(): if key in model.sig[neurons]: raise BuildError("State name %r overlaps with existing signal name" % key) if is_array_like(init): model.sig[neurons][key] = Signal( initial_value=init, name="%s.%s" % (neurons, key) ) state[key] = model.sig[neurons][key] elif isinstance(init, np.random.RandomState): # Pass through RandomState instances state[key] = init else: raise BuildError( "State %r is of type %r. Only array-likes and RandomStates are " "currently supported." % (key, type(init).__name__) ) model.sig[neurons]["out"] = ( state["spikes"] if neurontype.spiking else state["rates"] ) model.add_op( SimNeurons(neurons=neurontype, J=model.sig[neurons]["in"], state=state) )