Modify the network

deNEST provides a convenient way of modifying the state of some units within a network with the Layer.set_state() and Network.set_state() methods

  • Network.set_state() and Layer.set_state() support constant, multiplicative or additive changes (change_type parameter)
  • We can apply the same change for all units of the layer/population, or provide an array the same shape as the population to perform specific changes for each unit (from_array parameter). The array can be directly provided or loaded from file

In this tutorial we:

  1. Change the state of units within a single population with the Layer.set_state() method
    1. Option 1 ('from_array' == False): provide a single value , used to change the state of all units of a layer or population
      1. ‘constant’ changes
      2. ‘multiplicative’ changes
      3. ‘additive’ changes
    2. Option 2 ('from_array' == True): provide an array of values, mapped to units in the population. You can provide
      1. an NumPy array directly, or
      2. the path to a NumPy array stored on disk.
  2. Change the state of multiple populations at once with the Network.set_state() method
[1]:
import os
from pathlib import Path
from pprint import pprint

import yaml
import numpy as np
import nest

from denest import *
import denest
[2]:
PARAMS_DIR = Path('./data/params/network')

Change the state of units within a population

Using Layer.set_state()

[3]:
nest.ResetKernel()
net = Network(ParamsTree.read(PARAMS_DIR/'network_tree.yml'))
net.create()
2020-06-30 13:42:56,703 [denest.network] INFO: Build N=2 ``Model`` objects
2020-06-30 13:42:56,704 [denest.network] INFO: Build N=2 ``SynapseModel`` objects
2020-06-30 13:42:56,706 [denest.network] INFO: Build N=3 ``Model`` objects
2020-06-30 13:42:56,709 [denest.network] INFO: Build N=2 ``Layer`` or ``InputLayer`` objects.
2020-06-30 13:42:56,716 [denest.utils.validation] INFO: Object `proj_1_AMPA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-30 13:42:56,717 [denest.utils.validation] INFO: Object `proj_2_GABAA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-30 13:42:56,719 [denest.network] INFO: Build N=2 ``ProjectionModel`` objects
2020-06-30 13:42:56,725 [denest.network] INFO: Build N=3 ``TopoProjection`` objects
2020-06-30 13:42:56,728 [denest.network] INFO: Build N=2 population recorders.
2020-06-30 13:42:56,739 [denest.network] INFO: Build N=1 projection recorders.
2020-06-30 13:42:56,748 [denest.network] INFO: Creating neuron models...
100%|██████████| 2/2 [00:00<00:00, 777.37it/s]
2020-06-30 13:42:56,783 [denest.network] INFO: Creating synapse models...
100%|██████████| 2/2 [00:00<00:00, 1322.50it/s]
2020-06-30 13:42:56,789 [denest.network] INFO: Creating recorder models...
100%|██████████| 3/3 [00:00<00:00, 716.00it/s]
2020-06-30 13:42:56,818 [denest.network] INFO: Creating layers...
  0%|          | 0/2 [00:00<?, ?it/s]/Users/tom/nest/nest-simulator-2.20.0/lib/python3.7/site-packages/nest/lib/hl_api_helper.py:127: UserWarning:
GetNodes is deprecated and will be removed in NEST 3.0. Use             GIDCollection instead.
100%|██████████| 2/2 [00:00<00:00,  6.83it/s]
2020-06-30 13:42:57,121 [denest.network] INFO: Creating population recorders...
100%|██████████| 2/2 [00:00<00:00, 40.20it/s]
2020-06-30 13:42:57,187 [denest.network] INFO: Creating projection recorders...
100%|██████████| 1/1 [00:00<00:00, 414.42it/s]
2020-06-30 13:42:57,217 [denest.network] INFO: Connecting layers...
100%|██████████| 3/3 [00:00<00:00, 316.69it/s]
2020-06-30 13:42:57,242 [denest.network] INFO: Network size (including recorders and parrot neurons):
Number of nodes: 206
Number of projections: 6650
[4]:
layer_name = 'l1'
population_name = 'l1_exc'

layer = net.layers['l1']

print('layer shape: ', layer.shape)
print('population shapes: ', layer.population_shape)
layer shape:  (5, 5)
population shapes:  {'l1_exc': (5, 5, 4), 'l1_inh': (5, 5, 2)}

Option 1: provide a single value, used to change the state of all units of a layer or population

Use 'from_array'==False in in Layer.set_state().

“constant” change type

[5]:
nest_params = {
    'V_m': -69.0,
    'g_peak_AMPA': 0.2,
}
[6]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
print('Unique values for l1_inh: ', { param: set(nest.GetStatus(layer.gids(population='l1_inh'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'V_m': {-44.0}, 'g_peak_AMPA': {0.1}}
Unique values for l1_inh:  {'V_m': {-55.0}, 'g_peak_AMPA': {0.1}}
[7]:
# Change param for a single population

layer.set_state(
    nest_params=nest_params,
    population_name='l1_exc',
    change_type='constant',
    from_array=False,
)
2020-06-30 13:42:57,508 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'constant' change, param='V_m', from single value')
2020-06-30 13:42:57,509 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'constant' change, param='g_peak_AMPA', from single value')
[8]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
print('Unique values for l1_inh: ', { param: set(nest.GetStatus(layer.gids(population='l1_inh'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'V_m': {-69.0}, 'g_peak_AMPA': {0.2}}
Unique values for l1_inh:  {'V_m': {-55.0}, 'g_peak_AMPA': {0.1}}
[9]:
# Change param for  all populations

layer.set_state(
    nest_params=nest_params,
    population_name=None,
#     population_name='l1_inh',
    change_type='constant',
    from_array=False,
)
2020-06-30 13:42:58,496 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'constant' change, param='V_m', from single value')
2020-06-30 13:42:58,497 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'constant' change, param='g_peak_AMPA', from single value')
2020-06-30 13:42:59,372 [denest.network.layers] INFO: Layer='l1', pop='l1_inh': Applying 'constant' change, param='V_m', from single value')
2020-06-30 13:42:59,373 [denest.network.layers] INFO: Layer='l1', pop='l1_inh': Applying 'constant' change, param='g_peak_AMPA', from single value')
[10]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
print('Unique values for l1_inh: ', { param: set(nest.GetStatus(layer.gids(population='l1_inh'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'V_m': {-69.0}, 'g_peak_AMPA': {0.2}}
Unique values for l1_inh:  {'V_m': {-69.0}, 'g_peak_AMPA': {0.2}}

“multiplicative” change

[11]:
# Double the value
nest_params = {
    'g_peak_AMPA': 2.0,
}
[12]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
print('Unique values for l1_inh: ', { param: set(nest.GetStatus(layer.gids(population='l1_inh'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'g_peak_AMPA': {0.2}}
Unique values for l1_inh:  {'g_peak_AMPA': {0.2}}
[13]:
# Change param for a single population

layer.set_state(
    nest_params=nest_params,
    population_name=None,
    change_type='multiplicative',
    from_array=False,
)
2020-06-30 13:43:00,387 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'multiplicative' change, param='g_peak_AMPA', from single value')
2020-06-30 13:43:01,328 [denest.network.layers] INFO: Layer='l1', pop='l1_inh': Applying 'multiplicative' change, param='g_peak_AMPA', from single value')
[14]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
print('Unique values for l1_inh: ', { param: set(nest.GetStatus(layer.gids(population='l1_inh'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'g_peak_AMPA': {0.4}}
Unique values for l1_inh:  {'g_peak_AMPA': {0.4}}

“additive” change

[15]:
# Double the value
nest_params = {
    'V_m': 5.0,
}
[16]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
print('Unique values for l1_inh: ', { param: set(nest.GetStatus(layer.gids(population='l1_inh'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'V_m': {-69.0}}
Unique values for l1_inh:  {'V_m': {-69.0}}
[17]:
# Change param for a single population

layer.set_state(
    nest_params=nest_params,
    population_name=None,
    change_type='additive',
    from_array=False,
)
2020-06-30 13:43:01,955 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'additive' change, param='V_m', from single value')
2020-06-30 13:43:03,154 [denest.network.layers] INFO: Layer='l1', pop='l1_inh': Applying 'additive' change, param='V_m', from single value')
[18]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
print('Unique values for l1_inh: ', { param: set(nest.GetStatus(layer.gids(population='l1_inh'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'V_m': {-64.0}}
Unique values for l1_inh:  {'V_m': {-64.0}}

Option 2: provide an array the same shape as the population

For more flexible setting of the state of each individual unit, use 'from_array'==True in in Layer.set_state().

This can be used to set stimulator state arbitrarily (e.g. “spike_times” of a spike generator).

We can provide the array directly

[19]:
# Set V_m=-70 for all units except those at location [0, 0]

pop_shape = layer.population_shapes['l1_exc']
V_m_array = -70.0 * np.ones(pop_shape)
V_m_array[0, 0, :] = -60

or load the array from file

The ‘input_dir’ kwarg sets the directory from which arrays are loaded

[20]:
# Set g_peak_AMPA=0.33 for all units except those at location [0, 0]
g_peak_AMPA_array = 0.33 * np.ones(pop_shape)
g_peak_AMPA_array[0, 0, :] = 1.0

#  save the array to file
INPUT_DIR = Path('./data/input')
os.makedirs(INPUT_DIR, exist_ok=True)
array_path = INPUT_DIR/'g_peak_AMPA_array'
np.save(INPUT_DIR/'g_peak_AMPA_array', g_peak_AMPA_array)

np.load(INPUT_DIR/'g_peak_AMPA_array.npy').shape
[20]:
(5, 5, 4)
[21]:
# We provide either the array or the path to an array, relative to the 'input_dir' directory

nest_params = {
    'V_m': V_m_array,
    'g_peak_AMPA': Path('./g_peak_AMPA_array.npy'),
}
[22]:
print('Unique values for l1_exc: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
Unique values for l1_exc:  {'V_m': {-64.0}, 'g_peak_AMPA': {0.4}}
[23]:
# Change param for a single population

layer.set_state(
    nest_params=nest_params,
    input_dir=INPUT_DIR,
    population_name='l1_exc',
    change_type='constant',
    from_array=True,
)
2020-06-30 13:43:04,119 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'constant' change, param='V_m', from array')
2020-06-30 13:43:04,129 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'constant' change, param='g_peak_AMPA', from array')
[24]:
print('Unique values for l1_exc at location [0, 0]: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc', location=(0, 0)), param)) for param in nest_params.keys()} )
print('Unique values for l1_exc at all locations: ', { param: set(nest.GetStatus(layer.gids(population='l1_exc'), param)) for param in nest_params.keys()} )
Unique values for l1_exc at location [0, 0]:  {'V_m': {-60.0}, 'g_peak_AMPA': {1.0}}
Unique values for l1_exc at all locations:  {'V_m': {-70.0, -60.0}, 'g_peak_AMPA': {0.33, 1.0}}

Change the state of units throughout the network

Using Network.set_state(), we can specify modifications for multiple populations at once.

[25]:
# Set the input layer spike times

input_layer = net.layers['input_layer']
input_pop_shape = input_layer.population_shapes[
    input_layer.stimulator_model
]
print(f'`{input_layer.stimulator_model}` population shape: {input_pop_shape}')

# Build the spike times for each unit

spike_times = np.empty(input_pop_shape, dtype=np.object)

# Set the same spike times for all units...
for idx, _ in np.ndenumerate(spike_times):
    spike_times[idx] = [1.0, 10.0]

# Except one unit
idx = (0, 0, 0)
spike_times[idx] = [5.0]

print(f'input array shape: {spike_times.shape}')
print(f'spike_times array: {spike_times[:,:,0]}')
`spike_generator` population shape: (5, 5, 1)
input array shape: (5, 5, 1)
spike_times array: [[list([5.0]) list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0])
  list([1.0, 10.0])]
 [list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0])
  list([1.0, 10.0])]
 [list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0])
  list([1.0, 10.0])]
 [list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0])
  list([1.0, 10.0])]
 [list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0]) list([1.0, 10.0])
  list([1.0, 10.0])]]
[26]:
net.set_state(
    [
        {
            'layers': ['l1'],
            'population_name': None,
            'change_type': 'constant',
            'from_array': False,
            'nest_params': {
                'V_m': -69.9
            }

        },
        {
            'layers': ['input_layer'],
            'population_name': 'spike_generator',
            'change_type': 'constant',
            'from_array': True,
            'nest_params': {
                'spike_times': spike_times,
            }

        },

    ]
)
2020-06-30 13:43:05,218 [denest.network.layers] INFO: Layer='input_layer', pop='spike_generator': Applying 'constant' change, param='spike_times', from array')
2020-06-30 13:43:05,277 [denest.network.layers] INFO: Layer='l1', pop='l1_exc': Applying 'constant' change, param='V_m', from single value')
2020-06-30 13:43:06,332 [denest.network.layers] INFO: Layer='l1', pop='l1_inh': Applying 'constant' change, param='V_m', from single value')
[27]:
# Get status of spike generators
print(
    nest.GetStatus(net.layers['input_layer'].gids(population='spike_generator'), 'spike_times')
)
(array([5.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]), array([ 1., 10.]))
[28]:
# Get status of l1 units
print(
    nest.GetStatus(net.layers['l1'].gids(), 'V_m')
)
(-69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9, -69.9)