Quickstart¶
In this tutorial we do the following :
- Examine the
'network'parameter tree- Models (
neuron_models,recorder_models,synapse_models) - Layers (
layers) - Projections (
projection_modelsandtopologysubtrees - Population and projection recorders (
recorders)
- Models (
- Examine the full simulation parameter tree
networkparams subtreekernelparams subtreesession_modelsparams subtreesimulationparams subtree
- Run a full simulation
- Load parameter tree from one or multiple files
- Using the
Simulationobject - Using the
denest.run()method - From the command line
[1]:
import nest
import yaml
from pathlib import Path
from pprint import pprint
from denest import *
import denest
[2]:
PARAMS_DIR = Path('./data/params')
OUTPUT_DIR = Path('./data/outputs/output')
"network" tree¶
The "network" tree defines a full NEST network
A NEST network is represented in deNEST by the Network object.
The Network object is initialized from the full "network" parameter tree. The 'network' tree should have specific children subtree, each used to initialize different elements of the network.
[3]:
net_tree = ParamsTree.read(PARAMS_DIR/'network_tree.yml').children['network']
[4]:
# Full 'network' tree
net_tree
[4]:
ParamsTree(name='network', parent='None')
params: {}
nest_params: {}
neuron_models:
params: {}
nest_params: {}
my_neuron:
params:
nest_model: ht_neuron
nest_params:
g_KL: 1.0
g_NaL: 1.0
l1_exc:
params: {}
nest_params:
V_m: -44.0
... [116 lines] ...
- layers:
- input_layer
populations: null
model: my_spike_detector
projection_recorders:
- source_layers:
- l1
source_population: l1_exc
target_layers:
- l1
target_population: l1_inh
projection_model: proj_1_AMPA
model: weight_recorder
nest_params: {}
[5]:
print("'network' tree's expected subtrees:", net_tree.children.keys())
'network' tree's expected subtrees: dict_keys(['neuron_models', 'synapse_models', 'layers', 'projection_models', 'topology', 'recorder_models', 'recorders'])
Models ( 'neuron_model', 'synapse_models', 'recorder_models' subtrees)¶
We can define neuron, recorder, stimulator and synapse models with arbitrary parameters from parameter trees.
Each leaf corresponds to a new (named) model. Its nest_params and params are hierarchically inherited.
The nest_model used is specified in the leaf’s params
'neuron_models' tree¶
[6]:
net_tree.children['neuron_models']
[6]:
ParamsTree(name='neuron_models', parent='network')
params: {}
nest_params: {}
my_neuron:
params:
nest_model: ht_neuron
nest_params:
g_KL: 1.0
g_NaL: 1.0
l1_exc:
params: {}
nest_params:
V_m: -44.0
l1_inh:
params: {}
nest_params:
V_m: -55.0
Below are the leaves of the tree. Each initializes a model. The nest_params and params are inherited from ancestor leaves.
[7]:
print("Tree's leaves and their inherited parameters:")
[
(f'leaf name: {l.name})',
f'leaf `params`: {dict(l.params)}',
f'leaf `nest_params`: {dict(l.nest_params)}')
for l in net_tree.children['neuron_models'].leaves()
]
Tree's leaves and their inherited parameters:
[7]:
[('leaf name: l1_exc)',
"leaf `params`: {'nest_model': 'ht_neuron'}",
"leaf `nest_params`: {'g_KL': 1.0, 'g_NaL': 1.0, 'V_m': -44.0}"),
('leaf name: l1_inh)',
"leaf `params`: {'nest_model': 'ht_neuron'}",
"leaf `nest_params`: {'g_KL': 1.0, 'g_NaL': 1.0, 'V_m': -55.0}")]
'recorder_models' subtree¶
Same thing as for neuron models:
[8]:
net_tree.children['recorder_models']
[8]:
ParamsTree(name='recorder_models', parent='network')
params: {}
nest_params:
record_to:
- memory
- file
weight_recorder:
params:
nest_model: weight_recorder
nest_params: {}
my_multimeter:
params:
nest_model: multimeter
nest_params:
record_from:
- V_m
my_spike_detector:
params:
nest_model: spike_detector
nest_params: {}
[9]:
print("Tree's leaves and their inherited parameters:")
[
(f'leaf name: {l.name})',
f'leaf `params`: {dict(l.params)}',
f'leaf `nest_params`: {dict(l.nest_params)}')
for l in net_tree.children['recorder_models'].leaves()
]
Tree's leaves and their inherited parameters:
[9]:
[('leaf name: weight_recorder)',
"leaf `params`: {'nest_model': 'weight_recorder'}",
"leaf `nest_params`: {'record_to': ['memory', 'file']}"),
('leaf name: my_multimeter)',
"leaf `params`: {'nest_model': 'multimeter'}",
"leaf `nest_params`: {'record_to': ['memory', 'file'], 'record_from': ['V_m']}"),
('leaf name: my_spike_detector)',
"leaf `params`: {'nest_model': 'spike_detector'}",
"leaf `nest_params`: {'record_to': ['memory', 'file']}")]
'synapse_model' subtree¶
- Same thing as for neuron models, with as a bonus a convenient way of specifying the receptor type of the synapse
- If specifying the
receptor_typeandtarget_modelin theSynapseModelparams, the corresponding port is determined automatically - Connection ‘weight’ nest_param should be specified in the projections parameters rather than the synapse parameters
[10]:
pprint(net_tree.children['synapse_models'])
ParamsTree(name='synapse_models', parent='network')
params: {}
nest_params: {}
my_AMPA_synapse:
params:
nest_model: ht_synapse
receptor_type: AMPA
target_neuron: ht_neuron
nest_params: {}
my_GABAA_synapse:
params:
nest_model: ht_synapse
receptor_type: GABA_A
target_neuron: ht_neuron
nest_params: {}
[11]:
print("Tree's leaves and their inherited parameters:")
[
(f'leaf name: {l.name})',
f'leaf `params`: {dict(l.params)}',
f'leaf `nest_params`: {dict(l.nest_params)}')
for l in net_tree.children['synapse_models'].leaves()
]
Tree's leaves and their inherited parameters:
[11]:
[('leaf name: my_AMPA_synapse)',
"leaf `params`: {'nest_model': 'ht_synapse', 'receptor_type': 'AMPA', 'target_neuron': 'ht_neuron'}",
'leaf `nest_params`: {}'),
('leaf name: my_GABAA_synapse)',
"leaf `params`: {'nest_model': 'ht_synapse', 'receptor_type': 'GABA_A', 'target_neuron': 'ht_neuron'}",
'leaf `nest_params`: {}')]
Layers ( 'layers' subtree)¶
- As for models, we can create
nest.Topologylayers from the leaves of a tree. - The elements can be nest models with their default parameters, or the ones we just created with custom params
- For layers of stimulator devices, we can use the
InputLayerobject, which can automatically create paired parrot neurons for each stimulator units, by addingtype: 'InputLayer'to the params - The leaves should have a
populationentry in theparamsfield, that specifies the number of units of each model at each layer location (replaces theelementsnest_params)
[12]:
layer_tree = net_tree.children['layers']
layer_tree
[12]:
ParamsTree(name='layers', parent='network')
params: {}
nest_params: {}
layers:
params:
type: null
nest_params:
rows: 5
columns: 5
extent:
- 5.0
- 5.0
edge_wrap: true
input_layer:
params:
type: InputLayer
add_parrots: true
populations:
spike_generator: 1
nest_params: {}
l1:
params:
populations:
l1_exc: 4
l1_inh: 2
nest_params: {}
[13]:
print("Tree's leaves and their inherited parameters:")
[
(f'leaf name: {l.name})',
f'leaf `params`: {dict(l.params)}',
f'leaf `nest_params`: {dict(l.nest_params)}')
for l in layer_tree.leaves()
]
Tree's leaves and their inherited parameters:
[13]:
[('leaf name: input_layer)',
"leaf `params`: {'type': 'InputLayer', 'add_parrots': True, 'populations': {'spike_generator': 1}}",
"leaf `nest_params`: {'rows': 5, 'columns': 5, 'extent': [5.0, 5.0], 'edge_wrap': True}"),
('leaf name: l1)',
"leaf `params`: {'type': None, 'populations': {'l1_exc': 4, 'l1_inh': 2}}",
"leaf `nest_params`: {'rows': 5, 'columns': 5, 'extent': [5.0, 5.0], 'edge_wrap': True}")]
Projections ('projection_models' and 'topology' subtrees)¶
We create projections using a two step process:
- Create
ProjectionModelobjects from a tree. Each named leaf will define a template from which individual projections can inherit their parameters ('projection_modelssubtree) - Create
Projectionobjects from a list, specifying for each item the source layer x population, target layer x population and the projection model to inherit parameters from ('topology'subtree)
Define templates from the projection_models tree¶
[14]:
proj_model_tree = net_tree.children['projection_models']
proj_model_tree
[14]:
ParamsTree(name='projection_models', parent='network')
params: {}
nest_params:
connection_type: divergent
mask:
circular:
radius: 2.0
kernel: 1.0
proj_1_AMPA:
params: {}
nest_params:
synapse_model: my_AMPA_synapse
weights: 1.0
proj_2_GABAA:
params: {}
nest_params:
synapse_model: my_GABAA_synapse
weights: 2.0
[15]:
print("Tree's leaves and their inherited parameters:")
[
(f'leaf name: {l.name})',
f'leaf `params`: {dict(l.params)}',
f'leaf `nest_params`: {dict(l.nest_params)}')
for l in net_tree.children['projection_models'].leaves()
]
Tree's leaves and their inherited parameters:
[15]:
[('leaf name: proj_1_AMPA)',
'leaf `params`: {}',
"leaf `nest_params`: {'connection_type': 'divergent', 'mask': {'circular': {'radius': 2.0}}, 'kernel': 1.0, 'synapse_model': 'my_AMPA_synapse', 'weights': 1.0}"),
('leaf name: proj_2_GABAA)',
'leaf `params`: {}',
"leaf `nest_params`: {'connection_type': 'divergent', 'mask': {'circular': {'radius': 2.0}}, 'kernel': 1.0, 'synapse_model': 'my_GABAA_synapse', 'weights': 2.0}")]
Define individual projections from the topology tree¶
The list of projections is defined in the projections params of the topology tree
Check out the doc of Network.build_projections for expected formatting
For each item in the list, a projection is created for each of the <source_layer> x <target_layer> combinations. The params and nest_params are inherited from the template projection_model
[16]:
topo_tree = net_tree.children['topology']
topo_tree
[16]:
ParamsTree(name='topology', parent='network')
params:
projections:
- source_layers:
- input_layer
source_population: parrot_neuron
target_layers:
- l1
target_population: l1_exc
projection_model: proj_1_AMPA
- source_layers:
- l1
source_population: l1_exc
target_layers:
- l1
target_population: l1_inh
projection_model: proj_1_AMPA
- source_layers:
- l1
source_population: l1_inh
target_layers:
- l1
target_population: l1_exc
projection_model: proj_2_GABAA
nest_params: {}
Recorders (recorders subtree)¶
Similarly to the topology tree, recorders are defined from lists.
We separate recorders connected to synapses (eg weight recorder) and those connected to units (eg spike detectors), which are defined in the projection_recorders and population_recorders params (resp.) of the recorders tree.
Check out the doc of the Network.build_recorders, Network.build_population_recorders and Network.build_projection_recorders methods for expected formatting
The parameters of the recorders can be changed by using custom recorder models (in the recorder_models tree, see above)
[17]:
recorders_tree = net_tree.children['recorders']
recorders_tree
[17]:
ParamsTree(name='recorders', parent='network')
params:
population_recorders:
- layers:
- l1
populations:
- l1_exc
model: my_multimeter
- layers:
- input_layer
populations: null
model: my_spike_detector
projection_recorders:
- source_layers:
- l1
source_population: l1_exc
target_layers:
- l1
target_population: l1_inh
projection_model: proj_1_AMPA
model: weight_recorder
nest_params: {}
Full parameter tree¶
A full simulation consists in
- kernel initialization
- network creation
- running the network in multiple ‘sessions’ at the start of which some modifications to the network can be made
The full simulation is represented by the Simulation object, which is initialized from a parameter tree with the following subtrees:
'network'subtree, passed toNetwork()'kernel'subtree, passed toSimulation.init_kernel()'session_models'subtree, defining the parameters for each individualSession'simulation'subtree, containing the list of sessions that we run and the input/output directory paths
[18]:
full_tree = ParamsTree.read(PARAMS_DIR/'parameter_tree.yml')
[19]:
full_tree.children.keys()
[19]:
dict_keys(['kernel', 'simulation', 'session_models', 'network'])
'network' subtree¶
The 'network' subtree is expected to have the following children, as we saw above:
[20]:
net_tree = full_tree.children['network']
net_tree.children.keys()
[20]:
dict_keys(['neuron_models', 'synapse_models', 'layers', 'projection_models', 'topology', 'recorder_models', 'recorders'])
It is used to initialize a Network object, representing the full NEST network.
[21]:
net = Network(net_tree)
2020-06-29 12:30:53,600 [denest.network] INFO: Build N=2 ``Model`` objects
2020-06-29 12:30:53,602 [denest.network] INFO: Build N=2 ``SynapseModel`` objects
2020-06-29 12:30:53,603 [denest.network] INFO: Build N=3 ``Model`` objects
2020-06-29 12:30:53,607 [denest.network] INFO: Build N=2 ``Layer`` or ``InputLayer`` objects.
2020-06-29 12:30:53,611 [denest.utils.validation] INFO: Object `proj_1_AMPA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-29 12:30:53,614 [denest.utils.validation] INFO: Object `proj_2_GABAA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-29 12:30:53,614 [denest.network] INFO: Build N=2 ``ProjectionModel`` objects
2020-06-29 12:30:53,618 [denest.network] INFO: Build N=3 ``TopoProjection`` objects
2020-06-29 12:30:53,621 [denest.network] INFO: Build N=2 population recorders.
2020-06-29 12:30:53,622 [denest.network] INFO: Build N=1 projection recorders.
'kernel' subtree¶
The kernel subtree is passed to the Simulation.init_kernel method.
- All
nest_paramsare passed to nest.SetKernelStatus - Two optional
params:nest_seed(used to initialize at once the thread-wide and general nest rsg. Themsd,grng_seedandrng_seednest params are reserved)extension_modules(used to callnest.Install())
- Note the
data_pathparameter is set automatically from the simulation’s'output_dir'
[22]:
full_tree.children['kernel']
[22]:
ParamsTree(name='kernel', parent='None')
params:
nest_seed: 10
extension_modules: []
nest_params:
resolution: 0.5
overwrite_files: true
'session_models' subtree¶
The leaves of the 'session_models' subtree define templates from which the parameters of each Session object can be inherited.
The following params are recognized:
simulation_time(float): Duration of the session in ms. (mandatory)reset_network(bool): If true,nest.ResetNetwork()is called during session initialization (defaultFalse)record(bool): If false, thestart_timefield of recorder nodes in NEST is set to the end time of the session, so that no data is recorded during the session (defaultTrue)shift_origin(bool): If True, theoriginflag of the stimulation devices of all the network’sInputLayerlayers is set to the start of the session during initialization. Useful to repeat sessions when the stimulators are eg spike generators.unit_changes(list): List describing the changes applied to certain units before the start of the session. Passed toNetwork.set_state.synapse_changes(list): List describing the changes applied to certain synapses before the start of the session. Passed toNetwork.set_state. Refer to that method for a description of howsynapse_changesis formatted and interpreted. No changes happen if empty. (default [])
[23]:
full_tree.children['session_models']
[23]:
ParamsTree(name='session_models', parent='None')
params:
record: true
shift_origin: true
simulation_time: 100.0
nest_params: {}
warmup:
params:
record: false
nest_params: {}
2_spikes:
params:
unit_changes:
- layers:
- input_layer
population_name: spike_generator
... [3 lines] ...
- 10.0
nest_params: {}
3_spikes:
params:
unit_changes:
- layers:
- input_layer
population_name: spike_generator
nest_params:
spike_times:
- 1.0
- 10.0
- 20.0
nest_params: {}
[24]:
print("Tree's leaves and their inherited parameters:")
[
(f'leaf name: {l.name})',
f'leaf `params`: {dict(l.params)}',
f'leaf `nest_params`: {dict(l.nest_params)}')
for l in full_tree.children['session_models'].leaves()
]
Tree's leaves and their inherited parameters:
[24]:
[('leaf name: warmup)',
"leaf `params`: {'record': False, 'shift_origin': True, 'simulation_time': 100.0}",
'leaf `nest_params`: {}'),
('leaf name: 2_spikes)',
"leaf `params`: {'record': True, 'shift_origin': True, 'simulation_time': 100.0, 'unit_changes': [{'layers': ['input_layer'], 'population_name': 'spike_generator', 'nest_params': {'spike_times': [1.0, 10.0]}}]}",
'leaf `nest_params`: {}'),
('leaf name: 3_spikes)',
"leaf `params`: {'record': True, 'shift_origin': True, 'simulation_time': 100.0, 'unit_changes': [{'layers': ['input_layer'], 'population_name': 'spike_generator', 'nest_params': {'spike_times': [1.0, 10.0, 20.0]}}]}",
'leaf `nest_params`: {}')]
Check out the set_network_state tutorial or the documentation of the Network.set_state() method for details on how to modify the state of the network at the start of a simulation
'simulation' subtree¶
The ‘simulation’ subtree contains the following parameters:
output_dir(str): Path to the output directory (default ‘output’).input_dir(str): Path to the directory in which input files are searched for for each session. (default ‘input’)sessions(list(str)): Order in which sessions are run. Elements of the list should be the name of session models defined in thesession_modelsparameter subtree (default [])
[25]:
full_tree.children['simulation']
[25]:
ParamsTree(name='simulation', parent='None')
params:
output_dir: data_tuto/output
sessions:
- warmup
- 3_spikes
- 2_spikes
- 3_spikes
nest_params: {}
Run a full simulation¶
Load the full parameter tree¶
The full parameter tree can be loaded from a single file, or from multiple files which are merged.
From a single file by using the yaml.load or the ParamsTree.read methods:
[26]:
ParamsTree.read(PARAMS_DIR/'parameter_tree.yml')
[26]:
ParamsTree(name='None', parent=None)
params: {}
nest_params: {}
kernel:
params:
nest_seed: 10
extension_modules: []
nest_params:
resolution: 0.5
overwrite_files: true
simulation:
params:
output_dir: data_tuto/output
sessions:
- warmup
- 3_spikes
... [168 lines] ...
- layers:
- input_layer
populations: null
model: my_spike_detector
projection_recorders:
- source_layers:
- l1
source_population: l1_exc
target_layers:
- l1
target_population: l1_inh
projection_model: proj_1_AMPA
model: weight_recorder
nest_params: {}
By merging multiple files, using the denest.load_trees method:
[27]:
!cat {PARAMS_DIR/'tree_paths.yml'}
- './network_tree.yml'
- './simulation.yml'
- './session_models.yml'
- './kernel.yml'
[28]:
full_tree = load_trees(PARAMS_DIR/'tree_paths.yml')
2020-06-29 12:30:54,100 [denest] INFO: Loading parameter file paths from data/params/tree_paths.yml
2020-06-29 12:30:54,104 [denest] INFO: Finished loading parameter file paths
2020-06-29 12:30:54,105 [denest] INFO: Loading parameters files:
['./network_tree.yml',
'./simulation.yml',
'./session_models.yml',
'./kernel.yml']
[29]:
full_tree
[29]:
ParamsTree(name='data/params/tree_paths.yml', parent=None)
params: {}
nest_params: {}
simulation:
params:
output_dir: output
sessions:
- warmup
- 3_spikes
- 2_spikes
- 3_spikes
nest_params: {}
session_models:
params:
record: true
shift_origin: true
... [168 lines] ...
source_population: l1_exc
target_layers:
- l1
target_population: l1_inh
projection_model: proj_1_AMPA
model: weight_recorder
nest_params: {}
kernel:
params:
nest_seed: 10
extension_modules: []
nest_params:
resolution: 0.5
overwrite_files: true
Run with the Simulation object¶
We load the full parameter tree:
[30]:
tree = ParamsTree.read(PARAMS_DIR/'parameter_tree.yml')
And then initialize the Simulation:
[31]:
sim = Simulation(tree, output_dir=None, input_dir=None)
2020-06-29 12:30:54,322 [denest.utils.validation] INFO: Object `simulation`: params: using default value for optional parameters:
{'input_dir': 'input'}
2020-06-29 12:30:54,324 [denest.simulation] INFO: Initializing NEST kernel and seeds...
2020-06-29 12:30:54,326 [denest.simulation] INFO: Resetting NEST kernel...
2020-06-29 12:30:54,333 [denest.simulation] INFO: Setting NEST kernel status...
2020-06-29 12:30:54,334 [denest.simulation] INFO: Calling `nest.SetKernelStatus({'resolution': 0.5, 'overwrite_files': True})`
2020-06-29 12:30:54,371 [denest.simulation] INFO: Calling `nest.SetKernelStatus({'data_path': 'data_tuto/output/data', 'grng_seed': 11, 'rng_seeds': range(12, 13)})
2020-06-29 12:30:54,374 [denest.simulation] INFO: Finished setting NEST kernel status
2020-06-29 12:30:54,375 [denest.simulation] INFO: Installing external modules...
2020-06-29 12:30:54,381 [denest.simulation] INFO: Finished installing external modules
2020-06-29 12:30:54,384 [denest.simulation] INFO: Finished initializing kernel
2020-06-29 12:30:54,388 [denest.simulation] INFO: Build N=3 session models
2020-06-29 12:30:54,391 [denest.simulation] INFO: Build N=4 sessions
2020-06-29 12:30:54,393 [denest.session] INFO: Creating session "00_warmup"
2020-06-29 12:30:54,395 [denest.utils.validation] INFO: Object `00_warmup`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': [], 'unit_changes': []}
2020-06-29 12:30:54,397 [denest.session] INFO: Creating session "01_3_spikes"
2020-06-29 12:30:54,398 [denest.utils.validation] INFO: Object `01_3_spikes`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': []}
2020-06-29 12:30:54,400 [denest.session] INFO: Creating session "02_2_spikes"
2020-06-29 12:30:54,404 [denest.utils.validation] INFO: Object `02_2_spikes`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': []}
2020-06-29 12:30:54,406 [denest.session] INFO: Creating session "03_3_spikes"
2020-06-29 12:30:54,410 [denest.utils.validation] INFO: Object `03_3_spikes`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': []}
2020-06-29 12:30:54,428 [denest.simulation] INFO: Sessions: ['00_warmup', '01_3_spikes', '02_2_spikes', '03_3_spikes']
2020-06-29 12:30:54,432 [denest.simulation] INFO: Building network.
2020-06-29 12:30:54,449 [denest.network] INFO: Build N=2 ``Model`` objects
2020-06-29 12:30:54,450 [denest.network] INFO: Build N=2 ``SynapseModel`` objects
2020-06-29 12:30:54,454 [denest.network] INFO: Build N=3 ``Model`` objects
2020-06-29 12:30:54,456 [denest.network] INFO: Build N=2 ``Layer`` or ``InputLayer`` objects.
2020-06-29 12:30:54,457 [denest.utils.validation] INFO: Object `proj_1_AMPA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-29 12:30:54,459 [denest.utils.validation] INFO: Object `proj_2_GABAA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-29 12:30:54,460 [denest.network] INFO: Build N=2 ``ProjectionModel`` objects
2020-06-29 12:30:54,465 [denest.network] INFO: Build N=3 ``TopoProjection`` objects
2020-06-29 12:30:54,470 [denest.network] INFO: Build N=2 population recorders.
2020-06-29 12:30:54,471 [denest.network] INFO: Build N=1 projection recorders.
2020-06-29 12:30:54,472 [denest.simulation] INFO: Creating network.
2020-06-29 12:30:54,473 [denest.network] INFO: Creating neuron models...
100%|██████████| 2/2 [00:00<00:00, 2849.39it/s]
2020-06-29 12:30:54,490 [denest.network] INFO: Creating synapse models...
100%|██████████| 2/2 [00:00<00:00, 1310.92it/s]
2020-06-29 12:30:54,498 [denest.network] INFO: Creating recorder models...
100%|██████████| 3/3 [00:00<00:00, 1678.62it/s]
2020-06-29 12:30:54,505 [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, 10.80it/s]
2020-06-29 12:30:54,694 [denest.network] INFO: Creating population recorders...
100%|██████████| 2/2 [00:00<00:00, 143.57it/s]
2020-06-29 12:30:54,711 [denest.network] INFO: Creating projection recorders...
100%|██████████| 1/1 [00:00<00:00, 211.46it/s]
2020-06-29 12:30:54,721 [denest.network] INFO: Connecting layers...
100%|██████████| 3/3 [00:00<00:00, 642.94it/s]
2020-06-29 12:30:54,737 [denest.network] INFO: Network size (including recorders and parrot neurons):
Number of nodes: 206
Number of projections: 6650
2020-06-29 12:30:54,738 [denest.simulation] INFO: Finished creating network
2020-06-29 12:30:54,738 [denest.simulation] INFO: Saving simulation metadata...
2020-06-29 12:30:54,739 [denest.simulation] INFO: Creating output directory: data_tuto/output
2020-06-29 12:30:54,741 [denest.io.save] INFO: Clearing directory: data_tuto/output
2020-06-29 12:30:54,742 [denest.io.save] INFO: Clearing directory: data_tuto/output
2020-06-29 12:30:54,743 [denest.io.save] INFO: Clearing directory: data_tuto/output/data
2020-06-29 12:30:54,745 [denest.io.save] INFO: Clearing directory: data_tuto/output/data
2020-06-29 12:30:54,747 [denest.io.save] INFO: Clearing directory: data_tuto/output/data
2020-06-29 12:30:54,748 [denest.io.save] INFO: Clearing directory: data_tuto/output
2020-06-29 12:30:54,785 [denest.simulation] INFO: Finished saving simulation metadata
We can inspect the simulation elements:
[32]:
sim.network
[32]:
Network(params: {}
nest_params: {}
neuron_models:
params: {}
nest_params: {}
my_neuron:
params:
nest_model: ht_neuron
nest_params:
g_KL: 1.0
g_NaL: 1.0
l1_exc:
params: {}
nest_params:
V_m: -44.0
l1_inh:
params: {}
nest_params:
V_m: -55.0
synapse_models:
params: {}
nest_params: {}
my_AMPA_synapse:
params:
nest_model: ht_synapse
receptor_type: AMPA
target_neuron: ht_neuron
nest_params: {}
my_GABAA_synapse:
params:
nest_model: ht_synapse
receptor_type: GABA_A
target_neuron: ht_neuron
nest_params: {}
layers:
params: {}
nest_params: {}
layers:
params:
type: null
nest_params:
rows: 5
columns: 5
extent:
- 5.0
- 5.0
edge_wrap: true
input_layer:
params:
type: InputLayer
add_parrots: true
populations:
spike_generator: 1
nest_params: {}
l1:
params:
populations:
l1_exc: 4
l1_inh: 2
nest_params: {}
projection_models:
params: {}
nest_params:
connection_type: divergent
mask:
circular:
radius: 2.0
kernel: 1.0
proj_1_AMPA:
params: {}
nest_params:
synapse_model: my_AMPA_synapse
weights: 1.0
proj_2_GABAA:
params: {}
nest_params:
synapse_model: my_GABAA_synapse
weights: 2.0
topology:
params:
projections:
- source_layers:
- input_layer
source_population: parrot_neuron
target_layers:
- l1
target_population: l1_exc
projection_model: proj_1_AMPA
- source_layers:
- l1
source_population: l1_exc
target_layers:
- l1
target_population: l1_inh
projection_model: proj_1_AMPA
- source_layers:
- l1
source_population: l1_inh
target_layers:
- l1
target_population: l1_exc
projection_model: proj_2_GABAA
nest_params: {}
recorder_models:
params: {}
nest_params:
record_to:
- memory
- file
weight_recorder:
params:
nest_model: weight_recorder
nest_params: {}
my_multimeter:
params:
nest_model: multimeter
nest_params:
record_from:
- V_m
my_spike_detector:
params:
nest_model: spike_detector
nest_params: {}
recorders:
params:
population_recorders:
- layers:
- l1
populations:
- l1_exc
model: my_multimeter
- layers:
- input_layer
populations: null
model: my_spike_detector
projection_recorders:
- source_layers:
- l1
source_population: l1_exc
target_layers:
- l1
target_population: l1_inh
projection_model: proj_1_AMPA
model: weight_recorder
nest_params: {}
)
[33]:
sim.sessions
[33]:
[Session(00_warmup, {'record': False,
'reset_network': False,
'shift_origin': True,
'simulation_time': 100.0,
'synapse_changes': [],
'unit_changes': []}),
Session(01_3_spikes, {'record': True,
'reset_network': False,
'shift_origin': True,
'simulation_time': 100.0,
'synapse_changes': [],
'unit_changes': [{'layers': ['input_layer'],
'nest_params': {'spike_times': [1.0, 10.0, 20.0]},
'population_name': 'spike_generator'}]}),
Session(02_2_spikes, {'record': True,
'reset_network': False,
'shift_origin': True,
'simulation_time': 100.0,
'synapse_changes': [],
'unit_changes': [{'layers': ['input_layer'],
'nest_params': {'spike_times': [1.0, 10.0]},
'population_name': 'spike_generator'}]}),
Session(03_3_spikes, {'record': True,
'reset_network': False,
'shift_origin': True,
'simulation_time': 100.0,
'synapse_changes': [],
'unit_changes': [{'layers': ['input_layer'],
'nest_params': {'spike_times': [1.0, 10.0, 20.0]},
'population_name': 'spike_generator'}]})]
Finally, we actually run all the sessions in NEST by calling Simulation.run():
[34]:
sim.run()
2020-06-29 12:30:55,050 [denest.simulation] INFO: Running 4 sessions...
2020-06-29 12:30:55,051 [denest.simulation] INFO: Running session: '00_warmup'...
2020-06-29 12:30:55,053 [denest.session] INFO: Initializing session...
2020-06-29 12:30:55,057 [denest.network.recorders] INFO: Setting status for recorder my_multimeter_l1_l1_exc: {'start': 100.0}
2020-06-29 12:30:55,065 [denest.network.recorders] INFO: Setting status for recorder my_spike_detector_input_layer_parrot_neuron: {'start': 100.0}
2020-06-29 12:30:55,072 [denest.network.recorders] INFO: Setting status for recorder weight_recorder_proj_1_AMPA-l1-l1_exc-l1-l1_inh: {'start': 100.0}
2020-06-29 12:30:55,074 [denest.session] INFO: Setting `origin` flag to `0.0` for all stimulation devices in ``InputLayers`` for session `00_warmup`
2020-06-29 12:30:55,082 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:55,090 [denest.session] INFO: Running session '00_warmup' for 100 ms
2020-06-29 12:30:55,483 [denest.session] INFO: Finished running session
2020-06-29 12:30:55,484 [denest.session] INFO: Session '00_warmup' virtual running time: 100 ms
2020-06-29 12:30:55,485 [denest.session] INFO: Session '00_warmup' real running time: 0h:00m:00s
2020-06-29 12:30:55,511 [denest.simulation] INFO: Done running session '00_warmup'
2020-06-29 12:30:55,513 [denest.simulation] INFO: Running session: '01_3_spikes'...
2020-06-29 12:30:55,517 [denest.session] INFO: Initializing session...
2020-06-29 12:30:55,522 [denest.session] INFO: Setting `origin` flag to `100.0` for all stimulation devices in ``InputLayers`` for session `01_3_spikes`
2020-06-29 12:30:55,529 [denest.utils.validation] INFO: Object `Unit changes dictionary`: params: using default value for optional parameters:
{'change_type': 'constant', 'from_array': False}
2020-06-29 12:30:55,530 [denest.network.layers] INFO: Layer='input_layer', pop='spike_generator': Applying 'constant' change, param='spike_times', from single value')
2020-06-29 12:30:55,601 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:55,602 [denest.session] INFO: Running session '01_3_spikes' for 100 ms
2020-06-29 12:30:55,822 [denest.session] INFO: Finished running session
2020-06-29 12:30:55,822 [denest.session] INFO: Session '01_3_spikes' virtual running time: 100 ms
2020-06-29 12:30:55,823 [denest.session] INFO: Session '01_3_spikes' real running time: 0h:00m:00s
2020-06-29 12:30:55,856 [denest.simulation] INFO: Done running session '01_3_spikes'
2020-06-29 12:30:55,857 [denest.simulation] INFO: Running session: '02_2_spikes'...
2020-06-29 12:30:55,858 [denest.session] INFO: Initializing session...
2020-06-29 12:30:55,865 [denest.session] INFO: Setting `origin` flag to `200.0` for all stimulation devices in ``InputLayers`` for session `02_2_spikes`
2020-06-29 12:30:55,877 [denest.utils.validation] INFO: Object `Unit changes dictionary`: params: using default value for optional parameters:
{'change_type': 'constant', 'from_array': False}
2020-06-29 12:30:55,880 [denest.network.layers] INFO: Layer='input_layer', pop='spike_generator': Applying 'constant' change, param='spike_times', from single value')
2020-06-29 12:30:55,958 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:55,964 [denest.session] INFO: Running session '02_2_spikes' for 100 ms
2020-06-29 12:30:56,353 [denest.session] INFO: Finished running session
2020-06-29 12:30:56,355 [denest.session] INFO: Session '02_2_spikes' virtual running time: 100 ms
2020-06-29 12:30:56,356 [denest.session] INFO: Session '02_2_spikes' real running time: 0h:00m:00s
2020-06-29 12:30:56,358 [denest.simulation] INFO: Done running session '02_2_spikes'
2020-06-29 12:30:56,359 [denest.simulation] INFO: Running session: '03_3_spikes'...
2020-06-29 12:30:56,361 [denest.session] INFO: Initializing session...
2020-06-29 12:30:56,363 [denest.session] INFO: Setting `origin` flag to `300.0` for all stimulation devices in ``InputLayers`` for session `03_3_spikes`
2020-06-29 12:30:56,384 [denest.utils.validation] INFO: Object `Unit changes dictionary`: params: using default value for optional parameters:
{'change_type': 'constant', 'from_array': False}
2020-06-29 12:30:56,386 [denest.network.layers] INFO: Layer='input_layer', pop='spike_generator': Applying 'constant' change, param='spike_times', from single value')
2020-06-29 12:30:56,500 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:56,501 [denest.session] INFO: Running session '03_3_spikes' for 100 ms
2020-06-29 12:30:56,754 [denest.session] INFO: Finished running session
2020-06-29 12:30:56,757 [denest.session] INFO: Session '03_3_spikes' virtual running time: 100 ms
2020-06-29 12:30:56,773 [denest.session] INFO: Session '03_3_spikes' real running time: 0h:00m:00s
2020-06-29 12:30:56,784 [denest.simulation] INFO: Done running session '03_3_spikes'
2020-06-29 12:30:56,786 [denest.simulation] INFO: Finished running simulation
[35]:
!ls {sim.output_dir}
data parameter_tree.yml session_times.yml versions.txt
[36]:
!ls {Path(sim.output_dir)/'data'}
my_multimeter_l1_l1_exc-203-0.dat
my_multimeter_l1_l1_exc.yml
my_spike_detector_input_layer_parrot_neuron-204-0.gdf
my_spike_detector_input_layer_parrot_neuron.yml
weight_recorder_proj_1_AMPA-l1-l1_exc-l1-l1_inh-205-0.csv
weight_recorder_proj_1_AMPA-l1-l1_exc-l1-l1_inh.yml
Run using the denest.run function¶
The run function is a shorthand for the above steps:
[37]:
denest.run(PARAMS_DIR/'tree_paths.yml')
2020-06-29 12:30:57,267 [denest] INFO:
=== RUNNING SIMULATION ========================================================
2020-06-29 12:30:57,270 [denest] INFO: Loading parameter file paths from data/params/tree_paths.yml
2020-06-29 12:30:57,275 [denest] INFO: Finished loading parameter file paths
2020-06-29 12:30:57,277 [denest] INFO: Loading parameters files:
['./network_tree.yml',
'./simulation.yml',
'./session_models.yml',
'./kernel.yml']
2020-06-29 12:30:57,337 [denest] INFO: Initializing simulation...
2020-06-29 12:30:57,353 [denest.utils.validation] INFO: Object `simulation`: params: using default value for optional parameters:
{'input_dir': 'input'}
2020-06-29 12:30:57,355 [denest.simulation] INFO: Initializing NEST kernel and seeds...
2020-06-29 12:30:57,356 [denest.simulation] INFO: Resetting NEST kernel...
2020-06-29 12:30:57,384 [denest.simulation] INFO: Setting NEST kernel status...
2020-06-29 12:30:57,390 [denest.simulation] INFO: Calling `nest.SetKernelStatus({'resolution': 0.5, 'overwrite_files': True})`
2020-06-29 12:30:57,412 [denest.simulation] INFO: Calling `nest.SetKernelStatus({'data_path': 'output/data', 'grng_seed': 11, 'rng_seeds': range(12, 13)})
2020-06-29 12:30:57,414 [denest.simulation] INFO: Finished setting NEST kernel status
2020-06-29 12:30:57,416 [denest.simulation] INFO: Installing external modules...
2020-06-29 12:30:57,441 [denest.simulation] INFO: Finished installing external modules
2020-06-29 12:30:57,444 [denest.simulation] INFO: Finished initializing kernel
2020-06-29 12:30:57,447 [denest.simulation] INFO: Build N=3 session models
2020-06-29 12:30:57,453 [denest.simulation] INFO: Build N=4 sessions
2020-06-29 12:30:57,454 [denest.session] INFO: Creating session "00_warmup"
2020-06-29 12:30:57,456 [denest.utils.validation] INFO: Object `00_warmup`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': [], 'unit_changes': []}
2020-06-29 12:30:57,464 [denest.session] INFO: Creating session "01_3_spikes"
2020-06-29 12:30:57,469 [denest.utils.validation] INFO: Object `01_3_spikes`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': []}
2020-06-29 12:30:57,474 [denest.session] INFO: Creating session "02_2_spikes"
2020-06-29 12:30:57,477 [denest.utils.validation] INFO: Object `02_2_spikes`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': []}
2020-06-29 12:30:57,479 [denest.session] INFO: Creating session "03_3_spikes"
2020-06-29 12:30:57,480 [denest.utils.validation] INFO: Object `03_3_spikes`: params: using default value for optional parameters:
{'reset_network': False, 'synapse_changes': []}
2020-06-29 12:30:57,483 [denest.simulation] INFO: Sessions: ['00_warmup', '01_3_spikes', '02_2_spikes', '03_3_spikes']
2020-06-29 12:30:57,486 [denest.simulation] INFO: Building network.
2020-06-29 12:30:57,502 [denest.network] INFO: Build N=2 ``Model`` objects
2020-06-29 12:30:57,507 [denest.network] INFO: Build N=2 ``SynapseModel`` objects
2020-06-29 12:30:57,511 [denest.network] INFO: Build N=3 ``Model`` objects
2020-06-29 12:30:57,514 [denest.network] INFO: Build N=2 ``Layer`` or ``InputLayer`` objects.
2020-06-29 12:30:57,519 [denest.utils.validation] INFO: Object `proj_2_GABAA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-29 12:30:57,522 [denest.utils.validation] INFO: Object `proj_1_AMPA`: params: using default value for optional parameters:
{'type': 'topological'}
2020-06-29 12:30:57,524 [denest.network] INFO: Build N=2 ``ProjectionModel`` objects
2020-06-29 12:30:57,528 [denest.network] INFO: Build N=3 ``TopoProjection`` objects
2020-06-29 12:30:57,536 [denest.network] INFO: Build N=2 population recorders.
2020-06-29 12:30:57,537 [denest.network] INFO: Build N=1 projection recorders.
2020-06-29 12:30:57,539 [denest.simulation] INFO: Creating network.
2020-06-29 12:30:57,542 [denest.network] INFO: Creating neuron models...
100%|██████████| 2/2 [00:00<00:00, 1192.58it/s]
2020-06-29 12:30:57,553 [denest.network] INFO: Creating synapse models...
100%|██████████| 2/2 [00:00<00:00, 1034.23it/s]
2020-06-29 12:30:57,560 [denest.network] INFO: Creating recorder models...
100%|██████████| 3/3 [00:00<00:00, 969.63it/s]
2020-06-29 12:30:57,572 [denest.network] INFO: Creating layers...
100%|██████████| 2/2 [00:00<00:00, 9.98it/s]
2020-06-29 12:30:57,787 [denest.network] INFO: Creating population recorders...
100%|██████████| 2/2 [00:00<00:00, 12.65it/s]
2020-06-29 12:30:57,954 [denest.network] INFO: Creating projection recorders...
100%|██████████| 1/1 [00:00<00:00, 112.67it/s]
2020-06-29 12:30:57,971 [denest.network] INFO: Connecting layers...
100%|██████████| 3/3 [00:00<00:00, 785.35it/s]
2020-06-29 12:30:57,981 [denest.network] INFO: Network size (including recorders and parrot neurons):
Number of nodes: 206
Number of projections: 6650
2020-06-29 12:30:57,982 [denest.simulation] INFO: Finished creating network
2020-06-29 12:30:57,983 [denest.simulation] INFO: Saving simulation metadata...
2020-06-29 12:30:57,984 [denest.simulation] INFO: Creating output directory: output
2020-06-29 12:30:57,986 [denest.io.save] INFO: Clearing directory: output
2020-06-29 12:30:57,988 [denest.io.save] INFO: Clearing directory: output
2020-06-29 12:30:57,990 [denest.io.save] INFO: Clearing directory: output/data
2020-06-29 12:30:57,994 [denest.io.save] INFO: Clearing directory: output/data
2020-06-29 12:30:57,996 [denest.io.save] INFO: Clearing directory: output/data
2020-06-29 12:30:57,998 [denest.io.save] INFO: Clearing directory: output
2020-06-29 12:30:58,102 [denest.simulation] INFO: Finished saving simulation metadata
2020-06-29 12:30:58,103 [denest] INFO: Finished initializing simulation
2020-06-29 12:30:58,104 [denest] INFO: Running simulation...
2020-06-29 12:30:58,151 [denest.simulation] INFO: Running 4 sessions...
2020-06-29 12:30:58,153 [denest.simulation] INFO: Running session: '00_warmup'...
2020-06-29 12:30:58,156 [denest.session] INFO: Initializing session...
2020-06-29 12:30:58,159 [denest.network.recorders] INFO: Setting status for recorder my_multimeter_l1_l1_exc: {'start': 100.0}
2020-06-29 12:30:58,166 [denest.network.recorders] INFO: Setting status for recorder my_spike_detector_input_layer_parrot_neuron: {'start': 100.0}
2020-06-29 12:30:58,168 [denest.network.recorders] INFO: Setting status for recorder weight_recorder_proj_1_AMPA-l1-l1_exc-l1-l1_inh: {'start': 100.0}
2020-06-29 12:30:58,171 [denest.session] INFO: Setting `origin` flag to `0.0` for all stimulation devices in ``InputLayers`` for session `00_warmup`
2020-06-29 12:30:58,181 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:58,181 [denest.session] INFO: Running session '00_warmup' for 100 ms
2020-06-29 12:30:58,291 [denest.session] INFO: Finished running session
2020-06-29 12:30:58,292 [denest.session] INFO: Session '00_warmup' virtual running time: 100 ms
2020-06-29 12:30:58,293 [denest.session] INFO: Session '00_warmup' real running time: 0h:00m:00s
2020-06-29 12:30:58,297 [denest.simulation] INFO: Done running session '00_warmup'
2020-06-29 12:30:58,298 [denest.simulation] INFO: Running session: '01_3_spikes'...
2020-06-29 12:30:58,299 [denest.session] INFO: Initializing session...
2020-06-29 12:30:58,300 [denest.session] INFO: Setting `origin` flag to `100.0` for all stimulation devices in ``InputLayers`` for session `01_3_spikes`
2020-06-29 12:30:58,317 [denest.utils.validation] INFO: Object `Unit changes dictionary`: params: using default value for optional parameters:
{'change_type': 'constant', 'from_array': False}
2020-06-29 12:30:58,320 [denest.network.layers] INFO: Layer='input_layer', pop='spike_generator': Applying 'constant' change, param='spike_times', from single value')
2020-06-29 12:30:58,388 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:58,389 [denest.session] INFO: Running session '01_3_spikes' for 100 ms
2020-06-29 12:30:58,537 [denest.session] INFO: Finished running session
2020-06-29 12:30:58,538 [denest.session] INFO: Session '01_3_spikes' virtual running time: 100 ms
2020-06-29 12:30:58,539 [denest.session] INFO: Session '01_3_spikes' real running time: 0h:00m:00s
2020-06-29 12:30:58,542 [denest.simulation] INFO: Done running session '01_3_spikes'
2020-06-29 12:30:58,542 [denest.simulation] INFO: Running session: '02_2_spikes'...
2020-06-29 12:30:58,543 [denest.session] INFO: Initializing session...
2020-06-29 12:30:58,544 [denest.session] INFO: Setting `origin` flag to `200.0` for all stimulation devices in ``InputLayers`` for session `02_2_spikes`
2020-06-29 12:30:58,549 [denest.utils.validation] INFO: Object `Unit changes dictionary`: params: using default value for optional parameters:
{'change_type': 'constant', 'from_array': False}
2020-06-29 12:30:58,550 [denest.network.layers] INFO: Layer='input_layer', pop='spike_generator': Applying 'constant' change, param='spike_times', from single value')
2020-06-29 12:30:58,614 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:58,614 [denest.session] INFO: Running session '02_2_spikes' for 100 ms
2020-06-29 12:30:58,751 [denest.session] INFO: Finished running session
2020-06-29 12:30:58,753 [denest.session] INFO: Session '02_2_spikes' virtual running time: 100 ms
2020-06-29 12:30:58,754 [denest.session] INFO: Session '02_2_spikes' real running time: 0h:00m:00s
2020-06-29 12:30:58,755 [denest.simulation] INFO: Done running session '02_2_spikes'
2020-06-29 12:30:58,756 [denest.simulation] INFO: Running session: '03_3_spikes'...
2020-06-29 12:30:58,758 [denest.session] INFO: Initializing session...
2020-06-29 12:30:58,760 [denest.session] INFO: Setting `origin` flag to `300.0` for all stimulation devices in ``InputLayers`` for session `03_3_spikes`
2020-06-29 12:30:58,764 [denest.utils.validation] INFO: Object `Unit changes dictionary`: params: using default value for optional parameters:
{'change_type': 'constant', 'from_array': False}
2020-06-29 12:30:58,766 [denest.network.layers] INFO: Layer='input_layer', pop='spike_generator': Applying 'constant' change, param='spike_times', from single value')
2020-06-29 12:30:58,922 [denest.session] INFO: Finished initializing session
2020-06-29 12:30:58,923 [denest.session] INFO: Running session '03_3_spikes' for 100 ms
2020-06-29 12:30:59,100 [denest.session] INFO: Finished running session
2020-06-29 12:30:59,101 [denest.session] INFO: Session '03_3_spikes' virtual running time: 100 ms
2020-06-29 12:30:59,103 [denest.session] INFO: Session '03_3_spikes' real running time: 0h:00m:00s
2020-06-29 12:30:59,104 [denest.simulation] INFO: Done running session '03_3_spikes'
2020-06-29 12:30:59,105 [denest.simulation] INFO: Finished running simulation
2020-06-29 12:30:59,106 [denest] INFO: Finished running simulation
2020-06-29 12:30:59,108 [denest] INFO: Total simulation virtual time: 400.0 ms
2020-06-29 12:30:59,121 [denest] INFO: Total simulation real time: 0h:00m:01s
2020-06-29 12:30:59,126 [denest] INFO: Simulation output written to: /Users/tom/docker/nets-dev/docs/source/tutorials/output
Run from the command line¶
A simulation can also be run directly from the command line as follows:
python3 -m denest <tree_paths.yml> [-o <output_dir>]