bsb-nest 6.0.0a5__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.

@@ -0,0 +1,59 @@
1
+ import nest
2
+ import numpy as np
3
+ import quantities as pq
4
+ from bsb import ConfigurationError, _util, config, types
5
+ from neo import AnalogSignal
6
+
7
+ from ..device import NestDevice
8
+
9
+
10
+ @config.node
11
+ class Multimeter(NestDevice, classmap_entry="multimeter"):
12
+ weight = config.provide(1)
13
+ properties: list[str] = config.attr(type=types.list(str))
14
+ """List of properties to record in the Nest model."""
15
+ units: list[str] = config.attr(type=types.list(str))
16
+ """List of properties' units."""
17
+
18
+ def boot(self):
19
+ _util.assert_samelen(self.properties, self.units)
20
+ for i in range(len(self.units)):
21
+ if self.units[i] not in pq.units.__dict__:
22
+ raise ConfigurationError(
23
+ f"Unit {self.units[i]} not in the list of known units of quantities"
24
+ )
25
+
26
+ def implement(self, adapter, simulation, simdata):
27
+ targets_dict = self.get_dict_targets(adapter, simulation, simdata)
28
+ nodes = self._flatten_nodes_ids(targets_dict)
29
+ inv_targets = self._invert_targets_dict(targets_dict)
30
+ device = self.register_device(
31
+ simdata,
32
+ nest.Create(
33
+ "multimeter",
34
+ params={
35
+ "interval": self.simulation.resolution,
36
+ "record_from": self.properties,
37
+ },
38
+ ),
39
+ )
40
+ self.connect_to_nodes(device, nodes)
41
+
42
+ def recorder(segment):
43
+ senders = device.events["senders"]
44
+ for sender in np.unique(senders):
45
+ sender_filter = senders == sender
46
+ for prop, unit in zip(self.properties, self.units, strict=False):
47
+ segment.analogsignals.append(
48
+ AnalogSignal(
49
+ device.events[prop][sender_filter],
50
+ units=pq.units.__dict__[unit],
51
+ sampling_period=self.simulation.resolution * pq.ms,
52
+ name=self.name,
53
+ cell_type=inv_targets[sender],
54
+ cell_id=sender,
55
+ prop_recorded=prop,
56
+ )
57
+ )
58
+
59
+ simdata.result.create_recorder(recorder)
@@ -0,0 +1,42 @@
1
+ import nest
2
+ from bsb import config
3
+ from neo import SpikeTrain
4
+
5
+ from ..device import NestDevice
6
+
7
+
8
+ @config.node
9
+ class PoissonGenerator(NestDevice, classmap_entry="poisson_generator"):
10
+ rate = config.attr(type=float, required=True)
11
+ """Frequency of the poisson generator"""
12
+ start = config.attr(type=float, required=False, default=0.0)
13
+ """Activation time in ms"""
14
+ stop = config.attr(type=float, required=False, default=None)
15
+ """Deactivation time in ms.
16
+ If not specified, generator will last until the end of the simulation."""
17
+
18
+ def implement(self, adapter, simulation, simdata):
19
+ nodes = self.get_target_nodes(adapter, simulation, simdata)
20
+ params = {"rate": self.rate, "start": self.start}
21
+ if self.stop is not None and self.stop > self.start:
22
+ params["stop"] = self.stop
23
+ device = self.register_device(
24
+ simdata, nest.Create("poisson_generator", params=params)
25
+ )
26
+ sr = nest.Create("spike_recorder")
27
+ nest.Connect(device, sr)
28
+ self.connect_to_nodes(device, nodes)
29
+
30
+ def recorder(segment):
31
+ segment.spiketrains.append(
32
+ SpikeTrain(
33
+ sr.events["times"],
34
+ units="ms",
35
+ array_annotations={"senders": sr.events["senders"]},
36
+ t_stop=simulation.duration,
37
+ device=self.name,
38
+ pop_size=len(nodes),
39
+ )
40
+ )
41
+
42
+ simdata.result.create_recorder(recorder)
@@ -0,0 +1,63 @@
1
+ import nest
2
+ from bsb import ConfigurationError, config
3
+ from neo import SpikeTrain
4
+
5
+ from ..device import NestDevice
6
+
7
+
8
+ @config.node
9
+ class SinusoidalPoissonGenerator(
10
+ NestDevice, classmap_entry="sinusoidal_poisson_generator"
11
+ ):
12
+ rate = config.attr(type=float, required=True)
13
+ """Rate of the poisson generator"""
14
+ amplitude = config.attr(type=float, required=True)
15
+ """Amplitude of the sinusoidal signal"""
16
+ frequency = config.attr(type=float, required=True)
17
+ """Frequency of the sinusoidal signal"""
18
+ phase = config.attr(type=float, required=False, default=0.0)
19
+ """Phase of the sinusoidal signal"""
20
+ start = config.attr(type=float, required=False, default=0.0)
21
+ """Activation time in ms"""
22
+ stop = config.attr(type=float, required=False, default=None)
23
+ """Deactivation time in ms.
24
+ If not specified, generator will last until the end of the simulation."""
25
+
26
+ def boot(self):
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
+
33
+ def implement(self, adapter, simulation, simdata):
34
+ nodes = self.get_target_nodes(adapter, simulation, simdata)
35
+ params = {
36
+ "rate": self.rate,
37
+ "start": self.start,
38
+ "amplitude": self.amplitude,
39
+ "frequency": self.frequency,
40
+ "phase": self.phase,
41
+ }
42
+ if self.stop is not None:
43
+ params["stop"] = self.stop
44
+ device = self.register_device(
45
+ simdata, nest.Create("sinusoidal_poisson_generator", params=params)
46
+ )
47
+ sr = nest.Create("spike_recorder")
48
+ nest.Connect(device, sr)
49
+ self.connect_to_nodes(device, nodes)
50
+
51
+ def recorder(segment):
52
+ segment.spiketrains.append(
53
+ SpikeTrain(
54
+ sr.events["times"],
55
+ units="ms",
56
+ array_annotations={"senders": sr.events["senders"]},
57
+ t_stop=simulation.duration,
58
+ device=self.name,
59
+ pop_size=len(nodes),
60
+ )
61
+ )
62
+
63
+ simdata.result.create_recorder(recorder)
@@ -0,0 +1,29 @@
1
+ import nest
2
+ from bsb import config
3
+ from neo import SpikeTrain
4
+
5
+ from ..device import NestDevice
6
+
7
+
8
+ @config.node
9
+ class SpikeRecorder(NestDevice, classmap_entry="spike_recorder"):
10
+ weight = config.provide(1)
11
+
12
+ def implement(self, adapter, simulation, simdata):
13
+ nodes = self.get_target_nodes(adapter, simulation, simdata)
14
+ device = self.register_device(simdata, nest.Create("spike_recorder"))
15
+ self.connect_to_nodes(device, nodes)
16
+
17
+ def recorder(segment):
18
+ segment.spiketrains.append(
19
+ SpikeTrain(
20
+ device.events["times"],
21
+ units="ms",
22
+ array_annotations={"senders": device.events["senders"]},
23
+ t_stop=simulation.duration,
24
+ device=self.name,
25
+ pop_size=len(nodes),
26
+ )
27
+ )
28
+
29
+ simdata.result.create_recorder(recorder)
@@ -0,0 +1,60 @@
1
+ import builtins
2
+ import typing
3
+
4
+ import errr
5
+ import nest.random.hl_api_random as _distributions
6
+ from bsb import DistributionCastError, Scaffold, TypeHandler, config, types
7
+
8
+ _available_distributions = [d for d in _distributions.__all__]
9
+
10
+
11
+ @config.node
12
+ class NestRandomDistribution:
13
+ """
14
+ Class to handle NEST random distributions.
15
+ """
16
+
17
+ scaffold: "Scaffold"
18
+ distribution: str = config.attr(
19
+ type=types.in_(_available_distributions), required=True
20
+ )
21
+ """Distribution name. Should correspond to a function of nest.random.hl_api_random"""
22
+ parameters: dict[str, typing.Any] = config.catch_all(type=types.any_())
23
+ """Dictionary of parameters to assign to the distribution.
24
+ Should correspond to NEST's"""
25
+
26
+ def __init__(self, **kwargs):
27
+ try:
28
+ self._distr = getattr(_distributions, self.distribution)(**self.parameters)
29
+ except Exception as e:
30
+ errr.wrap(
31
+ DistributionCastError, e, prepend=f"Can't cast to '{self.distribution}': "
32
+ )
33
+
34
+ def __call__(self):
35
+ return self._distr
36
+
37
+ def __getattr__(self, attr):
38
+ # hasattr does not work here. So we use __dict__
39
+ if "_distr" not in self.__dict__:
40
+ raise AttributeError("No underlying _distr found for distribution node.")
41
+ return getattr(self._distr, attr)
42
+
43
+
44
+ class nest_parameter(TypeHandler):
45
+ """
46
+ Type validator. Type casts the value or node to a Nest parameter, that can be either
47
+ a value or a NestRandomDistribution.
48
+ """
49
+
50
+ def __call__(self, value, _key=None, _parent=None):
51
+ if isinstance(value, builtins.dict) and "distribution" in value:
52
+ return NestRandomDistribution(**value, _key=_key, _parent=_parent)
53
+ return value
54
+
55
+ @property
56
+ def __name__(self): # pragma: nocover
57
+ return "nest parameter"
58
+
59
+ def __inv__(self, value):
60
+ return value
bsb_nest/exceptions.py ADDED
@@ -0,0 +1,22 @@
1
+ class KernelWarning(Warning):
2
+ pass
3
+
4
+
5
+ class NestError(Exception):
6
+ pass
7
+
8
+
9
+ class NestKernelError(NestError):
10
+ pass
11
+
12
+
13
+ class NestModuleError(NestKernelError):
14
+ pass
15
+
16
+
17
+ class NestModelError(NestError):
18
+ pass
19
+
20
+
21
+ class NestConnectError(NestError):
22
+ pass
bsb_nest/simulation.py ADDED
@@ -0,0 +1,36 @@
1
+ from bsb import Simulation, config, types
2
+
3
+ from .cell import NestCell
4
+ from .connection import NestConnection
5
+ from .device import NestDevice
6
+
7
+
8
+ @config.node
9
+ class NestSimulation(Simulation):
10
+ """
11
+ Interface between the scaffold model and the NEST simulator.
12
+ """
13
+
14
+ modules = config.list(type=str)
15
+ """List of NEST modules to load at the beginning of the simulation"""
16
+ threads = config.attr(type=types.int(min=1), default=1)
17
+ """Number of threads to use during simulation"""
18
+ resolution = config.attr(type=types.float(min=0.0), required=True)
19
+ """Simulation time step size in milliseconds"""
20
+ verbosity = config.attr(type=str, default="M_ERROR")
21
+ """NEST verbosity level"""
22
+ seed = config.attr(type=int, default=None)
23
+ """Random seed for the simulations"""
24
+
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,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: bsb-nest
3
+ Version: 6.0.0a5
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-nest[test, docs] ; extra == "dev"
12
+ Requires-Dist: pre-commit~=3.5 ; extra == "dev"
13
+ Requires-Dist: snakeviz~=2.1 ; 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
+ Requires-Dist: bsb-core[parallel] ; extra == "parallel"
18
+ Requires-Dist: bsb-core[parallel] ; 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
+ Requires-Dist: coverage~=7.0 ; extra == "test"
23
+ Provides-Extra: dev
24
+ Provides-Extra: docs
25
+ Provides-Extra: parallel
26
+ Provides-Extra: test
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)
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)
30
+
31
+ # bsb-nest
32
+
33
+ bsb-nest is a plugin of the [BSB](https://github.com/dbbs-lab/bsb).
34
+ It contains the interfaces and tools to simulate BSB circuit with the
35
+ [NEST simulator](https://www.nest-simulator.org/).
36
+
@@ -0,0 +1,19 @@
1
+ bsb_nest/__init__.py,sha256=xWFB0-jFsVcMg3EiAbWXgIVLQeh7XGE2Vnj3bUpVVAc,558
2
+ bsb_nest/adapter.py,sha256=jdUTfPo_ui6KBYnBFIrs8l7ObOxjMfe_OD6Zl9HIGwI,7782
3
+ bsb_nest/cell.py,sha256=dkTpnF8z0rg5LjMXVMHeN2zXgwBEHlbee0MHxRxltZ8,1109
4
+ bsb_nest/connection.py,sha256=PdJWQrDl-pmOhKv019c7GUVVMW7hTNEZ-GfHn2XkHEw,6553
5
+ bsb_nest/device.py,sha256=ttLPsf4wZ4dwHEeTBSPpbjEVeM06xnE6dUJM8U3zT8Y,5276
6
+ bsb_nest/distributions.py,sha256=qkQk9KqlqDDS4WXbokwhNTnu4IA-fcLFcTbqbVfj5fM,1903
7
+ bsb_nest/exceptions.py,sha256=NX4oiGWIshrLG1DCak_nTfI8cQI7GTdewR71qNzsXGM,264
8
+ bsb_nest/simulation.py,sha256=BOQkJ5X2o1tIL1nGpT0Q-JFuhTAB-HdyTYzIOoqHTTc,1301
9
+ bsb_nest/devices/__init__.py,sha256=PJKVn09saFObRuiOYJ8fWAfDuxfvaZAJYqLp4altOAY,363
10
+ bsb_nest/devices/dc_generator.py,sha256=g-jRZorCl2yYhr5bx2I00ZlxYrblDcqVbtk0XYZzmdI,953
11
+ bsb_nest/devices/multimeter.py,sha256=Gy_rRBpEoxbPZVHCI14rVqp19GM31RMxoZL6_PQgq64,2246
12
+ bsb_nest/devices/poisson_generator.py,sha256=g0t-L0s-JdGjT7x17vNi9HwLcBIBZybcAim0I4rPvl8,1529
13
+ bsb_nest/devices/sinusoidal_poisson_generator.py,sha256=4L_EIXqn9qaUcozv17m3FK4IHlXW-mxEJcQAhRyC1ZI,2271
14
+ bsb_nest/devices/spike_recorder.py,sha256=1ViQBtFICt1xACzoemSf-f9WSD949QCdggRk_muwrww,916
15
+ bsb_nest-6.0.0a5.dist-info/entry_points.txt,sha256=uJlX9h2VeYfAumav18IM2PTJ3FssfwDUnUmfSFdR2SA,41
16
+ bsb_nest-6.0.0a5.dist-info/licenses/LICENSE,sha256=ljOS4DjXvqEo5VzGfdaRwgRZPbNScGBmfwyC8PChvmQ,32422
17
+ bsb_nest-6.0.0a5.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
18
+ bsb_nest-6.0.0a5.dist-info/METADATA,sha256=ijMGNhR701ua-HuXyWWphV6jKrfblh9Midrh2ADyzu8,1607
19
+ bsb_nest-6.0.0a5.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: flit 3.12.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [bsb.simulation_backends]
2
+ nest=bsb_nest
3
+