bsb-arbor 4.1.1__tar.gz → 6.0.0__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-arbor might be problematic. Click here for more details.
- bsb_arbor-6.0.0/PKG-INFO +23 -0
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/README.md +1 -1
- bsb_arbor-6.0.0/bsb_arbor/__init__.py +20 -0
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/adapter.py +117 -37
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/cell.py +13 -8
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/connection.py +7 -6
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/device.py +7 -2
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/devices/__init__.py +6 -0
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/devices/poisson_generator.py +4 -0
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/devices/probe.py +3 -4
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/bsb_arbor/devices/spike_recorder.py +4 -5
- bsb_arbor-6.0.0/bsb_arbor/simulation.py +38 -0
- bsb_arbor-6.0.0/pyproject.toml +69 -0
- bsb_arbor-4.1.1/PKG-INFO +0 -37
- bsb_arbor-4.1.1/bsb_arbor/__init__.py +0 -12
- bsb_arbor-4.1.1/bsb_arbor/simulation.py +0 -23
- bsb_arbor-4.1.1/pyproject.toml +0 -69
- {bsb_arbor-4.1.1 → bsb_arbor-6.0.0}/LICENSE +0 -0
bsb_arbor-6.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bsb-arbor
|
|
3
|
+
Version: 6.0.0
|
|
4
|
+
Summary: Arbor 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: numpy~=1.21
|
|
11
|
+
Requires-Dist: bsb-core~=6.0
|
|
12
|
+
Requires-Dist: arborize~=6.0
|
|
13
|
+
Requires-Dist: arbor~=0.10; sys_platform != 'win32'
|
|
14
|
+
|
|
15
|
+
[](https://github.com/dbbs-lab/bsb-arbor/actions/workflows/main.yml)
|
|
16
|
+
[](https://github.com/psf/black)
|
|
17
|
+
|
|
18
|
+
# bsb-arbor
|
|
19
|
+
|
|
20
|
+
bsb-arbor is a plugin of the [BSB](https://github.com/dbbs-lab/bsb) (see also
|
|
21
|
+
[bsb-core](https://github.com/dbbs-lab/bsb-core)).
|
|
22
|
+
It contains the interfaces and tools to simulate BSB circuit with the
|
|
23
|
+
[Arbor simulator](https://arbor-sim.org/).
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# bsb-arbor
|
|
5
5
|
|
|
6
|
-
bsb-
|
|
6
|
+
bsb-arbor is a plugin of the [BSB](https://github.com/dbbs-lab/bsb) (see also
|
|
7
7
|
[bsb-core](https://github.com/dbbs-lab/bsb-core)).
|
|
8
8
|
It contains the interfaces and tools to simulate BSB circuit with the
|
|
9
9
|
[Arbor simulator](https://arbor-sim.org/).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Arbor simulation adapter for the BSB framework.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from bsb import SimulationBackendPlugin
|
|
6
|
+
|
|
7
|
+
from .adapter import ArborAdapter
|
|
8
|
+
from .devices import PoissonGenerator, Probe, SpikeRecorder
|
|
9
|
+
from .simulation import ArborSimulation
|
|
10
|
+
|
|
11
|
+
__plugin__ = SimulationBackendPlugin(Simulation=ArborSimulation, Adapter=ArborAdapter)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"PoissonGenerator",
|
|
16
|
+
"Probe",
|
|
17
|
+
"SpikeRecorder",
|
|
18
|
+
"ArborAdapter",
|
|
19
|
+
"ArborSimulation",
|
|
20
|
+
]
|
|
@@ -7,7 +7,6 @@ import arbor
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
from arbor import units as U
|
|
9
9
|
from bsb import (
|
|
10
|
-
MPI,
|
|
11
10
|
AdapterError,
|
|
12
11
|
Chunk,
|
|
13
12
|
SimulationData,
|
|
@@ -22,9 +21,16 @@ if typing.TYPE_CHECKING:
|
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
class ArborSimulationData(SimulationData):
|
|
24
|
+
"""
|
|
25
|
+
Container class for simulation data.
|
|
26
|
+
"""
|
|
27
|
+
|
|
25
28
|
def __init__(self, simulation):
|
|
29
|
+
"""
|
|
30
|
+
Container class for simulation data.
|
|
31
|
+
"""
|
|
26
32
|
super().__init__(simulation)
|
|
27
|
-
self.arbor_sim:
|
|
33
|
+
self.arbor_sim: arbor.simulation = None
|
|
28
34
|
|
|
29
35
|
|
|
30
36
|
class ReceiverCollection(list):
|
|
@@ -47,7 +53,8 @@ class ReceiverCollection(list):
|
|
|
47
53
|
|
|
48
54
|
class SingleReceiverCollection(list):
|
|
49
55
|
"""
|
|
50
|
-
The single receiver collection redirects all incoming connections to the same
|
|
56
|
+
The single receiver collection redirects all incoming connections to the same
|
|
57
|
+
receiver.
|
|
51
58
|
"""
|
|
52
59
|
|
|
53
60
|
def append(self, rcv):
|
|
@@ -56,7 +63,20 @@ class SingleReceiverCollection(list):
|
|
|
56
63
|
|
|
57
64
|
|
|
58
65
|
class Population:
|
|
66
|
+
"""
|
|
67
|
+
Represents a population of cells for the Arbor simulator.
|
|
68
|
+
|
|
69
|
+
This class manages a collection of cells from a specific cell model, handling their
|
|
70
|
+
GID ranges and providing methods to access and manipulate subsets of the population.
|
|
71
|
+
"""
|
|
59
72
|
def __init__(self, simdata, cell_model, offset):
|
|
73
|
+
"""
|
|
74
|
+
Initialize a population of cells.
|
|
75
|
+
|
|
76
|
+
:param simdata: The simulation data container
|
|
77
|
+
:param cell_model: The cell model for this population
|
|
78
|
+
:param offset: The GID offset for this population
|
|
79
|
+
"""
|
|
60
80
|
self._model = cell_model
|
|
61
81
|
self._simdata = simdata
|
|
62
82
|
ps = cell_model.get_placement_set(simdata.chunks)
|
|
@@ -65,30 +85,70 @@ class Population:
|
|
|
65
85
|
|
|
66
86
|
@property
|
|
67
87
|
def model(self):
|
|
88
|
+
"""
|
|
89
|
+
Get the cell model associated with this population.
|
|
90
|
+
|
|
91
|
+
:return: The cell model object
|
|
92
|
+
"""
|
|
68
93
|
return self._model
|
|
69
94
|
|
|
70
95
|
@property
|
|
71
96
|
def offset(self):
|
|
97
|
+
"""
|
|
98
|
+
Get the GID offset for this population.
|
|
99
|
+
|
|
100
|
+
:return: The GID offset value
|
|
101
|
+
"""
|
|
72
102
|
return self._offset
|
|
73
103
|
|
|
74
104
|
def __len__(self):
|
|
105
|
+
"""
|
|
106
|
+
Get the total number of cells in this population.
|
|
107
|
+
|
|
108
|
+
:return: The number of cells
|
|
109
|
+
"""
|
|
75
110
|
return sum(stop - start for start, stop in self._ranges)
|
|
76
111
|
|
|
77
112
|
def __contains__(self, i):
|
|
113
|
+
"""
|
|
114
|
+
Check if a GID is part of this population.
|
|
115
|
+
|
|
116
|
+
:param i: The GID to check
|
|
117
|
+
:return: True if the GID is in this population, False otherwise
|
|
118
|
+
"""
|
|
78
119
|
return any(start <= i < stop for start, stop in self._ranges)
|
|
79
120
|
|
|
80
121
|
def copy(self):
|
|
122
|
+
"""
|
|
123
|
+
Create a copy of this population.
|
|
124
|
+
|
|
125
|
+
:return: A new Population instance with the same properties
|
|
126
|
+
"""
|
|
81
127
|
return Population(self._simdata, self._model, self._offset)
|
|
82
128
|
|
|
83
129
|
def __getitem__(self, item):
|
|
130
|
+
"""
|
|
131
|
+
Get a subset of the population based on the provided index or mask.
|
|
132
|
+
|
|
133
|
+
Supports various indexing methods:
|
|
134
|
+
- Boolean masking: Select cells based on a boolean mask
|
|
135
|
+
- Integer indexing: Select specific cells by their indices
|
|
136
|
+
- Slicing: Select a range of cells
|
|
137
|
+
|
|
138
|
+
:param item: The index, slice, or mask to use for selection
|
|
139
|
+
:return: A new Population instance containing the selected cells
|
|
140
|
+
:raises ValueError: If the dimensions of the mask don't match the population
|
|
141
|
+
:raises IndexError: If an index is out of bounds
|
|
142
|
+
"""
|
|
84
143
|
# Boolean masking, kind of
|
|
85
|
-
if getattr(item, "dtype", None) == bool or _all_bools(item):
|
|
144
|
+
if getattr(item, "dtype", None) == bool or _all_bools(item): # noqa: E721
|
|
86
145
|
if len(item) != len(self):
|
|
87
146
|
raise ValueError(
|
|
88
|
-
f"Dimension mismatch between population ({len(self)})
|
|
147
|
+
f"Dimension mismatch between population ({len(self)}) "
|
|
148
|
+
f"and mask ({len(item)})"
|
|
89
149
|
)
|
|
90
150
|
return self._subpop_np(np.array(self)[item])
|
|
91
|
-
elif getattr(item, "dtype", None) == int or _all_ints(item):
|
|
151
|
+
elif getattr(item, "dtype", None) == int or _all_ints(item): # noqa: E721
|
|
92
152
|
if getattr(item, "ndim", None) == 0:
|
|
93
153
|
return self._subpop_one(item)
|
|
94
154
|
return self._subpop_np(np.array(self)[item])
|
|
@@ -98,6 +158,14 @@ class Population:
|
|
|
98
158
|
return self._subpop_one(item)
|
|
99
159
|
|
|
100
160
|
def _get_ranges(self, chunks, ps, offset):
|
|
161
|
+
"""
|
|
162
|
+
Calculate the GID ranges for this population based on chunk statistics.
|
|
163
|
+
|
|
164
|
+
:param chunks: The simulation chunks to include
|
|
165
|
+
:param ps: The placement set for the cell model
|
|
166
|
+
:param offset: The starting GID offset
|
|
167
|
+
:return: A list of (start, stop) tuples representing GID ranges
|
|
168
|
+
"""
|
|
101
169
|
stats = ps.get_chunk_stats()
|
|
102
170
|
ranges = []
|
|
103
171
|
for chunk, len_ in sorted(
|
|
@@ -109,6 +177,15 @@ class Population:
|
|
|
109
177
|
return ranges
|
|
110
178
|
|
|
111
179
|
def _subpop_np(self, arr):
|
|
180
|
+
"""
|
|
181
|
+
Create a subpopulation from a numpy array of indices.
|
|
182
|
+
|
|
183
|
+
This method handles array-based indexing, including boolean masks,
|
|
184
|
+
integer arrays, and slices.
|
|
185
|
+
|
|
186
|
+
:param arr: A numpy array of indices to include in the subpopulation
|
|
187
|
+
:return: A new Population instance containing only the selected cells
|
|
188
|
+
"""
|
|
112
189
|
pop = self.copy()
|
|
113
190
|
if not len(pop):
|
|
114
191
|
return pop
|
|
@@ -130,6 +207,13 @@ class Population:
|
|
|
130
207
|
return pop
|
|
131
208
|
|
|
132
209
|
def _subpop_one(self, item):
|
|
210
|
+
"""
|
|
211
|
+
Create a subpopulation containing a single cell.
|
|
212
|
+
|
|
213
|
+
:param item: The index of the cell to include
|
|
214
|
+
:return: A new Population instance containing only the selected cell
|
|
215
|
+
:raises IndexError: If the index is out of bounds
|
|
216
|
+
"""
|
|
133
217
|
if item >= len(self):
|
|
134
218
|
raise IndexError(f"Index {item} out of bounds for size {len(self)}")
|
|
135
219
|
pop = self.copy()
|
|
@@ -142,9 +226,12 @@ class Population:
|
|
|
142
226
|
ptr += stop - start
|
|
143
227
|
|
|
144
228
|
def __iter__(self):
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
229
|
+
"""
|
|
230
|
+
Iterate over all GIDs in this population.
|
|
231
|
+
|
|
232
|
+
:yield: Each GID in the population's ranges
|
|
233
|
+
"""
|
|
234
|
+
yield from itertools.chain.from_iterable(range(r[0], r[1]) for r in self._ranges)
|
|
148
235
|
|
|
149
236
|
|
|
150
237
|
class GIDManager:
|
|
@@ -281,36 +368,27 @@ class ArborRecipe(arbor.recipe):
|
|
|
281
368
|
|
|
282
369
|
|
|
283
370
|
class ArborAdapter(SimulatorAdapter):
|
|
284
|
-
def __init__(self):
|
|
285
|
-
super().__init__()
|
|
286
|
-
self.simdata:
|
|
287
|
-
|
|
288
|
-
def
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return MPI.get_size()
|
|
293
|
-
|
|
294
|
-
def broadcast(self, data, root=0):
|
|
295
|
-
return MPI.bcast(data, root)
|
|
296
|
-
|
|
297
|
-
def barrier(self):
|
|
298
|
-
return MPI.barrier()
|
|
299
|
-
|
|
300
|
-
def prepare(self, simulation: "ArborSimulation", comm=None):
|
|
371
|
+
def __init__(self, comm=None):
|
|
372
|
+
super().__init__(comm)
|
|
373
|
+
self.simdata: dict[ArborSimulation, ArborSimulationData] = {}
|
|
374
|
+
|
|
375
|
+
def prepare(self, simulation: "ArborSimulation") -> ArborSimulationData:
|
|
376
|
+
"""
|
|
377
|
+
Prepares the arbor simulation engine with the given simulation.
|
|
378
|
+
"""
|
|
301
379
|
simdata = self._create_simdata(simulation)
|
|
302
380
|
try:
|
|
303
381
|
context = arbor.context(arbor.proc_allocation(threads=simulation.threads))
|
|
304
|
-
if
|
|
382
|
+
if self.comm.get_size() > 1:
|
|
305
383
|
if not arbor.config()["mpi4py"]:
|
|
306
384
|
warn(
|
|
307
385
|
f"Arbor does not seem to be built with MPI support, running"
|
|
308
|
-
"duplicate simulations on {
|
|
386
|
+
f"duplicate simulations on {self.comm.get_size()} nodes."
|
|
309
387
|
)
|
|
310
388
|
else:
|
|
311
389
|
context = arbor.context(
|
|
312
390
|
arbor.proc_allocation(threads=simulation.threads),
|
|
313
|
-
mpi=comm
|
|
391
|
+
mpi=self.comm.get_communicator(),
|
|
314
392
|
)
|
|
315
393
|
if simulation.profiling:
|
|
316
394
|
if arbor.config()["profiling"]:
|
|
@@ -318,7 +396,8 @@ class ArborAdapter(SimulatorAdapter):
|
|
|
318
396
|
arbor.profiler_initialize(context)
|
|
319
397
|
else:
|
|
320
398
|
raise RuntimeError(
|
|
321
|
-
"Arbor must be built with profiling support to use the
|
|
399
|
+
"Arbor must be built with profiling support to use the "
|
|
400
|
+
"`profiling` flag."
|
|
322
401
|
)
|
|
323
402
|
simdata.gid_manager = self.get_gid_manager(simulation, simdata)
|
|
324
403
|
simdata.populations = simdata.gid_manager.get_populations()
|
|
@@ -342,12 +421,13 @@ class ArborAdapter(SimulatorAdapter):
|
|
|
342
421
|
|
|
343
422
|
def prepare_samples(self, simulation, simdata):
|
|
344
423
|
for device in simulation.devices.values():
|
|
345
|
-
device.prepare_samples(simdata)
|
|
424
|
+
device.prepare_samples(simdata, comm=self.comm)
|
|
346
425
|
|
|
347
426
|
def run(self, *simulations):
|
|
348
427
|
if len(simulations) != 1:
|
|
349
428
|
raise RuntimeError(
|
|
350
|
-
"Can not run multiple simultaneous simulations. Composition not
|
|
429
|
+
"Can not run multiple simultaneous simulations. Composition not "
|
|
430
|
+
"implemented."
|
|
351
431
|
)
|
|
352
432
|
simulation = simulations[0]
|
|
353
433
|
try:
|
|
@@ -358,7 +438,7 @@ class ArborAdapter(SimulatorAdapter):
|
|
|
358
438
|
f"Can't run unprepared simulation '{simulation.name}'"
|
|
359
439
|
) from None
|
|
360
440
|
try:
|
|
361
|
-
if not
|
|
441
|
+
if not self.comm.get_rank():
|
|
362
442
|
arbor_sim.record(arbor.spike_recording.all)
|
|
363
443
|
|
|
364
444
|
start = time.time()
|
|
@@ -381,7 +461,7 @@ class ArborAdapter(SimulatorAdapter):
|
|
|
381
461
|
return ArborRecipe(simulation, simdata)
|
|
382
462
|
|
|
383
463
|
def _create_simdata(self, simulation):
|
|
384
|
-
self.simdata[simulation] = simdata =
|
|
464
|
+
self.simdata[simulation] = simdata = ArborSimulationData(simulation)
|
|
385
465
|
self._assign_chunks(simulation, simdata)
|
|
386
466
|
return simdata
|
|
387
467
|
|
|
@@ -431,14 +511,14 @@ class ArborAdapter(SimulatorAdapter):
|
|
|
431
511
|
|
|
432
512
|
def _assign_chunks(self, simulation, simdata):
|
|
433
513
|
chunk_stats = simulation.scaffold.storage.get_chunk_stats()
|
|
434
|
-
size =
|
|
435
|
-
all_chunks = [Chunk.from_id(int(chunk), None) for chunk in chunk_stats
|
|
514
|
+
size = self.comm.get_size()
|
|
515
|
+
all_chunks = [Chunk.from_id(int(chunk), None) for chunk in chunk_stats]
|
|
436
516
|
simdata.node_chunk_alloc = [all_chunks[rank::size] for rank in range(0, size)]
|
|
437
517
|
simdata.chunk_node_map = {}
|
|
438
518
|
for node, chunks in enumerate(simdata.node_chunk_alloc):
|
|
439
519
|
for chunk in chunks:
|
|
440
520
|
simdata.chunk_node_map[chunk] = node
|
|
441
|
-
simdata.chunks = simdata.node_chunk_alloc[
|
|
521
|
+
simdata.chunks = simdata.node_chunk_alloc[self.comm.get_rank()]
|
|
442
522
|
|
|
443
523
|
|
|
444
524
|
def _all_bools(arr):
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import abc
|
|
2
|
-
import typing
|
|
3
2
|
|
|
4
3
|
import arbor
|
|
5
|
-
from bsb import CellModel, ConfigurationError, config, types
|
|
4
|
+
from bsb import CellModel, ConfigurationError, PlacementSet, config, types
|
|
6
5
|
|
|
7
6
|
from .adapter import SingleReceiverCollection
|
|
8
7
|
|
|
9
|
-
if typing.TYPE_CHECKING:
|
|
10
|
-
from bsb import PlacementSet
|
|
11
|
-
|
|
12
8
|
|
|
13
9
|
@config.dynamic(
|
|
14
10
|
attr_name="model_strategy",
|
|
@@ -17,11 +13,18 @@ if typing.TYPE_CHECKING:
|
|
|
17
13
|
classmap_entry=None,
|
|
18
14
|
)
|
|
19
15
|
class ArborCell(CellModel):
|
|
16
|
+
model_strategy: config.ConfigurationAttribute
|
|
17
|
+
"""
|
|
18
|
+
Optional importable reference to a different modelling strategy than the default
|
|
19
|
+
Arborize strategy.
|
|
20
|
+
"""
|
|
20
21
|
gap = config.attr(type=bool, default=False)
|
|
22
|
+
"""Is this synapse a gap junction?"""
|
|
21
23
|
model = config.attr(type=types.class_(), required=True)
|
|
24
|
+
"""Importable reference to the arborize model describing the cell type."""
|
|
22
25
|
|
|
23
26
|
@abc.abstractmethod
|
|
24
|
-
def cache_population_data(self, simdata, ps:
|
|
27
|
+
def cache_population_data(self, simdata, ps: PlacementSet):
|
|
25
28
|
pass
|
|
26
29
|
|
|
27
30
|
@abc.abstractmethod
|
|
@@ -51,9 +54,11 @@ class ArborCell(CellModel):
|
|
|
51
54
|
@config.node
|
|
52
55
|
class LIFCell(ArborCell, classmap_entry="lif"):
|
|
53
56
|
model = config.unset()
|
|
57
|
+
"""Importable reference to the arborize model describing the cell type."""
|
|
54
58
|
constants = config.dict(type=types.any_())
|
|
59
|
+
"""Dictionary linking the parameters' name to its value."""
|
|
55
60
|
|
|
56
|
-
def cache_population_data(self, simdata, ps:
|
|
61
|
+
def cache_population_data(self, simdata, ps: PlacementSet):
|
|
57
62
|
pass
|
|
58
63
|
|
|
59
64
|
def discard_population_data(self):
|
|
@@ -66,7 +71,7 @@ class LIFCell(ArborCell, classmap_entry="lif"):
|
|
|
66
71
|
return arbor.cell_kind.lif
|
|
67
72
|
|
|
68
73
|
def get_description(self, gid):
|
|
69
|
-
cell = arbor.lif_cell(
|
|
74
|
+
cell = arbor.lif_cell("-1_-1", "-1_-1_0")
|
|
70
75
|
try:
|
|
71
76
|
for k, v in self.constants.items():
|
|
72
77
|
setattr(cell, k, v * getattr(cell, k).units)
|
|
@@ -41,8 +41,11 @@ class Connection:
|
|
|
41
41
|
@config.node
|
|
42
42
|
class ArborConnection(ConnectionModel):
|
|
43
43
|
gap = config.attr(type=bool, default=False)
|
|
44
|
+
"""Is this synapce a gap junction?"""
|
|
44
45
|
weight = config.attr(type=float, required=True)
|
|
46
|
+
"""Weight of the connection between the presynaptic and the postsynaptic cells."""
|
|
45
47
|
delay = config.attr(type=float, required=True)
|
|
48
|
+
"""Delay of the transmission between the presynaptic and the postsynaptic cells."""
|
|
46
49
|
|
|
47
50
|
def create_gap_junctions_on(self, gj_on_gid, conns):
|
|
48
51
|
for pre_loc, post_loc in conns:
|
|
@@ -56,12 +59,10 @@ class ArborConnection(ConnectionModel):
|
|
|
56
59
|
)
|
|
57
60
|
|
|
58
61
|
def create_connections_from(self, conns_from_gid, conns, pop_pre, pop_post):
|
|
59
|
-
for pre_loc,
|
|
62
|
+
for pre_loc, _post_loc in conns:
|
|
60
63
|
conns_from_gid[int(pre_loc[0] + pop_pre.offset)].append(pre_loc[1:])
|
|
61
64
|
|
|
62
65
|
def gap_junction(self, conn):
|
|
63
|
-
|
|
64
|
-
g = arbor.cell_global_label(
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
return arbor.gap_junction_connection(g, l, self.weight)
|
|
66
|
+
l_ = arbor.cell_local_label(f"gap_{conn.to_compartment.id}")
|
|
67
|
+
g = arbor.cell_global_label(int(conn.from_id), f"gap_{conn.from_compartment.id}")
|
|
68
|
+
return arbor.gap_junction_connection(g, l_, self.weight)
|
|
@@ -6,9 +6,14 @@ from bsb import DeviceModel, Targetting, config, types
|
|
|
6
6
|
|
|
7
7
|
@config.dynamic(attr_name="device", auto_classmap=True, classmap_entry=None)
|
|
8
8
|
class ArborDevice(DeviceModel):
|
|
9
|
+
device: config.ConfigurationAttribute
|
|
10
|
+
"""Optional importable reference to the device strategy."""
|
|
9
11
|
targetting = config.attr(type=Targetting, required=True)
|
|
12
|
+
"""Targets of the device, which should be either a population or a nest rule."""
|
|
10
13
|
resolution = config.attr(type=float)
|
|
14
|
+
"""Time resolution of the device."""
|
|
11
15
|
sampling_policy = config.attr(type=types.in_(["exact"]))
|
|
16
|
+
"""Policy used to sample simulation data from the device."""
|
|
12
17
|
|
|
13
18
|
def __init__(self, **kwargs):
|
|
14
19
|
self._probe_ids = []
|
|
@@ -19,7 +24,7 @@ class ArborDevice(DeviceModel):
|
|
|
19
24
|
def register_probe_id(self, gid, tag):
|
|
20
25
|
self._probe_ids.append((gid, tag))
|
|
21
26
|
|
|
22
|
-
def prepare_samples(self, simdata):
|
|
27
|
+
def prepare_samples(self, simdata, comm):
|
|
23
28
|
self._handles = [
|
|
24
29
|
self.sample(simdata.arbor_sim, probe_id) for probe_id in self._probe_ids
|
|
25
30
|
]
|
|
@@ -34,7 +39,7 @@ class ArborDevice(DeviceModel):
|
|
|
34
39
|
|
|
35
40
|
def get_meta(self):
|
|
36
41
|
attrs = ("name", "sampling_policy", "resolution")
|
|
37
|
-
return dict(zip(attrs, (getattr(self, attr) for attr in attrs)))
|
|
42
|
+
return dict(zip(attrs, (getattr(self, attr) for attr in attrs), strict=False))
|
|
38
43
|
|
|
39
44
|
@abc.abstractmethod
|
|
40
45
|
def implement_probes(self, simdata, target):
|
|
@@ -9,9 +9,13 @@ from ..device import ArborDevice
|
|
|
9
9
|
@config.node
|
|
10
10
|
class PoissonGenerator(ArborDevice, classmap_entry="poisson_generator"):
|
|
11
11
|
record = config.attr(type=bool, default=True)
|
|
12
|
+
"""Flag to save the spikes generated to file."""
|
|
12
13
|
rate = config.attr(type=float, required=True)
|
|
14
|
+
"""Frequency of the poisson generator."""
|
|
13
15
|
weight = config.attr(type=float, required=True)
|
|
16
|
+
"""Weight of the connection between the device and its target."""
|
|
14
17
|
delay = config.attr(type=float, required=True)
|
|
18
|
+
"""Delay of the transmission between the device and its target."""
|
|
15
19
|
|
|
16
20
|
def implement_probes(self, simdata, gid):
|
|
17
21
|
return []
|
|
@@ -11,7 +11,6 @@ class Probe(ArborDevice):
|
|
|
11
11
|
return f"cable_probe_{self.probe_type}"
|
|
12
12
|
|
|
13
13
|
def validate_specifics(self):
|
|
14
|
-
|
|
15
14
|
if self.get_probe_name() not in vars(arbor):
|
|
16
15
|
raise ConfigurationError(
|
|
17
16
|
f"`{self.probe_type}` is not a valid probe type for `{self.name}`"
|
|
@@ -22,9 +21,9 @@ class Probe(ArborDevice):
|
|
|
22
21
|
kwargs = dict((k, getattr(self, k)) for k in probe_args if hasattr(self, k))
|
|
23
22
|
return [getattr(arbor, self.get_probe_name())(**kwargs)]
|
|
24
23
|
|
|
25
|
-
def prepare_samples(self, sim):
|
|
26
|
-
super().prepare_samples(sim)
|
|
27
|
-
for probe_id, handle in zip(self._probe_ids, self._handles):
|
|
24
|
+
def prepare_samples(self, sim, comm):
|
|
25
|
+
super().prepare_samples(sim, comm)
|
|
26
|
+
for probe_id, handle in zip(self._probe_ids, self._handles, strict=False):
|
|
28
27
|
self.adapter.result.add(ProbeRecorder(self, sim, probe_id, handle))
|
|
29
28
|
|
|
30
29
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import neo
|
|
2
2
|
from bsb import config
|
|
3
|
-
from bsb.services import MPI
|
|
4
3
|
|
|
5
4
|
from ..device import ArborDevice
|
|
6
5
|
|
|
@@ -10,9 +9,9 @@ class SpikeRecorder(ArborDevice, classmap_entry="spike_recorder"):
|
|
|
10
9
|
def boot(self):
|
|
11
10
|
self._gids = set()
|
|
12
11
|
|
|
13
|
-
def prepare_samples(self, simdata):
|
|
14
|
-
super().prepare_samples(simdata)
|
|
15
|
-
if not
|
|
12
|
+
def prepare_samples(self, simdata, comm):
|
|
13
|
+
super().prepare_samples(simdata, comm)
|
|
14
|
+
if not comm.get_rank():
|
|
16
15
|
|
|
17
16
|
def record_device_spikes(segment):
|
|
18
17
|
spiketrain = list()
|
|
@@ -25,7 +24,7 @@ class SpikeRecorder(ArborDevice, classmap_entry="spike_recorder"):
|
|
|
25
24
|
neo.SpikeTrain(
|
|
26
25
|
spiketrain,
|
|
27
26
|
units="ms",
|
|
28
|
-
|
|
27
|
+
array_annotations={"senders": senders},
|
|
29
28
|
t_stop=self.simulation.duration,
|
|
30
29
|
device=self.name,
|
|
31
30
|
gids=list(self._gids),
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import psutil
|
|
2
|
+
from bsb import Simulation, config, types
|
|
3
|
+
|
|
4
|
+
from .cell import ArborCell
|
|
5
|
+
from .connection import ArborConnection
|
|
6
|
+
from .device import ArborDevice
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@config.node
|
|
10
|
+
class ArborSimulation(Simulation):
|
|
11
|
+
"""
|
|
12
|
+
Interface between the scaffold model and the Arbor simulator.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
resolution = config.attr(type=types.float(min=0.0), default=0.1)
|
|
16
|
+
"""Simulation time step size in milliseconds."""
|
|
17
|
+
profiling = config.attr(type=bool)
|
|
18
|
+
"""Flag to perform profiling during the simulation."""
|
|
19
|
+
cell_models: config._attrs.cfgdict[ArborCell] = config.dict(
|
|
20
|
+
type=ArborCell, required=True
|
|
21
|
+
)
|
|
22
|
+
"""Dictionary of cell models in the simulation."""
|
|
23
|
+
connection_models: config._attrs.cfgdict[ArborConnection] = config.dict(
|
|
24
|
+
type=ArborConnection, required=True
|
|
25
|
+
)
|
|
26
|
+
"""Dictionary of connection models in the simulation."""
|
|
27
|
+
devices: config._attrs.cfgdict[ArborDevice] = config.dict(
|
|
28
|
+
type=ArborDevice, required=True
|
|
29
|
+
)
|
|
30
|
+
"""Dictionary of devices in the simulation."""
|
|
31
|
+
|
|
32
|
+
@config.property(default=1)
|
|
33
|
+
def threads(self):
|
|
34
|
+
return self._threads
|
|
35
|
+
|
|
36
|
+
@threads.setter
|
|
37
|
+
def threads(self, value):
|
|
38
|
+
self._threads = value if value != "all" else psutil.cpu_count(logical=False)
|
|
@@ -0,0 +1,69 @@
|
|
|
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-arbor"
|
|
9
|
+
version = "6.0.0"
|
|
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 = [
|
|
17
|
+
"numpy~=1.21",
|
|
18
|
+
"bsb-core~=6.0",
|
|
19
|
+
"arborize~=6.0",
|
|
20
|
+
"arbor~=0.10; sys_platform != 'win32'"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[[project.authors]]
|
|
24
|
+
name = "Robin De Schepper"
|
|
25
|
+
email = "robin@alexandria.sc"
|
|
26
|
+
|
|
27
|
+
[[project.authors]]
|
|
28
|
+
name = "Dimitri Rodarie"
|
|
29
|
+
email = "dimitri.rodarie@unipv.it"
|
|
30
|
+
|
|
31
|
+
[project.license]
|
|
32
|
+
file = "LICENSE"
|
|
33
|
+
|
|
34
|
+
[project.entry-points."bsb.simulation_backends"]
|
|
35
|
+
arbor = "bsb_arbor"
|
|
36
|
+
|
|
37
|
+
[tool.uv]
|
|
38
|
+
default-groups = [ "dev", "docs", "test" ]
|
|
39
|
+
sources = { }
|
|
40
|
+
|
|
41
|
+
[tool.flit.module]
|
|
42
|
+
name = "bsb_arbor"
|
|
43
|
+
|
|
44
|
+
[tool.coverage.run]
|
|
45
|
+
branch = true
|
|
46
|
+
source = [ "bsb_arbor" ]
|
|
47
|
+
|
|
48
|
+
[tool.coverage.report]
|
|
49
|
+
exclude_lines = [ "if TYPE_CHECKING:" ]
|
|
50
|
+
show_missing = true
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
exclude = [ ".ruff_cache", ".svn", ".tox", ".venv", "dist" ]
|
|
54
|
+
line-length = 90
|
|
55
|
+
indent-width = 4
|
|
56
|
+
|
|
57
|
+
[tool.ruff.format]
|
|
58
|
+
quote-style = "double"
|
|
59
|
+
indent-style = "space"
|
|
60
|
+
skip-magic-trailing-comma = false
|
|
61
|
+
line-ending = "auto"
|
|
62
|
+
docstring-code-format = true
|
|
63
|
+
docstring-code-line-length = 90
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
select = [ "E", "F", "UP", "B", "SIM", "I" ]
|
|
67
|
+
ignore = [ ]
|
|
68
|
+
fixable = [ "ALL" ]
|
|
69
|
+
unfixable = [ ]
|
bsb_arbor-4.1.1/PKG-INFO
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: bsb-arbor
|
|
3
|
-
Version: 4.1.1
|
|
4
|
-
Summary: Arbor simulation adapter for the BSB framework
|
|
5
|
-
Author-email: Robin De Schepper <robingilbert.deschepper@unipv.it>
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
8
|
-
Requires-Dist: numpy~=1.21
|
|
9
|
-
Requires-Dist: bsb-core~=5.0
|
|
10
|
-
Requires-Dist: arbor~=0.10
|
|
11
|
-
Requires-Dist: arborize[arbor]~=4.0
|
|
12
|
-
Requires-Dist: bsb-arbor[test] ; extra == "dev"
|
|
13
|
-
Requires-Dist: build~=1.0 ; extra == "dev"
|
|
14
|
-
Requires-Dist: twine~=4.0 ; extra == "dev"
|
|
15
|
-
Requires-Dist: pre-commit~=3.5 ; extra == "dev"
|
|
16
|
-
Requires-Dist: black~=24.1.1 ; extra == "dev"
|
|
17
|
-
Requires-Dist: isort~=5.12 ; extra == "dev"
|
|
18
|
-
Requires-Dist: snakeviz~=2.1 ; extra == "dev"
|
|
19
|
-
Requires-Dist: bump-my-version~=0.24 ; extra == "dev"
|
|
20
|
-
Requires-Dist: bsb-core[parallel] ; extra == "parallel"
|
|
21
|
-
Requires-Dist: bsb-core[parallel] ; extra == "test"
|
|
22
|
-
Requires-Dist: bsb-hdf5~=5.0 ; extra == "test"
|
|
23
|
-
Requires-Dist: bsb-test~=4.0 ; extra == "test"
|
|
24
|
-
Requires-Dist: coverage~=7.0 ; extra == "test"
|
|
25
|
-
Provides-Extra: dev
|
|
26
|
-
Provides-Extra: parallel
|
|
27
|
-
Provides-Extra: test
|
|
28
|
-
|
|
29
|
-
[](https://github.com/dbbs-lab/bsb-arbor/actions/workflows/main.yml)
|
|
30
|
-
[](https://github.com/psf/black)
|
|
31
|
-
|
|
32
|
-
# bsb-arbor
|
|
33
|
-
|
|
34
|
-
bsb-nest is a plugin of the [BSB](https://github.com/dbbs-lab/bsb) (see also
|
|
35
|
-
[bsb-core](https://github.com/dbbs-lab/bsb-core)).
|
|
36
|
-
It contains the interfaces and tools to simulate BSB circuit with the
|
|
37
|
-
[Arbor simulator](https://arbor-sim.org/).
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Arbor simulation adapter for the BSB framework
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from bsb import SimulationBackendPlugin
|
|
6
|
-
|
|
7
|
-
from . import devices
|
|
8
|
-
from .adapter import ArborAdapter
|
|
9
|
-
from .simulation import ArborSimulation
|
|
10
|
-
|
|
11
|
-
__version__ = "4.1.1"
|
|
12
|
-
__plugin__ = SimulationBackendPlugin(Simulation=ArborSimulation, Adapter=ArborAdapter)
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import psutil
|
|
2
|
-
from bsb import Simulation, config, types
|
|
3
|
-
|
|
4
|
-
from .cell import ArborCell
|
|
5
|
-
from .connection import ArborConnection
|
|
6
|
-
from .device import ArborDevice
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@config.node
|
|
10
|
-
class ArborSimulation(Simulation):
|
|
11
|
-
resolution = config.attr(type=types.float(min=0.0), default=0.1)
|
|
12
|
-
profiling = config.attr(type=bool)
|
|
13
|
-
cell_models = config.dict(type=ArborCell, required=True)
|
|
14
|
-
connection_models = config.dict(type=ArborConnection, required=True)
|
|
15
|
-
devices = config.dict(type=ArborDevice, required=True)
|
|
16
|
-
|
|
17
|
-
@config.property(default=1)
|
|
18
|
-
def threads(self):
|
|
19
|
-
return self._threads
|
|
20
|
-
|
|
21
|
-
@threads.setter
|
|
22
|
-
def threads(self, value):
|
|
23
|
-
self._threads = value if value != "all" else psutil.cpu_count(logical=False)
|
bsb_arbor-4.1.1/pyproject.toml
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["flit_core >=3.2,<4"]
|
|
3
|
-
build-backend = "flit_core.buildapi"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "bsb-arbor"
|
|
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 = [
|
|
13
|
-
"numpy~=1.21",
|
|
14
|
-
"bsb-core~=5.0",
|
|
15
|
-
"arbor~=0.10",
|
|
16
|
-
"arborize[arbor]~=4.0"
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
[project.entry-points."bsb.simulation_backends"]
|
|
20
|
-
arbor = "bsb_arbor"
|
|
21
|
-
|
|
22
|
-
[tool.flit.module]
|
|
23
|
-
name = "bsb_arbor"
|
|
24
|
-
|
|
25
|
-
[project.optional-dependencies]
|
|
26
|
-
parallel = ["bsb-core[parallel]"]
|
|
27
|
-
test = [
|
|
28
|
-
"bsb-core[parallel]",
|
|
29
|
-
"bsb-hdf5~=5.0",
|
|
30
|
-
"bsb-test~=4.0",
|
|
31
|
-
"coverage~=7.0"
|
|
32
|
-
]
|
|
33
|
-
dev = [
|
|
34
|
-
"bsb-arbor[test]",
|
|
35
|
-
"build~=1.0",
|
|
36
|
-
"twine~=4.0",
|
|
37
|
-
"pre-commit~=3.5",
|
|
38
|
-
"black~=24.1.1",
|
|
39
|
-
"isort~=5.12",
|
|
40
|
-
"snakeviz~=2.1",
|
|
41
|
-
"bump-my-version~=0.24"
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
[tool.isort]
|
|
45
|
-
profile = "black"
|
|
46
|
-
|
|
47
|
-
[tool.bumpversion]
|
|
48
|
-
current_version = "4.1.1"
|
|
49
|
-
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
50
|
-
serialize = ["{major}.{minor}.{patch}"]
|
|
51
|
-
search = "{current_version}"
|
|
52
|
-
replace = "{new_version}"
|
|
53
|
-
regex = false
|
|
54
|
-
ignore_missing_version = false
|
|
55
|
-
tag = true
|
|
56
|
-
sign_tags = false
|
|
57
|
-
tag_name = "v{new_version}"
|
|
58
|
-
tag_message = "Bump version: {current_version} → {new_version}"
|
|
59
|
-
allow_dirty = false
|
|
60
|
-
commit = true
|
|
61
|
-
message = "Bump version: {current_version} → {new_version}"
|
|
62
|
-
commit_args = "--no-verify"
|
|
63
|
-
|
|
64
|
-
[tool.bumpversion.parts.pre_l]
|
|
65
|
-
values = ["dev", "a", "b", "rc", "final"]
|
|
66
|
-
optional_value = "final"
|
|
67
|
-
|
|
68
|
-
[[tool.bumpversion.files]]
|
|
69
|
-
filename = "bsb_arbor/__init__.py"
|
|
File without changes
|