bsb-nest 4.3.1__tar.gz → 6.0.0a5__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.

File without changes
@@ -1,36 +1,36 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: bsb-nest
3
- Version: 4.3.1
3
+ Version: 6.0.0a5
4
4
  Summary: NEST simulation adapter for the BSB framework.
5
- Author-email: Robin De Schepper <robingilbert.deschepper@unipv.it>
5
+ Author-email: Robin De Schepper <robin@alexandria.sc>, Dimitri Rodarie <dimitri.rodarie@unipv.it>
6
+ Requires-Python: >=3.10,<4
6
7
  Description-Content-Type: text/markdown
7
8
  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"
9
+ License-File: LICENSE
10
+ Requires-Dist: bsb-core~=6.0
11
+ Requires-Dist: bsb-nest[test, docs] ; extra == "dev"
12
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
13
  Requires-Dist: snakeviz~=2.1 ; extra == "dev"
16
- Requires-Dist: bump-my-version~=0.24 ; extra == "dev"
14
+ Requires-Dist: ruff>=0.8.2 ; extra == "dev"
15
+ Requires-Dist: furo~=2024.0 ; extra == "docs"
16
+ Requires-Dist: sphinxext-bsb~=6.0 ; extra == "docs"
17
17
  Requires-Dist: bsb-core[parallel] ; extra == "parallel"
18
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"
19
+ Requires-Dist: bsb-test~=6.0 ; extra == "test"
20
+ Requires-Dist: bsb-hdf5~=6.0 ; extra == "test"
21
+ Requires-Dist: bsb-arbor~=6.0 ; extra == "test"
22
22
  Requires-Dist: coverage~=7.0 ; extra == "test"
23
23
  Provides-Extra: dev
24
+ Provides-Extra: docs
24
25
  Provides-Extra: parallel
25
26
  Provides-Extra: test
26
27
 
27
28
  [![Build Status](https://github.com/dbbs-lab/bsb-nest/actions/workflows/main.yml/badge.svg)](https://github.com/dbbs-lab/bsb-nest/actions/workflows/main.yml)
28
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
29
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
29
30
 
30
31
  # bsb-nest
31
32
 
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)).
33
+ bsb-nest is a plugin of the [BSB](https://github.com/dbbs-lab/bsb).
34
34
  It contains the interfaces and tools to simulate BSB circuit with the
35
35
  [NEST simulator](https://www.nest-simulator.org/).
36
36
 
@@ -1,9 +1,8 @@
1
1
  [![Build Status](https://github.com/dbbs-lab/bsb-nest/actions/workflows/main.yml/badge.svg)](https://github.com/dbbs-lab/bsb-nest/actions/workflows/main.yml)
2
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
2
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
3
3
 
4
4
  # bsb-nest
5
5
 
6
- bsb-nest is a pluggin of [BSB](https://github.com/dbbs-lab/bsb) (see also
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
- waveforms=events["senders"],
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
- self.simdata = dict()
46
+ def __init__(self, comm=None):
47
+ super().__init__(comm=comm)
48
48
  self.loaded_modules = set()
49
49
 
50
- def simulate(self, simulation):
50
+ def simulate(self, *simulations, post_prepare=None):
51
51
  try:
52
52
  self.reset_kernel()
53
- return super().simulate(simulation)
53
+ return super().simulate(*simulations, post_prepare=post_prepare)
54
54
  finally:
55
55
  self.reset_kernel()
56
56
 
57
- def prepare(self, simulation, comm=None):
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, comm=None):
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(f"Simulation done.", level=2)
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. 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.
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 MPI.get_rank() == 0:
158
+ if self.comm.get_rank() == 0:
138
159
  iter = tqdm(iter, desc="", file=sys.stdout)
139
160
  for connection_model in iter:
140
- try:
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
- pass
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 as e:
163
- raise NestConnectError(f"{connection_model} error during connect.")
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() != MPI.get_size():
199
+ if nest.NumProcesses() != self.comm.get_size():
179
200
  raise RuntimeError(
180
- f"NEST is managing {nest.NumProcesses()} processes, but {MPI.get_size()}"
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 MPI, ConnectionModel, compose_nodes, config, types
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
- tag = config.attr(type=str)
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
- MPI.barrier()
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
- MPI.barrier()
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
- MPI.barrier()
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
- ) * MPI.get_size()
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 MPI.get_rank() == 0:
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 MPI.get_rank() == 0:
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 MPI.get_rank() == 0:
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 typing
1
+ import abc
2
2
  import warnings
3
3
 
4
4
  import nest
5
- from bsb import DeviceModel, SimulationData, Targetting, config, refs, types
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: "NestAdapter",
35
- simulation: "NestSimulation",
36
- simdata: "SimulationData",
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: "NestAdapter",
69
- simulation: "NestSimulation",
70
- simdata: "SimulationData",
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 not self.units[i] in pq.units.__dict__.keys():
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=sr.events["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
- if self.stop <= self.start:
29
- raise ConfigurationError(
30
- f"Stop time (given: {self.stop}) must be greater than start time (given: {self.start})."
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=sr.events["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=device.events["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. Should correspond to NEST's"""
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 a value or
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.keys():
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(type=NestCell, required=True)
26
- connection_models = config.dict(type=NestConnection, required=True)
27
- devices = config.dict(type=NestDevice, required=True)
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,80 @@
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 = "6.0.0-a5"
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
+ test = [
32
+ "bsb-core[parallel]",
33
+ "bsb-test~=6.0",
34
+ "bsb-hdf5~=6.0",
35
+ "bsb-arbor~=6.0",
36
+ "coverage~=7.0"
37
+ ]
38
+ docs = [ "furo~=2024.0", "sphinxext-bsb~=6.0" ]
39
+ dev = [
40
+ "bsb-nest[test, docs]",
41
+ "pre-commit~=3.5",
42
+ "snakeviz~=2.1",
43
+ "ruff>=0.8.2"
44
+ ]
45
+
46
+ [project.entry-points."bsb.simulation_backends"]
47
+ nest = "bsb_nest"
48
+
49
+ [tool.flit.module]
50
+ name = "bsb_nest"
51
+
52
+ [tool.uv]
53
+ sources = { }
54
+
55
+ [tool.coverage.run]
56
+ branch = true
57
+ source = [ "bsb_nest" ]
58
+
59
+ [tool.coverage.report]
60
+ exclude_lines = [ "if TYPE_CHECKING:" ]
61
+ show_missing = true
62
+
63
+ [tool.ruff]
64
+ exclude = [ ".ruff_cache", ".svn", ".tox", ".venv", "dist" ]
65
+ line-length = 90
66
+ indent-width = 4
67
+
68
+ [tool.ruff.format]
69
+ quote-style = "double"
70
+ indent-style = "space"
71
+ skip-magic-trailing-comma = false
72
+ line-ending = "auto"
73
+ docstring-code-format = true
74
+ docstring-code-line-length = 90
75
+
76
+ [tool.ruff.lint]
77
+ select = [ "E", "F", "UP", "B", "SIM", "I" ]
78
+ ignore = [ ]
79
+ fixable = [ "ALL" ]
80
+ unfixable = [ ]
@@ -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"
@@ -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"