Monitors (Brian 1 –> 2 conversion)
Monitoring spiking activity
The main class to record spiking activity is SpikeMonitor
which is created in
the same way as in Brian 1. However, the internal storage and retrieval of
spikes is different. In Brian 1, spikes were stored as a list of pairs
(i, t)
, the index and time of each spike. In Brian 2, spikes are stored as
two arrays i
and t
, storing the indices and times. You can access these
arrays as attributes of the monitor, there’s also a convenience attribute it
that returns both at the same time. The following table shows how the spike
indices and times can be retrieved in various forms in Brian 1 and Brian 2:
Brian 1 |
Brian 2 |
---|---|
mon = SpikeMonitor(group)
#... do the run
list_of_pairs = mon.spikes
index_list, time_list = zip(*list_of_pairs)
index_array = array(index_list)
time_array = array(time_list)
# time_array is unitless in Brian 1
|
mon = SpikeMonitor(group)
#... do the run
list_of_pairs = zip(*mon.it)
index_list = list(mon.i)
time_list = list(mon.t)
index_array, time_array = mon.i, mon.t
# time_array has units in Brian 2
|
You can also access the spike times for individual neurons. In Brian 1, you could directly index the monitor which is no longer allowed in Brian 2. Instead, ask for a dictionary of spike times and index the returned dictionary:
Brian 1 |
Brian 2 |
---|---|
# dictionary of spike times for each neuron:
spike_dict = mon.spiketimes
# all spikes for neuron 3:
spikes_3 = spike_dict[3] # (no units)
spikes_3 = mon[3] # alternative (no units)
|
# dictionary of spike times for each neuron:
spike_dict = mon.spike_trains()
# all spikes for neuron 3:
spikes_3 = spike_dict[3] # with units
|
In Brian 2, SpikeMonitor
also provides the functionality of the Brian 1
classes SpikeCounter
and PopulationSpikeCounter
. If you are only
interested in the counts and not in the individual spike events, use
record=False
to save the memory of storing them:
Brian 1 |
Brian 2 |
---|---|
counter = SpikeCounter(group)
pop_counter = PopulationSpikeCounter(group)
#... do the run
# Number of spikes for neuron 3:
count_3 = counter[3]
# Total number of spikes:
total_spikes = pop_counter.nspikes
|
counter = SpikeMonitor(group, record=False)
#... do the run
# Number of spikes for neuron 3
count_3 = counter.count[3]
# Total number of spikes:
total_spikes = counter.num_spikes
|
Currently Brian 2 provides no functionality to calculate statistics such as
correlations or histograms online, there is no equivalent to the following
classes that existed in Brian 1: AutoCorrelogram
, CoincidenceCounter
,
CoincidenceMatrixCounter
, ISIHistogramMonitor
, VanRossumMetric
.
You will therefore have to be calculate the corresponding statistiacs manually
after the simulation based on the information stored in the SpikeMonitor
. If
you use the default Runtime code generation, you can also create a new Python class that
calculates the statistic online
(see this example from a Brian 2 tutorial).
Monitoring variables
Single variables are recorded with a StateMonitor
in the same way as in
Brian 1, but the times and variable values are accessed differently:
Brian 1 |
Brian 2 |
---|---|
mon = StateMonitor(group, 'v',
record=True)
# ... do the run
# plot the trace of neuron 3:
plot(mon.times/ms, mon[3]/mV)
# plot the traces of all neurons:
plot(mon.times/ms, mon.values.T/mV)
|
mon = StateMonitor(group, 'v',
record=True)
# ... do the run
# plot the trace of neuron 3:
plot(mon.t/ms, mon[3].v/mV)
# plot the traces of all neurons:
plot(mon.t/ms, mon.v.T/mV)
|
Further differences:
StateMonitor
now records in the'start'
scheduling slot by default. This leads to a more intuitive correspondence between the recorded times and the values: in Brian 1 (whereStateMonitor
recorded in the'end'
slot) the recorded value at 0ms was not the initial value of the variable but the value after integrating it for a single time step. The disadvantage of this new default is that the very last value at the end of the last time step of a simulation is not recorded anymore. However, this value can be manually added to the monitor by callingStateMonitor.record_single_timestep
.To not record every time step, use the
dt
argument (as for all other classes) instead of specifying a number oftimesteps
.Using
record=False
does no longer provide mean and variance of the recorded variable.
In contrast to Brian 1, StateMonitor
can now record multiple variables and
therefore replaces Brian 1’s MultiStateMonitor
:
Brian 1 |
Brian 2 |
---|---|
mon = MultiStateMonitor(group, ['v', 'w'],
record=True)
# ... do the run
# plot the traces of v and w for neuron 3:
plot(mon['v'].times/ms, mon['v'][3]/mV)
plot(mon['w'].times/ms, mon['w'][3]/mV)
|
mon = StateMonitor(group, ['v', 'w'],
record=True)
# ... do the run
# plot the traces of v and w for neuron 3:
plot(mon.t/ms, mon[3].v/mV)
plot(mon.t/ms, mon[3].w/mV)
|
To record variable values at the times of spikes, Brian 2 no longer provides a
separate class as Brian 1 did (StateSpikeMonitor
). Instead, you can use
SpikeMonitor
to record additional variables (in addition to the neuron index
and the spike time):
Brian 1 |
Brian 2 |
---|---|
# We assume that "group" has a varying threshold
mon = StateSpikeMonitor(group, 'v')
# ... do the run
# plot the mean v at spike time for each neuron
mean_values = [mean(mon.values('v', idx))
for idx in range(len(group))]
plot(mean_values/mV, 'o')
|
# We assume that "group" has a varying threshold
mon = SpikeMonitor(group, variables='v')
# ... do the run
# plot the mean v at spike time for each neuron
values = mon.values('v')
mean_values = [mean(values[idx])
for idx in range(len(group))]
plot(mean_values/mV, 'o')
|
Note that there is no equivalent to StateHistogramMonitor
, you will have to
calculate the histogram from the recorded values or write your own custom
monitor class.