Graph generation¶
Principle¶
In order to keep the code as generic and easy to maintain as possible, the generation of graphs or networks is divided in several steps:
- Structured connectivity: a simple graph is generated as an assembly of nodes and edges, without any biological properties. This allows us to implement known graph-theoretical algorithms in a straightforward fashion.
- Populations: detailed properties can be implemented, such as inhibitory synapses and separation of the neurons into inhibitory and excitatory populations – these can be done while respecting user-defined constraints.
- Synaptic properties: eventually, synaptic properties such as weight/strength and delays can be added to the network.
Modularity¶
The library as been designed so that these various operations can be realized in any order!
- Juste to get work on a topological graph/network:
- Create graph class
- Connect
- Set connection weights (optional)
- Spatialize (optional)
- Set types (optional: to use with NEST)
- To work on a really spatially embedded graph/network:
- Create spatial graph/network
- Connect (can depend on positions)
- Set connection weights (optional, can depend on positions)
- Set types (optional)
- Or to model a complex neural network in NEST:
- Create spatial network (with space and neuron types)
- Connect (can depend on types and positions)
- Set connection weights and types (optional, can depend on types and positions)
Setting weights¶
The weights can be either user-defined or generated by one of the available distributions (MAKE A REF). User-defined weights are generated via:
- a list of edges
- a list of weights
Pre-defined distributions require the following variables:
- a distribution name (“constant”, “gaussian”...)
- a dictionary containing the distribution properties
- an optional attribute for distributions that are correlated to another (e.g. the distances between neurons)
- a optional value defining the variance of the Gaussian noise that should be applied on the weights
There are several ways of settings the weights of a graph which depend on the time at which you assign them.
- At graph creation
- You can define the weights by entering a
weight_prop
argument to the constructor; this should be a dictionary containing at least the name of the weight distribution:{"distrib": "distribution_name"}
. If entered, this will be stored as a graph property and used to assign the weights whenever new edges are created unless you specifically assign rules for those new edges’ weights. - At any given time
You can use the
Connections
class to set the weights of agraph
explicitely by using:>>> nngt.Connections.weights(graph, elist=edges_to_weigh, distrib="distrib_of_choice", ...)
Examples¶
import nngt
import nngt.generation as ng
Generating simple graphs:
# random graphs
g1 = ng.erdos_renyi(1000, avg_deg=25)
g2 = ng.erdos_renyi(1000, avg_deg=25, directed=False) # the same graph but undirected
# scale-free with Gaussian weight distribution
g3 = nngt.Graph(1000, weight_prop={"distrib":"gaussian", "distrib_prop":{"avg": 60., "std":5.}})
ng.random_scale_free(2.2, 2.9, from_graph=g3)
Generating a network with excitatory and inhibitory neurons:
# 800 excitatory neurons, 200 inhibitory
net = nngt.Network.ei_network(1000, ei_ratio=0.2)
# connect the populations
ng.connect_neural_types(net, 1, -1, "erdos_renyi", {"density": 0.035}) # exc -> inhib
ng.connect_neural_types(net, 1, 1, "newman_watts", {"coord_nb":10, "proba_shortcut": 0.1}) # exc -> exc
ng.connect_neural_types(net, -1, 1, "random_scale_free", {"in_exp": 2.1, "out_exp": 2.6, "density": 0.2}) # inhib -> exc
ng.connect_neural_types(net, -1, -1, "erdos_renyi", {"density": 0.04}) # inhib -> inhib