Skip to content

Commit 80e6b40

Browse files
committed
Merge branch 'refactor_matching' of github.com:samuelgarcia/spikeinterface into refactor_matching
2 parents f5bcb6a + 3de7cad commit 80e6b40

11 files changed

Lines changed: 102 additions & 80 deletions

File tree

src/spikeinterface/core/node_pipeline.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def compute(self, traces, start_frame, end_frame, segment_index, max_margin, *ar
8080

8181

8282
class PeakSource(PipelineNode):
83-
83+
8484
def get_trace_margin(self):
8585
raise NotImplementedError
8686

src/spikeinterface/sorters/internal/tests/test_spykingcircus2.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
from pathlib import Path
88

9+
910
class SpykingCircus2SorterCommonTestSuite(SorterCommonTestSuite, unittest.TestCase):
1011
SorterClass = Spykingcircus2Sorter
1112

1213

1314
if __name__ == "__main__":
1415
from spikeinterface import set_global_job_kwargs
16+
1517
set_global_job_kwargs(n_jobs=1, progress_bar=False)
1618
test = SpykingCircus2SorterCommonTestSuite()
1719
test.cache_folder = Path(__file__).resolve().parents[4] / "cache_folder" / "sorters"

src/spikeinterface/sorters/internal/tridesclous2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def _run_from_folder(cls, sorter_output_folder, params, verbose):
226226
matching_method = params["matching"]["method"]
227227
matching_params = params["matching"]["method_kwargs"].copy()
228228
matching_params["templates"] = templates
229-
if params["matching"]["method"] in ("tdc-peeler", ):
229+
if params["matching"]["method"] in ("tdc-peeler",):
230230
matching_params["noise_levels"] = noise_levels
231231
spikes = find_spikes_from_templates(
232232
recording_for_peeler, method=matching_method, method_kwargs=matching_params, **job_kwargs

src/spikeinterface/sortingcomponents/clustering/clustering_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ def detect_mixtures(templates, method_kwargs={}, job_kwargs={}, tmp_folder=None,
602602

603603
sub_recording = recording.frame_slice(t_start, t_stop)
604604
local_params.update({"ignore_inds": ignore_inds + [i]})
605-
605+
606606
spikes, more_outputs = find_spikes_from_templates(
607607
sub_recording, method="circus-omp-svd", method_kwargs=local_params, extra_outputs=True, **job_kwargs
608608
)

src/spikeinterface/sortingcomponents/matching/base.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@
1010
("segment_index", "int64"),
1111
]
1212

13+
1314
class BaseTemplateMatching(PeakDetector):
1415
def __init__(self, recording, templates, return_output=True, parents=None):
1516
# TODO make a sharedmem of template here
1617
# TODO maybe check that channel_id are the same with recording
1718

18-
assert isinstance(templates, Templates), (
19-
f"The templates supplied is of type {type(templates)} and must be a Templates"
20-
)
19+
assert isinstance(
20+
templates, Templates
21+
), f"The templates supplied is of type {type(templates)} and must be a Templates"
2122
self.templates = templates
2223
PeakDetector.__init__(self, recording, return_output=return_output, parents=parents)
2324

2425
def get_dtype(self):
2526
return np.dtype(_base_matching_dtype)
2627

2728
def get_trace_margin(self):
28-
raise NotImplementedError
29+
raise NotImplementedError
2930

3031
def compute(self, traces, start_frame, end_frame, segment_index, max_margin):
3132
spikes = self.compute_matching(traces, start_frame, end_frame, segment_index)
@@ -37,11 +38,11 @@ def compute(self, traces, start_frame, end_frame, segment_index, max_margin):
3738
spikes = spikes[keep]
3839

3940
# node pipeline need to return a tuple
40-
return (spikes, )
41+
return (spikes,)
4142

4243
def compute_matching(self, traces, start_frame, end_frame, segment_index):
4344
raise NotImplementedError
44-
45+
4546
def get_extra_outputs(self):
4647
# can be overwritten if need to ouput some variables with a dict
47-
return None
48+
return None

src/spikeinterface/sortingcomponents/matching/circus.py

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def compute_overlaps(templates, num_samples, num_channels, sparsities):
8888

8989
return new_overlaps
9090

91+
9192
class CircusOMPSVDPeeler(BaseTemplateMatching):
9293
"""
9394
Orthogonal Matching Pursuit inspired from Spyking Circus sorter
@@ -121,17 +122,21 @@ class CircusOMPSVDPeeler(BaseTemplateMatching):
121122
"""
122123

123124
_more_output_keys = [
124-
"norms",
125-
"temporal",
126-
"spatial",
127-
"singular",
128-
"units_overlaps",
129-
"unit_overlaps_indices",
130-
"normed_templates",
131-
"overlaps",
132-
]
133-
134-
def __init__(self, recording, return_output=True, parents=None,
125+
"norms",
126+
"temporal",
127+
"spatial",
128+
"singular",
129+
"units_overlaps",
130+
"unit_overlaps_indices",
131+
"normed_templates",
132+
"overlaps",
133+
]
134+
135+
def __init__(
136+
self,
137+
recording,
138+
return_output=True,
139+
parents=None,
135140
templates=None,
136141
amplitudes=[0.6, np.inf],
137142
stop_criteria="max_failures",
@@ -142,7 +147,7 @@ def __init__(self, recording, return_output=True, parents=None,
142147
ignore_inds=[],
143148
vicinity=3,
144149
precomputed=None,
145-
):
150+
):
146151

147152
BaseTemplateMatching.__init__(self, recording, templates, return_output=True, parents=None)
148153

@@ -169,7 +174,6 @@ def __init__(self, recording, return_output=True, parents=None,
169174
assert precomputed[key] is not None, "If templates are provided, %d should also be there" % key
170175
setattr(self, key, precomputed[key])
171176

172-
173177
self.ignore_inds = np.array(ignore_inds)
174178

175179
self.unit_overlaps_tables = {}
@@ -182,7 +186,6 @@ def __init__(self, recording, return_output=True, parents=None,
182186
else:
183187
self.margin = 2 * self.num_samples
184188

185-
186189
def _prepare_templates(self):
187190

188191
assert self.stop_criteria in ["max_failures", "omp_min_sps", "relative_error"]
@@ -256,11 +259,8 @@ def get_extra_outputs(self):
256259
output[key] = getattr(self, key)
257260
return output
258261

259-
260-
261-
262262
def get_trace_margin(self):
263-
return self.margin
263+
return self.margin
264264

265265
def compute_matching(self, traces, start_frame, end_frame, segment_index):
266266
import scipy.spatial
@@ -468,10 +468,8 @@ def compute_matching(self, traces, start_frame, end_frame, segment_index):
468468
if spikes.size > 0:
469469
order = np.argsort(spikes["sample_index"])
470470
spikes = spikes[order]
471-
472-
return spikes
473-
474471

472+
return spikes
475473

476474

477475
class CircusPeeler(BaseTemplateMatching):
@@ -519,19 +517,23 @@ class CircusPeeler(BaseTemplateMatching):
519517
520518
521519
"""
522-
def __init__(self, recording, return_output=True, parents=None,
523-
524-
templates=None,
525-
peak_sign="neg",
526-
exclude_sweep_ms=0.1,
527-
jitter_ms=0.1,
528-
detect_threshold=5,
529-
noise_levels=None,
530-
random_chunk_kwargs={},
531-
max_amplitude=1.5,
532-
min_amplitude=0.5,
533-
use_sparse_matrix_threshold=0.25,
534-
):
520+
521+
def __init__(
522+
self,
523+
recording,
524+
return_output=True,
525+
parents=None,
526+
templates=None,
527+
peak_sign="neg",
528+
exclude_sweep_ms=0.1,
529+
jitter_ms=0.1,
530+
detect_threshold=5,
531+
noise_levels=None,
532+
random_chunk_kwargs={},
533+
max_amplitude=1.5,
534+
min_amplitude=0.5,
535+
use_sparse_matrix_threshold=0.25,
536+
):
535537

536538
BaseTemplateMatching.__init__(self, recording, templates, return_output=True, parents=None)
537539

@@ -544,7 +546,9 @@ def __init__(self, recording, return_output=True, parents=None,
544546

545547
assert HAVE_SKLEARN, "CircusPeeler needs sklearn to work"
546548

547-
assert (use_sparse_matrix_threshold >= 0) and (use_sparse_matrix_threshold <= 1), f"use_sparse_matrix_threshold should be in [0, 1]"
549+
assert (use_sparse_matrix_threshold >= 0) and (
550+
use_sparse_matrix_threshold <= 1
551+
), f"use_sparse_matrix_threshold should be in [0, 1]"
548552

549553
self.num_channels = recording.get_num_channels()
550554
self.num_samples = templates.num_samples
@@ -580,8 +584,6 @@ def __init__(self, recording, return_output=True, parents=None,
580584
self.margin = max(self.nbefore, self.nafter) * 2
581585
self.peak_sign = peak_sign
582586

583-
584-
585587
def _prepare_templates(self):
586588
import scipy.spatial
587589
import scipy
@@ -617,7 +619,6 @@ def _prepare_templates(self):
617619
def get_trace_margin(self):
618620
return self.margin
619621

620-
621622
def compute_matching(self, traces, start_frame, end_frame, segment_index):
622623

623624
neighbor_window = self.num_samples - 1
@@ -702,4 +703,3 @@ def compute_matching(self, traces, start_frame, end_frame, segment_index):
702703
spikes = spikes[order]
703704

704705
return spikes
705-

src/spikeinterface/sortingcomponents/matching/main.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from spikeinterface.core.node_pipeline import run_node_pipeline
1111

1212

13-
1413
def find_spikes_from_templates(
1514
recording, method="naive", method_kwargs={}, extra_outputs=False, verbose=False, **job_kwargs
1615
) -> np.ndarray | tuple[np.ndarray, dict]:

src/spikeinterface/sortingcomponents/matching/naive.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,23 @@
88
from spikeinterface.sortingcomponents.peak_detection import DetectPeakLocallyExclusive
99

1010

11-
1211
from .base import BaseTemplateMatching, _base_matching_dtype
1312

13+
1414
class NaiveMatching(BaseTemplateMatching):
15-
def __init__(self, recording, return_output=True, parents=None,
15+
def __init__(
16+
self,
17+
recording,
18+
return_output=True,
19+
parents=None,
1620
templates=None,
1721
peak_sign="neg",
1822
exclude_sweep_ms=0.1,
1923
detect_threshold=5,
2024
noise_levels=None,
21-
radius_um=100.,
25+
radius_um=100.0,
2226
random_chunk_kwargs={},
23-
):
27+
):
2428

2529
BaseTemplateMatching.__init__(self, recording, templates, return_output=True, parents=None)
2630

@@ -37,15 +41,13 @@ def __init__(self, recording, return_output=True, parents=None,
3741
self.nafter = self.templates.nafter
3842
self.margin = max(self.nbefore, self.nafter)
3943

40-
4144
def get_trace_margin(self):
4245
return self.margin
4346

44-
4547
def compute_matching(self, traces, start_frame, end_frame, segment_index):
4648

4749
if self.margin > 0:
48-
peak_traces = traces[self.margin:-self.margin, :]
50+
peak_traces = traces[self.margin : -self.margin, :]
4951
else:
5052
peak_traces = traces
5153
peak_sample_ind, peak_chan_ind = DetectPeakLocallyExclusive.detect_peaks(
@@ -70,4 +72,3 @@ def compute_matching(self, traces, start_frame, end_frame, segment_index):
7072
spikes["amplitude"][i] = 0.0
7173

7274
return spikes
73-

src/spikeinterface/sortingcomponents/matching/tdc.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,29 @@ class TridesclousPeeler(BaseTemplateMatching):
3737
This method is quite fast but don't give exelent results to resolve
3838
spike collision when templates have high similarity.
3939
"""
40-
def __init__(self, recording, return_output=True, parents=None,
40+
41+
def __init__(
42+
self,
43+
recording,
44+
return_output=True,
45+
parents=None,
4146
templates=None,
4247
peak_sign="neg",
4348
peak_shift_ms=0.2,
4449
detect_threshold=5,
4550
noise_levels=None,
46-
radius_um=100.,
51+
radius_um=100.0,
4752
num_closest=5,
4853
sample_shift=3,
4954
ms_before=0.8,
5055
ms_after=1.2,
5156
num_peeler_loop=2,
5257
num_template_try=1,
53-
):
58+
):
5459

5560
BaseTemplateMatching.__init__(self, recording, templates, return_output=True, parents=None)
5661

57-
# maybe in base?
62+
# maybe in base?
5863
self.templates_array = templates.get_dense_templates()
5964

6065
unit_ids = templates.unit_ids
@@ -64,7 +69,7 @@ def __init__(self, recording, return_output=True, parents=None,
6469

6570
self.nbefore = templates.nbefore
6671
self.nafter = templates.nafter
67-
72+
6873
self.peak_sign = peak_sign
6974

7075
nbefore_short = int(ms_before * sr / 1000.0)
@@ -103,6 +108,7 @@ def __init__(self, recording, return_output=True, parents=None,
103108

104109
# distance between units
105110
import scipy
111+
106112
unit_distances = scipy.spatial.distance.cdist(unit_locations, unit_locations, metric="euclidean")
107113

108114
# seach for closet units and unitary discriminant vector
@@ -111,7 +117,7 @@ def __init__(self, recording, return_output=True, parents=None,
111117
order = np.argsort(unit_distances[unit_ind, :])
112118
closest_u = np.arange(unit_ids.size)[order].tolist()
113119
closest_u.remove(unit_ind)
114-
closest_u = np.array(closest_u[: num_closest])
120+
closest_u = np.array(closest_u[:num_closest])
115121

116122
# compute unitary discriminent vector
117123
(chans,) = np.nonzero(self.template_sparsity[unit_ind, :])
@@ -298,10 +304,8 @@ def _find_spikes_one_level(self, traces, level=0):
298304

299305
spikes["cluster_index"][i] = cluster_index
300306
spikes["amplitude"][i] = amplitude
301-
302-
return spikes
303-
304307

308+
return spikes
305309

306310

307311
if HAVE_NUMBA:
@@ -346,6 +350,3 @@ def numba_best_shift(traces, template, sample_index, nbefore, possible_shifts, d
346350
distances_shift[i] = sum_dist
347351

348352
return distances_shift
349-
350-
351-

0 commit comments

Comments
 (0)