Neural simulations
From NEST
A simulation of a network is like an experiment with the difference that it takes place inside the computer's memory rather than in the physical world.
Like in a real experiment, you need a system which you want to investigate. Moreover, you need a clear idea of what you want to learn from the experiment. In the context of a network simulation this means that you have to know which input you want to give to your network and which output you expect.
The next section will illustrate the main concepts of NEST simulations, using a simple simulation. The following sections will then give a step-by-step introduction to the main concepts of NEST simulations. Finally, we will discuss a complex example.
Contents |
A simple simulation
The simplest simulation in NEST is that of a network which contains just one neuron:
1: /iaf_neuron Create /neuron Set
We are going to simulate a standard integrate and fire model with
resting potential at -70 mV and spike threshold at -55 mV. In this
line, we use the model iaf_neuron to create a neuron.
The command Create returns a handle to the created
neuron, which we store in the variable neuron.
Next, we would like to add a stimulation and a recording device to the neuron, so that we will see something during the simulation.
In our little example, we want to inject a current into the neuron and record its membrane potential. Thus, we need to create the corresponding devices and connect them to the neuron:
2: /voltmeter Create /vm Set 3: vm neuron Connect
Line 2 creates a voltmeter node. The handle to the new voltmeter is
stored in the variable vm. In line 3, we see how the
stored handles are used in the call to Connect.
By default, the voltmeter will only record membrane potential values, so we configure it to show us the time stamp of each value as well. We also set it to print the time and potential to the screen and we set the recording interval to 0.1 ms. The default is 1.0 ms.
4: vm 5: << 6: /withtime true 7: /to_screen true 8: /interval 0.1 9: >> SetStatus
Now we create a DC generator which will supply a constant current to our neuron:
10: /dc_generator Create /stim Set 11: stim neuron Connect
Next, we want to set the amplitude of the DC generator such that it delivers enough current to elicit a spike in the neuron:
12: stim 13: << 14: /amplitude 550.0 15: >> SetStatus
The double angled brackets << and >>
delimit a dictionary definition which consists of successive
/key value pairs. In our case, there is only one pair: the key
/amplitude and its new value 500.0, which is
the amplitude in pA.
We can now run the simulation and expect to see some results:
15: 15.0 Simulate
The command Simulate runs the simulation for the
specified number of milliseconds. Below, you see a transcript of the
simulation:
Sep 03 15:10:58 Simulate [Info]:
Simulating 15 ms.
Sep 03 15:10:58 Scheduler::prepare_nodes [Info]:
Please wait. Preparing elements.
Sep 03 15:10:58 Scheduler::prepare_nodes [Info]:
Simulating 3 nodes.
0.1 -70
0.2 -70
0.3 -70
0.4 -70
0.5 -70
0.6 -70
0.7 -70
0.8 -70
0.9 -70
1.0 -70
1.1 -70
1.2 -69.7811
1.3 -69.5644
1.4 -69.3498
1.5 -69.1374
:
12.0 -55.3968
12.1 -55.3232
12.2 -55.2503
12.3 -55.1782
12.4 -55.1067
12.5 -55.036
12.6 -70
12.7 -70
12.8 -70
12.9 -70
13.0 -70
13.1 -70
13.2 -70
13.3 -70
13.4 -70
13.5 -70
13.6 -70
13.7 -70
13.8 -70
13.9 -70
14.0 -70
Sep 03 15:10:58 Scheduler::resume [Info]:
Simulation finished.
After some initial messages from the simulation scheduler, we see the output from the voltmeter. The numbers in the left column represent the network time in steps. The right column contains the values of the membrane potential at that step. The potential is given in mV.
By default, NEST uses a simulation stepsize of 0.1 ms. With a simulation time of 15.0 ms, we have 150 simulation steps.
The neuron that we have simulated was a standard integrate-and-fire neuron Tuckwell91 with a resting potential of -70 mV and a threshold at -55.0 mV. After 12.5 ms, the membrane potential has reached the threshold value and a spike event occurs. The membrane potential is then clamped to the resing value for 2 ms, the refractory period of the neuron. After the refractory period, the membrane continues to depolarize due to the continuing input current.
Nodes and Models
In NEST, the neural system is a collection of nodes and their interactions. Nodes correspond to things like neurons, synapses, and devices, and are implemented in C++. The network and its configuration are defined at the level of the simulation language interpreter.
Nodes are created from a set of prescribed models which are stored in the dictionary modeldict. The most important
models are:
| Model name | Description |
|---|---|
| iaf_neuron | Simple integrate-and-fire neuron with alpha-function PSCs. |
| voltmeter | Device to observe membrane potentials. |
| spike_detector | Device to observe spike times. |
| spike_generator | Device to generate spikes at specific times. |
| dc_generator | Device to generate a constant current. |
| ac_generator | Device to generate an alternating (sine) current. |
| poisson_generator | Device to generate poisson shotnoise. |
In order to make the models visible to the interpreter, the model dictionary has to be opened.
Creating nodes
Nodes are created from a model, using the command Create.
SLI ] /iaf_neuron Create == 1
In the fist line, we create one integrate and fire neuron from the model </code>iaf_neuron</code>.
The return value of Create is an integer that identifies the last node that was created in the network. This integer is called the node's global id. Another way of identifying a node is its address. It can be obtained using the function GetAddress:
SLI ] 1 GetAddress == [0 1]
In NEST, the network is represented as a tree. The address denotes the position of the node within this tree, while the global id is assigned to the node in the order of creation. The root of the network has the address [0]. The first child of the root node will receive the address [0 1], the second child will receive the address [0 2], and so on.
Often, it is neccessary to have a large number of nodes of the same type. The command Create can also be used for this purpose. The following lines of code create 10 integrate and fire neurons:
SLI ] /iaf_neuron 10 Create == 11
Sub-networks
We can introduce a new level to our network by adding a so called subnet:
SLI ] /subnet Create == 12
A subnet is like a directory in a file system. In it, we can create new nodes and even new subnets.
NEST maintains a pointer to the location where new nodes will be added. It is called current subnet. We can change the current working subnet, using the command ChangeSubnet:
SLI ] 12 ChangeSubnet
The command CurrentSubnet returns the address of the current subnet:
SLI ] CurrentSubnet == [0 12]
New nodes will now be created in the subnet at [0 12] and will have addresses which start with [0 12 ...]:
SLI ] /iaf_neuron Create GetAddress == [0 12 1]
Network structures
Many network models use two or more-dimensional arrangements of
identical nodes. In NEST, such structures can be conveniently created,
using the command LayoutNetwork. The following lines of code
create a 10 by 10 layer of integrate and fire neurons:
SLI ] /iaf_neuron [10 10] LayoutNetwork == 1
The return value is the global id of the top-level subnet.
Starting from a subnet, it is also possible to determine the
dimensions of this subnet, using the command
NetworkDimensions. Applied to the address of a subnet, it
returns an array with the dimensions of the subnet:
SLI ] 1 GetAddress NetworkDimensions == [10 10]
The command PrintNetwork can be used to display the
generated structure in a compressed format:
SLI ] 1 GetAddress 1 PrintNetwork +-[1] subnet dim=[10 10] | +-[1] subnet dim=[10] +-[2] subnet dim=[10] +-[3] subnet dim=[10] +-[4] subnet dim=[10] +-[5] subnet dim=[10] +-[6] subnet dim=[10] +-[7] subnet dim=[10] +-[8] subnet dim=[10] +-[9] subnet dim=[10] +-[10] subnet dim=[10]
The first parameter of PrintNetwork specifies at which
position in the network the command should start. The second parameter
defines how many levels will be printed. In this example, we started
from the node 1, showing the first level.
We see that LayoutNetwork has created a subnet which
contains 10 subnets with 10 integrate and fire neurons each.
Status information
Nodes have a state which can be extracted and modified. In the follwing example, we display the status information of one the neurons in the layer we have created above:
SLI ] [0 1 1 1] ShowStatus -------------------------------------------------- Name Type Value -------------------------------------------------- address arraytype <arraytype> archiver_length integertype 0 C_m doubletype 250 E_L doubletype -70 frozen booltype false global_id integertype 3 I_e doubletype 0 local booltype true local_id integertype 1 model literaltype iaf_neuron parent integertype 2 state integertype 0 tau_m doubletype 10 tau_minus doubletype 20 tau_minus_triplet doubletype 110 tau_syn doubletype 2 thread integertype 0 t_ref doubletype 2 t_spike doubletype -1 vp integertype 0 V_m doubletype -70 V_reset doubletype -70 V_th doubletype -55 -------------------------------------------------- Total number of entries: 23
Using the command SetStatus, it is possible to change the
entries of this so called status dictionary. The following
lines of code change the threshold value V_th to -60 mV:
SLI ] [0 1 1 1] << /V_th -60.0 >> SetStatus SLI ] [0 1 1 1] GetStatus /V_th get = -60
Please note, that SetStatus checks if a property really exists
in a node and will issue an error if it doesn't. This behavior can be changed
by the following command:
0 << /dict_miss_is_error false >> SetStatus
Then, NEST is very tolerant with respect to the property that you are trying to change: If it does not know the
property, or if the property cannot be changed, there will be no error, but only a warning. In any case, SetStatus does complain if the new value does not match in the expected type:
SLI ] [0 1 1 1] << /V_th (60) >> SetStatus
Dec 01 15:33:54 SetStatus_ad [Error]: TypeMismatch
Expected datatype: doubletype
Provided datatype: stringtype
In order to find out, which properties of a given model can be changed an which not, you have to refer to the model's documentation.
Connections
Connections between nodes define possible channels for interactions between them. A connection
between two nodes is established, using the command Connect.
Each connection has two basic parameters, weight and delay. The weight determines the strength of the connection, the delay determines how long an event needs to travel from the sending to the receiving node. The delay must be a positive number greater or equal to the simulation stepsize and is given in ms.
Example:
SLI ] /iaf_neuron Create /n1 Set SLI ] /iaf_neuron Create /n2 Set SLI ] /iaf_neuron Create /n3 Set SLI ] SLI ] n1 n2 Connect SLI ] n1 n3 Connect
To inspect the parameters of a connection, one first needs to obtain a handle to the connection. This
is done using the command FindConnections. It takes a dictionary that at least contains
the id of the source node and will return a list of handles for all outgoing connections. The search can be
restricted by using the optional parameters target and synapse_type.
Example:
SLI ] << /source n1 >> FindConnections /c1 Set SLI ] c1 length == 2 SLI ] << /source n1 /target n2 >> FindConnections c2 Set SLI ] c2 length == 1
To actually see the parameters of the connection, GetStatus is used, just like it is
for nodes.
Example:
SLI ] c1 0 get GetStatus info -------------------------------------------------- Name Type Value -------------------------------------------------- delay doubletype 1 receptor integertype 0 source integertype 1 synapse_type literaltype static_synapse target integertype 2 weight doubletype 1 -------------------------------------------------- Total number of entries: 6
To change the paramters of a connection, SetStatus is used, just like it is
for nodes.
Example:
SLI ] c1 0 get << /weight 2.0 >> SetStatus SLI ] c1 0 get GetStatus /weight get == 2.000000e+00
Devices
Devices are network nodes which provide input to the network or record its output. They encapsulate the stimulation and measurement process. If you want to extract certain information from a simulation, you need a device which is able to deliver this information. Likewise, if you want to send specific input to the network, you need a device which delivers this input.
Devices have a built-in timer which controls the period they are active. Outside this interval, a device will remain siltent. The timer can be configured using the command SetStatus.
By definition a device is active in the interval [t1,t2), if we
can observe events E with time stamps tE which obey
for all E.
In other words, the interval during which the device is active corresponds to the range of time-stamps of the device's events.
Note that it is not possible to generate/observe an event with time stamp 0. Because of this, the value of the property /origin (see below) must be larger than 0.
Device parameters
The following entries of the status dictionary are the same for all stimulation and recording devices:
| Property | Type | Decription |
|---|---|---|
/start | double | First time of activity, relative to the value
of |
/stop | double | First time of inactivity, relative to the value of
|
origin | double | Origin of the device clock, relative to the
network time in ms. |
In general, the following must hold:
- start + origin > 0
- stop <= start
- If stop = start, the device is inactive.
Recording devices
All devices which are used to observe the state of other network nodes are called recording devices. Examples are voltmeter and spike_detector.
Recording devices have properties which control the amount and format of their output.
Update and parallel simulation
NEST simulations are time driven. The simulation time proceeds in discrete steps of size $dt$. In each time slice, all nodes in the system are updated and pending events are delivered.
NEST can use multiple threads to update the system. On symmetric multi processor machines (SMP) this can lead to a considerable performance gain. By default, only one thread is used.
