bsb-nest 4.3.1__tar.gz → 5.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bsb-nest might be problematic. Click here for more details.
- bsb_nest-5.0.0/PKG-INFO +22 -0
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/README.md +2 -3
- bsb_nest-5.0.0/bsb_nest/__init__.py +27 -0
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/adapter.py +46 -25
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/cell.py +2 -0
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/connection.py +42 -16
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/device.py +46 -19
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/devices/__init__.py +8 -0
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/devices/multimeter.py +2 -3
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/devices/poisson_generator.py +1 -1
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/devices/sinusoidal_poisson_generator.py +6 -6
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/devices/spike_recorder.py +1 -2
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/distributions.py +6 -8
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/simulation.py +12 -3
- bsb_nest-5.0.0/pyproject.toml +67 -0
- bsb_nest-4.3.1/PKG-INFO +0 -36
- bsb_nest-4.3.1/bsb_nest/__init__.py +0 -12
- bsb_nest-4.3.1/pyproject.toml +0 -70
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/LICENSE +0 -0
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/devices/dc_generator.py +0 -0
- {bsb_nest-4.3.1 → bsb_nest-5.0.0}/bsb_nest/exceptions.py +0 -0
bsb_nest-5.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bsb-nest
|
|
3
|
+
Version: 5.0.0
|
|
4
|
+
Summary: NEST simulation adapter for the BSB framework.
|
|
5
|
+
Author-email: Robin De Schepper <robin@alexandria.sc>, Dimitri Rodarie <dimitri.rodarie@unipv.it>
|
|
6
|
+
Requires-Python: >=3.10,<4
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: bsb-core~=6.0
|
|
11
|
+
Requires-Dist: bsb-core[parallel] ; extra == "parallel"
|
|
12
|
+
Provides-Extra: parallel
|
|
13
|
+
|
|
14
|
+
[](https://github.com/dbbs-lab/bsb-nest/actions/workflows/main.yml)
|
|
15
|
+
[](https://github.com/astral-sh/ruff)
|
|
16
|
+
|
|
17
|
+
# bsb-nest
|
|
18
|
+
|
|
19
|
+
bsb-nest is a plugin of the [BSB](https://github.com/dbbs-lab/bsb).
|
|
20
|
+
It contains the interfaces and tools to simulate BSB circuit with the
|
|
21
|
+
[NEST simulator](https://www.nest-simulator.org/).
|
|
22
|
+
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
[](https://github.com/dbbs-lab/bsb-nest/actions/workflows/main.yml)
|
|
2
|
-
[](https://github.com/astral-sh/ruff)
|
|
3
3
|
|
|
4
4
|
# bsb-nest
|
|
5
5
|
|
|
6
|
-
bsb-nest is a
|
|
7
|
-
[bsb-core](https://github.com/dbbs-lab/bsb-core)).
|
|
6
|
+
bsb-nest is a plugin of the [BSB](https://github.com/dbbs-lab/bsb).
|
|
8
7
|
It contains the interfaces and tools to simulate BSB circuit with the
|
|
9
8
|
[NEST simulator](https://www.nest-simulator.org/).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NEST simulation adapter for the BSB framework.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from bsb import SimulationBackendPlugin
|
|
6
|
+
|
|
7
|
+
from .adapter import NestAdapter
|
|
8
|
+
from .devices import (
|
|
9
|
+
DCGenerator,
|
|
10
|
+
Multimeter,
|
|
11
|
+
PoissonGenerator,
|
|
12
|
+
SinusoidalPoissonGenerator,
|
|
13
|
+
SpikeRecorder,
|
|
14
|
+
)
|
|
15
|
+
from .simulation import NestSimulation
|
|
16
|
+
|
|
17
|
+
__plugin__ = SimulationBackendPlugin(Simulation=NestSimulation, Adapter=NestAdapter)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"DCGenerator",
|
|
21
|
+
"Multimeter",
|
|
22
|
+
"NestAdapter",
|
|
23
|
+
"NestSimulation",
|
|
24
|
+
"PoissonGenerator",
|
|
25
|
+
"SinusoidalPoissonGenerator",
|
|
26
|
+
"SpikeRecorder",
|
|
27
|
+
]
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import sys
|
|
2
3
|
import typing
|
|
3
4
|
|
|
4
5
|
import nest
|
|
5
6
|
from bsb import (
|
|
6
|
-
MPI,
|
|
7
7
|
AdapterError,
|
|
8
8
|
AdapterProgress,
|
|
9
9
|
SimulationData,
|
|
@@ -32,7 +32,7 @@ class NestResult(SimulationResult):
|
|
|
32
32
|
segment.spiketrains.append(
|
|
33
33
|
SpikeTrain(
|
|
34
34
|
events["times"],
|
|
35
|
-
|
|
35
|
+
array_annotations={"senders": events["senders"]},
|
|
36
36
|
t_stop=nest.biological_time,
|
|
37
37
|
units="ms",
|
|
38
38
|
**annotations,
|
|
@@ -43,18 +43,38 @@ class NestResult(SimulationResult):
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class NestAdapter(SimulatorAdapter):
|
|
46
|
-
def __init__(self):
|
|
47
|
-
|
|
46
|
+
def __init__(self, comm=None):
|
|
47
|
+
super().__init__(comm=comm)
|
|
48
48
|
self.loaded_modules = set()
|
|
49
49
|
|
|
50
|
-
def simulate(self,
|
|
50
|
+
def simulate(self, *simulations, post_prepare=None):
|
|
51
51
|
try:
|
|
52
52
|
self.reset_kernel()
|
|
53
|
-
return super().simulate(
|
|
53
|
+
return super().simulate(*simulations, post_prepare=post_prepare)
|
|
54
54
|
finally:
|
|
55
55
|
self.reset_kernel()
|
|
56
56
|
|
|
57
|
-
def prepare(self, simulation
|
|
57
|
+
def prepare(self, simulation):
|
|
58
|
+
"""
|
|
59
|
+
Prepare the simulation environment in NEST.
|
|
60
|
+
|
|
61
|
+
This method initializes internal data structures and performs all
|
|
62
|
+
setup steps required before running the simulation:
|
|
63
|
+
|
|
64
|
+
- Loads and installs required NEST modules.
|
|
65
|
+
- Applies simulation-level settings (e.g., resolution, verbosity, seed).
|
|
66
|
+
- Creates neuron populations based on cell models.
|
|
67
|
+
- Establishes connectivity between neurons using connection models.
|
|
68
|
+
- Instantiates devices (e.g., recorders, stimuli) used in the simulation.
|
|
69
|
+
|
|
70
|
+
If any error occurs during preparation, the corresponding internal state
|
|
71
|
+
is cleaned up to avoid partial setups.
|
|
72
|
+
|
|
73
|
+
:param simulation: The simulation configuration to prepare.
|
|
74
|
+
:type simulation: NestSimulation
|
|
75
|
+
:returns: The prepared simulation data associated with the given simulation.
|
|
76
|
+
:rtype: bsb.simulation.adapter.SimulationData
|
|
77
|
+
"""
|
|
58
78
|
self.simdata[simulation] = SimulationData(
|
|
59
79
|
simulation, result=NestResult(simulation)
|
|
60
80
|
)
|
|
@@ -79,7 +99,7 @@ class NestAdapter(SimulatorAdapter):
|
|
|
79
99
|
# to appropriately warn them when they load them twice.
|
|
80
100
|
self.loaded_modules = set()
|
|
81
101
|
|
|
82
|
-
def run(self, *simulations
|
|
102
|
+
def run(self, *simulations):
|
|
83
103
|
unprepared = [sim for sim in simulations if sim not in self.simdata]
|
|
84
104
|
if unprepared:
|
|
85
105
|
raise AdapterError(f"Unprepared for simulations: {', '.join(unprepared)}")
|
|
@@ -96,7 +116,7 @@ class NestAdapter(SimulatorAdapter):
|
|
|
96
116
|
for sim in simulations:
|
|
97
117
|
del self.simdata[sim]
|
|
98
118
|
progress.complete()
|
|
99
|
-
report(
|
|
119
|
+
report("Simulation done.", level=2)
|
|
100
120
|
return results
|
|
101
121
|
|
|
102
122
|
def load_modules(self, simulation):
|
|
@@ -107,9 +127,10 @@ class NestAdapter(SimulatorAdapter):
|
|
|
107
127
|
except Exception as e:
|
|
108
128
|
if e.errorname == "DynamicModuleManagementError":
|
|
109
129
|
if "loaded already" in e.message:
|
|
110
|
-
# Modules stay loaded in between `ResetKernel` calls.
|
|
111
|
-
# is not in the `loaded_modules` set, then
|
|
112
|
-
# `reset`/`prepare` cycle,
|
|
130
|
+
# Modules stay loaded in between `ResetKernel` calls.
|
|
131
|
+
# If the module is not in the `loaded_modules` set, then
|
|
132
|
+
# it's the first time this `reset`/`prepare` cycle,
|
|
133
|
+
# and there is no user-side issue.
|
|
113
134
|
if module in self.loaded_modules:
|
|
114
135
|
warn(f"Already loaded '{module}'.", KernelWarning)
|
|
115
136
|
elif "file not found" in e.message:
|
|
@@ -134,33 +155,33 @@ class NestAdapter(SimulatorAdapter):
|
|
|
134
155
|
"""
|
|
135
156
|
simdata = self.simdata[simulation]
|
|
136
157
|
iter = simulation.connection_models.values()
|
|
137
|
-
if
|
|
158
|
+
if self.comm.get_rank() == 0:
|
|
138
159
|
iter = tqdm(iter, desc="", file=sys.stdout)
|
|
139
160
|
for connection_model in iter:
|
|
140
|
-
|
|
141
|
-
iter.set_description(connection_model.name)
|
|
142
|
-
except AttributeError:
|
|
161
|
+
with contextlib.suppress(AttributeError):
|
|
143
162
|
# Only rank 0 should report progress bar
|
|
144
|
-
|
|
163
|
+
iter.set_description(connection_model.name)
|
|
145
164
|
cs = simulation.scaffold.get_connectivity_set(
|
|
146
165
|
connection_model.tag or connection_model.name
|
|
147
166
|
)
|
|
148
167
|
try:
|
|
149
168
|
pre_nodes = simdata.populations[simulation.get_model_of(cs.pre_type)]
|
|
150
169
|
except KeyError:
|
|
151
|
-
raise NestModelError(f"No model found for {cs.pre_type}")
|
|
170
|
+
raise NestModelError(f"No model found for {cs.pre_type}") from None
|
|
152
171
|
try:
|
|
153
172
|
post_nodes = simdata.populations[simulation.get_model_of(cs.post_type)]
|
|
154
173
|
except KeyError:
|
|
155
|
-
raise NestModelError(f"No model found for {cs.post_type}")
|
|
174
|
+
raise NestModelError(f"No model found for {cs.post_type}") from None
|
|
156
175
|
try:
|
|
157
176
|
simdata.connections[connection_model] = (
|
|
158
177
|
connection_model.create_connections(
|
|
159
|
-
simdata, pre_nodes, post_nodes, cs
|
|
178
|
+
simdata, pre_nodes, post_nodes, cs, self.comm
|
|
160
179
|
)
|
|
161
180
|
)
|
|
162
|
-
except Exception
|
|
163
|
-
raise NestConnectError(
|
|
181
|
+
except Exception:
|
|
182
|
+
raise NestConnectError(
|
|
183
|
+
f"{connection_model} error during connect."
|
|
184
|
+
) from None
|
|
164
185
|
|
|
165
186
|
def create_devices(self, simulation):
|
|
166
187
|
simdata = self.simdata[simulation]
|
|
@@ -175,8 +196,8 @@ class NestAdapter(SimulatorAdapter):
|
|
|
175
196
|
nest.rng_seed = simulation.seed
|
|
176
197
|
|
|
177
198
|
def check_comm(self):
|
|
178
|
-
if nest.NumProcesses() !=
|
|
199
|
+
if nest.NumProcesses() != self.comm.get_size():
|
|
179
200
|
raise RuntimeError(
|
|
180
|
-
f"NEST is managing {nest.NumProcesses()} processes, but
|
|
181
|
-
" were detected. Please check your MPI setup."
|
|
201
|
+
f"NEST is managing {nest.NumProcesses()} processes, but "
|
|
202
|
+
f"{self.comm.get_size()} were detected. Please check your MPI setup."
|
|
182
203
|
)
|
|
@@ -7,7 +7,9 @@ from .distributions import NestRandomDistribution, nest_parameter
|
|
|
7
7
|
@config.node
|
|
8
8
|
class NestCell(CellModel):
|
|
9
9
|
model = config.attr(type=str, default="iaf_psc_alpha")
|
|
10
|
+
"""Importable reference to the NEST model describing the cell type."""
|
|
10
11
|
constants = config.dict(type=nest_parameter())
|
|
12
|
+
"""Dictionary of the constants values to assign to the cell model."""
|
|
11
13
|
|
|
12
14
|
def create_population(self, simdata):
|
|
13
15
|
n = len(simdata.placement[self])
|
|
@@ -4,7 +4,7 @@ import sys
|
|
|
4
4
|
import nest
|
|
5
5
|
import numpy as np
|
|
6
6
|
import psutil
|
|
7
|
-
from bsb import
|
|
7
|
+
from bsb import ConnectionModel, compose_nodes, config, types
|
|
8
8
|
from tqdm import tqdm
|
|
9
9
|
|
|
10
10
|
from .distributions import nest_parameter
|
|
@@ -13,17 +13,32 @@ from .exceptions import NestConnectError
|
|
|
13
13
|
|
|
14
14
|
@config.node
|
|
15
15
|
class NestSynapseSettings:
|
|
16
|
+
"""
|
|
17
|
+
Class interfacing a NEST synapse model.
|
|
18
|
+
"""
|
|
19
|
+
|
|
16
20
|
model = config.attr(type=str, default="static_synapse")
|
|
21
|
+
"""Importable reference to the NEST model describing the synapse type."""
|
|
17
22
|
weight = config.attr(type=float, required=True)
|
|
23
|
+
"""Weight of the connection between the presynaptic and the postsynaptic cells."""
|
|
18
24
|
delay = config.attr(type=float, required=True)
|
|
25
|
+
"""Delay of the transmission between the presynaptic and the postsynaptic cells."""
|
|
19
26
|
receptor_type = config.attr(type=int)
|
|
27
|
+
"""Index of the postsynaptic receptor to target."""
|
|
20
28
|
constants = config.catch_all(type=nest_parameter())
|
|
29
|
+
"""Dictionary of the constants values to assign to the synapse model."""
|
|
21
30
|
|
|
22
31
|
|
|
23
32
|
@config.node
|
|
24
33
|
class NestConnectionSettings:
|
|
34
|
+
"""
|
|
35
|
+
Class interfacing a NEST connection rule.
|
|
36
|
+
"""
|
|
37
|
+
|
|
25
38
|
rule = config.attr(type=str)
|
|
39
|
+
"""Importable reference to the Nest connection rule used to connect the cells."""
|
|
26
40
|
constants = config.catch_all(type=types.any_())
|
|
41
|
+
"""Dictionary of parameters to assign to the connection rule."""
|
|
27
42
|
|
|
28
43
|
|
|
29
44
|
class LazySynapseCollection:
|
|
@@ -50,10 +65,21 @@ class LazySynapseCollection:
|
|
|
50
65
|
|
|
51
66
|
@config.dynamic(attr_name="model_strategy", required=False)
|
|
52
67
|
class NestConnection(compose_nodes(NestConnectionSettings, ConnectionModel)):
|
|
53
|
-
|
|
68
|
+
"""
|
|
69
|
+
Class interfacing a NEST connection, including its connection rule and synaptic
|
|
70
|
+
parameters.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
model_strategy: str
|
|
74
|
+
"""
|
|
75
|
+
Specifies the strategy used by the connection model for synapse creation and
|
|
76
|
+
management.
|
|
77
|
+
"""
|
|
78
|
+
|
|
54
79
|
synapse = config.attr(type=NestSynapseSettings, required=True)
|
|
80
|
+
"""Nest synapse model with its parameters."""
|
|
55
81
|
|
|
56
|
-
def create_connections(self, simdata, pre_nodes, post_nodes, cs):
|
|
82
|
+
def create_connections(self, simdata, pre_nodes, post_nodes, cs, comm):
|
|
57
83
|
import nest
|
|
58
84
|
|
|
59
85
|
syn_spec = self.get_syn_spec()
|
|
@@ -64,11 +90,11 @@ class NestConnection(compose_nodes(NestConnectionSettings, ConnectionModel)):
|
|
|
64
90
|
if self.rule is not None:
|
|
65
91
|
nest.Connect(pre_nodes, post_nodes, self.get_conn_spec(), syn_spec)
|
|
66
92
|
else:
|
|
67
|
-
|
|
93
|
+
comm.barrier()
|
|
68
94
|
for pre_locs, post_locs in self.predict_mem_iterator(
|
|
69
|
-
pre_nodes, post_nodes, cs
|
|
95
|
+
pre_nodes, post_nodes, cs, comm
|
|
70
96
|
):
|
|
71
|
-
|
|
97
|
+
comm.barrier()
|
|
72
98
|
if len(pre_locs) == 0 or len(post_locs) == 0:
|
|
73
99
|
continue
|
|
74
100
|
cell_pairs, multiplicity = np.unique(
|
|
@@ -89,36 +115,36 @@ class NestConnection(compose_nodes(NestConnectionSettings, ConnectionModel)):
|
|
|
89
115
|
ssw,
|
|
90
116
|
return_synapsecollection=False,
|
|
91
117
|
)
|
|
92
|
-
|
|
118
|
+
comm.barrier()
|
|
93
119
|
return LazySynapseCollection(pre_nodes, post_nodes)
|
|
94
120
|
|
|
95
|
-
def predict_mem_iterator(self, pre_nodes, post_nodes, cs):
|
|
121
|
+
def predict_mem_iterator(self, pre_nodes, post_nodes, cs, comm):
|
|
96
122
|
avmem = psutil.virtual_memory().available
|
|
97
123
|
predicted_all_mem = (
|
|
98
124
|
len(pre_nodes) * 8 * 2 + len(post_nodes) * 8 * 2 + len(cs) * 6 * 8 * (16 + 2)
|
|
99
|
-
) *
|
|
125
|
+
) * comm.get_size()
|
|
100
126
|
n_chunks = len(cs.get_local_chunks("out"))
|
|
101
127
|
predicted_local_mem = (predicted_all_mem / n_chunks) if n_chunks > 0 else 0.0
|
|
102
128
|
if predicted_local_mem > avmem / 2:
|
|
103
129
|
# Iterate block-by-block
|
|
104
|
-
return self.block_iterator(cs)
|
|
130
|
+
return self.block_iterator(cs, comm)
|
|
105
131
|
elif predicted_all_mem > avmem / 2:
|
|
106
132
|
# Iterate local hyperblocks
|
|
107
|
-
return self.local_iterator(cs)
|
|
133
|
+
return self.local_iterator(cs, comm)
|
|
108
134
|
else:
|
|
109
135
|
# Iterate all
|
|
110
136
|
return (cs.load_connections().as_globals().all(),)
|
|
111
137
|
|
|
112
|
-
def block_iterator(self, cs):
|
|
138
|
+
def block_iterator(self, cs, comm):
|
|
113
139
|
locals = cs.get_local_chunks("out")
|
|
114
140
|
|
|
115
141
|
def block_iter():
|
|
116
142
|
iter = locals
|
|
117
|
-
if
|
|
143
|
+
if comm.get_rank() == 0:
|
|
118
144
|
iter = tqdm(iter, desc="hyperblocks", file=sys.stdout)
|
|
119
145
|
for local in iter:
|
|
120
146
|
inner_iter = cs.load_connections().as_globals().from_(local)
|
|
121
|
-
if
|
|
147
|
+
if comm.get_rank() == 0:
|
|
122
148
|
yield from tqdm(
|
|
123
149
|
inner_iter,
|
|
124
150
|
desc="blocks",
|
|
@@ -131,9 +157,9 @@ class NestConnection(compose_nodes(NestConnectionSettings, ConnectionModel)):
|
|
|
131
157
|
|
|
132
158
|
return block_iter()
|
|
133
159
|
|
|
134
|
-
def local_iterator(self, cs):
|
|
160
|
+
def local_iterator(self, cs, comm):
|
|
135
161
|
iter = cs.get_local_chunks("out")
|
|
136
|
-
if
|
|
162
|
+
if comm.get_rank() == 0:
|
|
137
163
|
iter = tqdm(iter, desc="hyperblocks", file=sys.stdout)
|
|
138
164
|
yield from (
|
|
139
165
|
cs.load_connections().as_globals().from_(local).all() for local in iter
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
import abc
|
|
2
2
|
import warnings
|
|
3
3
|
|
|
4
4
|
import nest
|
|
5
|
-
from bsb import DeviceModel,
|
|
6
|
-
|
|
7
|
-
if typing.TYPE_CHECKING:
|
|
8
|
-
from .adapter import NestAdapter
|
|
9
|
-
from .simulation import NestSimulation
|
|
5
|
+
from bsb import DeviceModel, Targetting, config, refs, types
|
|
10
6
|
|
|
11
7
|
|
|
12
8
|
@config.node
|
|
13
9
|
class NestRule:
|
|
10
|
+
"""
|
|
11
|
+
Interface to connect a device directly through the NEST interface.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
14
|
rule = config.attr(type=str, required=True)
|
|
15
|
+
"""Connection rule to connect """
|
|
15
16
|
constants = config.catch_all(type=types.any_())
|
|
17
|
+
"""Dictionary of parameters for the targetting rule."""
|
|
16
18
|
cell_models = config.reflist(refs.sim_cell_model_ref)
|
|
19
|
+
"""Reference to the Nest cell model to target with the Device"""
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
@config.dynamic(attr_name="device", auto_classmap=True, default="external")
|
|
20
23
|
class NestDevice(DeviceModel):
|
|
24
|
+
device: str
|
|
25
|
+
"""Name of the NEST device model (e.g., "spike_generator", "poisson_generator")."""
|
|
21
26
|
weight = config.attr(type=float, required=True)
|
|
22
27
|
"""weight of the connection between the device and its target"""
|
|
23
28
|
delay = config.attr(type=float, required=True)
|
|
@@ -31,17 +36,17 @@ class NestDevice(DeviceModel):
|
|
|
31
36
|
|
|
32
37
|
def get_dict_targets(
|
|
33
38
|
self,
|
|
34
|
-
adapter
|
|
35
|
-
simulation
|
|
36
|
-
simdata
|
|
39
|
+
adapter,
|
|
40
|
+
simulation,
|
|
41
|
+
simdata,
|
|
37
42
|
) -> dict:
|
|
38
43
|
"""
|
|
39
44
|
Get a dictionary from a target group to its NEST Collection
|
|
40
45
|
for each target group of the device.
|
|
41
46
|
|
|
42
|
-
:param bsb_nest.NestAdapter adapter:
|
|
43
|
-
:param bsb_nest.NestSimulation simulation: Nest simulation instance
|
|
44
|
-
:param bsb.SimulationData simdata: Simulation data instance
|
|
47
|
+
:param bsb_nest.adapter.NestAdapter adapter: Nest adapter instance
|
|
48
|
+
:param bsb_nest.simulation.NestSimulation simulation: Nest simulation instance
|
|
49
|
+
:param bsb.simulation.adapter.SimulationData simdata: Simulation data instance
|
|
45
50
|
:return: dictionary of device target group to NEST Collection
|
|
46
51
|
:rtype: dict
|
|
47
52
|
"""
|
|
@@ -65,16 +70,16 @@ class NestDevice(DeviceModel):
|
|
|
65
70
|
|
|
66
71
|
def get_target_nodes(
|
|
67
72
|
self,
|
|
68
|
-
adapter
|
|
69
|
-
simulation
|
|
70
|
-
simdata
|
|
73
|
+
adapter,
|
|
74
|
+
simulation,
|
|
75
|
+
simdata,
|
|
71
76
|
):
|
|
72
77
|
"""
|
|
73
78
|
Get the NEST Collection of the targets of the device.
|
|
74
79
|
|
|
75
|
-
:param bsb_nest.NestAdapter adapter:
|
|
76
|
-
:param bsb_nest.NestSimulation simulation: Nest simulation instance
|
|
77
|
-
:param bsb.SimulationData simdata: Simulation data instance
|
|
80
|
+
:param bsb_nest.adapter.NestAdapter adapter:
|
|
81
|
+
:param bsb_nest.simulation.NestSimulation simulation: Nest simulation instance
|
|
82
|
+
:param bsb.simulation.adapter.SimulationData simdata: Simulation data instance
|
|
78
83
|
:return: Flattened NEST collection with all the targets of the device
|
|
79
84
|
"""
|
|
80
85
|
targets_dict = self.get_dict_targets(adapter, simulation, simdata)
|
|
@@ -82,7 +87,7 @@ class NestDevice(DeviceModel):
|
|
|
82
87
|
|
|
83
88
|
def connect_to_nodes(self, device, nodes):
|
|
84
89
|
if len(nodes) == 0:
|
|
85
|
-
warnings.warn(f"{self.name} has no targets")
|
|
90
|
+
warnings.warn(f"{self.name} has no targets", stacklevel=2)
|
|
86
91
|
else:
|
|
87
92
|
try:
|
|
88
93
|
nest.Connect(
|
|
@@ -108,11 +113,33 @@ class NestDevice(DeviceModel):
|
|
|
108
113
|
simdata.devices[self] = device
|
|
109
114
|
return device
|
|
110
115
|
|
|
116
|
+
@abc.abstractmethod
|
|
117
|
+
def implement(
|
|
118
|
+
self,
|
|
119
|
+
adapter,
|
|
120
|
+
simulation,
|
|
121
|
+
simdata,
|
|
122
|
+
):
|
|
123
|
+
"""
|
|
124
|
+
Create, connect and register the Nest device.
|
|
125
|
+
|
|
126
|
+
:param bsb_nest.adapter.NestAdapter adapter:
|
|
127
|
+
:param bsb_nest.simulation.NestSimulation simulation: Nest simulation instance
|
|
128
|
+
:param bsb.simulation.adapter.SimulationData simdata: Simulation data instance
|
|
129
|
+
"""
|
|
130
|
+
pass
|
|
131
|
+
|
|
111
132
|
|
|
112
133
|
@config.node
|
|
113
134
|
class ExtNestDevice(NestDevice, classmap_entry="external"):
|
|
135
|
+
"""
|
|
136
|
+
Class interfacing Nest devices.
|
|
137
|
+
"""
|
|
138
|
+
|
|
114
139
|
nest_model = config.attr(type=str, required=True)
|
|
140
|
+
"""Importable reference to the NEST model describing the device type."""
|
|
115
141
|
constants = config.dict(type=types.or_(types.number(), str))
|
|
142
|
+
"""Dictionary of the constants values to assign to the device model."""
|
|
116
143
|
|
|
117
144
|
def implement(self, adapter, simulation, simdata):
|
|
118
145
|
simdata.devices[self] = device = nest.Create(
|
|
@@ -3,3 +3,11 @@ from .multimeter import Multimeter
|
|
|
3
3
|
from .poisson_generator import PoissonGenerator
|
|
4
4
|
from .sinusoidal_poisson_generator import SinusoidalPoissonGenerator
|
|
5
5
|
from .spike_recorder import SpikeRecorder
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"DCGenerator",
|
|
9
|
+
"Multimeter",
|
|
10
|
+
"PoissonGenerator",
|
|
11
|
+
"SinusoidalPoissonGenerator",
|
|
12
|
+
"SpikeRecorder",
|
|
13
|
+
]
|
|
@@ -18,13 +18,12 @@ class Multimeter(NestDevice, classmap_entry="multimeter"):
|
|
|
18
18
|
def boot(self):
|
|
19
19
|
_util.assert_samelen(self.properties, self.units)
|
|
20
20
|
for i in range(len(self.units)):
|
|
21
|
-
if
|
|
21
|
+
if self.units[i] not in pq.units.__dict__:
|
|
22
22
|
raise ConfigurationError(
|
|
23
23
|
f"Unit {self.units[i]} not in the list of known units of quantities"
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
def implement(self, adapter, simulation, simdata):
|
|
27
|
-
|
|
28
27
|
targets_dict = self.get_dict_targets(adapter, simulation, simdata)
|
|
29
28
|
nodes = self._flatten_nodes_ids(targets_dict)
|
|
30
29
|
inv_targets = self._invert_targets_dict(targets_dict)
|
|
@@ -44,7 +43,7 @@ class Multimeter(NestDevice, classmap_entry="multimeter"):
|
|
|
44
43
|
senders = device.events["senders"]
|
|
45
44
|
for sender in np.unique(senders):
|
|
46
45
|
sender_filter = senders == sender
|
|
47
|
-
for prop, unit in zip(self.properties, self.units):
|
|
46
|
+
for prop, unit in zip(self.properties, self.units, strict=False):
|
|
48
47
|
segment.analogsignals.append(
|
|
49
48
|
AnalogSignal(
|
|
50
49
|
device.events[prop][sender_filter],
|
|
@@ -32,7 +32,7 @@ class PoissonGenerator(NestDevice, classmap_entry="poisson_generator"):
|
|
|
32
32
|
SpikeTrain(
|
|
33
33
|
sr.events["times"],
|
|
34
34
|
units="ms",
|
|
35
|
-
senders
|
|
35
|
+
array_annotations={"senders": sr.events["senders"]},
|
|
36
36
|
t_stop=simulation.duration,
|
|
37
37
|
device=self.name,
|
|
38
38
|
pop_size=len(nodes),
|
|
@@ -24,11 +24,11 @@ class SinusoidalPoissonGenerator(
|
|
|
24
24
|
If not specified, generator will last until the end of the simulation."""
|
|
25
25
|
|
|
26
26
|
def boot(self):
|
|
27
|
-
if self.stop is not None:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
if self.stop is not None and self.stop <= self.start:
|
|
28
|
+
raise ConfigurationError(
|
|
29
|
+
f"Stop time (given: {self.stop}) must be greater than start time "
|
|
30
|
+
f"(given: {self.start})."
|
|
31
|
+
)
|
|
32
32
|
|
|
33
33
|
def implement(self, adapter, simulation, simdata):
|
|
34
34
|
nodes = self.get_target_nodes(adapter, simulation, simdata)
|
|
@@ -53,7 +53,7 @@ class SinusoidalPoissonGenerator(
|
|
|
53
53
|
SpikeTrain(
|
|
54
54
|
sr.events["times"],
|
|
55
55
|
units="ms",
|
|
56
|
-
senders
|
|
56
|
+
array_annotations={"senders": sr.events["senders"]},
|
|
57
57
|
t_stop=simulation.duration,
|
|
58
58
|
device=self.name,
|
|
59
59
|
pop_size=len(nodes),
|
|
@@ -10,7 +10,6 @@ class SpikeRecorder(NestDevice, classmap_entry="spike_recorder"):
|
|
|
10
10
|
weight = config.provide(1)
|
|
11
11
|
|
|
12
12
|
def implement(self, adapter, simulation, simdata):
|
|
13
|
-
|
|
14
13
|
nodes = self.get_target_nodes(adapter, simulation, simdata)
|
|
15
14
|
device = self.register_device(simdata, nest.Create("spike_recorder"))
|
|
16
15
|
self.connect_to_nodes(device, nodes)
|
|
@@ -20,7 +19,7 @@ class SpikeRecorder(NestDevice, classmap_entry="spike_recorder"):
|
|
|
20
19
|
SpikeTrain(
|
|
21
20
|
device.events["times"],
|
|
22
21
|
units="ms",
|
|
23
|
-
senders
|
|
22
|
+
array_annotations={"senders": device.events["senders"]},
|
|
24
23
|
t_stop=simulation.duration,
|
|
25
24
|
device=self.name,
|
|
26
25
|
pop_size=len(nodes),
|
|
@@ -3,10 +3,7 @@ import typing
|
|
|
3
3
|
|
|
4
4
|
import errr
|
|
5
5
|
import nest.random.hl_api_random as _distributions
|
|
6
|
-
from bsb import DistributionCastError, TypeHandler, config, types
|
|
7
|
-
|
|
8
|
-
if typing.TYPE_CHECKING:
|
|
9
|
-
from bsb import Scaffold
|
|
6
|
+
from bsb import DistributionCastError, Scaffold, TypeHandler, config, types
|
|
10
7
|
|
|
11
8
|
_available_distributions = [d for d in _distributions.__all__]
|
|
12
9
|
|
|
@@ -23,7 +20,8 @@ class NestRandomDistribution:
|
|
|
23
20
|
)
|
|
24
21
|
"""Distribution name. Should correspond to a function of nest.random.hl_api_random"""
|
|
25
22
|
parameters: dict[str, typing.Any] = config.catch_all(type=types.any_())
|
|
26
|
-
"""Dictionary of parameters to assign to the distribution.
|
|
23
|
+
"""Dictionary of parameters to assign to the distribution.
|
|
24
|
+
Should correspond to NEST's"""
|
|
27
25
|
|
|
28
26
|
def __init__(self, **kwargs):
|
|
29
27
|
try:
|
|
@@ -45,12 +43,12 @@ class NestRandomDistribution:
|
|
|
45
43
|
|
|
46
44
|
class nest_parameter(TypeHandler):
|
|
47
45
|
"""
|
|
48
|
-
Type validator. Type casts the value or node to a Nest parameter, that can be either
|
|
49
|
-
a NestRandomDistribution.
|
|
46
|
+
Type validator. Type casts the value or node to a Nest parameter, that can be either
|
|
47
|
+
a value or a NestRandomDistribution.
|
|
50
48
|
"""
|
|
51
49
|
|
|
52
50
|
def __call__(self, value, _key=None, _parent=None):
|
|
53
|
-
if isinstance(value, builtins.dict) and "distribution" in value
|
|
51
|
+
if isinstance(value, builtins.dict) and "distribution" in value:
|
|
54
52
|
return NestRandomDistribution(**value, _key=_key, _parent=_parent)
|
|
55
53
|
return value
|
|
56
54
|
|
|
@@ -22,6 +22,15 @@ class NestSimulation(Simulation):
|
|
|
22
22
|
seed = config.attr(type=int, default=None)
|
|
23
23
|
"""Random seed for the simulations"""
|
|
24
24
|
|
|
25
|
-
cell_models = config.dict(
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
cell_models: config._attrs.cfgdict[NestCell] = config.dict(
|
|
26
|
+
type=NestCell, required=True
|
|
27
|
+
)
|
|
28
|
+
"""Dictionary of cell models in the simulation."""
|
|
29
|
+
connection_models: config._attrs.cfgdict[NestConnection] = config.dict(
|
|
30
|
+
type=NestConnection, required=True
|
|
31
|
+
)
|
|
32
|
+
"""Dictionary of connection models in the simulation."""
|
|
33
|
+
devices: config._attrs.cfgdict[NestDevice] = config.dict(
|
|
34
|
+
type=NestDevice, required=True
|
|
35
|
+
)
|
|
36
|
+
"""Dictionary of devices in the simulation."""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
dependency-groups = { }
|
|
2
|
+
|
|
3
|
+
[build-system]
|
|
4
|
+
requires = [ "flit_core >=3.2,<4" ]
|
|
5
|
+
build-backend = "flit_core.buildapi"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "bsb-nest"
|
|
9
|
+
version = "5.0.0"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.10,<4"
|
|
12
|
+
dynamic = [ "description" ]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)"
|
|
15
|
+
]
|
|
16
|
+
dependencies = [ "bsb-core~=6.0" ]
|
|
17
|
+
|
|
18
|
+
[[project.authors]]
|
|
19
|
+
name = "Robin De Schepper"
|
|
20
|
+
email = "robin@alexandria.sc"
|
|
21
|
+
|
|
22
|
+
[[project.authors]]
|
|
23
|
+
name = "Dimitri Rodarie"
|
|
24
|
+
email = "dimitri.rodarie@unipv.it"
|
|
25
|
+
|
|
26
|
+
[project.license]
|
|
27
|
+
file = "LICENSE"
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
parallel = [ "bsb-core[parallel]" ]
|
|
31
|
+
|
|
32
|
+
[project.entry-points."bsb.simulation_backends"]
|
|
33
|
+
nest = "bsb_nest"
|
|
34
|
+
|
|
35
|
+
[tool.uv]
|
|
36
|
+
default-groups = [ "dev", "docs", "test" ]
|
|
37
|
+
sources = { }
|
|
38
|
+
|
|
39
|
+
[tool.flit.module]
|
|
40
|
+
name = "bsb_nest"
|
|
41
|
+
|
|
42
|
+
[tool.coverage.run]
|
|
43
|
+
branch = true
|
|
44
|
+
source = [ "bsb_nest" ]
|
|
45
|
+
|
|
46
|
+
[tool.coverage.report]
|
|
47
|
+
exclude_lines = [ "if TYPE_CHECKING:" ]
|
|
48
|
+
show_missing = true
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
exclude = [ ".ruff_cache", ".svn", ".tox", ".venv", "dist" ]
|
|
52
|
+
line-length = 90
|
|
53
|
+
indent-width = 4
|
|
54
|
+
|
|
55
|
+
[tool.ruff.format]
|
|
56
|
+
quote-style = "double"
|
|
57
|
+
indent-style = "space"
|
|
58
|
+
skip-magic-trailing-comma = false
|
|
59
|
+
line-ending = "auto"
|
|
60
|
+
docstring-code-format = true
|
|
61
|
+
docstring-code-line-length = 90
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint]
|
|
64
|
+
select = [ "E", "F", "UP", "B", "SIM", "I" ]
|
|
65
|
+
ignore = [ ]
|
|
66
|
+
fixable = [ "ALL" ]
|
|
67
|
+
unfixable = [ ]
|
bsb_nest-4.3.1/PKG-INFO
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: bsb-nest
|
|
3
|
-
Version: 4.3.1
|
|
4
|
-
Summary: NEST simulation adapter for the BSB framework.
|
|
5
|
-
Author-email: Robin De Schepper <robingilbert.deschepper@unipv.it>
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
8
|
-
Requires-Dist: bsb-core~=4.1
|
|
9
|
-
Requires-Dist: bsb-nest[test] ; extra == "dev"
|
|
10
|
-
Requires-Dist: build~=1.0 ; extra == "dev"
|
|
11
|
-
Requires-Dist: twine~=4.0 ; extra == "dev"
|
|
12
|
-
Requires-Dist: pre-commit~=3.5 ; extra == "dev"
|
|
13
|
-
Requires-Dist: black~=24.1.1 ; extra == "dev"
|
|
14
|
-
Requires-Dist: isort~=5.12 ; extra == "dev"
|
|
15
|
-
Requires-Dist: snakeviz~=2.1 ; extra == "dev"
|
|
16
|
-
Requires-Dist: bump-my-version~=0.24 ; extra == "dev"
|
|
17
|
-
Requires-Dist: bsb-core[parallel] ; extra == "parallel"
|
|
18
|
-
Requires-Dist: bsb-core[parallel] ; extra == "test"
|
|
19
|
-
Requires-Dist: bsb-test~=4.0 ; extra == "test"
|
|
20
|
-
Requires-Dist: bsb-hdf5~=4.0 ; extra == "test"
|
|
21
|
-
Requires-Dist: bsb-arbor~=4.0 ; extra == "test"
|
|
22
|
-
Requires-Dist: coverage~=7.0 ; extra == "test"
|
|
23
|
-
Provides-Extra: dev
|
|
24
|
-
Provides-Extra: parallel
|
|
25
|
-
Provides-Extra: test
|
|
26
|
-
|
|
27
|
-
[](https://github.com/dbbs-lab/bsb-nest/actions/workflows/main.yml)
|
|
28
|
-
[](https://github.com/psf/black)
|
|
29
|
-
|
|
30
|
-
# bsb-nest
|
|
31
|
-
|
|
32
|
-
bsb-nest is a pluggin of [BSB](https://github.com/dbbs-lab/bsb) (see also
|
|
33
|
-
[bsb-core](https://github.com/dbbs-lab/bsb-core)).
|
|
34
|
-
It contains the interfaces and tools to simulate BSB circuit with the
|
|
35
|
-
[NEST simulator](https://www.nest-simulator.org/).
|
|
36
|
-
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
NEST simulation adapter for the BSB framework.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from bsb import SimulationBackendPlugin
|
|
6
|
-
|
|
7
|
-
from . import devices
|
|
8
|
-
from .adapter import NestAdapter
|
|
9
|
-
from .simulation import NestSimulation
|
|
10
|
-
|
|
11
|
-
__plugin__ = SimulationBackendPlugin(Simulation=NestSimulation, Adapter=NestAdapter)
|
|
12
|
-
__version__ = "4.3.1"
|
bsb_nest-4.3.1/pyproject.toml
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["flit_core >=3.2,<4"]
|
|
3
|
-
build-backend = "flit_core.buildapi"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "bsb-nest"
|
|
7
|
-
authors = [{name = "Robin De Schepper", email = "robingilbert.deschepper@unipv.it"}]
|
|
8
|
-
readme = "README.md"
|
|
9
|
-
license = {file = "LICENSE"}
|
|
10
|
-
classifiers = ["License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)"]
|
|
11
|
-
dynamic = ["version", "description"]
|
|
12
|
-
dependencies = ["bsb-core~=4.1"]
|
|
13
|
-
|
|
14
|
-
[tool.flit.module]
|
|
15
|
-
name = "bsb_nest"
|
|
16
|
-
|
|
17
|
-
[project.entry-points."bsb.simulation_backends"]
|
|
18
|
-
nest = "bsb_nest"
|
|
19
|
-
|
|
20
|
-
[project.optional-dependencies]
|
|
21
|
-
parallel = ["bsb-core[parallel]"]
|
|
22
|
-
test = [
|
|
23
|
-
"bsb-core[parallel]",
|
|
24
|
-
"bsb-test~=4.0",
|
|
25
|
-
"bsb-hdf5~=4.0",
|
|
26
|
-
# Required to load the Brunel config file
|
|
27
|
-
"bsb-arbor~=4.0",
|
|
28
|
-
"coverage~=7.0"
|
|
29
|
-
]
|
|
30
|
-
dev = [
|
|
31
|
-
"bsb-nest[test]",
|
|
32
|
-
"build~=1.0",
|
|
33
|
-
"twine~=4.0",
|
|
34
|
-
"pre-commit~=3.5",
|
|
35
|
-
"black~=24.1.1",
|
|
36
|
-
"isort~=5.12",
|
|
37
|
-
"snakeviz~=2.1",
|
|
38
|
-
"bump-my-version~=0.24"
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
[tool.black]
|
|
42
|
-
line-length = 90
|
|
43
|
-
|
|
44
|
-
[tool.isort]
|
|
45
|
-
profile = "black"
|
|
46
|
-
known_third_party = ["nest"]
|
|
47
|
-
|
|
48
|
-
[tool.bumpversion]
|
|
49
|
-
current_version = "4.3.1"
|
|
50
|
-
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
51
|
-
serialize = ["{major}.{minor}.{patch}"]
|
|
52
|
-
search = "{current_version}"
|
|
53
|
-
replace = "{new_version}"
|
|
54
|
-
regex = false
|
|
55
|
-
ignore_missing_version = false
|
|
56
|
-
tag = true
|
|
57
|
-
sign_tags = false
|
|
58
|
-
tag_name = "v{new_version}"
|
|
59
|
-
tag_message = "Bump version: {current_version} → {new_version}"
|
|
60
|
-
allow_dirty = false
|
|
61
|
-
commit = true
|
|
62
|
-
message = "Bump version: {current_version} → {new_version}"
|
|
63
|
-
commit_args = "--no-verify"
|
|
64
|
-
|
|
65
|
-
[tool.bumpversion.parts.pre_l]
|
|
66
|
-
values = ["dev", "a", "b", "rc", "final"]
|
|
67
|
-
optional_value = "final"
|
|
68
|
-
|
|
69
|
-
[[tool.bumpversion.files]]
|
|
70
|
-
filename = "bsb_nest/__init__.py"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|