Connectivity#
Connection strategies connect cell types together after they’ve been placed into the
simulation volume. They are defined in the configuration under connectivity
:
{
"connectivity": {
"cell_A_to_cell_B": {
"strategy": "bsb.connectivity.VoxelIntersection",
"presynaptic": {
"cell_types": ["cell_A"]
},
"postsynaptic": {
"cell_types": ["cell_B"]
}
}
}
}
The strategy specifies which
ConnectionStrategy
to load. The pre and
post specify the two hemitypes
.
Creating your own#
You can create custom connectivity patterns by creating an importable module (refer to the
Python documentation) with inside a
class inheriting from ConnectionStrategy
.
What follows is an example implementation, that we’ll deconstruct, step by step. The
example connects cells that are near each other between a min
and max
distance:
from bsb.connectivity import ConnectionStrategy
from bsb.exceptions import ConfigurationError
from bsb import config
import numpy as np
import scipy.spatial.distance as dist
@config.node
class ConnectBetween(ConnectionStrategy):
# Define the class' configuration attributes
min = config.attr(type=float, default=0)
max = config.attr(type=float, required=True)
def __init__(self, **kwargs):
# Here you can check if the object was properly configured
if self.max < self.min:
raise ConfigurationError("Max distance should be larger than min distance.")
def connect(self, pre, post):
# The `connect` function is responsible for deciding which cells get connected.
# Use the `.placement` to get a dictionary of `PlacementSet`s to connect.
for from_type, from_set in pre.placement.items():
from_pos = from_set.load_positions()
for to_type, to_set in post.placement.items():
to_pos = to_set.load_positions()
pairw_dist = dist.cdist(from_pos, to_pos)
matches = (pairw_dist <= max) & (pairw_dist >= min)
# Some more numpy code to convert the distance matches to 2 location matrices
# ...
pre_locs = ...
post_locs = ...
self.connect_cells(from_type, to_type, pre_locs, post_locs)
An example using this strategy, assuming it is importable from the my_module
module:
{
"connectivity": {
"cell_A_to_cell_B": {
"class": "my_module.ConnectBetween",
"min": 10,
"max": 15.5,
"presynaptic": {
"cell_types": ["cell_A"]
},
"postsynaptic": {
"cell_types": ["cell_B"]
}
}
}
}
Then, when it is time, the framework will call the strategy’s
connect()
method.
Accessing configuration values
In short, the objects that are decorated with @config.node
will already be fully
configured before __init__
is called and all attributes available under self
(e.g.
self.min
and self.max
). For specifics on configuration nodes, see
Nodes.
Accessing placement data
The connect
function is handed the placement information as the pre
and post
parameters. The .placement
attribute contains the placement data under consideration
as PlacementSets
.
Note
The connect
function is called multiple times, usually once per postsynaptic “chunk”
populated by the postsynaptic cell types. For each chunk, a region of interest is
determined of chunks that could contain cells to be connected. This is transparent to
you, as long as you use the pre.placement
and post.placement
given to you; they
show you an encapsulated view of the placement data matching the current task. Note
carefully that if you use the regular get_placement_set
functions that they will not
be encapsulated, and duplicate data processing might occur.
Creating connections
Finally you should call self.scaffold.connect_cells(tag, matrix)
to connect the cells.
The tag is free to choose, the matrix should be rows of pre to post cell ID pairs.
Connection types and labels#
Warning
The following documentation has not been updated to v4 yet, please bother a dev to do so 😜.
When defining a connection type under connectivity
in the configuration file, it is
possible to select specific subpopulations inside the attributes from_cell_types
and/or to_cell_types
. By including the attribute with_label
in the
connectivity
configuration, you can define the subpopulation label:
{
"connectivity": {
"cell_A_to_cell_B": {
"class": "my_module.ConnectBetween",
"from_cell_types": [
{
"type": "cell_A",
"with_label": "cell_A_type_1"
}
],
"to_cell_types": [
{
"type": "cell_B",
"with_label": "cell_B_type_3"
}
]
}
}
}
Note
The labels used in the configuration file must correspond to the labels assigned during cell placement.
Using more than one label#
If under connectivity
more than one label has been specified, it is possible to choose
whether the labels must be used serially or in a mixed way, by including a new attribute
mix_labels
. For instance:
{
"connectivity": {
"cell_A_to_cell_B": {
"class": "my_module.ConnectBetween",
"from_cell_types": [
{
"type": "cell_A","with_label": ["cell_A_type_2","cell_A_type_1"]
}
],
"to_cell_types": [
{
"type": "cell_B","with_label": ["cell_B_type_3","cell_B_type_2"]
}
]
}
}
}
Using the above configuration file, the established connections are:
From
cell_A_type_2
tocell_B_type_3
From
cell_A_type_1
tocell_B_type_2
Here there is another example of configuration setting:
{
"connectivity": {
"cell_A_to_cell_B": {
"class": "my_module.ConnectBetween",
"from_cell_types": [
{
"type": "cell_A","with_label": ["cell_A_type_2","cell_A_type_1"]
}
],
"to_cell_types": [
{
"type": "cell_B","with_label": ["cell_B_type_3","cell_B_type_2"]
}
],
"mix_labels": true,
}
}
}
In this case, thanks to the mix_labels
attribute,the established connections are:
From
cell_A_type_2
tocell_B_type_3
From
cell_A_type_2
tocell_B_type_2
From
cell_A_type_1
tocell_B_type_3
From
cell_A_type_1
tocell_B_type_2