Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1f6c639
ENH: Add private RNG normalizer
bruAristimunha Aug 24, 2026
c519f50
ENH: Add rng to permutation helpers
bruAristimunha Aug 24, 2026
141a14d
ENH: Add rng to cluster permutation tests
bruAristimunha Aug 24, 2026
5de77f2
ENH: Add rng to epoch sampling
bruAristimunha Aug 24, 2026
d80f382
ENH: Add rng to simulation helpers
bruAristimunha Aug 24, 2026
6a77759
ENH: Add rng to label sampling
bruAristimunha Aug 24, 2026
a41de38
ENH: Add rng to ICA
bruAristimunha Aug 24, 2026
062a3c6
ENH: Add rng to sparse inverse solvers
bruAristimunha Aug 24, 2026
dde920f
MAINT: Make random state explicit
bruAristimunha Aug 24, 2026
21efb67
DOC: Use rng in examples
bruAristimunha Aug 24, 2026
a95af3a
MAINT Preserve legacy RNG fixtures
bruAristimunha Aug 24, 2026
5d540a6
DOC Preserve solver rationale comments
bruAristimunha Aug 24, 2026
d17aae8
DOC Preserve permutation rationale
bruAristimunha Aug 24, 2026
8e092a5
TEST: Cover explicit RNG behavior
bruAristimunha Aug 24, 2026
f237da1
FIX: Preserve legacy RNG transition behavior
bruAristimunha Aug 24, 2026
59bae3b
FIX: Honor Infomax verbosity and RNG aliases
bruAristimunha Aug 24, 2026
6cb0b17
MAINT: Allow lazy simulation API
bruAristimunha Aug 24, 2026
f587781
FIX: Preserve integer RNG seeds at ICA boundaries
bruAristimunha Aug 24, 2026
ac4da16
Merge branch 'main' into rng-foundation
bruAristimunha Aug 24, 2026
25c2487
FIX: Support legacy RandomState in rng parameters
bruAristimunha Aug 24, 2026
c931567
MAINT: Consolidate RNG transition into single decorator
bruAristimunha Aug 24, 2026
c097a4c
MAINT: Remove boilerplate from RNG transition
bruAristimunha Aug 25, 2026
f80d36e
MAINT: Compact RNG transition tests
bruAristimunha Aug 25, 2026
8fbf77c
MAINT: Retain legacy RNG parameters
bruAristimunha Aug 25, 2026
5dacac4
DOC: Simplify RNG migration wording
bruAristimunha Aug 25, 2026
d37e0e8
MAINT: Simplify RNG checks
bruAristimunha Aug 25, 2026
dbbcce0
MAINT: Trim redundant RNG tests
bruAristimunha Aug 25, 2026
63c71fd
MAINT: Inspect sklearn RNG signatures
bruAristimunha Aug 25, 2026
b70abb2
MAINT: Inspect RNG callables directly
bruAristimunha Aug 25, 2026
23389c5
API: Make legacy RNG arguments keyword-only
bruAristimunha Aug 25, 2026
25d3170
DOC: Vary fixed random seeds
bruAristimunha Aug 25, 2026
aa3b31e
TEST: Soft-import sklearn in RNG check
bruAristimunha Aug 25, 2026
d2c5dfe
TEST: Soft-import sklearn RNG callables
bruAristimunha Aug 25, 2026
45c0836
TEST: Capture RNG migration logs at info level
bruAristimunha Aug 25, 2026
97c5f1c
FIX: Docstring
larsoner Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changes/dev/14199.apichange.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``rng`` parameters to APIs that use randomness, while retaining keyword-only ``seed`` and ``random_state`` for compatibility (:gh:`9233` by `Bruno Aristimunha`_).
2 changes: 1 addition & 1 deletion examples/datasets/spm_faces_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
raw.resample(100)
raw.filter(1.0, None) # high-pass
reject = dict(mag=5e-12)
ica = ICA(n_components=0.95, max_iter="auto", random_state=0)
ica = ICA(n_components=0.95, max_iter="auto", rng=97)
ica.fit(raw, reject=reject)
# compute correlation scores, get bad indices sorted by score
eog_epochs = create_eog_epochs(raw, ch_name="MRT31-2908", reject=reject)
Expand Down
2 changes: 1 addition & 1 deletion examples/decoding/decoding_csp_eeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
scores = []
epochs_data = epochs.get_data(copy=False)
epochs_data_train = epochs_train.get_data(copy=False)
cv = ShuffleSplit(10, test_size=0.2, random_state=42)
cv = ShuffleSplit(10, test_size=0.2, random_state=103)
cv_split = cv.split(epochs_data_train)

# Assemble a classifier
Expand Down
2 changes: 1 addition & 1 deletion examples/decoding/decoding_csp_timefreq.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
LinearDiscriminantAnalysis(),
)
n_splits = 3 # for cross-validation, 5 is better, here we use 3 for speed
cv = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42)
cv = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=191)

# Classification & time-frequency parameters
tmin, tmax = -0.200, 2.000
Expand Down
6 changes: 3 additions & 3 deletions examples/decoding/decoding_rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,13 @@
# to focus the classifier on the time interval with best SNR.
clf = make_pipeline(
StandardScaler(),
OneVsRestClassifier(LogisticRegression(C=1)),
OneVsRestClassifier(LogisticRegression(C=1, random_state=79)),
)
X = epochs.get_data(tmin=0.05, tmax=0.3).mean(axis=2)
y = epochs.events[:, 2]

classes = set(y)
cv = StratifiedKFold(n_splits=5, random_state=0, shuffle=True)
cv = StratifiedKFold(n_splits=5, random_state=83, shuffle=True)

# Compute confusion matrix for each cross-validation fold
y_pred = np.zeros((len(y), len(classes)))
Expand Down Expand Up @@ -173,7 +173,7 @@
chance = 0.5
# TODO VERSION: this is MDS(2, n_init=4, init='random', metric='precomputed'),
# but that spelling requires scikit-learn >= 1.8
summary, _ = smacof(chance - confusion, n_components=2, n_init=4, random_state=0)
summary, _ = smacof(chance - confusion, n_components=2, n_init=4, random_state=89)
cmap = plt.colormaps["rainbow"]
colors = ["r", "b"]
names = list(conds["condition"].values)
Expand Down
2 changes: 1 addition & 1 deletion examples/decoding/decoding_spatio_temporal_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
clf = make_pipeline(
StandardScaler(), # z-score normalization
SelectKBest(f_classif, k=500), # select features for speed
LinearModel(LogisticRegression(C=1, solver="liblinear")),
LinearModel(LogisticRegression(C=1, solver="liblinear", random_state=107)),
)
time_decod = SlidingEstimator(clf, scoring="roc_auc")

Expand Down
2 changes: 1 addition & 1 deletion examples/decoding/decoding_spoc_CMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@

# Classification pipeline with SPoC spatial filtering and Ridge Regression
spoc = SPoC(n_components=2, log=True, reg="oas", rank="full")
clf = make_pipeline(spoc, Ridge())
clf = make_pipeline(spoc, Ridge(random_state=127))
# Define a two fold cross-validation
cv = KFold(n_splits=2, shuffle=False)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
# and test on all right visual vs auditory trials.
clf = make_pipeline(
StandardScaler(),
LogisticRegression(solver="liblinear"), # liblinear is faster than lbfgs
# liblinear is faster than lbfgs
LogisticRegression(solver="liblinear", random_state=131),
)
time_gen = GeneralizingEstimator(clf, scoring="roc_auc", n_jobs=None, verbose=True)

Expand Down
6 changes: 4 additions & 2 deletions examples/decoding/decoding_unsupervised_spatial_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@

##############################################################################
# Transform data with PCA computed on the average ie evoked response
pca = UnsupervisedSpatialFilter(PCA(30), average=False)
pca = UnsupervisedSpatialFilter(PCA(30, random_state=193), average=False)
pca_data = pca.fit_transform(X)
ev = mne.EvokedArray(
np.mean(pca_data, axis=0),
Expand All @@ -74,7 +74,9 @@

##############################################################################
# Transform data with ICA computed on the raw epochs (no averaging)
ica = UnsupervisedSpatialFilter(FastICA(30, whiten="unit-variance"), average=False)
ica = UnsupervisedSpatialFilter(
FastICA(30, whiten="unit-variance", random_state=197), average=False
)
ica_data = ica.fit_transform(X)
ev1 = mne.EvokedArray(
np.mean(ica_data, axis=0),
Expand Down
6 changes: 4 additions & 2 deletions examples/decoding/decoding_xdawn_eeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@
XdawnTransformer(n_components=n_filter),
Vectorizer(),
MinMaxScaler(),
OneVsRestClassifier(LogisticRegression(solver="liblinear", **kwargs)),
OneVsRestClassifier(
LogisticRegression(solver="liblinear", random_state=157, **kwargs)
),
)

# Get the data and labels
Expand All @@ -89,7 +91,7 @@
y = epochs.events[:, -1]

# Cross validator
cv = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
cv = StratifiedKFold(n_splits=10, shuffle=True, random_state=163)

# Do cross-validation
preds = np.empty(len(y))
Expand Down
5 changes: 3 additions & 2 deletions examples/decoding/linear_model_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
# Decoding in sensor space using a LogisticRegression classifier
# --------------------------------------------------------------

clf = LogisticRegression(solver="liblinear") # liblinear is faster than lbfgs
# liblinear is faster than lbfgs
clf = LogisticRegression(solver="liblinear", random_state=137)
scaler = StandardScaler()

# create a linear model with LogisticRegression
Expand Down Expand Up @@ -127,7 +128,7 @@
Vectorizer(), # 1) vectorize across time and channels
StandardScaler(), # 2) normalize features across trials
LinearModel( # 3) fits a logistic regression
LogisticRegression(solver="liblinear")
LogisticRegression(solver="liblinear", random_state=139)
),
)
clf.fit(X, y)
Expand Down
4 changes: 2 additions & 2 deletions examples/inverse/mixed_norm_inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@
return_residual=True,
return_as_dipoles=True,
verbose=True,
random_state=0,
rng=251,
# for this dataset we know we should use a high alpha, so avoid some
# of the slower (lower) alpha values
sure_alpha_grid=np.linspace(100, 40, 10),
sure_alpha_grid=np.linspace(90, 30, 10),
)

t = 0.083
Expand Down
2 changes: 1 addition & 1 deletion examples/preprocessing/find_ref_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
ica_kwargs = dict(
method="picard",
fit_params=dict(tol=1e-4), # use a high tol here for speed
random_state=99,
rng=99,
)
all_picks = mne.pick_types(raw_tog.info, meg=True, ref_meg=True)
ica_tog = ICA(n_components=60, max_iter="auto", allow_ref_meg=True, **ica_kwargs)
Expand Down
2 changes: 1 addition & 1 deletion examples/preprocessing/ica_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def run_ica(method, fit_params=None):
method=method,
fit_params=fit_params,
max_iter="auto",
random_state=0,
rng=29,
)
t0 = time()
ica.fit(raw, reject=reject)
Expand Down
6 changes: 2 additions & 4 deletions examples/preprocessing/muscle_ica.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@

# %%
# Run ICA
ica = mne.preprocessing.ICA(
n_components=15, method="picard", max_iter="auto", random_state=97
)
ica = mne.preprocessing.ICA(n_components=15, method="picard", max_iter="auto", rng=97)
ica.fit(raw)

# %%
Expand Down Expand Up @@ -104,7 +102,7 @@

# Run ICA
ica = mne.preprocessing.ICA(
n_components=15, method="picard", max_iter="auto", random_state=97
n_components=15, method="picard", max_iter="auto", rng=97
)
ica.fit(raw)
ica.plot_sources(raw)
Expand Down
10 changes: 5 additions & 5 deletions examples/simulation/plot_stc_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
spatial_deviation_error,
)

random_state = 42 # set random state to make this example deterministic
random_state = 229 # set random state to make this example deterministic

# Import sample data
data_path = sample.data_path()
Expand Down Expand Up @@ -76,7 +76,7 @@
location=location,
extent=extent,
subjects_dir=subjects_dir,
random_state=random_state,
rng=random_state,
)

# Dipole
Expand All @@ -88,7 +88,7 @@
location=location,
extent=extent,
subjects_dir=subjects_dir,
random_state=random_state,
rng=random_state,
)

# WHAT?
Expand Down Expand Up @@ -128,15 +128,15 @@
raw_region = raw_region.pick(picks=["eeg", "stim"], exclude="bads")
cov = mne.make_ad_hoc_cov(raw_region.info)
mne.simulation.add_noise(
raw_region, cov, iir_filter=[0.2, -0.2, 0.04], random_state=random_state
raw_region, cov, iir_filter=[0.2, -0.2, 0.04], rng=random_state
)

# Dipole
raw_dipole = mne.simulation.simulate_raw(info, source_simulator_dipole, forward=fwd)
raw_dipole = raw_dipole.pick(picks=["eeg", "stim"], exclude="bads")
cov = mne.make_ad_hoc_cov(raw_dipole.info)
mne.simulation.add_noise(
raw_dipole, cov, iir_filter=[0.2, -0.2, 0.04], random_state=random_state
raw_dipole, cov, iir_filter=[0.2, -0.2, 0.04], rng=random_state
)

###############################################################################
Expand Down
2 changes: 1 addition & 1 deletion examples/simulation/simulate_evoked_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def data_fun(times):
fwd["src"],
n_dipoles=2,
times=times,
random_state=42,
rng=239,
labels=labels,
data_fun=data_fun,
)
Expand Down
8 changes: 4 additions & 4 deletions examples/simulation/simulate_raw_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def data_fun(times):
fwd = mne.read_forward_solution(fwd_fname)
src = fwd["src"]
stc = simulate_sparse_stc(
src, n_dipoles=n_dipoles, times=times, data_fun=data_fun, random_state=rng
src, n_dipoles=n_dipoles, times=times, data_fun=data_fun, rng=rng
)
# look at our source data
fig, ax = plt.subplots(1)
Expand All @@ -79,9 +79,9 @@ def data_fun(times):
# Simulate raw data
raw_sim = simulate_raw(raw.info, [stc] * 10, forward=fwd, verbose=True)
cov = make_ad_hoc_cov(raw_sim.info)
add_noise(raw_sim, cov, iir_filter=[0.2, -0.2, 0.04], random_state=rng)
add_ecg(raw_sim, random_state=rng)
add_eog(raw_sim, random_state=rng)
add_noise(raw_sim, cov, iir_filter=[0.2, -0.2, 0.04], rng=rng)
add_ecg(raw_sim, rng=rng)
add_eog(raw_sim, rng=rng)
raw_sim.plot()

##############################################################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ def data_fun(times, latency, duration):
raw_sim = mne.simulation.simulate_raw(info, source_simulator, forward=fwd)
raw_sim.set_eeg_reference(projection=True)

mne.simulation.add_noise(raw_sim, cov=noise_cov, random_state=0)
mne.simulation.add_eog(raw_sim, random_state=0)
mne.simulation.add_ecg(raw_sim, random_state=0)
mne.simulation.add_noise(raw_sim, cov=noise_cov, rng=211)
mne.simulation.add_eog(raw_sim, rng=223)
mne.simulation.add_ecg(raw_sim, rng=227)

# Plot original and simulated raw data.
raw_sim.plot(title="Simulated raw data")
Expand Down
2 changes: 1 addition & 1 deletion examples/simulation/source_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class to generate source estimates and raw data. It is meant to be a brief
# simulator can be given directly to the simulate_raw function.
raw = mne.simulation.simulate_raw(info, source_simulator, forward=fwd)
cov = mne.make_ad_hoc_cov(raw.info)
mne.simulation.add_noise(raw, cov, iir_filter=[0.2, -0.2, 0.04], random_state=97)
mne.simulation.add_noise(raw, cov, iir_filter=[0.2, -0.2, 0.04], rng=97)
raw.plot()

# %%
Expand Down
2 changes: 1 addition & 1 deletion examples/stats/cluster_stats_evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
threshold=threshold,
tail=1,
n_jobs=None,
seed=0,
rng=23,
out_type="mask",
)

Expand Down
2 changes: 1 addition & 1 deletion examples/stats/sensor_permutation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
data = np.mean(data[:, :, temporal_mask], axis=2)

n_permutations = 50000
T0, p_values, H0 = permutation_t_test(data, n_permutations, n_jobs=None, seed=0)
T0, p_values, H0 = permutation_t_test(data, n_permutations, n_jobs=None, rng=17)

significant_sensors = picks[p_values <= 0.05]
significant_sensors_names = [raw.ch_names[k] for k in significant_sensors]
Expand Down
2 changes: 1 addition & 1 deletion examples/time_frequency/time_frequency_erds.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
cnorm = TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax) # min, center & max ERDS

kwargs = dict(
n_permutations=100, step_down_p=0.05, seed=1, buffer_size=None, out_type="mask"
n_permutations=100, step_down_p=0.05, rng=1, buffer_size=None, out_type="mask"
) # for cluster test

# %%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def stat_fun(x):
ax.plot(times, gfp, label=freq_name, color=color, linewidth=2.5)
ax.axhline(0, linestyle="--", color="grey", linewidth=2)
ci_low, ci_up = bootstrap_confidence_interval(
average.data, random_state=0, stat_fun=stat_fun
average.data, rng=109, stat_fun=stat_fun
)
ci_low = rescale(ci_low, average.times, baseline=(None, 0))
ci_up = rescale(ci_up, average.times, baseline=(None, 0))
Expand Down
2 changes: 1 addition & 1 deletion examples/time_frequency/time_frequency_simulated.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

n_times = 1024 # Just over 1 second epochs
n_epochs = 40
seed = 42
seed = 181
rng = np.random.default_rng(seed)
data = rng.standard_normal((len(ch_names), n_times * n_epochs + 200)) # buffer

Expand Down
2 changes: 1 addition & 1 deletion examples/visualization/channel_epochs_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def order_func(times, data):
this_data /= np.sqrt(np.sum(this_data**2, axis=1))[:, np.newaxis]
return np.argsort(
spectral_embedding(
rbf_kernel(this_data, gamma=1.0), n_components=1, random_state=0
rbf_kernel(this_data, gamma=1.0), n_components=1, random_state=233
).ravel()
)

Expand Down
2 changes: 1 addition & 1 deletion mne/_fiff/tests/test_what.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_what(tmp_path, verbose_debug):
"""Test mne.what."""
pytest.importorskip("sklearn")
# ICA
ica = ICA(max_iter=1, random_state=0)
ica = ICA(max_iter=1, rng=0)
raw = RawArray(
np.random.default_rng(0).standard_normal((3, 10)), create_info(3, 1000.0, "eeg")
)
Expand Down
3 changes: 1 addition & 2 deletions mne/beamformer/tests/test_rap_music.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ def simu_data(evoked, forward, noise_cov, n_dipoles, times, nave=1):
tmin, tstep = times.min(), 1 / evoked.info["sfreq"]
stc = mne.SourceEstimate(data, vertices=vertices, tmin=tmin, tstep=tstep)

# noise seed chosen to keep the explained-variance and gof values well
# inside the bounds asserted in the tests
# The bounds below were calibrated against this legacy noise stream.
sim_evoked = mne.simulation.simulate_evoked(
forward, stc, evoked.info, noise_cov, nave=nave, random_state=106
)
Expand Down
6 changes: 3 additions & 3 deletions mne/cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -1524,12 +1524,12 @@ def _auto_low_rank_model(
iter_n_components = np.arange(5, data.shape[1], 5)
from sklearn.decomposition import PCA, FactorAnalysis

random_state = method_params.pop("random_state", 0)
if mode == "factor_analysis":
est = FactorAnalysis
est = FactorAnalysis(random_state=random_state, **method_params)
else:
assert mode == "pca"
est = PCA
est = est(**method_params)
est = PCA(random_state=random_state, **method_params)
est.n_components = 1
scores = np.empty_like(iter_n_components, dtype=np.float64)
scores.fill(np.nan)
Expand Down
6 changes: 4 additions & 2 deletions mne/decoding/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,9 @@ def __init__(self, model=None):
def __sklearn_tags__(self):
"""Get sklearn tags."""
tags = super().__sklearn_tags__()
model = self.model if self.model is not None else LogisticRegression()
model = (
self.model if self.model is not None else LogisticRegression(random_state=0)
)
model_tags = model.__sklearn_tags__()
tags.estimator_type = model_tags.estimator_type
if tags.estimator_type is not None:
Expand Down Expand Up @@ -538,7 +540,7 @@ def fit(self, X, y, **fit_params):
self.model_ = (
clone(self.model)
if self.model is not None
else LogisticRegression(solver="liblinear")
else LogisticRegression(solver="liblinear", random_state=0)
)
self.model_.fit(X, y, **fit_params)

Expand Down
Loading
Loading