Using NEST with MUSIC¶
Introduction¶
NEST supports the MUSIC interface, a standard by the INCF, which allows the transmission of data between applications at runtime [1]. It can be used to couple NEST with other simulators, with applications for stimulus generation and data analysis and visualization and with custom applications that also use the MUSIC interface.
Basically, all communication with MUSIC is mediated via proxies that receive/send data from/to remote applications using MUSIC. Different proxies are used for the different types of data. At the moment, NEST supports sending and receiving spike events and receiving continuous data and string messages.
You can find the installation instructions for MUSIC on their Github Page: INCF/MUSIC
Reference¶
Sending and receiving spike events¶
A minimal example for the exchange of spikes between two independent
instances of NEST is given in the example
examples/nest/music/minimalmusicsetup.music
.
It sends spikes using the music_event_out_proxy
script and receives the
spikes using a music_event_in_proxy
.
stoptime=0.01
[from]
binary=./minimalmusicsetup_sendnest.py
np=1
[to]
binary=./minimalmusicsetup_receivenest.py
np=1
from.spikes_out -> to.spikes_in [1]
This configuration file sets up two applications, from
and to
,
which are both instances of NEST. The first runs a script to send
spike events on the MUSIC port spikes_out
to the second, which
receives the events on the port spikes_in
. The width of the port is
1.
The content of minimalmusicsetup_sendnest.py
is contained in the
following listing.
First, we import nest and set up a check to ensure MUSIC is installed before continuing.
import nest
if not nest.ll_api.sli_func("statusdict/have_music ::"):
import sys
print("NEST was not compiled with support for MUSIC, not running.")
sys.exit()
nest.set_verbosity("M_ERROR")
Next we create a spike_generator
and set the spike times. We then create
our neuron model (iaf_psc_alpha
) and connect the neuron with the spike
generator.
sg = nest.Create('spike_generator')
nest.SetStatus(sg, {'spike_times': [1.0, 1.5, 2.0]})
n = nest.Create('iaf_psc_alpha')
nest.Connect(sg, n, 'one_to_one', {'weight': 750.0, 'delay': 1.0})
We then create a voltmeter, which will measure the membrane potential, and connect it with the neuron.
vm = nest.Create('voltmeter')
nest.SetStatus(vm, {'record_to': 'screen'})
nest.Connect(vm, n)
Finally, we create a music_event_out_proxy
, which forwards the spikes it
receives directly to the MUSIC event output port spikes_out
. The spike
generator is connected to the music_event_out_proxy
on channel 0 and the
network is simulated for 10 milliseconds.
meop = nest.Create('music_event_out_proxy')
nest.SetStatus(meop, {'port_name': 'spikes_out'})
nest.Connect(sg, meop, 'one_to_one', {'music_channel': 0})
nest.Simulate(10)
The next listing contains the content of
minimalmusicsetup_receivenest.py
, which is set up similarly to the above
script, but without the spike generator.
import nest
if not nest.ll_api.sli_func("statusdict/have_music ::"):
import sys
print("NEST was not compiled with support for MUSIC, not running.")
sys.exit()
nest.set_verbosity("M_ERROR")
meip = nest.Create('music_event_in_proxy')
nest.SetStatus(meip, {'port_name': 'spikes_in', 'music_channel': 0})
n = nest.Create('iaf_psc_alpha')
nest.Connect(meip, n, 'one_to_one', {'weight': 750.0})
vm = nest.Create('voltmeter')
nest.SetStatus(vm, {'record_to': 'screen'})
nest.Connect(vm, n)
nest.Simulate(10)
Running the example using mpirun -np 2 music minimalmusicsetup.music
yields the following output, which shows that the neurons in both
processes receive the same input from the spike_generator
in the
first NEST process and show the same membrane potential trace.
2 1 -70
2 2 -70
2 3 -68.1559
2 4 -61.9174
2 5 -70
2 6 -70
2 7 -70
2 8 -65.2054
2 9 -62.1583
2 1 -70
2 2 -70
2 3 -68.1559
2 4 -61.9174
2 5 -70
2 6 -70
2 7 -70
2 8 -65.2054
2 9 -62.1583
Receiving string messages¶
Currently, NEST is only able to receive messages, and unable to send string
messages. We thus use MUSIC’s messagesource
program for the
generation of messages in the following example. The configuration file
(msgtest.music
) is shown below
stoptime=1.0
np=1
[from]
binary=messagesource
args=messages
[to]
binary=./msgtest.py
from.out -> to.msgdata [0]
This configuration file connects MUSIC’s messagesource
program to
the port msgdata
of a NEST instance. The messagesource
program
needs a data file, which contains the messages and the corresponding
time stamps. For this example, we use the data file, messages0.dat
:
0.3 Hello
0.7 !
Note
In MUSIC, the default unit for time is seconds for the specification of times, while NEST uses miliseconds.
The script that sets up the receiving side (msgtest.py
)
of the example is shown in the following script.
We first import NEST and create an instance of the music_message_in_proxy
.
We then set the name of the port it listens on to msgdata
. The network is
simulated in steps of 10 ms.
#!/usr/bin/python3
import nest
mmip = nest.Create ('music_message_in_proxy')
nest.SetStatus (mmip, {'port_name' : 'msgdata'})
# Simulate and get message data with a granularity of 10 ms:
time = 0
while time < 1000:
nest.Simulate (10)
data = nest.GetStatus(mmip, 'data')
print data
time += 10
We then run the example using
mpirun -np 2 music msgtest.music
which yields the following output:
Nov 23 11:18:23 music_message_in_proxy::pre_run_hook() [Info]:
Mapping MUSIC input port 'msgdata' with width=0 and acceptable latency=0
ms.
Nov 23 11:18:23 NodeManager::prepare_nodes [Info]:
Preparing 1 node for simulation.
Nov 23 11:18:23 MUSICManager::enter_runtime [Info]:
Entering MUSIC runtime with tick = 0.1 ms
Nov 23 11:18:23 SimulationManager::start_updating_ [Info]:
Number of local nodes: 1
Simulation time (ms): 10
Number of OpenMP threads: 1
Number of MPI processes: 1
Nov 23 11:18:23 SimulationManager::run [Info]:
Simulation finished.
({'messages_times': array([], dtype=float64), 'messages': ()},)
Nov 23 11:18:23 NodeManager::prepare_nodes [Info]:
Preparing 1 node for simulation.
Nov 23 11:18:23 SimulationManager::start_updating_ [Info]:
Number of local nodes: 1
Simulation time (ms): 10
Number of OpenMP threads: 1
Number of MPI processes: 1
Nov 23 11:18:23 SimulationManager::run [Info]:
Simulation finished.
({'messages_times': array([], dtype=float64), 'messages': ()},)
.
.
Nov 23 11:18:23 NodeManager::prepare_nodes [Info]:
Preparing 1 node for simulation.
Nov 23 11:18:23 SimulationManager::start_updating_ [Info]:
Number of local nodes: 1
Simulation time (ms): 10
Number of OpenMP threads: 1
Number of MPI processes: 1
Nov 23 11:18:23 SimulationManager::run [Info]:
Simulation finished.
({'messages_times': array([ 300., 700.]), 'messages': ('Hello', '!')},)
Receiving continuous data¶
As in the case of string message, NEST currently only supports receiving
continuous data, but not sending. This means that we have to use another
of MUSIC’s test programs to generate the data for us. This time, we use
constsource
, which generates a sequence of numbers form 0 to w,
where w is the width of the port. The MUSIC configuration file
(conttest.music
) is shown in the following listing:
stoptime=1.0
[from]
np=1
binary=constsource
[to]
np=1
binary=./conttest.py
from.contdata -> to.contdata [10]
The receiving side is again implemented using a
PyNEST script (conttest.py
).
We first import the NEST and create an instance of the
music_cont_in_proxy
. we set the name of the port
it listens on to msgdata
. We then simulate the network in
steps of 10 ms.
#!/usr/bin/python3
import nest
mcip = nest.Create('music_cont_in_proxy')
nest.SetStatus(mcip, {'port_name' : 'cont_in'})
# Simulate and get vector data with a granularity of 10 ms:
time = 0
while time < 1000:
nest.Simulate (10)
data = nest.GetStatus (mcip, 'data')
print data
time += 10
The example is run using
mpirun -np 2 music conttest.music
which yields the following output:
Nov 23 11:33:26 music_cont_in_proxy::pre_run_hook() [Info]:
Mapping MUSIC input port 'contdata' with width=10.
Nov 23 11:33:26 NodeManager::prepare_nodes [Info]:
Preparing 1 node for simulation.
Nov 23 11:33:26 MUSICManager::enter_runtime [Info]:
Entering MUSIC runtime with tick = 0.1 ms
Nov 23 11:33:28 SimulationManager::start_updating_ [Info]:
Number of local nodes: 1
Simulation time (ms): 10
Number of OpenMP threads: 1
Number of MPI processes: 1
Nov 23 11:33:28 SimulationManager::run [Info]:
Simulation finished.
(array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]),)
.
.
Nov 23 11:33:28 NodeManager::prepare_nodes [Info]:
Preparing 1 node for simulation.
Nov 23 11:33:28 SimulationManager::start_updating_ [Info]:
Number of local nodes: 1
Simulation time (ms): 10
Number of OpenMP threads: 1
Number of MPI processes: 1
Nov 23 11:33:28 SimulationManager::run [Info]:
Simulation finished.
(array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]),)