Skip to content

Commit

Permalink
Merge pull request #380 from genn-team/spike_event_time
Browse files Browse the repository at this point in the history
Tracking of spike-like-event times
  • Loading branch information
neworderofjamie authored Dec 17, 2020
2 parents af922df + 5ffb045 commit 8b18c6f
Show file tree
Hide file tree
Showing 29 changed files with 831 additions and 152 deletions.
6 changes: 6 additions & 0 deletions include/genn/genn/code_generator/codeGenUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ void neuronSubstitutionsInSynapticCode(CodeGenerator::Substitutions &substitutio
substitutions.addVarSubstitution("prev_sT" + sourceSuffix,
"(" + delayOffset + varPrefix + "group->prevST" + destSuffix + "[" + prevSpikeTimeOffset + idx + "]" + varSuffix + ")");

// Substitute spike-like-event times
substitutions.addVarSubstitution("seT" + sourceSuffix,
"(" + delayOffset + varPrefix + "group->seT" + destSuffix + "[" + offset + idx + "]" + varSuffix + ")");
substitutions.addVarSubstitution("prev_seT" + sourceSuffix,
"(" + delayOffset + varPrefix + "group->prevSET" + destSuffix + "[" + prevSpikeTimeOffset + idx + "]" + varSuffix + ")");

// Substitute neuron variables
const auto *nm = archetypeNG->getNeuronModel();
if(useLocalNeuronVars) {
Expand Down
34 changes: 28 additions & 6 deletions include/genn/genn/neuronGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,18 @@ class GENN_EXPORT NeuronGroup
//! Set location of this neuron group's output spike times
/*! This is ignored for simulations on hardware with a single memory space */
void setSpikeTimeLocation(VarLocation loc) { m_SpikeTimeLocation = loc; }

//! Set location of this neuron group's previous output spike times
/*! This is ignored for simulations on hardware with a single memory space */
void setPrevSpikeTimeLocation(VarLocation loc) { m_PrevSpikeTimeLocation = loc; }

//! Set location of this neuron group's output spike-like-event times
/*! This is ignored for simulations on hardware with a single memory space */
void setSpikeEventTimeLocation(VarLocation loc) { m_SpikeEventTimeLocation = loc; }

//! Set location of this neuron group's previous output spike-like-event times
/*! This is ignored for simulations on hardware with a single memory space */
void setPrevSpikeEventTimeLocation(VarLocation loc) { m_PrevSpikeEventTimeLocation = loc; }

//! Set variable location of neuron model state variable
/*! This is ignored for simulations on hardware with a single memory space */
Expand Down Expand Up @@ -131,6 +139,8 @@ class GENN_EXPORT NeuronGroup

bool isSpikeTimeRequired() const;
bool isPrevSpikeTimeRequired() const;
bool isSpikeEventTimeRequired() const;
bool isPrevSpikeEventTimeRequired() const;
bool isTrueSpikeRequired() const;
bool isSpikeEventRequired() const;

Expand All @@ -150,6 +160,12 @@ class GENN_EXPORT NeuronGroup
//! Get location of this neuron group's previous output spike times
VarLocation getPrevSpikeTimeLocation() const { return m_PrevSpikeTimeLocation; }

//! Get location of this neuron group's output spike-like-event times
VarLocation getSpikeEventTimeLocation() const { return m_SpikeEventTimeLocation; }

//! Get location of this neuron group's previous output spike-like-event times
VarLocation getPrevSpikeEventTimeLocation() const { return m_PrevSpikeEventTimeLocation; }

//! Get location of neuron model state variable by name
VarLocation getVarLocation(const std::string &varName) const;

Expand Down Expand Up @@ -185,8 +201,8 @@ class GENN_EXPORT NeuronGroup
VarLocation defaultVarLocation, VarLocation defaultExtraGlobalParamLocation) :
m_Name(name), m_NumNeurons(numNeurons), m_NeuronModel(neuronModel), m_Params(params), m_VarInitialisers(varInitialisers),
m_NumDelaySlots(1), m_VarQueueRequired(varInitialisers.size(), false), m_SpikeLocation(defaultVarLocation), m_SpikeEventLocation(defaultVarLocation),
m_SpikeTimeLocation(defaultVarLocation), m_PrevSpikeTimeLocation(defaultVarLocation), m_VarLocation(varInitialisers.size(), defaultVarLocation),
m_ExtraGlobalParamLocation(neuronModel->getExtraGlobalParams().size(), defaultExtraGlobalParamLocation),
m_SpikeTimeLocation(defaultVarLocation), m_PrevSpikeTimeLocation(defaultVarLocation), m_SpikeEventTimeLocation(defaultVarLocation), m_PrevSpikeEventTimeLocation(defaultVarLocation),
m_VarLocation(varInitialisers.size(), defaultVarLocation), m_ExtraGlobalParamLocation(neuronModel->getExtraGlobalParams().size(), defaultExtraGlobalParamLocation),
m_SpikeRecordingEnabled(false), m_SpikeEventRecordingEnabled(false)
{
}
Expand Down Expand Up @@ -284,18 +300,24 @@ class GENN_EXPORT NeuronGroup
//! Vector specifying which variables require queues
std::vector<bool> m_VarQueueRequired;

//! Whether spikes from neuron group should use zero-copied memory
//! Location of spikes from neuron group
VarLocation m_SpikeLocation;

//! Whether spike-like events from neuron group should use zero-copied memory
//! Location of spike-like events from neuron group
VarLocation m_SpikeEventLocation;

//! Whether spike times from neuron group should use zero-copied memory
//! Location of spike times from neuron group
VarLocation m_SpikeTimeLocation;

//! Location of previous spike times
VarLocation m_PrevSpikeTimeLocation;

//! Location of spike-like-event times
VarLocation m_SpikeEventTimeLocation;

//! Location of previous spike-like-event times
VarLocation m_PrevSpikeEventTimeLocation;

//! Location of individual state variables
std::vector<VarLocation> m_VarLocation;

Expand Down
8 changes: 8 additions & 0 deletions include/genn/genn/weightUpdateModels.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@

#define SET_NEEDS_PRE_SPIKE_TIME(PRE_SPIKE_TIME_REQUIRED) virtual bool isPreSpikeTimeRequired() const override{ return PRE_SPIKE_TIME_REQUIRED; }
#define SET_NEEDS_POST_SPIKE_TIME(POST_SPIKE_TIME_REQUIRED) virtual bool isPostSpikeTimeRequired() const override{ return POST_SPIKE_TIME_REQUIRED; }
#define SET_NEEDS_PRE_SPIKE_EVENT_TIME(PRE_SPIKE_EVENT_TIME_REQUIRED) virtual bool isPreSpikeEventTimeRequired() const override{ return PRE_SPIKE_EVENT_TIME_REQUIRED; }

#define SET_NEEDS_PREV_PRE_SPIKE_TIME(PREV_PRE_SPIKE_TIME_REQUIRED) virtual bool isPrevPreSpikeTimeRequired() const override{ return PREV_PRE_SPIKE_TIME_REQUIRED; }
#define SET_NEEDS_PREV_POST_SPIKE_TIME(PREV_POST_SPIKE_TIME_REQUIRED) virtual bool isPrevPostSpikeTimeRequired() const override{ return PREV_POST_SPIKE_TIME_REQUIRED; }
#define SET_NEEDS_PREV_PRE_SPIKE_EVENT_TIME(PREV_PRE_SPIKE_EVENT_TIME_REQUIRED) virtual bool isPrevPreSpikeEventTimeRequired() const override{ return PREV_PRE_SPIKE_EVENT_TIME_REQUIRED; }

//----------------------------------------------------------------------------
// WeightUpdateModels::Base
Expand Down Expand Up @@ -120,12 +122,18 @@ class GENN_EXPORT Base : public Models::Base
//! Whether postsynaptic spike times are needed or not
virtual bool isPostSpikeTimeRequired() const{ return false; }

//! Whether presynaptic spike-like-event times are needed or not
virtual bool isPreSpikeEventTimeRequired() const { return false; }

//! Whether PREVIOUS presynaptic spike times are needed or not
virtual bool isPrevPreSpikeTimeRequired() const{ return false; }

//! Whether PREVIOUS postsynaptic spike times are needed or not
virtual bool isPrevPostSpikeTimeRequired() const{ return false; }

//! Whether PREVIOUS presynaptic spike-like-event times are needed or not
virtual bool isPrevPreSpikeEventTimeRequired() const { return false; }

//------------------------------------------------------------------------
// Public methods
//------------------------------------------------------------------------
Expand Down
14 changes: 10 additions & 4 deletions pygenn/genn_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,15 +279,21 @@ def spike_recording_data(self):

# Unpack data (results in one byte per bit)
# **THINK** is there a way to avoid this step?
data_unpack = data_unpack = np.unpackbits(data_bytes, axis=1,
count=self.size,
bitorder="little")
data_unpack = np.unpackbits(data_bytes, axis=1,
count=self.size,
bitorder="little")

# Calculate indices where there are spikes
spikes = np.where(data_unpack == 1)

# Calculate start time of recording
start_time_ms = (self._model.timestep - data_bytes.shape[0]) * self._model.dT
if start_time_ms < 0.0:
raise Exception("spike_recording_data can only be "
"accessed once buffer is full.")

# Convert spike times to ms
spike_times = spikes[0] * self._model.dT
spike_times = start_time_ms + (spikes[0] * self._model.dT)

return spike_times, spikes[1]

Expand Down
110 changes: 62 additions & 48 deletions pygenn/genn_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,8 +956,10 @@ def create_custom_weight_update_class(class_name, param_names=None,
extra_global_params=None,
is_pre_spike_time_required=None,
is_post_spike_time_required=None,
is_pre_spike_event_time_required=None,
is_prev_pre_spike_time_required=None,
is_prev_post_spike_time_required=None,
is_prev_pre_spike_event_time_required=None,
custom_body=None):
"""This helper function creates a custom WeightUpdateModel class.
See also:
Expand All @@ -968,56 +970,60 @@ def create_custom_weight_update_class(class_name, param_names=None,
create_custom_sparse_connect_init_snippet_class
Args:
class_name -- name of the new class
class_name -- name of the new class
Keyword args:
param_names -- list of strings with param names of
the model
var_name_types -- list of pairs of strings with variable
names and types of the model
pre_var_name_types -- list of pairs of strings with
presynaptic variable names and
types of the model
post_var_name_types -- list of pairs of strings with
postsynaptic variable names and
types of the model
derived_params -- list of pairs, where the first member
is string with name of the derived
parameter and the second MUST be an
instance of a class which inherits from
``pygenn.genn_wrapper.DerivedParamFunc``
sim_code -- string with the simulation code
event_code -- string with the event code
learn_post_code -- string with the code to include in
learn_synapse_post kernel/function
synapse_dynamics_code -- string with the synapse dynamics code
event_threshold_condition_code -- string with the event threshold
condition code
pre_spike_code -- string with the code run once per
spiking presynaptic neuron
post_spike_code -- string with the code run once per
spiking postsynaptic neuron
pre_dynamics_code -- string with the code run every
timestep on presynaptic neuron
post_dynamics_code -- string with the code run every
timestep on postsynaptic neuron
sim_support_code -- string with simulation support code
learn_post_support_code -- string with support code for
learn_synapse_post kernel/function
synapse_dynamics_suppport_code -- string with synapse dynamics
support code
extra_global_params -- list of pairs of strings with names and
types of additional parameters
is_pre_spike_time_required -- boolean, is presynaptic spike time
required in any weight update kernels?
is_post_spike_time_required -- boolean, is postsynaptic spike time
required in any weight update kernels?
is_prev_pre_spike_time_required -- boolean, are previous presynaptic spike time
required in any weight update kernels?
is_prev_post_spike_time_required -- boolean, are previous postsynaptic spike time
required in any weight update kernels?
custom_body -- dictionary with additional attributes
and methods of the new class
param_names -- list of strings with param names of
the model
var_name_types -- list of pairs of strings with variable
names and types of the model
pre_var_name_types -- list of pairs of strings with
presynaptic variable names and
types of the model
post_var_name_types -- list of pairs of strings with
postsynaptic variable names and
types of the model
derived_params -- list of pairs, where the first member
is string with name of the derived
parameter and the second MUST be an
instance of a class which inherits from
``pygenn.genn_wrapper.DerivedParamFunc``
sim_code -- string with the simulation code
event_code -- string with the event code
learn_post_code -- string with the code to include in
learn_synapse_post kernel/function
synapse_dynamics_code -- string with the synapse dynamics code
event_threshold_condition_code -- string with the event threshold
condition code
pre_spike_code -- string with the code run once per
spiking presynaptic neuron
post_spike_code -- string with the code run once per
spiking postsynaptic neuron
pre_dynamics_code -- string with the code run every
timestep on presynaptic neuron
post_dynamics_code -- string with the code run every
timestep on postsynaptic neuron
sim_support_code -- string with simulation support code
learn_post_support_code -- string with support code for
learn_synapse_post kernel/function
synapse_dynamics_suppport_code -- string with synapse dynamics
support code
extra_global_params -- list of pairs of strings with names and
types of additional parameters
is_pre_spike_time_required -- boolean, is presynaptic spike time
required in any weight update kernels?
is_post_spike_time_required -- boolean, is postsynaptic spike time
required in any weight update kernels?
is_pre_spike_event_time_required -- boolean, is presynaptic spike-like-event
time required in any weight update kernels?
is_prev_pre_spike_time_required -- boolean, is _previous_ presynaptic spike time
required in any weight update kernels?
is_prev_post_spike_time_required -- boolean, is _previous_ postsynaptic spike time
required in any weight update kernels?
is_prev_pre_spike_event_time_required -- boolean, is _previous_ presynaptic spike-like-event
time required in any weight update kernels?
custom_body -- dictionary with additional attributes
and methods of the new class
"""
if not isinstance(custom_body, dict) and custom_body is not None:
raise ValueError("custom_body must be an instance of dict or None")
Expand Down Expand Up @@ -1086,6 +1092,10 @@ def create_custom_weight_update_class(class_name, param_names=None,
body["is_post_spike_time_required"] = \
lambda self: is_post_spike_time_required

if is_pre_spike_event_time_required is not None:
body["is_pre_spike_event_time_required"] = \
lambda self: is_pre_spike_event_time_required

if is_prev_pre_spike_time_required is not None:
body["is_prev_pre_spike_time_required"] = \
lambda self: is_prev_pre_spike_time_required
Expand All @@ -1094,6 +1104,10 @@ def create_custom_weight_update_class(class_name, param_names=None,
body["is_prev_post_spike_time_required"] = \
lambda self: is_prev_post_spike_time_required

if is_prev_pre_spike_event_time_required is not None:
body["is_prev_pre_spike_event_time_required"] = \
lambda self: is_prev_pre_spike_event_time_required

if custom_body is not None:
body.update(custom_body)

Expand Down
6 changes: 3 additions & 3 deletions src/genn/backends/cuda/optimiser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ void calcGroupSizes(const CUDA::Preferences &preferences, const ModelSpecInterna
// Add number of neurons to initialisation kernel (all neuron groups at least require spike counts initialising)
groupSizes[KernelInitialize].push_back(n.second.getNumNeurons());

// If neuron group requires previous spike times to be reset after update
// If neuron group requires previous spike or spike-like-event times to be reset after update
// i.e. in the pre-neuron reset kernel, add number of neurons to kernel
if(n.second.isPrevSpikeTimeRequired()) {
if(n.second.isPrevSpikeTimeRequired() || n.second.isPrevSpikeEventTimeRequired()) {
groupSizes[KernelPreNeuronReset].push_back(n.second.getNumNeurons());
}
}
Expand Down Expand Up @@ -147,7 +147,7 @@ void calcGroupSizes(const CUDA::Preferences &preferences, const ModelSpecInterna

// Add group sizes for reset kernels
// **NOTE** individual pre-neuron reset groups have already been added for neuron groups
// which require previous spike times, just add single big group for remainder here
// which require previous spike or spike-like-event times, just add single big group for remainder here
groupSizes[KernelPreNeuronReset].push_back(model.getNeuronGroups().size() - groupSizes[KernelPreNeuronReset].size());
groupSizes[KernelPreSynapseReset].push_back(numPreSynapseResetGroups);
}
Expand Down
Loading

0 comments on commit 8b18c6f

Please sign in to comment.