dbbs-lab / bsb-core

The Brain Scaffold Builder
https://bsb.readthedocs.io
GNU General Public License v3.0
21 stars 16 forks source link

What's up with `ConnectomeIOPurkinje`? #91

Closed Helveg closed 4 years ago

Helveg commented 4 years ago

There was a note in the docstring of the ConnectomeIOPurkinje class:

Purkinje cells are clustered (number of clusters is the number of IO cells), and each clusters is innervated by 1 IO cell

But the algorithm doesn't do any sort of clustering at all: it just did len(purkinje_cells) shuffles of io_cells and picked the first shuffled IO cell and matched it with the ith purkinje cell. (An expensive way of picking a random IO cell to connect to each Purkinje cell)

So I went looking and found this other implementation for this strategy:

class ConnectomeIOPurkinje(ConnectionStrategy):
    '''
        Legacy implementation for the connection between inferior olive and Purkinje cells.
        Purkinje cells are clustered (number of clusters is the number of IO cells), and each clusters
        is innervated by 1 IO cell
    '''

    required = ['divergence']

    def validate(self):
        pass

    def connect(self):
        io_cell_type = self.from_cell_types[0]
        purkinje_cell_type = self.to_cell_types[0]
        io_cells = self.scaffold.cells_by_type[io_cell_type.name]
        purkinje_cells = self.scaffold.cells_by_type[purkinje_cell_type.name]
        convergence = 1             # Purkinje cells should be always constrained to receive signal from only 1 Inferior Olive neuron
        divergence = self.divergence
        tolerance = self.tolerance_divergence

        def connectome_io_purkinje(io_cells, purkinje_cells, div_io):

            ## TODO: Check divergence, number of io_cells and number of purkinje_cells consistency

            number_clusters = len(io_cells)
            kmeans = KMeans(n_clusters=number_clusters).fit(purkinje_cells[:,2:4])
            label_clusters = kmeans.labels_
            target_clusters = {i: np.where(kmeans.labels_ == i)[0] for i in range(kmeans.n_clusters)}
            io_purkinje = np.empty([len(purkinje_cells), 2])
            mi = 0
            for io in range(len(io_cells)):
                target_purkinje_ids = purkinje_cells[target_clusters[io], 0]
                io_ids = np.repeat(io,len(target_clusters[io]))
                nmi = mi + len(target_purkinje_ids)
                io_purkinje[mi:nmi] = np.column_stack((io_ids, target_purkinje_ids))
                mi = nmi
            return io_purkinje

        results = connectome_io_purkinje(io_cells, purkinje_cells, divergence)
        self.scaffold.connect_cells(self, results)

This code does include these clusters. It seems to be mostly your commits @AliceGem, do you remember the story here? Is there a reason that the clusters were removed or is this by accident?

AliceGem commented 4 years ago

@alberto-antonietti we had decided to put back the clustering method? I can't find notes on this, so I don't remember if I should have put it back or not (very poor memory......)

alberto-antonietti commented 4 years ago

If I remember correctly, we decided to leave out the clustering because it was causing problems with the labelling (positive and negative microzones). We wanted to be sure that positive PCs were connected to positive IOs. In addition, we decided to cut out the clustering of PCs since it did not have a specific biological counterpart.

Helveg commented 4 years ago

So, conclusion, the current algorithm that selects 1 random IO per PC is fine?

AliceGem commented 4 years ago

Yes, 1 random PC within the same microzone, to have the right divergence value