GeNN
4.9.0
GPU enhanced Neuronal Networks (GeNN)
|
In this tutorial we will learn to add synapsePopulations to connect neurons in neuron groups to each other with synaptic models. As an example we will connect the ten Hodgkin-Huxley neurons from tutorial 1 in a ring of excitatory synapses.
First, copy the files from Tutorial 1 into a new directory and rename the tenHHModel.cc
to tenHHRingModel.cc
and tenHHSimulation.cc
to tenHHRingSimulation.cc
, e.g. on Linux or Mac:
Finally, to reduce confusion we should rename the model itself. Open tenHHRingModel.cc
, change the model name inside,
We want to connect our ten neurons into a ring where each neuron connects to its neighbours. In order to initialise this connectivity we need to add a sparse connectivity initialisation snippet at the top of tenHHRingModel.cc
:
The SET_ROW_BUILD_CODE
code string will be called to generate each row of the synaptic matrix (connections coming from a single presynaptic neuron) and, in this case, each row consists of a single synapses from the presynaptic neuron $(id_pre) to $(id_pre) + 1 (the modulus operator is used to ensure that the final connection between neuron 9
and 0
is made correctly). In order to allow GeNN to better optimise the generated code we also provide a maximum row length. In this case each row always contains only one synapse but, when more complex connectivity is used, the number of neurons in the pre and postsynaptic population as well as any parameters used to configure the snippet can be accessed from this function.
Now we need additional initial values and parameters for the synapse and post-synaptic models. We will use the standard WeightUpdateModels::StaticPulse weight update model and PostsynapticModels::ExpCond post-synaptic model. They need the following initial variables and parameters:
We can then add a synapse population at the end of the modelDefinition(...)
function,
The addSynapsePopulation parameters are
Adding the addSynapsePopulation command to the model definition informs GeNN that there will be synapses between the named neuron populations, here between population Pop1
and itself. At this point our model definition file tenHHRingModel.cc
should look like this
We can now build our new model:
-c
option as described in Tutorial 1 (C++).Now we can open the tenHHRingSimulation.cc
file and update the file name of the model includes to match the name we set previously:
Additionally, we need to add a call to a second initialisation function to main()
after we call initialize()
:
This initializes any variables associated with the sparse connectivity we have added (and will also copy any manually initialised variables to the GPU). Then, after using the genn-create-user-project
tool to create a new project with a model name of tenHHRing
and using tenHHRingSimulation.cc
rather than tenHHSimulation.cc
, we can build and run our new simulator in the same way we did in Tutorial 1 (C++). However, even after all our hard work, if we plot the content of the first column against the subsequent 10 columns of tenHHexample.V.dat
it looks very similar to the plot we obtained at the end of Tutorial 1 (C++).
This is because none of the neurons are spiking so there are no spikes to propagate around the ring.
We can use a NeuronModels::SpikeSourceArray to inject an initial spike into the first neuron in the ring during the first timestep to start spikes propagating. We can do this by adding the following neuron and synapse populations to the network at the end of the modelDefinition(...)
function:
Each neuron in the spike source array population has its own list of spikes which are concatenated together and stored in an array made accesible to all neurons in the population using an extra global parameter (see Extra global parameters). Although, in this model, there is only one neuron in the Stim population so startSpike
and endSpike
could be automatically initialised in the model description, typically they need to be initialised manually in the user-code so we mark these as uninitialised using uninitialisedVar(). We then connect the spike source array to Pop1 using another synapse population with sparse connectivity, initialised using the built in InitSparseConnectivitySnippet::OneToOne model so the single neuron in the Stim population is connected to the first neuron in the ring. Next, we add the code to initialise startSpike
and endSpike
to tenHHSimulation.cc
, between the calls to initialize()
and initializeSparse()
:
Finally, we also add code to allocate, intialise and push the spike times to the extra global parameter:
At this point our user code tenHHRingModel.cc
should look like this
and tenHHRingSimulation.cc
` should look like this:
Finally if we build, make and run this model; and plot the first 200 ms of the ten neurons' membrane voltages - they now looks like this: