bsb-nest 4.0.0rc2__py2.py3-none-any.whl → 4.1.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 CHANGED
@@ -9,4 +9,4 @@ from .adapter import NestAdapter
9
9
  from .simulation import NestSimulation
10
10
 
11
11
  __plugin__ = SimulationBackendPlugin(Simulation=NestSimulation, Adapter=NestAdapter)
12
- __version__ = "4.0.0-rc2"
12
+ __version__ = "4.1.0"
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 bsb import Simulation
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: "Simulation"):
171
- nest.set_verbosity(simulation.verbosity)
172
- nest.resolution = simulation.resolution
173
- nest.overwrite_files = True
174
-
175
- def check_comm(self):
176
- if nest.NumProcesses() != MPI.get_size():
177
- raise RuntimeError(
178
- f"NEST is managing {nest.NumProcesses()} processes, but {MPI.get_size()}"
179
- " were detected. Please check your MPI setup."
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, types
3
-
4
-
5
- @config.node
6
- class NestCell(CellModel):
7
- model = config.attr(type=str, default="iaf_psc_alpha")
8
- constants = config.dict(type=types.any_())
9
-
10
- def create_population(self, simdata):
11
- n = len(simdata.placement[self])
12
- population = nest.Create(self.model, n) if n else nest.NodeCollection([])
13
- self.set_constants(population)
14
- self.set_parameters(population, simdata)
15
- return population
16
-
17
- def set_constants(self, population):
18
- population.set(self.constants)
19
-
20
- def set_parameters(self, population, simdata):
21
- ps = simdata.placement[self]
22
- for param in self.parameters:
23
- population.set(param.name, param.get_value(ps))
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))