Skip to content

Commit 41cfb17

Browse files
kjohnsenCopilotCopilot
authored
Fix spiking functionality and sample period handling (#70)
* make spiking work when no neurons are recorded * fix bug where sample period couldn't be passed in for RWSLFPSignalFromPSCs * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Avoid mutable default for inject_kwargs in RWSLFP test helper * bump tklfp dependency --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 942fbdb commit 41cfb17

5 files changed

Lines changed: 46 additions & 12 deletions

File tree

‎cleo/ephys/lfp.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ def connect_to_neuron_group(self, neuron_group: NeuronGroup, **kwparams):
612612
self._init_wslfp_calc(neuron_group, **kwparams)
613613

614614
buf_len_ampa, buf_len_gaba = self._get_buf_lens_for_wslfp(
615-
self._wslfps[neuron_group]
615+
self._wslfps[neuron_group], **kwparams
616616
)
617617
self._t_ampa_bufs[neuron_group] = deque(maxlen=buf_len_ampa)
618618
self._I_ampa_bufs[neuron_group] = deque(maxlen=buf_len_ampa)
@@ -633,8 +633,8 @@ def _get_buf_lens_for_wslfp(self, wslfp_calc, **kwparams):
633633
try:
634634
sample_period = self.probe.sim.io_processor.sample_period
635635
except AttributeError: # probably means sim doesn't have io_processor
636-
raise Exception(
637-
"RSWLFPSignalFromPSCs needs to know the sampling period."
636+
raise RuntimeError(
637+
"RWSLFPSignalFromPSCs needs to know the sampling period."
638638
" Either set the simulator's IO processor before injecting"
639639
f" {self.probe.name} or "
640640
f" specify it on injection: .inject({self.probe.name}"

‎cleo/ephys/spiking.py‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ def connect_to_neuron_group(self, neuron_group: NeuronGroup, **kwparams):
245245
# spike sorters don't sort by multi-channel recall; we only do this
246246
# for computational efficiency, to not get super distant neurons
247247

248+
# needed even if no neurons are recorded
249+
if not self._prev_t:
250+
self._prev_t = self.probe.sim.network.t
251+
if self._mu_eap is None:
252+
self._mu_eap = np.zeros((0, self.n_channels))
253+
248254
n2keep = np.sum(where_to_keep)
249255
if n2keep == 0:
250256
warnings.warn(
@@ -271,14 +277,8 @@ def connect_to_neuron_group(self, neuron_group: NeuronGroup, **kwparams):
271277
assert len(self.i_ng_by_i_probe) == i_probe_start
272278
self.i_ng_by_i_probe.extend(list(zip([neuron_group] * n2keep, i2keep)))
273279

274-
if not self._prev_t:
275-
self._prev_t = self.probe.sim.network.t
276-
277280
# store neuron-channel mean and stdev of EAPs to use in subclasses
278-
if self._mu_eap is None:
279-
self._mu_eap = snr[where_to_keep]
280-
else:
281-
self._mu_eap = np.concatenate((self._mu_eap, snr[where_to_keep]), axis=0)
281+
self._mu_eap = np.concatenate((self._mu_eap, snr[where_to_keep]), axis=0)
282282

283283
return snr[where_to_keep].max(axis=1), new_i_probe
284284

@@ -642,7 +642,11 @@ def sorted_units_snr(self) -> Float[np.ndarray, "{self.n_sorted}"]:
642642
return self._mu_eap[self._i_probe_by_i_sorted]
643643

644644
def connect_to_neuron_group(self, neuron_group, **kwparams):
645-
snr, i_probe = super().connect_to_neuron_group(neuron_group, **kwparams)
645+
connect_result = super().connect_to_neuron_group(neuron_group, **kwparams)
646+
if connect_result is None:
647+
# No neurons with high-enough multi-channel recall to record
648+
return
649+
snr, i_probe = connect_result
646650
n_recorded_ng = len(snr)
647651
# filter by SNR (measured on peak channel)
648652
above_cutoff = snr >= self.snr_cutoff

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ dependencies = [
1818
"scipy>=1.7.2,<1.14.1; python_version<'3.13'",
1919
"scipy>=1.14.1; python_version>='3.13'",
2020
"numpy >= 2.1.0,<3.0.0; python_version>='3.13'",
21-
"tklfp>=0.2,<0.3",
21+
"tklfp>=0.3,<0.4",
2222
"attrs>=23.0,<24.0",
2323
"neo>=0.12.0,<0.13.0",
2424
"wslfp>=0.2.1,<0.3.0",

‎tests/ephys/test_lfp.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,10 @@ def add_rwslfp_sig(
369369
Iampa_var_names=["Iampa1"],
370370
Igaba_var_names=["Igaba1"],
371371
name=None,
372+
inject_kwargs=None,
372373
):
374+
if inject_kwargs is None:
375+
inject_kwargs = {}
373376
rwslfp_sig = RWSLFPSignalFromPSCs(
374377
pop_aggregate=pop_agg,
375378
amp_func=amp_func,
@@ -387,6 +390,7 @@ def add_rwslfp_sig(
387390
orientation=ornt,
388391
Iampa_var_names=Iampa_var_names,
389392
Igaba_var_names=Igaba_var_names,
393+
**inject_kwargs,
390394
)
391395
return rwslfp_sig
392396

@@ -416,6 +420,12 @@ def add_rwslfp_sig(
416420
f"{sig1.name} and {sig2.name} not yielding different results"
417421
)
418422

423+
# test missing sampling period error
424+
sim.set_io_processor(None)
425+
with pytest.raises(RuntimeError, match="needs to know the sampling period"):
426+
add_rwslfp_sig()
427+
add_rwslfp_sig(inject_kwargs={"sample_period": 5 * ms})
428+
419429

420430
def test_psc_buffer():
421431
sig = RWSLFPSignalFromPSCs()

‎tests/ephys/test_spiking.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,26 @@ def test_cutoff(rand_seed):
530530
)
531531

532532

533+
def test_no_neurons():
534+
"""When neurons are too far away to be picked up at all."""
535+
sgg = spike_generator_group([9000, 9001, 9002] * mm, period=1 * ms)
536+
sim = CLSimulator(Network(sgg))
537+
assert hasattr(sim, "network")
538+
ss = SortedSpiking()
539+
mua = MultiUnitActivity()
540+
probe = Probe([0, 0, 0] * um, [ss, mua])
541+
sim.inject(probe, sgg)
542+
sim.run(10 * ms)
543+
probe.get_state()
544+
sim.run(10 * ms)
545+
probe.get_state()
546+
for sig in [ss, mua]:
547+
assert len(sig.i) == 0
548+
assert len(sig.t) == 0
549+
assert len(sig.t_samp) == 0
550+
assert sig.n_neurons == 0
551+
552+
533553
def _test_spiking_to_neo(spike_signal_class):
534554
spike_sig = spike_signal_class()
535555
n_channels = 50

0 commit comments

Comments
 (0)