bsb-nest 5.0.0__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 +27 -0
- bsb_nest/adapter.py +203 -0
- bsb_nest/cell.py +32 -0
- bsb_nest/connection.py +193 -0
- bsb_nest/device.py +149 -0
- bsb_nest/devices/__init__.py +13 -0
- bsb_nest/devices/dc_generator.py +23 -0
- bsb_nest/devices/multimeter.py +59 -0
- bsb_nest/devices/poisson_generator.py +42 -0
- bsb_nest/devices/sinusoidal_poisson_generator.py +63 -0
- bsb_nest/devices/spike_recorder.py +29 -0
- bsb_nest/distributions.py +60 -0
- bsb_nest/exceptions.py +22 -0
- bsb_nest/simulation.py +36 -0
- bsb_nest-5.0.0.dist-info/METADATA +22 -0
- bsb_nest-5.0.0.dist-info/RECORD +19 -0
- bsb_nest-5.0.0.dist-info/WHEEL +4 -0
- bsb_nest-5.0.0.dist-info/entry_points.txt +3 -0
- bsb_nest-5.0.0.dist-info/licenses/LICENSE +619 -0
|
@@ -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,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
|
+
|
|
@@ -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-5.0.0.dist-info/entry_points.txt,sha256=uJlX9h2VeYfAumav18IM2PTJ3FssfwDUnUmfSFdR2SA,41
|
|
16
|
+
bsb_nest-5.0.0.dist-info/licenses/LICENSE,sha256=ljOS4DjXvqEo5VzGfdaRwgRZPbNScGBmfwyC8PChvmQ,32422
|
|
17
|
+
bsb_nest-5.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
18
|
+
bsb_nest-5.0.0.dist-info/METADATA,sha256=DQ9rCxDtoV_C7yDLQR47m6WXmAA6ItYoW0I2WzOOcMU,1013
|
|
19
|
+
bsb_nest-5.0.0.dist-info/RECORD,,
|