bsb-nest 4.0.0rc2__py2.py3-none-any.whl → 4.2.0__py2.py3-none-any.whl
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/__init__.py +1 -1
- bsb_nest/adapter.py +182 -180
- bsb_nest/cell.py +30 -23
- bsb_nest/connection.py +164 -165
- bsb_nest/device.py +72 -70
- bsb_nest/devices/__init__.py +4 -2
- bsb_nest/devices/dc_generator.py +23 -0
- bsb_nest/devices/multimeter.py +52 -0
- bsb_nest/devices/poisson_generator.py +41 -32
- bsb_nest/devices/spike_recorder.py +30 -30
- bsb_nest/distributions.py +62 -0
- bsb_nest/exceptions.py +22 -18
- bsb_nest/simulation.py +27 -21
- {bsb_nest-4.0.0rc2.dist-info → bsb_nest-4.2.0.dist-info}/LICENSE +619 -619
- bsb_nest-4.2.0.dist-info/METADATA +36 -0
- bsb_nest-4.2.0.dist-info/RECORD +18 -0
- bsb_nest-4.0.0rc2.dist-info/METADATA +0 -23
- bsb_nest-4.0.0rc2.dist-info/RECORD +0 -15
- {bsb_nest-4.0.0rc2.dist-info → bsb_nest-4.2.0.dist-info}/WHEEL +0 -0
- {bsb_nest-4.0.0rc2.dist-info → bsb_nest-4.2.0.dist-info}/entry_points.txt +0 -0
bsb_nest/__init__.py
CHANGED
bsb_nest/adapter.py
CHANGED
|
@@ -1,180 +1,182 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import typing
|
|
3
|
-
|
|
4
|
-
import nest
|
|
5
|
-
from bsb import (
|
|
6
|
-
MPI,
|
|
7
|
-
AdapterError,
|
|
8
|
-
AdapterProgress,
|
|
9
|
-
SimulationData,
|
|
10
|
-
SimulationResult,
|
|
11
|
-
SimulatorAdapter,
|
|
12
|
-
report,
|
|
13
|
-
warn,
|
|
14
|
-
)
|
|
15
|
-
from neo import SpikeTrain
|
|
16
|
-
from tqdm import tqdm
|
|
17
|
-
|
|
18
|
-
from .exceptions import NestConnectError, NestModelError, NestModuleError
|
|
19
|
-
|
|
20
|
-
if typing.TYPE_CHECKING:
|
|
21
|
-
from
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class NestResult(SimulationResult):
|
|
25
|
-
def record(self, nc, **annotations):
|
|
26
|
-
recorder = nest.Create("spike_recorder", params={"record_to": "memory"})
|
|
27
|
-
nest.Connect(nc, recorder)
|
|
28
|
-
|
|
29
|
-
def flush(segment):
|
|
30
|
-
events = recorder.events[0]
|
|
31
|
-
|
|
32
|
-
segment.spiketrains.append(
|
|
33
|
-
SpikeTrain(
|
|
34
|
-
events["times"],
|
|
35
|
-
waveforms=events["senders"],
|
|
36
|
-
t_stop=nest.biological_time,
|
|
37
|
-
units="ms",
|
|
38
|
-
**annotations,
|
|
39
|
-
)
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
self.create_recorder(flush)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class NestAdapter(SimulatorAdapter):
|
|
46
|
-
def __init__(self):
|
|
47
|
-
self.simdata = dict()
|
|
48
|
-
self.loaded_modules = set()
|
|
49
|
-
|
|
50
|
-
def simulate(self, simulation):
|
|
51
|
-
try:
|
|
52
|
-
self.reset_kernel()
|
|
53
|
-
return super().simulate(simulation)
|
|
54
|
-
finally:
|
|
55
|
-
self.reset_kernel()
|
|
56
|
-
|
|
57
|
-
def prepare(self, simulation, comm=None):
|
|
58
|
-
self.simdata[simulation] = SimulationData(
|
|
59
|
-
simulation, result=NestResult(simulation)
|
|
60
|
-
)
|
|
61
|
-
try:
|
|
62
|
-
report("Installing NEST modules...", level=2)
|
|
63
|
-
self.load_modules(simulation)
|
|
64
|
-
self.set_settings(simulation)
|
|
65
|
-
report("Creating neurons...", level=2)
|
|
66
|
-
self.create_neurons(simulation)
|
|
67
|
-
report("Creating connections...", level=2)
|
|
68
|
-
self.connect_neurons(simulation)
|
|
69
|
-
report("Creating devices...", level=2)
|
|
70
|
-
self.create_devices(simulation)
|
|
71
|
-
return self.simdata[simulation]
|
|
72
|
-
except Exception:
|
|
73
|
-
del self.simdata[simulation]
|
|
74
|
-
raise
|
|
75
|
-
|
|
76
|
-
def reset_kernel(self):
|
|
77
|
-
nest.ResetKernel()
|
|
78
|
-
# Reset which modules we should consider explicitly loaded by the user
|
|
79
|
-
# to appropriately warn them when they load them twice.
|
|
80
|
-
self.loaded_modules = set()
|
|
81
|
-
|
|
82
|
-
def run(self, *simulations, comm=None):
|
|
83
|
-
unprepared = [sim for sim in simulations if sim not in self.simdata]
|
|
84
|
-
if unprepared:
|
|
85
|
-
raise AdapterError(f"Unprepared for simulations: {', '.join(unprepared)}")
|
|
86
|
-
report("Simulating...", level=2)
|
|
87
|
-
duration = max(sim.duration for sim in simulations)
|
|
88
|
-
progress = AdapterProgress(duration)
|
|
89
|
-
try:
|
|
90
|
-
with nest.RunManager():
|
|
91
|
-
for oi, i in progress.steps(step=1):
|
|
92
|
-
nest.Run(i - oi)
|
|
93
|
-
progress.tick(i)
|
|
94
|
-
finally:
|
|
95
|
-
results = [self.simdata[sim].result for sim in simulations]
|
|
96
|
-
for sim in simulations:
|
|
97
|
-
del self.simdata[sim]
|
|
98
|
-
progress.complete()
|
|
99
|
-
report(f"Simulation done.", level=2)
|
|
100
|
-
return results
|
|
101
|
-
|
|
102
|
-
def load_modules(self, simulation):
|
|
103
|
-
for module in simulation.modules:
|
|
104
|
-
try:
|
|
105
|
-
nest.Install(module)
|
|
106
|
-
self.loaded_modules.add(module)
|
|
107
|
-
except Exception as e:
|
|
108
|
-
if e.errorname == "DynamicModuleManagementError":
|
|
109
|
-
if "loaded already" in e.message:
|
|
110
|
-
# Modules stay loaded in between `ResetKernel` calls. If the module
|
|
111
|
-
# is not in the `loaded_modules` set, then it's the first time this
|
|
112
|
-
# `reset`/`prepare` cycle, and there is no user-side issue.
|
|
113
|
-
if module in self.loaded_modules:
|
|
114
|
-
warn(f"Already loaded '{module}'.", KernelWarning)
|
|
115
|
-
elif "file not found" in e.message:
|
|
116
|
-
raise NestModuleError(f"Module {module} not found") from None
|
|
117
|
-
else:
|
|
118
|
-
raise
|
|
119
|
-
else:
|
|
120
|
-
raise
|
|
121
|
-
|
|
122
|
-
def create_neurons(self, simulation):
|
|
123
|
-
"""
|
|
124
|
-
Create a population of nodes in the NEST simulator based on the cell model
|
|
125
|
-
configurations.
|
|
126
|
-
"""
|
|
127
|
-
simdata = self.simdata[simulation]
|
|
128
|
-
for cell_model in simulation.cell_models.values():
|
|
129
|
-
simdata.populations[cell_model] = cell_model.create_population(simdata)
|
|
130
|
-
|
|
131
|
-
def connect_neurons(self, simulation):
|
|
132
|
-
"""
|
|
133
|
-
Connect the cells in NEST according to the connection model configurations
|
|
134
|
-
"""
|
|
135
|
-
simdata = self.simdata[simulation]
|
|
136
|
-
iter = simulation.connection_models.values()
|
|
137
|
-
if MPI.get_rank() == 0:
|
|
138
|
-
iter = tqdm(iter, desc="", file=sys.stdout)
|
|
139
|
-
for connection_model in iter:
|
|
140
|
-
try:
|
|
141
|
-
iter.set_description(connection_model.name)
|
|
142
|
-
except AttributeError:
|
|
143
|
-
# Only rank 0 should report progress bar
|
|
144
|
-
pass
|
|
145
|
-
cs = simulation.scaffold.get_connectivity_set(
|
|
146
|
-
connection_model.tag or connection_model.name
|
|
147
|
-
)
|
|
148
|
-
try:
|
|
149
|
-
pre_nodes = simdata.populations[simulation.get_model_of(cs.pre_type)]
|
|
150
|
-
except KeyError:
|
|
151
|
-
raise NestModelError(f"No model found for {cs.pre_type}")
|
|
152
|
-
try:
|
|
153
|
-
post_nodes = simdata.populations[simulation.get_model_of(cs.post_type)]
|
|
154
|
-
except KeyError:
|
|
155
|
-
raise NestModelError(f"No model found for {cs.post_type}")
|
|
156
|
-
try:
|
|
157
|
-
simdata.connections[connection_model] = (
|
|
158
|
-
connection_model.create_connections(
|
|
159
|
-
simdata, pre_nodes, post_nodes, cs
|
|
160
|
-
)
|
|
161
|
-
)
|
|
162
|
-
except Exception as e:
|
|
163
|
-
raise NestConnectError(f"{connection_model} error during connect.")
|
|
164
|
-
|
|
165
|
-
def create_devices(self, simulation):
|
|
166
|
-
simdata = self.simdata[simulation]
|
|
167
|
-
for device_model in simulation.devices.values():
|
|
168
|
-
device_model.implement(self, simulation, simdata)
|
|
169
|
-
|
|
170
|
-
def set_settings(self, simulation: "
|
|
171
|
-
nest.set_verbosity(simulation.verbosity)
|
|
172
|
-
nest.resolution = simulation.resolution
|
|
173
|
-
nest.overwrite_files = True
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
1
|
+
import sys
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import nest
|
|
5
|
+
from bsb import (
|
|
6
|
+
MPI,
|
|
7
|
+
AdapterError,
|
|
8
|
+
AdapterProgress,
|
|
9
|
+
SimulationData,
|
|
10
|
+
SimulationResult,
|
|
11
|
+
SimulatorAdapter,
|
|
12
|
+
report,
|
|
13
|
+
warn,
|
|
14
|
+
)
|
|
15
|
+
from neo import SpikeTrain
|
|
16
|
+
from tqdm import tqdm
|
|
17
|
+
|
|
18
|
+
from .exceptions import KernelWarning, NestConnectError, NestModelError, NestModuleError
|
|
19
|
+
|
|
20
|
+
if typing.TYPE_CHECKING:
|
|
21
|
+
from .simulation import NestSimulation
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NestResult(SimulationResult):
|
|
25
|
+
def record(self, nc, **annotations):
|
|
26
|
+
recorder = nest.Create("spike_recorder", params={"record_to": "memory"})
|
|
27
|
+
nest.Connect(nc, recorder)
|
|
28
|
+
|
|
29
|
+
def flush(segment):
|
|
30
|
+
events = recorder.events[0]
|
|
31
|
+
|
|
32
|
+
segment.spiketrains.append(
|
|
33
|
+
SpikeTrain(
|
|
34
|
+
events["times"],
|
|
35
|
+
waveforms=events["senders"],
|
|
36
|
+
t_stop=nest.biological_time,
|
|
37
|
+
units="ms",
|
|
38
|
+
**annotations,
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self.create_recorder(flush)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class NestAdapter(SimulatorAdapter):
|
|
46
|
+
def __init__(self):
|
|
47
|
+
self.simdata = dict()
|
|
48
|
+
self.loaded_modules = set()
|
|
49
|
+
|
|
50
|
+
def simulate(self, simulation):
|
|
51
|
+
try:
|
|
52
|
+
self.reset_kernel()
|
|
53
|
+
return super().simulate(simulation)
|
|
54
|
+
finally:
|
|
55
|
+
self.reset_kernel()
|
|
56
|
+
|
|
57
|
+
def prepare(self, simulation, comm=None):
|
|
58
|
+
self.simdata[simulation] = SimulationData(
|
|
59
|
+
simulation, result=NestResult(simulation)
|
|
60
|
+
)
|
|
61
|
+
try:
|
|
62
|
+
report("Installing NEST modules...", level=2)
|
|
63
|
+
self.load_modules(simulation)
|
|
64
|
+
self.set_settings(simulation)
|
|
65
|
+
report("Creating neurons...", level=2)
|
|
66
|
+
self.create_neurons(simulation)
|
|
67
|
+
report("Creating connections...", level=2)
|
|
68
|
+
self.connect_neurons(simulation)
|
|
69
|
+
report("Creating devices...", level=2)
|
|
70
|
+
self.create_devices(simulation)
|
|
71
|
+
return self.simdata[simulation]
|
|
72
|
+
except Exception:
|
|
73
|
+
del self.simdata[simulation]
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
def reset_kernel(self):
|
|
77
|
+
nest.ResetKernel()
|
|
78
|
+
# Reset which modules we should consider explicitly loaded by the user
|
|
79
|
+
# to appropriately warn them when they load them twice.
|
|
80
|
+
self.loaded_modules = set()
|
|
81
|
+
|
|
82
|
+
def run(self, *simulations, comm=None):
|
|
83
|
+
unprepared = [sim for sim in simulations if sim not in self.simdata]
|
|
84
|
+
if unprepared:
|
|
85
|
+
raise AdapterError(f"Unprepared for simulations: {', '.join(unprepared)}")
|
|
86
|
+
report("Simulating...", level=2)
|
|
87
|
+
duration = max(sim.duration for sim in simulations)
|
|
88
|
+
progress = AdapterProgress(duration)
|
|
89
|
+
try:
|
|
90
|
+
with nest.RunManager():
|
|
91
|
+
for oi, i in progress.steps(step=1):
|
|
92
|
+
nest.Run(i - oi)
|
|
93
|
+
progress.tick(i)
|
|
94
|
+
finally:
|
|
95
|
+
results = [self.simdata[sim].result for sim in simulations]
|
|
96
|
+
for sim in simulations:
|
|
97
|
+
del self.simdata[sim]
|
|
98
|
+
progress.complete()
|
|
99
|
+
report(f"Simulation done.", level=2)
|
|
100
|
+
return results
|
|
101
|
+
|
|
102
|
+
def load_modules(self, simulation):
|
|
103
|
+
for module in simulation.modules:
|
|
104
|
+
try:
|
|
105
|
+
nest.Install(module)
|
|
106
|
+
self.loaded_modules.add(module)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
if e.errorname == "DynamicModuleManagementError":
|
|
109
|
+
if "loaded already" in e.message:
|
|
110
|
+
# Modules stay loaded in between `ResetKernel` calls. If the module
|
|
111
|
+
# is not in the `loaded_modules` set, then it's the first time this
|
|
112
|
+
# `reset`/`prepare` cycle, and there is no user-side issue.
|
|
113
|
+
if module in self.loaded_modules:
|
|
114
|
+
warn(f"Already loaded '{module}'.", KernelWarning)
|
|
115
|
+
elif "file not found" in e.message:
|
|
116
|
+
raise NestModuleError(f"Module {module} not found") from None
|
|
117
|
+
else:
|
|
118
|
+
raise
|
|
119
|
+
else:
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
def create_neurons(self, simulation):
|
|
123
|
+
"""
|
|
124
|
+
Create a population of nodes in the NEST simulator based on the cell model
|
|
125
|
+
configurations.
|
|
126
|
+
"""
|
|
127
|
+
simdata = self.simdata[simulation]
|
|
128
|
+
for cell_model in simulation.cell_models.values():
|
|
129
|
+
simdata.populations[cell_model] = cell_model.create_population(simdata)
|
|
130
|
+
|
|
131
|
+
def connect_neurons(self, simulation):
|
|
132
|
+
"""
|
|
133
|
+
Connect the cells in NEST according to the connection model configurations
|
|
134
|
+
"""
|
|
135
|
+
simdata = self.simdata[simulation]
|
|
136
|
+
iter = simulation.connection_models.values()
|
|
137
|
+
if MPI.get_rank() == 0:
|
|
138
|
+
iter = tqdm(iter, desc="", file=sys.stdout)
|
|
139
|
+
for connection_model in iter:
|
|
140
|
+
try:
|
|
141
|
+
iter.set_description(connection_model.name)
|
|
142
|
+
except AttributeError:
|
|
143
|
+
# Only rank 0 should report progress bar
|
|
144
|
+
pass
|
|
145
|
+
cs = simulation.scaffold.get_connectivity_set(
|
|
146
|
+
connection_model.tag or connection_model.name
|
|
147
|
+
)
|
|
148
|
+
try:
|
|
149
|
+
pre_nodes = simdata.populations[simulation.get_model_of(cs.pre_type)]
|
|
150
|
+
except KeyError:
|
|
151
|
+
raise NestModelError(f"No model found for {cs.pre_type}")
|
|
152
|
+
try:
|
|
153
|
+
post_nodes = simdata.populations[simulation.get_model_of(cs.post_type)]
|
|
154
|
+
except KeyError:
|
|
155
|
+
raise NestModelError(f"No model found for {cs.post_type}")
|
|
156
|
+
try:
|
|
157
|
+
simdata.connections[connection_model] = (
|
|
158
|
+
connection_model.create_connections(
|
|
159
|
+
simdata, pre_nodes, post_nodes, cs
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
except Exception as e:
|
|
163
|
+
raise NestConnectError(f"{connection_model} error during connect.")
|
|
164
|
+
|
|
165
|
+
def create_devices(self, simulation):
|
|
166
|
+
simdata = self.simdata[simulation]
|
|
167
|
+
for device_model in simulation.devices.values():
|
|
168
|
+
device_model.implement(self, simulation, simdata)
|
|
169
|
+
|
|
170
|
+
def set_settings(self, simulation: "NestSimulation"):
|
|
171
|
+
nest.set_verbosity(simulation.verbosity)
|
|
172
|
+
nest.resolution = simulation.resolution
|
|
173
|
+
nest.overwrite_files = True
|
|
174
|
+
if simulation.seed is not None:
|
|
175
|
+
nest.rng_seed = simulation.seed
|
|
176
|
+
|
|
177
|
+
def check_comm(self):
|
|
178
|
+
if nest.NumProcesses() != MPI.get_size():
|
|
179
|
+
raise RuntimeError(
|
|
180
|
+
f"NEST is managing {nest.NumProcesses()} processes, but {MPI.get_size()}"
|
|
181
|
+
" were detected. Please check your MPI setup."
|
|
182
|
+
)
|
bsb_nest/cell.py
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
|
-
import nest
|
|
2
|
-
from bsb import CellModel, config
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
self
|
|
14
|
-
self.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
import nest
|
|
2
|
+
from bsb import CellModel, config
|
|
3
|
+
|
|
4
|
+
from .distributions import NestRandomDistribution, nest_parameter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@config.node
|
|
8
|
+
class NestCell(CellModel):
|
|
9
|
+
model = config.attr(type=str, default="iaf_psc_alpha")
|
|
10
|
+
constants = config.dict(type=nest_parameter())
|
|
11
|
+
|
|
12
|
+
def create_population(self, simdata):
|
|
13
|
+
n = len(simdata.placement[self])
|
|
14
|
+
population = nest.Create(self.model, n) if n else nest.NodeCollection([])
|
|
15
|
+
self.set_constants(population)
|
|
16
|
+
self.set_parameters(population, simdata)
|
|
17
|
+
return population
|
|
18
|
+
|
|
19
|
+
def set_constants(self, population):
|
|
20
|
+
population.set(
|
|
21
|
+
{
|
|
22
|
+
k: (v() if isinstance(v, NestRandomDistribution) else v)
|
|
23
|
+
for k, v in self.constants.items()
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def set_parameters(self, population, simdata):
|
|
28
|
+
ps = simdata.placement[self]
|
|
29
|
+
for param in self.parameters:
|
|
30
|
+
population.set(param.name, param.get_value(ps))
|