1# -*- coding: utf-8 -*-
2#
3# plot_weight_matrices.py
4#
5# This file is part of NEST.
6#
7# Copyright (C) 2004 The NEST Initiative
8#
9# NEST is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 2 of the License, or
12# (at your option) any later version.
13#
14# NEST is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with NEST.  If not, see <http://www.gnu.org/licenses/>.
21
22"""
23Plot weight matrices example
24----------------------------
25
26This example demonstrates how to extract the connection strength
27for all the synapses among two populations of neurons and gather
28these values in weight matrices for further analysis and visualization.
29
30All connection types between these populations are considered, i.e.,
31four weight matrices are created and plotted.
32
33"""
34
35###############################################################################
36# First, we import all necessary modules to extract, handle and plot
37# the connectivity matrices
38
39import numpy as np
40import matplotlib.pyplot as plt
41import nest
42import matplotlib.gridspec as gridspec
43from mpl_toolkits.axes_grid1 import make_axes_locatable
44
45###############################################################################
46# We now specify a function to extract and plot weight matrices for all
47# connections among `E_neurons` and `I_neurons`.
48#
49# We initialize all the matrices, whose dimensionality is determined by the
50# number of elements in each population.
51# Since in this example, we have 2 populations (E/I), :math:`2^2` possible
52# synaptic connections exist (EE, EI, IE, II).
53#
54# Note the use of "post-pre" notation when referring to synaptic connections.
55# As a matter of convention in computational neuroscience, we refer to the
56# connection from inhibitory to excitatory neurons (I->E) as EI (post-pre) and
57# connections from excitatory to inhibitory neurons (E->I) as IE (post-pre).
58
59
60def plot_weight_matrices(E_neurons, I_neurons):
61
62    W_EE = np.zeros([len(E_neurons), len(E_neurons)])
63    W_EI = np.zeros([len(I_neurons), len(E_neurons)])
64    W_IE = np.zeros([len(E_neurons), len(I_neurons)])
65    W_II = np.zeros([len(I_neurons), len(I_neurons)])
66
67    a_EE = nest.GetConnections(E_neurons, E_neurons)
68
69    # Using `get`, we can extract the value of the connection weight,
70    # for all the connections between these populations
71    c_EE = a_EE.weight
72
73    # Repeat the two previous steps for all other connection types
74    a_EI = nest.GetConnections(I_neurons, E_neurons)
75    c_EI = a_EI.weight
76    a_IE = nest.GetConnections(E_neurons, I_neurons)
77    c_IE = a_IE.weight
78    a_II = nest.GetConnections(I_neurons, I_neurons)
79    c_II = a_II.weight
80
81    # We now iterate through the range of all connections of each type.
82    # To populate the corresponding weight matrix, we begin by identifying
83    # the source-node_id (by using .source) and the target-node_id.
84    # For each node_id, we subtract the minimum node_id within the corresponding
85    # population, to assure the matrix indices range from 0 to the size of
86    # the population.
87
88    # After determining the matrix indices [i, j], for each connection
89    # object, the corresponding weight is added to the entry W[i,j].
90    # The procedure is then repeated for all the different connection types.
91    a_EE_src = a_EE.source
92    a_EE_trg = a_EE.target
93    a_EI_src = a_EI.source
94    a_EI_trg = a_EI.target
95    a_IE_src = a_IE.source
96    a_IE_trg = a_IE.target
97    a_II_src = a_II.source
98    a_II_trg = a_II.target
99
100    for idx in range(len(a_EE)):
101        W_EE[a_EE_src[idx] - min(E_neurons),
102             a_EE_trg[idx] - min(E_neurons)] += c_EE[idx]
103    for idx in range(len(a_EI)):
104        W_EI[a_EI_src[idx] - min(I_neurons),
105             a_EI_trg[idx] - min(E_neurons)] += c_EI[idx]
106    for idx in range(len(a_IE)):
107        W_IE[a_IE_src[idx] - min(E_neurons),
108             a_IE_trg[idx] - min(I_neurons)] += c_IE[idx]
109    for idx in range(len(a_II)):
110        W_II[a_II_src[idx] - min(I_neurons),
111             a_II_trg[idx] - min(I_neurons)] += c_II[idx]
112
113    fig = plt.figure()
114    fig.subtitle('Weight matrices', fontsize=14)
115    gs = gridspec.GridSpec(4, 4)
116    ax1 = plt.subplot(gs[:-1, :-1])
117    ax2 = plt.subplot(gs[:-1, -1])
118    ax3 = plt.subplot(gs[-1, :-1])
119    ax4 = plt.subplot(gs[-1, -1])
120
121    plt1 = ax1.imshow(W_EE, cmap='jet')
122
123    divider = make_axes_locatable(ax1)
124    cax = divider.append_axes("right", "5%", pad="3%")
125    plt.colorbar(plt1, cax=cax)
126
127    ax1.set_title('W_{EE}')
128    plt.tight_layout()
129
130    plt2 = ax2.imshow(W_IE)
131    plt2.set_cmap('jet')
132    divider = make_axes_locatable(ax2)
133    cax = divider.append_axes("right", "5%", pad="3%")
134    plt.colorbar(plt2, cax=cax)
135    ax2.set_title('W_{EI}')
136    plt.tight_layout()
137
138    plt3 = ax3.imshow(W_EI)
139    plt3.set_cmap('jet')
140    divider = make_axes_locatable(ax3)
141    cax = divider.append_axes("right", "5%", pad="3%")
142    plt.colorbar(plt3, cax=cax)
143    ax3.set_title('W_{IE}')
144    plt.tight_layout()
145
146    plt4 = ax4.imshow(W_II)
147    plt4.set_cmap('jet')
148    divider = make_axes_locatable(ax4)
149    cax = divider.append_axes("right", "5%", pad="3%")
150    plt.colorbar(plt4, cax=cax)
151    ax4.set_title('W_{II}')
152    plt.tight_layout()
153
154#################################################################################
155# The script iterates through the list of all connections of each type.
156# To populate the corresponding weight matrix, we identify the source-node_id
157# (first element of each connection object, `n[0]`) and the target-node_id (second
158# element of each connection object, `n[1]`).
159# For each `node_id`, we subtract the minimum `node_id` within the corresponding
160# population, to assure the matrix indices range from 0 to the size of the
161# population.
162#
163# After determining the matrix indices `[i, j]`, for each connection object, the
164# corresponding weight is added to the entry `W[i,j]`. The procedure is then
165# repeated for all the different connection types.
166#
167# We then plot the figure, specifying the properties we want. For example, we
168# can display all the weight matrices in a single figure, which requires us to
169# use ``GridSpec`` to specify the spatial arrangement of the axes.
170# A subplot is subsequently created for each connection type. Using ``imshow``,
171# we can visualize the weight matrix in the corresponding axis. We can also
172# specify the colormap for this image.
173# Using the ``axis_divider`` module from ``mpl_toolkits``, we can allocate a small
174# extra space on the right of the current axis, which we reserve for a
175# colorbar.
176# We can set the title of each axis and adjust the axis subplot parameters.
177# Finally, the last three steps are repeated for each synapse type.
178