Skip to content

Commit 9ec48d7

Browse files
authored
Merge pull request #4167 from samuelgarcia/generation_ajust_params
Fix bug in waveform generation + adapt parameters
2 parents aa77be0 + 8ee2627 commit 9ec48d7

19 files changed

Lines changed: 123 additions & 81 deletions

File tree

examples/tutorials/comparison/generate_erroneous_sorting.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def generate_erroneous_sorting():
4444
units_err = {}
4545

4646
# sorting_true have 10 units
47-
np.random.seed(0)
47+
# np.random.seed(0)
48+
rng = np.random.default_rng(seed=0)
4849

4950
# unit 1 2 are perfect
5051
for u in [1, 2]:
@@ -54,29 +55,29 @@ def generate_erroneous_sorting():
5455
# unit 3 4 (medium) 10 (low) have medium to low agreement
5556
for u, score in [(3, 0.8), (4, 0.75), (10, 0.3)]:
5657
st = sorting_true.get_unit_spike_train(u)
57-
st = np.sort(np.random.choice(st, size=int(st.size * score), replace=False))
58+
st = np.sort(rng.choice(st, size=int(st.size * score), replace=False))
5859
units_err[u] = st
5960

6061
# unit 5 6 are over merge
6162
st5 = sorting_true.get_unit_spike_train(5)
6263
st6 = sorting_true.get_unit_spike_train(6)
6364
st = np.unique(np.concatenate([st5, st6]))
64-
st = np.sort(np.random.choice(st, size=int(st.size * 0.7), replace=False))
65+
st = np.sort(rng.choice(st, size=int(st.size * 0.7), replace=False))
6566
units_err[56] = st
6667

6768
# unit 7 is over split in 2 part
6869
st7 = sorting_true.get_unit_spike_train(7)
6970
st70 = st7[::2]
7071
units_err[70] = st70
7172
st71 = st7[1::2]
72-
st71 = np.sort(np.random.choice(st71, size=int(st71.size * 0.9), replace=False))
73+
st71 = np.sort(rng.choice(st71, size=int(st71.size * 0.9), replace=False))
7374
units_err[71] = st71
7475

7576
# unit 8 is redundant 3 times
7677
st8 = sorting_true.get_unit_spike_train(8)
77-
st80 = np.sort(np.random.choice(st8, size=int(st8.size * 0.65), replace=False))
78-
st81 = np.sort(np.random.choice(st8, size=int(st8.size * 0.6), replace=False))
79-
st82 = np.sort(np.random.choice(st8, size=int(st8.size * 0.55), replace=False))
78+
st80 = np.sort(rng.choice(st8, size=int(st8.size * 0.65), replace=False))
79+
st81 = np.sort(rng.choice(st8, size=int(st8.size * 0.6), replace=False))
80+
st82 = np.sort(rng.choice(st8, size=int(st8.size * 0.55), replace=False))
8081
units_err[80] = st80
8182
units_err[81] = st81
8283
units_err[82] = st82

src/spikeinterface/core/generate.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def generate_sorting_to_inject(
296296
injected_spike_train = injected_spike_train[~violations]
297297

298298
if len(injected_spike_train) > n_injection:
299-
injected_spike_train = np.sort(np.random.choice(injected_spike_train, n_injection, replace=False))
299+
injected_spike_train = np.sort(rng.choice(injected_spike_train, n_injection, replace=False))
300300

301301
injected_spike_trains[segment_index][unit_id] = injected_spike_train
302302

@@ -1519,7 +1519,7 @@ def exp_growth(start_amp, end_amp, duration_ms, tau_ms, sampling_frequency, flip
15191519
return y[:-1]
15201520

15211521

1522-
def get_ellipse(positions, center, b=1, c=1, x_angle=0, y_angle=0, z_angle=0):
1522+
def get_ellipse(positions, center, x_factor=1, y_factor=1, x_angle=0, y_angle=0, z_angle=0):
15231523
"""
15241524
Compute the distances to a particular ellipsoid in order to take into account
15251525
spatial inhomogeneities while generating the template. In a carthesian, centered
@@ -1537,7 +1537,7 @@ def get_ellipse(positions, center, b=1, c=1, x_angle=0, y_angle=0, z_angle=0):
15371537
z - z0
15381538
15391539
In this new space, we can compute the radius of the ellipsoidal shape given the same formula
1540-
R = X**2 + (Y/b)**2 + (Z/c)**2
1540+
R = (X/x_factor)**2 + (Y/y_factor)**2 + (Z/1)**2
15411541
15421542
and thus obtain putative amplitudes given the ellipsoidal projections. Note that in case of a=b=1 and
15431543
no rotation, the distance is the same as the euclidean distance
@@ -1555,7 +1555,7 @@ def get_ellipse(positions, center, b=1, c=1, x_angle=0, y_angle=0, z_angle=0):
15551555
Rx = np.zeros((3, 3))
15561556
Rx[0, 0] = 1
15571557
Rx[1, 1] = np.cos(-x_angle)
1558-
Rx[1, 0] = -np.sin(-x_angle)
1558+
Rx[1, 2] = -np.sin(-x_angle)
15591559
Rx[2, 1] = np.sin(-x_angle)
15601560
Rx[2, 2] = np.cos(-x_angle)
15611561

@@ -1573,10 +1573,12 @@ def get_ellipse(positions, center, b=1, c=1, x_angle=0, y_angle=0, z_angle=0):
15731573
Rz[1, 0] = np.sin(-z_angle)
15741574
Rz[1, 1] = np.cos(-z_angle)
15751575

1576-
inv_matrix = np.dot(Rx, Ry, Rz)
1577-
P = np.dot(inv_matrix, p)
1576+
rot_matrix = Rx @ Ry @ Rz
1577+
P = rot_matrix @ p
15781578

1579-
return np.sqrt(P[0] ** 2 + (P[1] / b) ** 2 + (P[2] / c) ** 2)
1579+
distances = np.sqrt((P[0] / x_factor) ** 2 + (P[1] / y_factor) ** 2 + (P[2] / 1) ** 2)
1580+
1581+
return distances
15801582

15811583

15821584
def generate_single_fake_waveform(
@@ -1632,7 +1634,10 @@ def generate_single_fake_waveform(
16321634
smooth_kernel = np.exp(-(bins**2) / (2 * smooth_size**2))
16331635
smooth_kernel /= np.sum(smooth_kernel)
16341636
# smooth_kernel = smooth_kernel[4:]
1637+
old_max = np.max(np.abs(wf))
16351638
wf = np.convolve(wf, smooth_kernel, mode="same")
1639+
new_max = np.max(np.abs(wf))
1640+
wf *= old_max / new_max
16361641

16371642
# ensure the the peak to be extatly at nbefore (smooth can modify this)
16381643
ind = np.argmin(wf)
@@ -1653,13 +1658,10 @@ def generate_single_fake_waveform(
16531658
recovery_ms=(1.0, 1.5),
16541659
positive_amplitude=(0.1, 0.25),
16551660
smooth_ms=(0.03, 0.07),
1656-
spatial_decay=(20, 40),
1661+
spatial_decay=(10.0, 45.0),
16571662
propagation_speed=(250.0, 350.0), # um / ms
1658-
b=(0.1, 1),
1659-
c=(0.1, 1),
1660-
x_angle=(0, np.pi),
1661-
y_angle=(0, np.pi),
1662-
z_angle=(0, np.pi),
1663+
ellipse_shrink=(0.4, 1),
1664+
ellipse_angle=(0, np.pi * 2),
16631665
)
16641666

16651667

@@ -1813,21 +1815,21 @@ def generate_templates(
18131815
distances = get_ellipse(
18141816
channel_locations,
18151817
units_locations[u],
1816-
1,
1817-
1,
1818-
0,
1819-
0,
1820-
0,
1818+
x_factor=1,
1819+
y_factor=1,
1820+
x_angle=0,
1821+
y_angle=0,
1822+
z_angle=0,
18211823
)
18221824
elif mode == "ellipsoid":
18231825
distances = get_ellipse(
18241826
channel_locations,
18251827
units_locations[u],
1826-
params["b"][u],
1827-
params["c"][u],
1828-
params["x_angle"][u],
1829-
params["y_angle"][u],
1830-
params["z_angle"][u],
1828+
x_factor=1,
1829+
y_factor=params["ellipse_shrink"][u],
1830+
x_angle=0,
1831+
y_angle=0,
1832+
z_angle=params["ellipse_angle"][u],
18311833
)
18321834

18331835
channel_factors = alpha * np.exp(-distances / spatial_decay)
@@ -2166,7 +2168,7 @@ def _generate_multimodal(rng, size, num_modes, lim0, lim1):
21662168
sigma = mode_step / 5.0
21672169
prob += np.exp(-((bins - center) ** 2) / (2 * sigma**2))
21682170
prob /= np.sum(prob)
2169-
choices = np.random.choice(np.arange(bins.size), size, p=prob)
2171+
choices = rng.choice(np.arange(bins.size), size, p=prob)
21702172
values = bins[choices] + rng.uniform(low=-bin_step / 2, high=bin_step / 2, size=size)
21712173
return values
21722174

src/spikeinterface/curation/curation_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def _find_duplicated_spikes_numpy(
4646

4747
def _find_duplicated_spikes_random(spike_train: np.ndarray, censored_period: int, seed: int) -> np.ndarray:
4848
# random seed
49-
rng = np.random.RandomState(seed=seed)
49+
rng = np.random.default_rng(seed=seed)
5050

5151
indices_of_duplicates = []
5252
while not np.all(np.diff(np.delete(spike_train, indices_of_duplicates)) > censored_period):

src/spikeinterface/curation/tests/test_sortingview_curation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ def test_label_inheritance_str():
212212
num_timepoints = int(sampling_frequency * duration)
213213
num_spikes = 1000
214214
times = np.int_(np.sort(np.random.uniform(0, num_timepoints, num_spikes)))
215-
labels = np.random.choice(["a", "b", "c", "d", "e", "f", "g"], size=num_spikes)
215+
rng = np.random.default_rng(seed=None)
216+
labels = rng.choice(["a", "b", "c", "d", "e", "f", "g"], size=num_spikes)
216217

217218
sorting = se.NumpySorting.from_samples_and_labels(times, labels, sampling_frequency)
218219
# print(f"Sorting: {sorting.get_unit_ids()}")

src/spikeinterface/generation/drifting_generator.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
generate_sorting,
1919
generate_templates,
2020
_ensure_unit_params,
21+
_ensure_seed,
2122
)
2223
from .drift_tools import DriftingTemplates, make_linear_displacement, InjectDriftingTemplatesRecording
2324
from .noise_tools import generate_noise
@@ -136,7 +137,7 @@ def make_one_displacement_vector(
136137

137138
min_bump_interval, max_bump_interval = bump_interval_s
138139

139-
rg = np.random.RandomState(seed=seed)
140+
rg = np.random.default_rng(seed=seed)
140141
diff = rg.uniform(min_bump_interval, max_bump_interval, size=int(duration / min_bump_interval))
141142
bumps_times = np.cumsum(diff) + t_start_drift
142143
bumps_times = bumps_times[bumps_times < t_end_drift]
@@ -152,8 +153,8 @@ def make_one_displacement_vector(
152153
displacement_vector[ind0:ind1] = -0.5
153154

154155
elif drift_mode == "random_walk":
155-
rg = np.random.RandomState(seed=seed)
156-
steps = rg.random_integers(low=0, high=1, size=num_samples)
156+
rg = np.random.default_rng(seed=seed)
157+
steps = rg.integers(low=0, high=1, size=num_samples, endpoint=True)
157158
steps = steps.astype("float64")
158159
# 0 -> -1 and 1 -> 1
159160
steps = steps * 2 - 1
@@ -340,12 +341,14 @@ def generate_drifting_recording(
340341
ms_after=3.0,
341342
mode="ellipsoid",
342343
unit_params=dict(
343-
alpha=(150.0, 500.0),
344+
alpha=(100.0, 500.0),
344345
spatial_decay=(10, 45),
346+
ellipse_shrink=(0.4, 1),
347+
ellipse_angle=(0, np.pi * 2),
345348
),
346349
),
347350
generate_sorting_kwargs=dict(firing_rates=(2.0, 8.0), refractory_period_ms=4.0),
348-
generate_noise_kwargs=dict(noise_levels=(12.0, 15.0), spatial_decay=25.0),
351+
generate_noise_kwargs=dict(noise_levels=(6.0, 8.0), spatial_decay=25.0),
349352
extra_outputs=False,
350353
seed=None,
351354
):
@@ -400,6 +403,9 @@ def generate_drifting_recording(
400403
401404
This can be helpfull for motion benchmark.
402405
"""
406+
407+
seed = _ensure_seed(seed)
408+
403409
# probe
404410
if generate_probe_kwargs is None:
405411
generate_probe_kwargs = _toy_probes[probe_name]

src/spikeinterface/generation/splitting_tools.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def split_sorting_by_times(
3131
"""
3232

3333
sorting = sorting_analyzer.sorting
34-
rng = np.random.RandomState(seed)
34+
rng = np.random.default_rng(seed)
3535
fs = sorting_analyzer.sampling_frequency
3636

3737
nb_splits = int(splitting_probability * len(sorting.unit_ids))
@@ -60,7 +60,7 @@ def split_sorting_by_times(
6060
ind_mask = spike_indices[0][unit_id]
6161
m = np.median(spikes[0][ind_mask]["sample_index"])
6262
time_mask = spikes[0][ind_mask]["sample_index"] > m
63-
mask = time_mask & (rng.rand(len(ind_mask)) <= partial_split_prob).astype(bool)
63+
mask = time_mask & (rng.uniform(size=len(ind_mask)) <= partial_split_prob).astype(bool)
6464
new_index = int(unit_id) * np.ones(len(mask), dtype=bool)
6565
new_index[mask] = max_index + 1
6666
new_spikes["unit_index"][ind_mask] = new_index
@@ -102,7 +102,7 @@ def split_sorting_by_amplitudes(
102102
if sorting_analyzer.get_extension("spike_amplitudes") is None:
103103
sorting_analyzer.compute("spike_amplitudes")
104104

105-
rng = np.random.RandomState(seed)
105+
rng = np.random.default_rng(seed)
106106
fs = sorting_analyzer.sampling_frequency
107107
from spikeinterface.core.template_tools import get_template_extremum_channel
108108

@@ -135,7 +135,7 @@ def split_sorting_by_amplitudes(
135135
ind_mask = spike_indices[0][unit_id]
136136
thresh = np.median(amplitudes[ind_mask])
137137
amplitude_mask = amplitudes[ind_mask] > thresh
138-
mask = amplitude_mask & (rng.rand(len(ind_mask)) <= partial_split_prob).astype(bool)
138+
mask = amplitude_mask & (rng.uniform(size=len(ind_mask)) <= partial_split_prob).astype(bool)
139139
new_index = int(unit_id) * np.ones(len(mask))
140140
new_index[mask] = max_index + 1
141141
new_spikes["unit_index"][ind_mask] = new_index

src/spikeinterface/preprocessing/tests/test_astype.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212

1313
def test_astype():
14-
rng = np.random.RandomState(0)
15-
traces = (rng.randn(10000, 4) * 100).astype("float32")
14+
rng = np.random.default_rng(0)
15+
traces = (rng.normal(size=(10000, 4)) * 100).astype("float32")
1616
rec_float32 = NumpyRecording(traces, sampling_frequency=30000)
1717
traces_int16 = traces.astype("int16")
1818
np.testing.assert_array_equal(traces_int16, astype(rec_float32, "int16", round=False).get_traces())

src/spikeinterface/preprocessing/tests/test_detect_bad_channels.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,8 @@ def test_detect_bad_channels_ibl(num_channels):
168168
recording.set_channel_offsets(0)
169169

170170
# Generate random channels to be dead / noisy
171-
is_bad = np.random.choice(
172-
np.arange(num_channels - 3), size=np.random.randint(5, int(num_channels * 0.25)), replace=False
173-
)
171+
rng = np.random.default_rng(seed=None)
172+
is_bad = rng.choice(np.arange(num_channels - 3), size=np.random.randint(5, int(num_channels * 0.25)), replace=False)
174173
is_noisy, is_dead = np.array_split(is_bad, 2)
175174
not_noisy = np.delete(np.arange(num_channels), is_noisy)
176175

@@ -230,8 +229,9 @@ def test_detect_bad_channels_ibl(num_channels):
230229
assert np.array_equal(recording.ids_to_indices(bad_channel_ids), np.where(bad_channel_labels_ibl != 0)[0])
231230

232231
# Test on randomly sorted channels
232+
rng = np.random.default_rng(seed=None)
233233
recording_scrambled = recording.channel_slice(
234-
np.random.choice(recording.channel_ids, len(recording.channel_ids), replace=False)
234+
rng.choice(recording.channel_ids, len(recording.channel_ids), replace=False)
235235
)
236236
bad_channel_ids_scrambled, bad_channel_label_scrambled = detect_bad_channels(
237237
recording_scrambled,

src/spikeinterface/preprocessing/tests/test_highpass_spatial_filter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def test_highpass_spatial_filter_synthetic_data(num_channels, ntr_pad, ntr_tap,
9292
options = dict(lagc=lagc, ntr_pad=ntr_pad, ntr_tap=ntr_tap, butter_kwargs=butter_kwargs)
9393

9494
durations = [2, 2]
95-
rng = np.random.RandomState(seed=100)
95+
rng = np.random.default_rng(seed=100)
9696
si_recording = generate_recording(num_channels=num_channels, durations=durations)
9797

9898
_, si_highpass_spatial_filter = run_si_highpass_filter(si_recording, get_traces=False, **options)

src/spikeinterface/preprocessing/tests/test_interpolate_bad_channels.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ def test_compare_real_data_with_ibl():
7575
)
7676

7777
num_channels = si_recording.get_num_channels()
78-
bad_channel_indexes = np.random.choice(num_channels, 10, replace=False)
78+
rng = np.random.default_rng(seed=None)
79+
bad_channel_indexes = rng.choice(num_channels, 10, replace=False)
7980
bad_channel_ids = si_recording.channel_ids[bad_channel_indexes]
8081
si_recording = spre.scale(si_recording, dtype="float32")
8182

@@ -123,12 +124,13 @@ def test_compare_input_argument_ranges_against_ibl(shanks, p, sigma_um, num_chan
123124
recording = generate_recording(num_channels=num_channels, durations=[1])
124125

125126
# distribute default probe locations across 4 shanks if set
126-
x = np.random.choice(shanks, num_channels)
127+
rng = np.random.default_rng(seed=None)
128+
x = rng.choice(shanks, num_channels)
127129
for idx, __ in enumerate(recording._properties["contact_vector"]):
128130
recording._properties["contact_vector"][idx][1] = x[idx]
129131

130132
# generate random bad channel locations
131-
bad_channel_indexes = np.random.choice(num_channels, np.random.randint(1, int(num_channels / 5)), replace=False)
133+
bad_channel_indexes = rng.choice(num_channels, rng.randint(1, int(num_channels / 5)), replace=False)
132134
bad_channel_ids = recording.channel_ids[bad_channel_indexes]
133135

134136
# Run SI and IBL interpolation and check against eachother

0 commit comments

Comments
 (0)