nest / nestml

A domain specific language for neuron and synapse models in spiking neural network simulation
GNU General Public License v2.0
46 stars 45 forks source link

Adding attributes to spike events #946

Open clinssen opened 1 year ago

clinssen commented 1 year ago

Currently, neurons can emit spikes by calling the NESTML predefined function emit_spike(), without any arguments. Synapses can emit spikes by calling emit_spike(w, d), passing the weight and delay parameters.

It would be great if we could unify these functions and make them more generic. Could we rewrite this in a way where there is a generic "event", to which we can attach attributes (like weight and delay, or multiplicity, or any named parameter at all)? Then we could do something like:

emit_spike()    # neuron emits unweighted spikes, no parameters needed
emit_spike(weight=w_, delay=1 ms)   # synapse emits a spike with an attribute "weight" which gets its value from the local variable w_, and an attribute "delay" which is assigned a constant value here

These attributes could subsequently be read out in an event handler as follows:

onReceive(exc_spikes):
    I_syn_exc += exc_spikes.weight
    foo += exc_spikes.multiplicity

as opposed to the current implementation, where exc_spikes is defined as a sum of delta pulses (with physical units 1/s):

onReceive(exc_spikes):
    I_syn_exc += exc_spikes

In the new implementation, the event attribute "weight" would simply have the units of whatever we put in on the sending side, but we would still need a way to specify the expected unit on the receiving side, which might be tricky syntactically. Perhaps we could define something like

input:
    exc_spikes <- spike(weight: real, delay: ms)

Possibly, the attributes and types should also be specifed as part of the output port:

output:
    spike(weight: real, delay: ms)

One issue is that naming conventions might be different between the sending side and receiving side (for instance, "w" and "weight"). We could use the code generator options dictionary to make a mapping between these, as we are already doing for the port names currently.

Vector input ports

Possibly related is the support for a vector of input ports. Constructs like onReceive(exc_spikes[5]) should be allowed if exc_spikes is defined as a vector of suitable length.

Consider the follow example, but where exc_spikes is a vector:

onReceive(exc_spikes):
    I_syn_exc += exc_spikes.weight

Is it implied that exc_spikes.weight is also a vector?

pnbabu commented 1 week ago

As discussed in the NESTML meeting, the syntax for the onReceive block with vector input ports will be as follows:

input:
  spikes[3] <- spike(weight:real, multiplicity: real)

onReceive(spikes[0]):
  I_syn += spikes[0]

onReceive(spikes[1]):
  # using weight and multiplicity of the spike event
  I_syn += spikes[1].weight * spikes[1].multiplicity

onReceive(spikes[2]):
  ...

Each input port of the vector spikes[3] would have to be individually handled in their own onReceive block. Each of these spiking events will have its attributes, for example, weight, multiplicity, delay, etc, which can be specified as spikes[1].weight.

The input port cannot be used on its own without a vector index in an onReceive block. Thus, the following syntax is not allowed:

onReceive(spikes)
  I_syn += spikes[0] + spikes[1] + ...