Connection types¶
Connection types connect cell types together after they’ve been placed into the simulation
volume. They are defined in the configuration under connection_types
:
{
"connection_types": {
"cell_A_to_cell_B": {
"class": "bsb.connectivity.VoxelIntersection",
"from_cell_types": [
{
"type": "cell_A",
"compartments": ["axon"]
}
],
"to_cell_types": [
{
"type": "cell_B",
"compartments": ["dendrites", "soma"]
}
]
}
}
}
The class specifies which ConnectionStrategy
to load for this conenction
type. The from_cell_types and to_cell_types specify which pre- and
postsynaptic cell types to use respectively. The cell type definitions in those lists have
to contain a type that links to an existing cell type and can optionally
contain hints to which compartments of the morphology to use.
Creating your own¶
In order to create your own connection type, create an importable module (refer to the
Python documentation) with inside
a class inheriting from connectivity.ConnectionStrategy
. Let’s start by
deconstructing a full code example that connects cells that are near each other between
a min
and max
distance:
from bsb.connectivity import ConnectionStrategy
from bsb.exceptions import ConfigurationError
import scipy.spatial.distance as dist
class ConnectBetween(ConnectionStrategy):
# Casts given configuration values to a certain type
casts = {
"min": float,
"max": float,
}
# Default values for the configuration attributes
defaults = {
"min": 0.,
}
# Configuration attributes that the user must give or an error is thrown.
required = ["max"]
# The function to check whether the given values are all correct
def validate(self):
if self.max < self.min:
raise ConfigurationError("Max distance should be larger than min distance.")
# The function to determine which cell pairs should be connected
def connect(self):
for ft in self.from_cell_types:
ps_from = self.scaffold.get_placement_set(ft)
fpos = ps_from.positions
for tt in self.to_cell_types:
ps_to = self.scaffold.get_placement_set(tt)
tpos = ps_to.positions
pairw_dist = dist.cdist(fpos, tpos)
pairs = ((pairw_dist <= max) & (pairw_dist >= min)).nonzero()
# More code to convert `pairs` into a Nx2 matrix of pre & post synaptic pair IDs
# ...
self.scaffold.connect_cells(f"{ft.name}_to_{tt.name}", pairs)
An example using this strategy, assuming it is importable as the my_module
module:
{
"connection_types": {
"cell_A_to_cell_B": {
"class": "my_module.ConnectBetween",
"min": 10,
"max": 15.5,
"from_cell_types": [
{
"type": "cell_A"
}
],
"to_cell_types": [
{
"type": "cell_B"
}
]
}
}
}
Configuration attributes¶
All keys present on the connection type in the configuration will be available on the
connection strategy under self.<key>
(e.g. min will become self.min
).
Additionally the scaffold object is available under self.scaffold
.
Configuration attributes will by default have the data type they have in JSON, which can
be any of int
, float
, str
, list
or dict
. This data type can be
overridden by using the class attribute casts
. Any key present in this dictionary
will use the value as a conversion function if the configuration attribute is encountered.
In this example both min and max will be converted to float
.
You can also provide your own functions or lambdas as long as they take the configuration
value as only argument:
casts = {"cake_or_pie": lambda x: "pie" if x < 10 else "cake"}
You can provide default values for configuration attributes giving the defaults
class
variable dictionary. You can also specify that certain attributes are required
to be
provided. If they occur in the defaults
dictionary the default value will be used
when no value is provided in the configuration.
Validation handling¶
The given configuration attributes can be further validated using the validate
method.
From inside the validate
method a ConfigurationError
can be thrown when the user
given values aren’t valid. This method is required, if no validation is required a noop
function should be given:
def validate(self):
pass
Connection handling¶
Inside of the connect
function the from and to cell types will be available. You can
access their placement data using self.scaffold.get_placement_set(type)
. The
properties of a PlacementSet
are expensive IO operations, cache them:
# WRONG! Will read the data from file 200 times
for i in range(100):
ps1.positions - ps2.positions
# Correct! Will read the data from file only 2 times
pos1 = ps1.positions
pos2 = ps2.Positions
for i in range(100):
pos1 - pos2
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¶
When defining a connection type under connection_types
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 connection_types
configuration, you can define the subpopulation label:
{
"connection_types": {
"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 connection_types
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:
{
"connection_types": {
"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:
{
"connection_types": {
"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