# SPDX-License-Identifier: MIT
# Copyright (c) 2018 University of Zurich

Visualizer

Model: The model represents the data, and does nothing else. The model does NOT depend on the controller or the view.

  • EventsModel

  • StateVariablesModel

View: The view displays the model data, and sends user actions (e.g. button clicks) to the controller. The view can be independent of both the model and the controller or actually be the controller, and therefore depend on the model.

  • HistogramViewer

  • RasterplotViewer

  • LineplotViewer

Controller: The controller provides model data to the view, and interprets user actions such as button clicks. The controller depends on the view and the model. In some cases, the controller and the view are the same object.

  • HistogramController (called Histogram)

  • RasterplotController (called Rasterplot)

  • LineplotController (called Lineplot)

PlotSettings()
  • font sizes

Backends Currently, the visualizer supports matplotlib and pyqtgraph as backends. To switch between the two backends you simply have to change one input flag of the controller. This is shown below in the examples which are given for pyqtgraph and for matplotlib, however the figure are only shown for matplotlib.

How to use it - in short

1) get data

spikemonN1, spikemonN2, statemonN1, statemonN2 = run_your_own_brian_network()

2) define PLOTSETTING

from teili.tools.visualizer.DataViewers import PlotSettings
MyPlotSettings = PlotSettings(fontsize_title=20, fontsize_legend=14,
    Fontsize_axis_labels=14, marker_size = 30,colors = [‘r’, ‘b’, ‘g’, ‘c’,
    ‘k’, ‘m’, ‘y’])

3) call CONTROLLER of desired type of plot, e.g. Histogram, Rasterplot, Lineplot, …

from teili.tools.visualizer.DataControllers import Histogram
DataModel_to_attr = [(spikemonN1, ‘i’), (spikemonN2, ‘i’)]
subgroup_labels = [‘N1’, ‘N2’]
HC = Histogram(MyPlotSettings=MyPlotSettings, DataModel_to_attr=DataModel_to_attr, subgroup_labels=subgroup_labels, backend=‘matplotlib’)

If you want to change anything else on the main figure/window or one of the subplots you can directly access the mainfigure (matplotlib: figure, pyqtgraph: qt-window) as my_controller.mainfig or the corresponding subplot under my_controller.subfig

How to use it - the slightly longer version

Import the required modules and functions …

%pylab inline
import numpy as np
import os

from brian2 import us, ms, second, prefs, defaultclock, start_scope, SpikeGeneratorGroup, SpikeMonitor, StateMonitor
import matplotlib.pylab as plt
import pyqtgraph as pg
from PyQt5 import QtGui

from teili.core.groups import Neurons, Connections
from teili import TeiliNetwork
from teili.models.neuron_models import DPI
from teili.models.synapse_models import DPISyn
from teili.models.parameters.dpi_neuron_param import parameters as neuron_model_param
from teili.models.parameters.dpi_synapse_param import parameters as synapse_model_param

QtApp = QtGui.QApplication([])
def run_brian_network():
    prefs.codegen.target = "numpy"
    defaultclock.dt = 10 * us

    start_scope()
    N_input, N_N1, N_N2 = 1, 5, 3
    duration_sim = 100
    Net = TeiliNetwork()
    # setup spike generator
    spikegen_spike_times = np.sort(np.random.choice(size=500, a=np.arange(float(defaultclock.dt), float(duration_sim*ms)*0.9,
                                                                          float(defaultclock.dt*5)), replace=False)) * second
    spikegen_neuron_ids  = np.zeros_like(spikegen_spike_times) / ms
    gInpGroup = SpikeGeneratorGroup(N_input, indices=spikegen_neuron_ids,
                                    times=spikegen_spike_times, name='gtestInp')
    # setup neurons
    testNeurons1 = Neurons(N_N1, equation_builder=DPI(num_inputs=2), name="testNeuron")
    testNeurons1.set_params(neuron_model_param)
    testNeurons2 = Neurons(N_N2, equation_builder=DPI(num_inputs=2), name="testNeuron2")
    testNeurons2.set_params(neuron_model_param)
    # setup connections
    InpSyn = Connections(gInpGroup, testNeurons1, equation_builder=DPISyn(), name="testSyn", verbose=False)
    InpSyn.connect(True)
    InpSyn.weight = '200 + rand() * 100'
    Syn = Connections(testNeurons1, testNeurons2, equation_builder=DPISyn(), name="testSyn2", verbose=False)
    Syn.connect(True)
    Syn.weight = '200 + rand() * 100'
    # spike monitors input and network
    spikemonInp = SpikeMonitor(gInpGroup, name='spikemonInp')
    spikemonN1 = SpikeMonitor(testNeurons1, name='spikemon')
    spikemonN2 = SpikeMonitor(testNeurons2, name='spikemonOut')
    # state monitor neurons
    # statemonN1 = StateMonitor(testNeurons1, variables=["Iin", "Imem"], record=[0, 3], name='statemonNeu')
    statemonN1 = StateMonitor(testNeurons1, variables=["Iin", "Iahp"], record=True, name='statemonNeu')
    # statemonN2 = StateMonitor(testNeurons2, variables=['Iahp'], record=0, name='statemonNeuOut')
    statemonN2 = StateMonitor(testNeurons2, variables=['Imem'], record=True, name='statemonNeuOut')

    Net.add(gInpGroup, testNeurons1, testNeurons2, InpSyn, Syn, spikemonN1, spikemonN2, statemonN1, statemonN2)
    # run simulation
    Net.run(duration_sim * ms)
    print ('Simulation run for {} ms'.format(duration_sim))
    return spikemonN1, spikemonN2, statemonN1, statemonN2

Get the data to plot

Option A: run brian network to get SpikeMonitors and StateMonitors

spikemonN1, spikemonN2, statemonN1, statemonN2 = run_brian_network()

Option B: create DataModel instance from arrays, lists or brian-SpikeMonitors/StateMonitors

Available DataModels:

EventsModel: stores neuron_ids and spike_times

Example shows how to create it from array/list …

from teili.tools.visualizer.DataModels import EventsModel
neuron_ids  = [1, 1, 1, 2, 3, 1, 4, 5]
spike_times = [11, 14, 14, 16, 17, 25, 36, 40]
EM = EventsModel(neuron_ids=neuron_ids, spike_times=spike_times)

… or create from brian spike monitor

EM = EventsModel.from_brian_spike_monitor(spikemonN1)

Then the created EventsModel EM has the following attributes:

neuron_ids :
 [3 4 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4
 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4 1 0 2 3 4 1 0 3 2 4 1 3 0 2 4 1 3
 0 2 4 1 3 0 2 4 1 3 0 2 4 3 1 0 2 4 3 1 0 2 4 3 1 0 2 4 3 1 0 2 4 3 1 0 2
 4 3 1 0 2 4 3 1 0 2 4 3 1 0 2 3 4 1 0 2 3 4 1 0 3 2 4 1 0 3 4 2 1 3 0 4 2
 1 3 0 4 2 1 3 0 4 2 1 3 0 4 2 1 3 4 0 2 1 3 4 0 2 1 3 4 0 2 1 3 4 0 2 3 1
 4 0 2 3 1 4 0 2 3 1 4 0 3 2 1 4 0 3 2 1]
spike_times :
 [0.00387 0.004   0.00405 0.00411 0.00413 0.00628 0.00651 0.00659 0.00669
 0.00673 0.0085  0.0088  0.00891 0.00903 0.00908 0.0107  0.0111  0.01124
 0.01139 0.01145 0.01278 0.01326 0.01344 0.01363 0.0137  0.01491 0.01552
 0.01574 0.01595 0.01603 0.01699 0.01764 0.01788 0.01812 0.01821 0.01907
 0.01984 0.02012 0.02039 0.02049 0.02108 0.02183 0.02216 0.02247 0.02259
 0.02304 0.02391 0.02426 0.0246  0.02473 0.02506 0.02604 0.02644 0.02684
 0.027   0.02719 0.02822 0.02867 0.02914 0.02932 0.02935 0.03047 0.03094
 0.03139 0.03144 0.03155 0.03261 0.03313 0.03351 0.03359 0.03375 0.0347
 0.03525 0.03554 0.03576 0.03594 0.03682 0.03731 0.03748 0.03781 0.038
 0.03885 0.03937 0.03945 0.03987 0.04005 0.04084 0.04141 0.04142 0.042
 0.04222 0.04304 0.04359 0.04372 0.04438 0.04459 0.04526 0.04569 0.04591
 0.04663 0.04688 0.04752 0.04786 0.04816 0.04878 0.04902 0.04953 0.0498
 0.05021 0.0509  0.05117 0.05161 0.0518  0.05233 0.05309 0.05338 0.05373
 0.05384 0.05449 0.05522 0.0555  0.05575 0.05579 0.05655 0.05743 0.05774
 0.05788 0.05792 0.0587  0.05949 0.05978 0.05978 0.05989 0.06072 0.06163
 0.0618  0.06194 0.06198 0.06289 0.06389 0.06391 0.06419 0.06423 0.06514
 0.06605 0.06618 0.06643 0.06656 0.06748 0.06828 0.06855 0.06871 0.0689
 0.06966 0.07033 0.07075 0.07086 0.07114 0.07195 0.0725  0.07303 0.07306
 0.07343 0.07423 0.07471 0.0754  0.07545 0.0759  0.07656 0.07688 0.07763
 0.07774 0.07815 0.07875 0.07899 0.07997 0.08017 0.08062 0.0811  0.08119
 0.08212 0.08238 0.08286 0.08328 0.0833  0.08428 0.08459 0.08509 0.08537
 0.0855  0.08647 0.08687 0.08744 0.08757 0.08783 0.08879 0.08929 0.08982
 0.08987 0.0902  0.09128 0.0921  0.09261 0.09306 0.09351]

StateVariablesModel: stores any number of variables with their name and the list of timepoints when the variable was sampled

Example shows how to create it from array/list or from brian spike monitor

from teili.tools.visualizer.DataModels import StateVariablesModel

# create from array/list
state_variable_names = ['var_name']
num_neurons, num_timesteps = 6, 50
state_variables       = [np.random.random((num_neurons, num_timesteps))]
state_variables_times = [np.linspace(0, 100, num_timesteps)]
SVM = StateVariablesModel(state_variable_names, state_variables, state_variables_times)

# create from brian state monitorS
skip_not_rec_neuron_ids=False
SVM = StateVariablesModel.from_brian_state_monitors([statemonN1, statemonN2], skip_not_rec_neuron_ids)
skip_not_rec_neuron_ids=True
SVM = StateVariablesModel.from_brian_state_monitors([statemonN1, statemonN2], skip_not_rec_neuron_ids)

Then the created StateVariablesModel SVM has the following attributes:

Iin :
 [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
  0.00000000e+00]
 [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
  0.00000000e+00]
 [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
  0.00000000e+00]
 ...
 [6.82521123e-09 7.02939025e-09 6.74769896e-09 7.76629202e-09
  7.21872104e-09]
 [6.81237889e-09 7.01617406e-09 6.73501234e-09 7.75169045e-09
  7.20514890e-09]
 [6.79957068e-09 7.00298271e-09 6.72234958e-09 7.73711634e-09
  7.19160228e-09]]
t_Iin :
 [0.000e+00 1.000e-05 2.000e-05 ... 9.997e-02 9.998e-02 9.999e-02]
Iahp :
 [[5.00000000e-13 5.00000000e-13 5.00000000e-13 5.00000000e-13
  5.00000000e-13]
 [5.00000000e-13 5.00000000e-13 5.00000000e-13 5.00000000e-13
  5.00000000e-13]
 [5.00000000e-13 5.00000000e-13 5.00000000e-13 5.00000000e-13
  5.00000000e-13]
 ...
 [2.35349697e-11 2.45322975e-11 2.37997191e-11 2.54116963e-11
  2.38412778e-11]
 [2.35283328e-11 2.45253794e-11 2.37930076e-11 2.54045302e-11
  2.38345545e-11]
 [2.35216978e-11 2.45184633e-11 2.37862979e-11 2.53973662e-11
  2.38278332e-11]]
t_Iahp :
 [0.000e+00 1.000e-05 2.000e-05 ... 9.997e-02 9.998e-02 9.999e-02]
Imem :
 [[0.00000000e+00 0.00000000e+00 0.00000000e+00]
 [4.74578721e-33 4.74578721e-33 4.74578721e-33]
 [9.49157441e-33 9.49157441e-33 9.49157441e-33]
 ...
 [1.14559533e-10 2.80317027e-10 3.29995059e-10]
 [1.15005619e-10 2.80084652e-10 3.29576244e-10]
 [1.15447969e-10 2.79851599e-10 3.29157605e-10]]
t_Imem :
 [0.000e+00 1.000e-05 2.000e-05 ... 9.997e-02 9.998e-02 9.999e-02]

Plot the collected data

Define PlotSettings

  • The PlotSettings are defined only once for all the plots that will be created. This should make it easier to get consistent color-codings, fontsizes and markersize across different plots.

  • The colors can be defined as RGBA to additionally define the transparency

from teili.tools.visualizer.DataViewers import PlotSettings
MyPlotSettings = PlotSettings(fontsize_title=20, fontsize_legend=14, fontsize_axis_labels=14,
                               marker_size = 30,             # default 5
                               colors = ['r', 'b'],          # default ['r', 'b', 'g', 'c', 'k', 'm', 'y']
)

Call the DataController of the desired type of plot

So far in teili:
  • Histogram

  • Rasterplot

  • Lineplot

Histogram

Histogram - Inputs

* DataModel_to_attr             --> e.g. [(spikemonN1, 'i'), (spikemonN2, 'i')] OR
                                            [(EventsModel, 'i'), (EventsModel, 'i')]
* MyPlotSettings=PlotSettings()
* subgroup_labels=None          --> e.g. ['Neurongroup N1', 'Neurongroup N2']
* bins=None                     --> e.g. range(0,9)
* orientation='vertical'        --> 'horizontal' OR 'vertical'
* title='histogram
* xlabel='bins'
* ylabel='count',
* backend='matplotlib'
* show_immediately=False

Simple example to plot a histogram of two NeuronGroups to plot data from BrianSpikeMontiors/StateMonitors (or Datamodels) with matplotlib backend:

from teili.tools.visualizer.DataControllers import Histogram
# brian spike monitor
DataModel_to_attr =  [(spikemonN1, 'i'), (spikemonN2, 'i')]

# or plot data from DataModels
# EM1 = EventsModel.from_brian_spike_monitor(spikemonN1)
# EM2 = EventsModel.from_brian_spike_monitor(spikemonN2)
# DataModel_to_attr = {EM1: 'neuron_ids', EM2:'neuron_ids'}
subgroup_labels = ['N1', 'N2']

HC = Histogram(DataModel_to_attr=DataModel_to_attr,
                MyPlotSettings=MyPlotSettings,
                subgroup_labels=subgroup_labels,
                backend='matplotlib')
../_images/example_histogram.png

or PYQTGRAPH backend:

HC = Histogram(DataModel_to_attr=DataModel_to_attr,
                MyPlotSettings=MyPlotSettings,
                subgroup_labels=subgroup_labels,
                backend='pyqtgraph',
                QtApp=QtApp, show_immediately=True)

Rasterplot

Rasterplot - Inputs

* MyEventsModels                --> list of EventsModel or BrianSpikeMonitors
* MyPlotSettings=PlotSettings()
* subgroup_labels=None          --> ['N1', 'N2']
* time_range=None               --> (0, 0.9)
* neuron_id_range=None,         --> (0, 4)
* title='raster plot'
* xlabel='time
* ylabel='count',
* backend='matplotlib'
* add_histogram=False           --> show histogram of spikes per neuron id next to rasterplot
* show_immediately=False

Simple example to plot a raster plot of two NeuronGroups to plot data from BrianSpikeMontiors/StateMonitors (or Datamodels) with matplotlib backend:

from teili.tools.visualizer.DataControllers import Rasterplot
# plot data from BrianSpikeMontiors
MyEventsModels = [spikemonN1, spikemonN2]

# or plot data from EventsModel
# EM1 = EventsModel.from_brian_spike_monitor(spikemonN1)
# EM2 = EventsModel.from_brian_spike_monitor(spikemonN2)
# MyEventsModels = [EM1, EM2]

subgroup_labels = ['N1', 'N2']

# MATPLOTLIB backend - WITHOUT HISTOGRAM
RC = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels, backend='matplotlib')
# MATPLOTLIB backend - WITH HISTOGRAM
RC = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels, add_histogram=True)
../_images/example_rasterplot.png ../_images/example_rasterplot_with_histogram.png

or PYQTGRAPH backend:

# PYQTGRAPH backend - WITHOUT HISTOGRAM
RC = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels, backend='pyqtgraph', QtApp=QtApp)
# PYQTGRAPH backend - WITH HISTOGRAM
RC = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels,
                    add_histogram=True, backend='pyqtgraph', QtApp=QtApp, show_immediately=True)

LinePlot

Lineplot - Inputs

* DataModel_to_x_and_y_attr --> e.g. [(statemonN1, ('Imem', 't_Imem')),
                                               (statemonN2, ('Iahp', 't_Iahp'))]
                                        OR
                                     [(StateVariablesModel_N1, ('Imem', 't_Imem')),
                                              (StateVariablesModel_N2, ('Iahp', 't_Iahp'))]
* MyPlotSettings=PlotSettings()
* subgroup_labels=None      --> ['N1', 'N2']
* x_range=None,             --> (0, 0.9)
* y_range=None,             --> (0, 4)
* title='Lineplot'
* xlabel=None
* ylabel=None
* backend='matplotlib'
* show_immediately=False

Simple example to plot a line plot of two NeuronGroups to plot data from BrianSpikeMontiors/StateMonitors (or Datamodels) with matplotlib backend:

from teili.tools.visualizer.DataControllers import Lineplot
# plot data from BrianSpikeMontiors
DataModel_to_x_and_y_attr = [(statemonN1, ('t', 'Iin')), (statemonN2, ('t', 'Imem'))]
# or plot data from StateVariablesModel
SVM_N1 = StateVariablesModel.from_brian_state_monitors([statemonN1])
SVM_N2 = StateVariablesModel.from_brian_state_monitors([statemonN2])
DataModel_to_x_and_y_attr = [(SVM_N1, ('t_Iin', 'Iin')), (SVM_N2, ('t_Imem', 'Imem'))]

subgroup_labels = ['N1', 'N2']
LC = Lineplot(DataModel_to_x_and_y_attr=DataModel_to_x_and_y_attr,
                  MyPlotSettings=MyPlotSettings,
                  subgroup_labels=subgroup_labels,
                  backend='matplotlib')
../_images/example_lineplot.png

or PYQTGRAPH backend:

LC = Lineplot(DataModel_to_x_and_y_attr=DataModel_to_x_and_y_attr,
                  MyPlotSettings=MyPlotSettings,
                  subgroup_labels=subgroup_labels,
                  backend='pyqtgraph', QtApp=QtApp, show_immediately=True)

Additional functionalities

Combine different plots

You can combine different plot in any kind of way. Just specifiy the location and size of the subplots you would want to have and then pass the respective subplots on to the different controller. When using more than one controller make sure to set show_immediately only to true in the controller that is called last.

… with matplotlib

# define plot structure BEFOREHAND
mainfig = plt.figure()
subfig1 = mainfig.add_subplot(321)
subfig2 = mainfig.add_subplot(322)
subfig3 = mainfig.add_subplot(324)
subfig4 = mainfig.add_subplot(325)

plt.subplots_adjust(left=0.125, right=0.9, bottom=0.1, top=4., wspace=0.05, hspace=0.2)

MyEventsModels = [spikemonN1, spikemonN2]
subgroup_labels = ['N1', 'N2']
RC = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels,
                 mainfig=mainfig, subfig_rasterplot=subfig1, subfig_histogram = subfig2,
                 add_histogram=True, show_immediately=False)

DataModel_to_attr = [(spikemonN1, 'i')]
subgroup_labels = ['N1']
HC = Histogram(DataModel_to_attr=DataModel_to_attr, MyPlotSettings=MyPlotSettings,
                 subgroup_labels=subgroup_labels, mainfig=mainfig, subfig=subfig3, show_immediately=False)

DataModel_to_attr = [(spikemonN2, 'i')]
subgroup_labels = ['N2']
HC = Histogram(DataModel_to_attr=DataModel_to_attr, MyPlotSettings=MyPlotSettings,
             subgroup_labels=subgroup_labels, mainfig=mainfig, subfig=subfig4, show_immediately=True)
../_images/example_combinedplots.png

… with pyqtgraph

# define plot structure BEFOREHAND
mainfig = pg.GraphicsWindow()
subfig1 = mainfig.addPlot(row=0, col=0)
subfig2 = mainfig.addPlot(row=0, col=1)
subfig2.setYLink(subfig1)
subfig3 = mainfig.addPlot(row=1, col=1)
subfig4 = mainfig.addPlot(row=2, col=0)

plt.subplots_adjust(left=0.125, right=0.9, bottom=0.1, top=4., wspace=0.05, hspace=0.2)

MyEventsModels = [spikemonN1, spikemonN2]
subgroup_labels = ['N1', 'N2']
RC = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels,
                         mainfig=mainfig, subfig_rasterplot=subfig1, subfig_histogram = subfig2, QtApp=QtApp,
                         backend='pyqtgraph', add_histogram=True, show_immediately=False)

DataModel_to_attr = [(spikemonN1, 'i')]
subgroup_labels = ['N1']
HC = Histogram(DataModel_to_attr=DataModel_to_attr, MyPlotSettings=MyPlotSettings,
                         subgroup_labels=subgroup_labels,
                         backend='pyqtgraph', mainfig=mainfig, subfig=subfig3,  QtApp=QtApp,
                         show_immediately=False)

DataModel_to_attr = [(spikemonN1, 'i')]
subgroup_labels = ['N2']
HC = Histogram(DataModel_to_attr=DataModel_to_attr, MyPlotSettings=MyPlotSettings,
                         subgroup_labels=subgroup_labels,
                         backend='pyqtgraph', mainfig=mainfig, subfig=subfig4, QtApp=QtApp,
                         show_immediately=True)

Add second plot with a detailed view of a given plot

Create original plot of which you would like to have a detailed version as well TWICE

MyEventsModels = [spikemonN1, spikemonN2]
subgroup_labels = ['N1', 'N2']

mainfig = pg.GraphicsWindow()
subfig1 = mainfig.addPlot(row=0, col=0)
mainfig.nextRow()
subfig2 = mainfig.addPlot(row=1, col=0)


RC_org = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels,
                              mainfig=mainfig, subfig_rasterplot=subfig1,
                         QtApp=QtApp, backend='pyqtgraph', show_immediately=False)
RC_detail = Rasterplot(MyEventsModels=MyEventsModels, MyPlotSettings=MyPlotSettings, subgroup_labels=subgroup_labels,
                                 mainfig=mainfig, subfig_rasterplot=subfig2,
                         QtApp=QtApp, backend='pyqtgraph', show_immediately=False)

RC_org.connect_detailed_subplot(filled_subplot_original_view=RC_org.viewer.subfig_rasterplot,
                                filled_subplot_detailed_view=RC_detail.viewer.subfig_rasterplot,
                              ~