211 lines
7.5 KiB
Python
211 lines
7.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
# SPDX-FileCopyrightText: 2015-2023 Tanguy Fardet
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
# doc/examples/introduction_to_groups.py
|
|
|
|
''' Introduction to neural groups '''
|
|
|
|
import numpy as np
|
|
|
|
import nngt
|
|
from nngt import Group, MetaGroup, Structure
|
|
from nngt import MetaNeuralGroup, NeuralGroup, NeuralPop
|
|
|
|
|
|
''' ------------- #
|
|
# Creating groups #
|
|
# ------------- '''
|
|
|
|
# the default group is empty, which is not very useful in general
|
|
empty_group = nngt.Group()
|
|
print(empty_group)
|
|
print("Group is empty?", empty_group.size == 0, "\nIt has therefore no ids:",
|
|
empty_group.ids, "\n")
|
|
|
|
# to create a useful group, one can just say how many nodes it should contain
|
|
group1 = Group(500) # a group with 500 nodes
|
|
print("Ids are not created", group1.ids, "len(group1) is", len(group1),
|
|
"but the size is stored in group1.size:", group1.size, "\n")
|
|
|
|
# if you want to set the ids directly, you can pass them directly, otherwise
|
|
# they will be determine automatically when a Network is created with the group
|
|
group2 = NeuralGroup(range(10, 20)) # neurons with ids from 10 to 19
|
|
print("Neuron ids are:", group2.ids, "\n")
|
|
|
|
|
|
''' ------------------------- #
|
|
# Creating a structured graph #
|
|
# ------------------------- '''
|
|
|
|
room1 = nngt.Group(25)
|
|
room2 = nngt.Group(50)
|
|
room3 = nngt.Group(40)
|
|
room4 = nngt.Group(35)
|
|
|
|
names = ["R1", "R2", "R3", "R4"]
|
|
|
|
struct = nngt.Structure.from_groups((room1, room2, room3, room4), names)
|
|
|
|
g = nngt.Graph(structure=struct)
|
|
|
|
for room in struct:
|
|
nngt.generation.connect_groups(g, room, room, "all_to_all")
|
|
|
|
nngt.generation.connect_groups(g, (room1, room2), struct, "erdos_renyi",
|
|
avg_deg=10, ignore_invalid=True)
|
|
|
|
nngt.generation.connect_groups(g, room3, room1, "erdos_renyi", avg_deg=20)
|
|
|
|
nngt.generation.connect_groups(g, room4, room3, "erdos_renyi", avg_deg=10)
|
|
|
|
if nngt.get_config("with_plot"):
|
|
# chord diagram
|
|
sg = g.get_structure_graph()
|
|
|
|
nngt.plot.chord_diagram(sg, names="name", sort="distance",
|
|
use_gradient=True, show=True)
|
|
|
|
# spring-block layout
|
|
nngt.plot.library_draw(g, node_cmap="viridis", show=True)
|
|
|
|
|
|
''' ------------------- #
|
|
# More group properties #
|
|
# ------------------- '''
|
|
|
|
# group can have names
|
|
named_group = Group(500, name="named_group")
|
|
print("I'm a named group!", named_group, "\n")
|
|
|
|
# NeuralGroups can store whether neurons are excitatory or inhibitory
|
|
exc = NeuralGroup(800, neuron_type=1) # excitatory group
|
|
exc2 = NeuralGroup(800, neuron_type=1) # also excitatory
|
|
inhib = NeuralGroup(200, neuron_type=-1) # inhibitory group
|
|
print("'exc2' is an excitatory group:", exc2.neuron_type == 1,
|
|
"/ 'inhib' is an inhibitory group:", inhib.neuron_type == -1, "\n")
|
|
|
|
|
|
''' ---------------------------------- #
|
|
# Complete groups for NEST simulations #
|
|
# ---------------------------------- '''
|
|
|
|
# to make a complete neuronal group, one must include a valid neuronal type,
|
|
# model and (optionally) associated parameters
|
|
|
|
pyr = NeuralGroup(800, neuron_type=1, neuron_model="iaf_psc_alpha",
|
|
neuron_param={"tau_m": 50.}, name="pyramidal_cells")
|
|
|
|
fsi = NeuralGroup(200, neuron_type=-1, neuron_model="iaf_psc_alpha",
|
|
neuron_param={"tau_m": 20.},
|
|
name="fast_spiking_interneurons")
|
|
|
|
|
|
''' --------------------------- #
|
|
# Creating neuronal populations #
|
|
# --------------------------- '''
|
|
|
|
pop = NeuralPop.from_groups((pyr, fsi))
|
|
|
|
# making populations from scratch
|
|
pop = nngt.NeuralPop(with_models=False) # empty population
|
|
pop.create_group(200, "first_group") # create excitatory group
|
|
pop.create_group(5, "second_group", neuron_type=-1) # create inhibitory group
|
|
|
|
print("E/I population has size", pop.size, "and contains",
|
|
len(pop), "groups:", pop.keys(), "\n")
|
|
|
|
# the two default populations
|
|
unif_pop = NeuralPop.uniform(1000) # only excitatory
|
|
ei_pop = NeuralPop.exc_and_inhib(1000, iratio=0.25) # 25% inhibitory
|
|
|
|
# check the groups inside
|
|
print("Uniform population has size", unif_pop.size, "and contains",
|
|
len(unif_pop), "group:", unif_pop.keys(), "\n")
|
|
print("E/I population has size", ei_pop.size, "and contains",
|
|
len(ei_pop), "groups:", ei_pop.keys(), "\n")
|
|
|
|
# A population can also be created from existing groups.
|
|
# Here we pass ``with_models=False`` to the population because these groups do
|
|
# not contain the information necessary to create a network in NEST (a valid
|
|
# neuron model).
|
|
print(exc.neuron_type, exc2.neuron_type, inhib.neuron_type)
|
|
ei_pop2 = NeuralPop.from_groups([exc, exc2, inhib], ["e1", "e2", "i"],
|
|
with_models=False)
|
|
|
|
print("E/I population has size", ei_pop2.size,
|
|
"({} + {} + {}) and contains".format(exc.size, exc2.size, inhib.size),
|
|
len(ei_pop2), "groups:", ei_pop2.keys(), "\n")
|
|
|
|
|
|
''' --------------------- #
|
|
# NEST-enabled population #
|
|
# --------------------- '''
|
|
|
|
# Let's create a population which will be used to make a network that can then
|
|
# be simulated with NEST.
|
|
# We create it from the pyramidal and fast spiking interneurons groups and
|
|
# add synaptic properties to the connections that will be made.
|
|
# (because the group already have names, we don't need to specify them again)
|
|
|
|
# optional synaptic properties
|
|
syn_spec = {
|
|
'default': {"synaptic_model": "tsodyks2_synapse"}, # default connections
|
|
("pyramidal_cells", "pyramidal_cells"): {"U": 0.6} # change a parameter
|
|
}
|
|
|
|
nest_pop = NeuralPop.from_groups([pyr, fsi], syn_spec=syn_spec)
|
|
|
|
|
|
''' ------------------------------- #
|
|
# Complex population and metagroups #
|
|
# ------------------------------- '''
|
|
|
|
# Let's model part of a cortical column with
|
|
# - granule cells in layer 2 and 4
|
|
# - pyramidal cells and interneurons in layers 3 and 5
|
|
# - indiscriminate cells in layer 6
|
|
|
|
nmod = "iaf_psc_exp"
|
|
|
|
idsL2gc = range(100)
|
|
idsL3py, idsL3i = range(100, 200), range(200, 300)
|
|
idsL4gc = range(300, 400)
|
|
idsL5py, idsL5i = range(400, 500), range(500, 600)
|
|
idsL6 = range(600, 700)
|
|
|
|
L2GC = NeuralGroup(idsL2gc, neuron_model=nmod, name="L2GC", neuron_type=1)
|
|
L3Py = NeuralGroup(idsL3py, neuron_model=nmod, name="L3Py", neuron_type=1)
|
|
L3I = NeuralGroup(idsL3i, neuron_model=nmod, name="L3I", neuron_type=-1)
|
|
L4GC = NeuralGroup(idsL4gc, neuron_model=nmod, name="L4GC", neuron_type=1)
|
|
L5Py = NeuralGroup(idsL5py, neuron_model=nmod, name="L5Py", neuron_type=1)
|
|
L5I = NeuralGroup(idsL5i, neuron_model=nmod, name="L5I", neuron_type=-1)
|
|
L6c = NeuralGroup(idsL6, neuron_model=nmod, name="L6c", neuron_type=1)
|
|
|
|
# We can also group them by layers using metagroups (for L2/L3/L6 it is not
|
|
# really useful but it gives a coherent notation)
|
|
L2 = MetaGroup(idsL2gc, name="L2")
|
|
L3 = MetaNeuralGroup(L3Py.ids + L3I.ids, name="L3")
|
|
L4 = MetaGroup(idsL4gc, name="L4")
|
|
L5 = MetaNeuralGroup(L5Py.ids + L5I.ids, name="L5")
|
|
L6 = MetaGroup(idsL6, name="L6")
|
|
|
|
# Then we create the population from the groups
|
|
pop_column = NeuralPop.from_groups(
|
|
[L2GC, L3Py, L3I, L4GC, L5Py, L5I, L6c], meta_groups=[L2, L3, L4, L5, L6])
|
|
|
|
# We can also add additional meta-groups for pyramidal, granule, and
|
|
# interneurons
|
|
pyr = MetaGroup(L3Py.ids + L5Py.ids, name="pyramidal")
|
|
pop_column.add_meta_group(pyr) # add from existing meta-group
|
|
|
|
pop_column.create_meta_group(L3I.ids + L5I.ids, "interneurons") # single line
|
|
|
|
pop_column.create_meta_group(L2GC.ids + L4GC.ids, "granule")
|
|
|
|
print("Column has meta-groups:", pop_column.meta_groups.keys(), "\n")
|
|
|
|
# Note that we used MetaNeuralGroups for L3 and L5 because they can separate
|
|
# excitatory and inhibitory neurons
|
|
print("Excitatory L3 neurons:", L3.excitatory, "\n")
|
|
print("Inhibitory L3 neurons:", L3.inhibitory)
|