Skip to content

Conversation

@Moritz-Alexander-Kern
Copy link
Member

@Moritz-Alexander-Kern Moritz-Alexander-Kern commented Nov 15, 2024

The pull request addresses inconsistent behavior with different options in combination with various inputs, specifically:

Issue:

  • pool_spiketrains = True with list[spiketrains] does not pool spiketrains as expected. The output should be a single neo.AnalogSignal.

Fix

  • check for other types, previously only neo.spiketrainlist was regarded
  • add tests

@coveralls
Copy link
Collaborator

coveralls commented Nov 15, 2024

Coverage Status

coverage: 88.184% (+0.1%) from 88.053%
when pulling 239ea19 on INM-6:fix/instantaneous_rate
into db5a5f0 on NeuralEnsemble:master.

@Moritz-Alexander-Kern Moritz-Alexander-Kern added this to the v1.2.0 milestone Dec 10, 2024
@Moritz-Alexander-Kern Moritz-Alexander-Kern changed the title [Fix] instantaneous rate with trials [Fix] instantaneous rate with list[neo.SpikeTrain] Jan 20, 2025
@Moritz-Alexander-Kern Moritz-Alexander-Kern marked this pull request as ready for review January 20, 2025 12:47
Copy link
Contributor

@kohlerca kohlerca left a comment

Choose a reason for hiding this comment

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

I checked the code and made suggestions. Regarding the proposed solution, this needs to be investigated further to ensure the problem was only due to a wrong class membership check. According to the comment on line 1034, the check that originated the error might be misplaced and redundant.


if isinstance(spiketrains, neo.core.spiketrainlist.SpikeTrainList) and (
pool_spike_trains):
if is_list_spiketrains(spiketrains) and (pool_spike_trains):
Copy link
Contributor

Choose a reason for hiding this comment

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

Parentheses around pool_spike_trains not needed. Also, the order in the condition should be

if pool_spike_trains and is_list_spiketrains(spiketrains):

This avoids unnecessary calls to the function if not pooling (lazy evaluation of the AND statement).

Copy link
Contributor

Choose a reason for hiding this comment

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

However, analyzing the function flow, the new and old checks for a list of neo.SpikeTrain objects may not be needed at all:

  • in lines 908-911 it is ensured that there will be an Iterable with at least one spike train
  • in lines 913-916 an error will be generated if the input is not an iterable that contains only spike trains

Therefore, at this point, it would be possible to only check if pooling or not the spike trains.

pool_spike_trains=False,
pool_trials=True)
self.assertIsInstance(rate, neo.core.AnalogSignal)
self.assertEqual(rate.shape[1], self.trial_object.n_spiketrains_trial_by_trial[0])
Copy link
Contributor

Choose a reason for hiding this comment

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

This test checks for consistency between the dimensions of the computed rate and the data in the Trials objects. However, an additional test against the integer values would protect against errors in implementing the object attributes. These hard expected values are supposed to be known when designing the test data in line 493.

pool_spike_trains=True,
pool_trials=False)
self.assertIsInstance(rate, list)
self.assertEqual(len(rate), self.trial_object.n_trials)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above, where the test does not compare directly to the expected values, but retrieves them dynamically from the objects.

pool_spike_trains=False,
pool_trials=False)
self.assertIsInstance(rate, list)
self.assertEqual(len(rate), self.trial_object.n_trials)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above, where the test does not compare directly to the expected values, but retrieves them dynamically from the objects.

pool_trials=False)
self.assertIsInstance(rate, neo.core.AnalogSignal)
self.assertEqual(rate.magnitude.shape[1], 2)
self.assertEqual(rate.magnitude.shape[1], self.trial_object.n_spiketrains_trial_by_trial[0])
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above, where the test does not compare directly to the expected values, but retrieves them dynamically from the objects. The integer in the old test could be updated, and the new one just added as an additional check.

self.assertEqual(rate.shape[1], self.trial_object.n_spiketrains_trial_by_trial[0])

def test_instantaneous_rate_list_pool_spike_trains(self):
def test_instantaneous_rate_trials_pool_spiketrains(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

A suggestion to improve understanding of these unit tests, where the dimensions of the outputs change depending on the pooling, is to include a comment block at the beginning to explicitly state the input dimensions --> expected output dimensions. It would be easier to assess the behavior of the object and what is being tested in each test case.

@CozySocksAlways CozySocksAlways self-assigned this Feb 2, 2026
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.

4 participants