Skip to content

Commit 007b64d

Browse files
committed
Allow use_binary_file=None (default) and add delete_recording_dat param
1 parent 10b7e1a commit 007b64d

2 files changed

Lines changed: 68 additions & 28 deletions

File tree

.github/scripts/test_kilosort4_ci.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -415,27 +415,46 @@ def test_use_binary_file(self, tmp_path):
415415
sorting_ks4 = si.run_sorter(
416416
"kilosort4",
417417
recording,
418-
folder=tmp_path / "spikeinterface_output_dir_wrapper",
419-
use_binary_file=False,
418+
folder=tmp_path / "ks4_output_si_wrapper_default",
419+
use_binary_file=None,
420420
remove_existing_folder=True,
421421
)
422422
sorting_ks4_bin = si.run_sorter(
423423
"kilosort4",
424424
recording_bin,
425-
folder=tmp_path / "spikeinterface_output_dir_bin",
425+
folder=tmp_path / "ks4_output_bin_default",
426+
use_binary_file=None,
427+
remove_existing_folder=True,
428+
)
429+
sorting_ks4_force_binary = si.run_sorter(
430+
"kilosort4",
431+
recording,
432+
folder=tmp_path / "ks4_output_force_bin",
433+
use_binary_file=True,
434+
remove_existing_folder=True,
435+
)
436+
assert not (tmp_path / "ks4_output_force_bin" / "sorter_output" / "recording.dat").exists()
437+
sorting_ks4_force_non_binary = si.run_sorter(
438+
"kilosort4",
439+
recording_bin,
440+
folder=tmp_path / "ks4_output_force_wrapper",
426441
use_binary_file=False,
427442
remove_existing_folder=True,
428443
)
429-
sorting_ks4_non_bin = si.run_sorter(
444+
# test deleting recording.dat
445+
sorting_ks4_force_binary_keep = si.run_sorter(
430446
"kilosort4",
431447
recording,
432-
folder=tmp_path / "spikeinterface_output_dir_non_bin",
448+
folder=tmp_path / "ks4_output_force_bin_keep",
433449
use_binary_file=True,
450+
delete_recording_dat=False,
434451
remove_existing_folder=True,
435452
)
453+
assert (tmp_path / "ks4_output_force_bin_keep" / "sorter_output" / "recording.dat").exists()
436454

437455
check_sortings_equal(sorting_ks4, sorting_ks4_bin)
438-
check_sortings_equal(sorting_ks4, sorting_ks4_non_bin)
456+
check_sortings_equal(sorting_ks4, sorting_ks4_force_binary)
457+
check_sortings_equal(sorting_ks4, sorting_ks4_force_non_binary)
439458

440459
@pytest.mark.parametrize(
441460
"param_to_test",

src/spikeinterface/sorters/external/kilosort4.py

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class Kilosort4Sorter(BaseSorter):
6565
"save_preprocessed_copy": False,
6666
"torch_device": "auto",
6767
"bad_channels": None,
68-
"use_binary_file": False,
68+
"use_binary_file": None,
69+
"delete_recording_dat": True,
6970
}
7071

7172
_params_description = {
@@ -110,8 +111,10 @@ class Kilosort4Sorter(BaseSorter):
110111
"save_preprocessed_copy": "save a pre-processed copy of the data (including drift correction) to temp_wh.dat in the results directory and format Phy output to use that copy of the data",
111112
"torch_device": "Select the torch device auto/cuda/cpu",
112113
"bad_channels": "A list of channel indices (rows in the binary file) that should not be included in sorting. Listing channels here is equivalent to excluding them from the probe dictionary.",
113-
"use_binary_file": "If True and the recording is not binary compatible, then Kilosort is written to a binary file in the output folder. If False, the Kilosort is run on the recording object directly using the RecordingExtractorAsArray object. "
114-
"If the recording is binary compatible, then the sorter will always use the binary file. Default is False.",
114+
"use_binary_file": "If True then Kilosort is run using a binary file. In this case, if the input recording is not binaru compatible, it is written to a binary file in the output folder. "
115+
"If False then Kilosort is run on the recording object directly using the RecordingExtractorAsArray object. If None, then if the recording is binary compatible, the sorter will use the binary file, otherwise the RecordingExtractorAsArray. "
116+
"Default is None.",
117+
"delete_recording_dat": "If True, if a temporary binary file is created, it is deleted after the sorting is done. Default is True.",
115118
}
116119

117120
sorter_description = """Kilosort4 is a Python package for spike sorting on GPUs with template matching.
@@ -172,15 +175,16 @@ def _setup_recording(cls, recording, sorter_output_folder, params, verbose):
172175
probe_filename = sorter_output_folder / "probe.prb"
173176
write_prb(probe_filename, pg)
174177

175-
if params["use_binary_file"] and not recording.binary_compatible_with(time_axis=0, file_paths_lenght=1):
176-
# local copy needed
177-
binary_file_path = sorter_output_folder / "recording.dat"
178-
write_binary_recording(
179-
recording=recording,
180-
file_paths=[binary_file_path],
181-
**get_job_kwargs(params, verbose),
182-
)
183-
params["filename"] = str(binary_file_path)
178+
if params["use_binary_file"]:
179+
if not recording.binary_compatible_with(time_axis=0, file_paths_lenght=1):
180+
# local copy needed
181+
binary_file_path = sorter_output_folder / "recording.dat"
182+
write_binary_recording(
183+
recording=recording,
184+
file_paths=[binary_file_path],
185+
**get_job_kwargs(params, verbose),
186+
)
187+
params["filename"] = str(binary_file_path)
184188

185189
@classmethod
186190
def _run_from_folder(cls, sorter_output_folder, params, verbose):
@@ -227,18 +231,30 @@ def _run_from_folder(cls, sorter_output_folder, params, verbose):
227231
probe = load_probe(probe_path=probe_filename)
228232
probe_name = ""
229233

230-
if recording.binary_compatible_with(time_axis=0, file_paths_lenght=1):
231-
# no copy
232-
binary_description = recording.get_binary_description()
233-
filename = str(binary_description["file_paths"][0])
234-
file_object = None
234+
if params["use_binary_file"] is None:
235+
if recording.binary_compatible_with(time_axis=0, file_paths_lenght=1):
236+
# no copy
237+
binary_description = recording.get_binary_description()
238+
filename = str(binary_description["file_paths"][0])
239+
file_object = None
240+
else:
241+
# the recording is not binary compatible and no binary copy has been written.
242+
# in this case, we use the RecordingExtractorAsArray object
243+
filename = ""
244+
file_object = RecordingExtractorAsArray(recording_extractor=recording)
235245
elif params["use_binary_file"]:
236-
# a local copy has been written
237-
filename = str(sorter_output_folder / "recording.dat")
238-
file_object = None
246+
# here we force the use of a binary file
247+
if recording.binary_compatible_with(time_axis=0, file_paths_lenght=1):
248+
# no copy
249+
binary_description = recording.get_binary_description()
250+
filename = str(binary_description["file_paths"][0])
251+
file_object = None
252+
else:
253+
# a local copy has been written
254+
filename = str(sorter_output_folder / "recording.dat")
255+
file_object = None
239256
else:
240-
# the recording is not binary compatible and no binary copy has been written.
241-
# in this case, we use the RecordingExtractorAsArray object
257+
# here we force the use of the RecordingExtractorAsArray object
242258
filename = ""
243259
file_object = RecordingExtractorAsArray(recording_extractor=recording)
244260

@@ -362,6 +378,11 @@ def _run_from_folder(cls, sorter_output_folder, params, verbose):
362378
save_preprocessed_copy=save_preprocessed_copy,
363379
)
364380

381+
if params["delete_recording_dat"]:
382+
# only delete dat file if it was created by the wrapper
383+
if (sorter_output_folder / "recording.dat").is_file():
384+
(sorter_output_folder / "recording.dat").unlink()
385+
365386
@classmethod
366387
def _get_result_from_folder(cls, sorter_output_folder):
367388
return KilosortBase._get_result_from_folder(sorter_output_folder)

0 commit comments

Comments
 (0)