Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arbitrarily nested neuron groups with per-group iterator #3322

Open
otcathatsya opened this issue Oct 1, 2024 · 3 comments
Open

Arbitrarily nested neuron groups with per-group iterator #3322

otcathatsya opened this issue Oct 1, 2024 · 3 comments
Labels
S: Normal Handle this with default priority stale Automatic marker for inactivity, please have another look here T: Discussion Still searching for the right way to proceed / suggestions welcome T: Enhancement New functionality, model or documentation

Comments

@otcathatsya
Copy link
Contributor

(Discussed during Bernstein conference with @babsey @heplesser @terhorstd @poojanbabu)
In the future it may be desirable to support creating arbitrary nested groups of populations in NEST the way it is currently proposed for NEST Desktop on a Python level by @babsey. This would allow applying connection rules and e.g. weight values on a per-group-item level via a group item aware iterator as opposed to strictly flattened across all neuron in a NodeCollection.

Syntax brainstorming:

pop1 = nest.Create("iaf_psc_exp", 100)
pop2 = nest.Create("iaf_psc_exp", 200)

group1 = nest.Group([pop1, pop2])
# Retrieve:
pop1 = group1[0] // same as pop1
pop1_neuron0 = group1[0][0] // same as pop1[0]

nest.Connect(g1, g1, "all_to_all", syn_spec={"weight":[23, 25]})
// Would apply weight 23.0 to all connections from n1-> nX and 25.0 to n2->nX
// However, this could also be taken to mean n1->n1: 23.0 and n2->n2: 25.0. Needs a clearer syntax!
  • Doing parameters purely as lists might get really messy and unclear, calls for named arg implementation.
  • Some of the functionality already exists as part of composite node collections on a C++ level, but these are currently not exposed to the user.
  • There might not be a need for an explicit nest.Group but some method or operator overload is needed to distinguish from the default n1 + n2 flattened collection addition.
  • While there's some similarities to pooling connections as done in exabrainprep this should be a conceptually independent change.
@otcathatsya otcathatsya added T: Enhancement New functionality, model or documentation T: Discussion Still searching for the right way to proceed / suggestions welcome S: Normal Handle this with default priority labels Oct 1, 2024
@babsey
Copy link
Contributor

babsey commented Oct 2, 2024

Great step that you open this tread. One or more extensions/suggestions/corrections to your comment:

  • g1 is undefined. You named the variable group1.
  • We also talked about naming the group, e.g. nest.Group([pop1, pop2], name="my_group")
    But naming is optional.

To better understand, we can make a usecase: lets say popE is excitatory and popI is inhibitory. A group of popE and popI represents a layer.

popE = nest.Create("iaf_psc_alpha", 800) # excitatory
popI = nest.Create("iaf_psc_alpha", 200) # inhibitory
layer = nest.Group([popE, popI], name="L2/3")

nest.Connect(layer, layer, syn_spec={"weight": [2, -8]}) 
# This implies that popE connects to layer with weight `w1=2` whereas popI to layer with `w2=-8`
# As summary the weight is shown as [wE, wI]

# A more complex connection step could be
nest.Connect(layer, layer, syn_spec={"weight": [[4, 2], [-8, -12]]})
# In my understanding the weight is shown as [[wEE, wIE], [wEI, wII]]

@heplesser
Copy link
Contributor

Actually, we don't need anything new in NEST (almost ...) to make this happen: We can get it for free from Pandas DataFrames!

The following code generates something like the neural populations of the multi-area model, but with three interneuron populations per layer, randomly selected neuron numbers and each population assigned randomly one of three neuron models and stores them in a Pandas dataframe:

import nest
import pandas as pd
import random

random.seed(123)

areas = ['V1', 'V2', 'VP', 'V3', 'V3A', 'MT', 'V4t', 'V4', 'VOT', 'MSTd',
                      'PIP', 'PO', 'DP', 'MIP', 'MDP', 'VIP', 'LIP', 'PITv', 'PITd',
                      'MSTl', 'CITv', 'CITd', 'FEF', 'TF', 'AITv', 'FST', '7a', 'STPp',
                      'STPa', '46', 'AITd', 'TH']
layers = ['L23', 'L4', 'L5', 'L6']
pops = ['Pyr', 'SOM', 'VIP', 'PV']
models = ['iaf_psc_alpha', 'aeif_psc_alpha', 'iaf_psc_delta']

d = pd.DataFrame.from_records(
    {'Area': a, 'Layer': l, 'Population': p, 
     'NC': nest.Create(random.choice(models), n=random.randint(1, 2000))}
    for a in areas for l in layers for p in pops
    )

Using the multilevel indexing features of Pandas, we can then create nice tables:

df = d.set_index(['Area', 'Layer', 'Population']).unstack(1).unstack().droplevel(0, axis=1)
df.style.format('{:5}')

will give (requires small patch to NodeCollection in hl_api_types.py to work)

image

and to pick L5 from two areas

df.loc[['46', 'AITd'], 'L5'].style.format('{:5}')

yields
image

We can do much more with Pandas multi-level indexing and table reshaping tools.

Everything we select are collections of node collections, so by just summing over values, we can pass things to Connect commands. For example, to connect all L5 populations of areas 46 and AITd to all populations in L4 and L6 of area 7a, we can just do

src = df.loc[['46', 'AITd'], 'L5']
tgt = df.loc['7a', ['L4', 'L6']].to_frame().T
srcn = src.sum().sum()
tgtn = tgt.sum().sum()
nest.Connect(srcn, tgtn, {'rule': 'fixed_indegree', 'indegree': 10})

The double summing is needed to sum over rows and columns of data frames.

There are still some hickups, in particular to NodeCollection.__getattr__() forwarding too much to the NEST kernel for lookup, but that can be ironed out on the Python level.

Copy link

github-actions bot commented Dec 3, 2024

Issue automatically marked stale!

@github-actions github-actions bot added the stale Automatic marker for inactivity, please have another look here label Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S: Normal Handle this with default priority stale Automatic marker for inactivity, please have another look here T: Discussion Still searching for the right way to proceed / suggestions welcome T: Enhancement New functionality, model or documentation
Projects
None yet
Development

No branches or pull requests

3 participants