NNGT example notebook

This notebook provides the following examples

  • Basic graph creation
  • Use of generation algorithms
  • Using node and edge attributes
  • Creation of complex networks containing various groups of neurons
  • NEST interaction
  • Space-embedded networks

Last update: 7/11/2017

Required imports


In [1]:
%matplotlib inline
import matplotlib
#matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (15, 6)

import numpy as np

import nngt
from nngt import generation as ng
from nngt import analysis as na
from nngt import plot as nplt

nngt.seed(0)


[INFO @ nngt.geometry]: Could not import dxftools: No module named dxfgrabber
[INFO @ nngt]: 
    -----------
    NNGT loaded
    -----------
Graph library:  graph-tool
Multithreading: False (1 thread)
Plotting:       True
NEST support:   NEST 2.12.0
Database:       False
    -----------

Basic graph creation


In [2]:
''' Create an empty Graph '''
g = nngt.Graph()

In [3]:
''' Add nodes '''
g.new_node(10)  # create nodes 0, 1, ... to 9
g.node_nb()     # returns 10


Out[3]:
10

In [4]:
''' Add edges between these nodes '''
g.new_edge(1, 4)  # create on connection going from 11 to 56
g.edge_nb()       # returns 1
g.new_edges([(0, 3), (5, 9), (9, 3)])
g.edge_nb()       # returns 4


Out[4]:
4

Node and edge attributes


In [5]:
''' Adding a node with attributes '''
g2 = nngt.Graph()
g2.new_node(attributes={'size': 2., 'color': 'blue'}, value_types={'size': 'double', 'color': 'string'})
g2.node_attributes


Out[5]:
{'color': array(['blue'], dtype=object), 'size': array([ 2.])}

In [6]:
''' Adding several nodes with attributes '''
g2.new_node(3, attributes={'size': [4., 5., 1.], 'color': ['r', 'g', 'b']}, value_types={'size': 'double', 'color': 'string'})
g2.node_attributes


Out[6]:
{'color': array(['blue', 'r', 'g', 'b'], dtype=object),
 'size': array([ 2.,  4.,  5.,  1.])}

In [7]:
''' Default filled if no attributes are provided '''
g2.new_node(3)
g2.node_attributes


Out[7]:
{'color': array(['blue', 'r', 'g', 'b', '', '', ''], dtype=object),
 'size': array([ 2.,  4.,  5.,  1.,  0.,  0.,  0.])}

In [8]:
''' Attributes can also be created afterwards '''
g3 = nngt.Graph(nodes=100)
g3.new_node_attribute('size', 'double', values=np.random.uniform(0, 20, 100))
g3.node_attributes


Out[8]:
{'size': array([ 10.97627008,  14.30378733,  12.05526752,  10.89766366,
          8.47309599,  12.91788226,   8.75174423,  17.83546002,
         19.27325521,   7.66883038,  15.83450076,  10.5778984 ,
         11.36089122,  18.51193277,   1.42072116,   1.74258599,
          0.40436795,  16.65239691,  15.56313502,  17.40024296,
         19.57236684,  15.98317128,   9.22958725,  15.61058353,
          2.36548852,  12.79842043,   2.86706575,  18.89337834,
         10.43696644,   8.2932388 ,   5.29111224,  15.48467379,
          9.12300664,  11.36867898,   0.37579601,  12.35270994,
         12.24191445,  12.33867994,  18.87496157,  13.63640598,
          7.19015801,   8.74063908,  13.95262392,   1.20450943,
         13.33533431,  13.41275739,   4.20765122,   2.57852595,
          6.30856702,   7.27421542,  11.40393541,   8.77203027,
         19.76747676,   2.04089621,   4.17753512,   3.22619036,
         13.06216651,   5.06583205,   9.32621546,   4.88851184,
          3.17939167,   2.20750282,  13.12659179,   2.76365903,
          3.93164723,   7.37450341,  16.4198646 ,   1.94202552,
         16.75889815,   1.92196816,  19.5291893 ,   9.37302403,
         19.53522176,  12.09691039,  14.78527159,   0.78375585,
          5.65613925,   2.40393122,   5.92280395,   2.37455438,
          6.35966359,   8.28525989,   1.28294993,  13.84944239,
         11.33202908,   5.30778982,  10.46496107,   1.87881022,
         11.51892991,  18.58592395,   6.37137905,  13.3482076 ,
          2.63595725,  14.32654408,   5.78812186,   3.66382724,
         11.7302587 ,   0.40215092,  16.57880058,   0.09390952])}

In [9]:
edges = g3.new_edges(np.random.randint(0, 100, (50, 2)))
g3.new_edge_attribute('rank', 'int', val=0)
g3.set_edge_attribute('rank', val=2, edges=edges[:3, :])
g3.edge_attributes


Out[9]:
{'rank': array([2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0], dtype=int32),
 'weight': array([ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,
         1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.])}

Generating and analyzing more complex networks

NNGT provides a whole set of methods to connect nodes in specific fashions inside a graph. These methods are present in the nngt.generation module, and the network properties can then be plotted and analyzed via the tools present in the nngt.plot and nngt.analysis modules.

NNGT implements some fast generation tools to create several of the standard networks, such as

Erdős-Rényi


In [10]:
'''
Create an Erdos-Renyi network,
then look at its in/total-degree distributions
and its clustering coefficient
'''
g = ng.erdos_renyi(nodes=1000, avg_deg=100)
nplt.degree_distribution(g, ('in', 'total'), show=True)
print("Clustering: {}".format(na.clustering(g)))


Clustering: 0.189790270199

Scale-free

More heterogeneous networks, with scale-free degree distribution (but no correlations like in Barabasi-Albert networks and user-defined exponents)


In [11]:
''' Compare in/out-degree distributions + clustering '''
g = ng.random_scale_free(1.8, 3.2, nodes=1000, avg_deg=100)
nplt.degree_distribution(g, ('in', 'out'), num_bins=30, logx=True, logy=True, show=True)
print("Clustering: {}".format(na.clustering(g)))


Clustering: 0.245245150598

Towards realistic neuronal networks: neural groups and spatial embedding

NNGT provides two classes to deal with spatial embedding and neuronal properties: the SpatialGraph and Network classes, which can be combined to get a SpatialNetwork, including both neuronal properties and spatial embedding.

Creating groups of neurons with specific properties

The NeuralGroup class allows the user to create groups of neurons that share common electrophysiological and synaptic properties.


In [12]:
''' Create groups with different parameters '''
base_params = {'E_L': -60., 'V_th': -55., 'b': 10., 'tau_w': 100., 'V_reset': -65., 't_ref': 1., 'g_L': 10., 'C_m': 250.}
params1, params2, params3 = base_params.copy(), base_params.copy(), base_params.copy()
params1.update({'E_L': -65., 'b': 30., 'I_e': 350., 'tau_w': 400.})  # oscillators
params2.update({'b': 20., 'V_reset': -50., 'tau_w': 500.})           # bursters

g1 = nngt.NeuralGroup(nodes=400, neuron_model='aeif_psc_alpha', neuron_param=params1, syn_model='tsodyks2_synapse', syn_param={'U': 0.5})
g2 = nngt.NeuralGroup(nodes=200, neuron_model='aeif_psc_alpha', neuron_param=params2, syn_model='tsodyks2_synapse')
g3 = nngt.NeuralGroup(nodes=200, neuron_model='aeif_psc_alpha', neuron_param=params3, syn_model='tsodyks2_synapse')

''' Create the population that will represent the neuronal network from these groups '''
pop = nngt.NeuralPop.from_groups([g1, g2, g3], names=['oscillators', 'bursters', 'adaptive'])

''' Create the network from this population, using a Gaussian in-degree '''
net = nngt.generation.gaussian_degree(100., 15., population=pop, weights=1500.)

In [13]:
'''
Compare the properties across the different groups
(even though it makes little sense here since they
all belong to the same ER graph)
'''
# we will plot al them on the same axis
fig, ax = plt.subplots()

nplt.degree_distribution(net, nodes=pop['oscillators'].ids, axis=ax, color='r', label='osc.', alpha=0.5)
nplt.degree_distribution(net, nodes=pop['bursters'].ids, axis=ax, color='b', label='burst.', alpha=0.5)
nplt.degree_distribution(net, nodes=pop['adaptive'].ids, axis=ax, color='k', label='adapt.', alpha=0.5)

plt.show()



In [14]:
''' Create the network in NEST and monitor the activity '''
import nngt.simulation as ns
import nest

nest.ResetKernel()
nest.SetKernelStatus({'local_num_threads': 4})

# send the network to NEST
gids = net.to_nest()
# record each group separately
recorders, records = ns.monitor_groups(
    pop.keys(), net, ['spike_detector', 'multimeter'],
    [{}, {'record_from': ['V_m', 'w', 'I_syn_ex'], 'to_accumulator': True}])

# simulate
nest.Simulate(2000.)

# plot the resulting activity
ns.plot_activity(recorders, records, network=net, label=2*['oscillators', 'bursters', 'adaptive'], normalize=net.population)

plt.show()



In [15]:
''' Correcting the initial synchrony '''
import nngt.simulation as ns
import nest

rnd_states = {
    'V_m': ['uniform', -80., -60.],
    'w': ['uniform', 200., 2000.]
}

nest.ResetKernel()
nest.SetKernelStatus({'local_num_threads': 4})

gids = net.to_nest()
ns.randomize_neural_states(net, rnd_states)  # randomize potential and adaptation variable
recorders, records = ns.monitor_groups(pop.keys(), net)

nest.Simulate(2000.)

ns.plot_activity(recorders, records, network=net, label=['oscillators', 'bursters', 'adaptive'])
plt.show()



In [ ]: