Skip to content

Add neuron selection support to SequenceInterpolator and SpikeInterpolator#126

Open
Vrittigyl wants to merge 4 commits into
sensorium-competition:mainfrom
Vrittigyl:neuron-selection
Open

Add neuron selection support to SequenceInterpolator and SpikeInterpolator#126
Vrittigyl wants to merge 4 commits into
sensorium-competition:mainfrom
Vrittigyl:neuron-selection

Conversation

@Vrittigyl

Copy link
Copy Markdown
Contributor

Description

This PR adds support for selecting specific neurons in both SequenceInterpolator and SpikeInterpolator.

Users can now pass either:

  • neuron_ids: biological neuron IDs mapped using meta/unit_ids.npy
  • indexes: direct neuron indexes

If both are provided:

  • A warning is raised if they refer to the same neurons.
  • A ValueError is raised if they refer to different neurons.

Changes

  • SequenceInterpolator

    • Added neuron filtering using neuron_ids or indexes.
    • Filters loaded data and normalization statistics to match selected neurons.
  • PhaseShiftedSequenceInterpolator

    • Filters phase_shifts to match selected neurons when neuron selection is used.
  • SpikeInterpolator

    • Added neuron filtering using neuron_ids or indexes.
    • Rebuilds the spike array and indices to include only selected neurons.

This allows loading and processing only a subset of neurons, reducing memory usage and improving flexibility when working with imaging datasets.

Closes #124

@gitnotebooks

gitnotebooks Bot commented Mar 10, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds neuron-subset selection to interpolators so callers can load/process only specific neurons by biological IDs (meta/unit_ids.npy) or by direct column/neuron indexes, reducing memory usage for large datasets (Issue #124).

Changes:

  • Extend SequenceInterpolator to accept neuron_ids / indexes and filter loaded data + normalization stats accordingly.
  • Extend PhaseShiftedSequenceInterpolator to filter phase_shifts consistently with neuron selection.
  • Extend SpikeInterpolator to accept neuron_ids / indexes and rebuild spikes/indices arrays for the selected neurons.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py
Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
@codecov

codecov Bot commented Mar 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.45455% with 26 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
experanto/interpolators.py 70.45% 26 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread experanto/interpolators.py Outdated
Comment on lines 286 to 291
@@ -230,20 +290,34 @@ def __init__(
else:
self._data = np.load(self.root_folder / "data.npy")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IMO if data gets cached, we should only cache the indexed neurons. Downside is that if someone alters self.indexes of the instance later manually, it is still relying on the old data. But I think I still prefer this. What do you think @pollytur ?

If changing indexes later is a real use case, one could handle it with a function that takes care of it and would reload the data appropriately, but I am not sure we need it right now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I’ve changed the logic so caching happens after applying neuron_indices, so only the selected subset is loaded into memory. According to me this would keep memory usage efficient for large data.
can revert changes if needed

Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
Comment thread experanto/interpolators.py Outdated
else:
self._data = np.load(self.root_folder / "data.npy")

# Apply indexing BEFORE caching

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi @pollytur
as far as I know, if one applies indexes to memmap as lists or arrays, this loads a copy into RAM (see https://stackoverflow.com/questions/18614927/how-to-slice-memmap-efficiently or https://stackoverflow.com/questions/78426050/how-to-index-a-numpy-memmap-without-creating-an-in-memory-copy)
Only regular continuous slicing creates just a view, but I don't think neuron ids will always be continuous.

Have you thought about this? We might need to set caching to True if neurons are indexed. Or we find a workaround (I haven't investigated it yet).

@pollytur pollytur Mar 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

great catch, no I have not thought about it tbh
we probably want to investigate the workaround (changing order of neurons as we need and save it as a temp memmap file is the first though but thats insanly memory inefficient since neuronal responses are also the heaviest part of the dataset from a memory perspective...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Or maybe instead of directly indexing, we could iteratively fetch only the required columns (or in chunks) and optionally cache them.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Vrittigyl could you please provide an implementation for what you proposed and show that it still caches the data if cache_data == True and doesn't cache the data otherwise, also when we select only certain neurons? Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I’ve implemented this and tested it using test functions.
When cache_data=False, _data stays as memmap and only required neuron columns are accessed during interpolation. When cache_data=True, only selected neurons are loaded into memory.
I verified this by checking data types, shapes, and comparing outputs.

Comment thread experanto/interpolators.py Outdated
@pollytur

Copy link
Copy Markdown
Contributor

@Vrittigyl could you please take a look why tests are failing now?

@Vrittigyl

Copy link
Copy Markdown
Contributor Author

@Vrittigyl could you please take a look why tests are failing now?

_data was expected to be sliced after neuron selection, but now _data stays full to support memmap.
I’ve updated the tests to check n_signals and interpolation output instead.

@pollytur

pollytur commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

@Vrittigyl please merge current main into your branch - I resolved the conflict but its not a full merge, so ruff CI / CD fails because of it

please ping me after its done and I will take a look

@Vrittigyl Vrittigyl force-pushed the neuron-selection branch 3 times, most recently from b2a1376 to f6a99a0 Compare April 13, 2026 20:09
@Vrittigyl

Copy link
Copy Markdown
Contributor Author

@Vrittigyl please merge current main into your branch - I resolved the conflict but its not a full merge, so ruff CI / CD fails because of it

please ping me after its done and I will take a look

@pollytur I’ve merged the latest main into my branch, resolved the conflicts, and fixed the CI issues. Please take a look!

@pollytur

pollytur commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

@Vrittigyl sorry for back and forward
could we please do it again (merge main into your branch) since I have just merged #113 in main and that PR added a bunch of tests which i would love to see here as well

Also you have been editing same files so merging is quite crucial here

if len(chunk) == 0:
continue
video_array = np.stack(chunk, axis=0)
video_array = np.stack(list(chunk), axis=0)

@pollytur pollytur Apr 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why do we need it?

@Vrittigyl Vrittigyl Apr 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I changed this because the previous version was causing pyright failures with np.stack when chunk wasn’t a proper sequence. Converting it to list(chunk) ensures it works consistently.

Comment thread tests/test_sequence_interpolator.py Outdated
Comment thread tests/test_sequence_interpolator.py Outdated
Comment thread tests/test_sequence_interpolator.py Outdated
Comment thread tests/test_spikes_interpolator.py Outdated
Comment thread tests/test_spikes_interpolator.py Outdated
Comment thread tests/test_spikes_interpolator.py Outdated
Comment on lines +82 to +83
if isinstance(signal, tuple):
signal = signal[0]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why do we need it here and everywhere else in this file?

logger = logging.getLogger(__name__)


def resolve_neuron_indices(neuron_ids, neuron_indices, unit_ids, n_signals):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

imho resolve_neuron_indices should go to utils.py
@reneburghardt what would you say?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes fine for me

return validate_neuron_indices(neuron_indices, n_signals)


def validate_neuron_indices(neuron_indices, n_signals):

@pollytur pollytur Apr 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

imho validate_neuron_indices should go to utils.py
@reneburghardt what would you say?

I am also tempted to put some function like select_channels to SequenceInterpolator class. Because in principle we might also want to select only certain channels from eye tracker as well. And a particular case of this function would call the function from utils for the neurons (to resolve unit_ids meta to indexes and validate it)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sorry for the late reply. I'll move validate_neuron_indices to utils.py
Should I also implement select_channels in utils.py, or keep it inside SequenceInterpolator?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right now we use neuron_indices, but if we add something like select_channels for other modalities (like eye tracker), the idea is basically the same - selecting columns using indices.
So would it make sense to rename neuron_indices to channel_indices?
or is it better to keep neuron_indices separate?

Also, am I thinking about this the right way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add an option to select specific unit_ids or just indexes of neurons

4 participants