Skip to content

Conversation

@yger
Copy link
Collaborator

@yger yger commented Nov 13, 2025

This implements the use of sparsity to compute templates in the Analyzer. Currently, analzers can handle sparsity mask, and while they are used to compute waveforms, they are not used to estimate templates. This can lead to large memory usage with numerous threads. This PR makes sure the analyzer, if sparse, uses the sparsity masks while estimating templates ONLY in the run() method. For compatibility with downstream extensions, the internal storage is kept dense*

See #4194

@yger yger added enhancement New feature or request core Changes to core module labels Nov 13, 2025
@yger
Copy link
Collaborator Author

yger commented Nov 14, 2025

Kudo for the code rewriting... All tests are failing :-)

@samuelgarcia
Copy link
Member

@alejoe91 @chrishalcrow : this will help a lot ith very high channel count (in vitro mea).
Have a look.

@alejoe91 alejoe91 added this to the 0.103.2 milestone Nov 27, 2025
@samuelgarcia
Copy link
Member

@alejoe91 we need to squash

)
for unit_index, unit_id in enumerate(self.sorting_analyzer.unit_ids):
chan_inds = self.sparsity.unit_id_to_channel_indices[unit_id]
dense_arr[unit_index][:, chan_inds] = arr[unit_index, :, : chan_inds.size]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the exact same logic in ChannelSparsity.densify_waveforms:

My proposal is to add a densify_templates function to the ChannelSparsity class (it already has a sparsify_templates):

def densify_templates(self, templates_array: np.ndarray) -> np.ndarray:
    assert templates_array.shape[0] == self.num_units

    densified_shape = (self.num_units, templates_array.shape[1], self.num_channels)
    dense_templates = np.zeros(shape=densified_shape, dtype=templates_array.dtype)
    for unit_index, unit_id in enumerate(self.unit_ids):
        sparse_template = templates_array[unit_index, ...]
        dense_template = self.densify_waveforms(waveforms=sparse_template[np.newaxis, :, :], unit_id=unit_id)
        dense_templates[unit_index, :, :] = dense_template

    return dense_templates

Then the logic in the ComputeTemplates extension could simply become:

if self.sparsity is not None:
    # make average and std dense again
    for k, arr in data.items():
        dense_arr = self.sparsity.densify_templates(arr)
        data[k] = dense_arr

What do you think?

Copy link
Member

@alejoe91 alejoe91 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use the ChannelSparsity methods

@chrishalcrow chrishalcrow merged commit d019d64 into SpikeInterface:main Dec 3, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Changes to core module enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants