Skip to content

fix(ica): use a local RNG for BWS scores - #14382

Draft
viranovskaya wants to merge 1 commit into
mne-tools:mainfrom
viranovskaya:fix/scipy-rng-14379
Draft

viranovskaya wants to merge 1 commit into
mne-tools:mainfrom
viranovskaya:fix/scipy-rng-14379

Conversation

@viranovskaya

@viranovskaya viranovskaya commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Reference issue (if any)

What does this implement/fix?

Summary

Fix the BWS score function used by ICA.score_sources so that permutation sampling uses a local random number generator instead of NumPy's global random state.

Changes

  • Pass an explicit scipy.stats.PermutationMethod to bws_test.
  • Use a local np.random.default_rng() instance.
  • Support both the newer rng and older random_state PermutationMethod APIs for SciPy compatibility.
  • Add a regression test verifying that computing BWS scores does not modify NumPy's global random state.
  • Add a changelog entry.

This also avoids upcoming SciPy RNG-related warnings while keeping the existing BWS scoring behavior unchanged.

Additional information

AI assistance: Codex audited the relevant SciPy calls, prepared the implementation and regression test, simplified the test, and ran checks. I personally reviewed and verified all changes before submission. This disclosure note was prepared with Codex.

Comment on lines +97 to +110
def test_bws_score_local_rng():
"""BWS scores must not consume NumPy's global random state."""
rng = np.random.default_rng(0)
# Enough observations to require random, rather than exact, permutations.
data = rng.standard_normal((2, 20))
target = rng.standard_normal(20)
global_rng = check_random_state(None)
state = global_rng.get_state()
try:
scores = get_score_funcs()["bws_test"](data, target)
assert_equal(global_rng.get_state(), state)
assert_equal(scores.shape, (2,))
finally:
global_rng.set_state(state)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? Does mne/tests/test_docstring_parameters::test_no_global_rng already check this for us?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new test does not duplicate test_no_global_rng: the existing check is static and cannot detect global RNG use inside SciPy.
I verified this experimentally: with the old helper, the existing test passes, but the new one fails—all 624 elements of the RNG state array change. With the fix, both tests pass. I think this makes the new regression test worth keeping.

@drammock

drammock commented Oct 7, 2026

Copy link
Copy Markdown
Member

@viranovskaya are you sure this addresses all calls we make into the SciPy API (such that it would make sense to close #14379)? For now I've edited the PR description to remove the fixes magic, but feel free to put it back if you verify that this is indeed the only place that needs updating.

@viranovskaya

Copy link
Copy Markdown
Contributor Author

@drammock I audited MNE’s SciPy calls, including import aliases and dynamically selected functions, and cross-checked them against the RNG transitions in SciPy PR #26277. I found no additional calls requiring migration beyond the PermutationMethod usage addressed here.

The two stats.t.rvs(random_state=0) calls in the Infomax tests were left unchanged intentionally: random_state is supported there, while passing rng raises a TypeError.

The targeted RNG and Infomax tests passed on both SciPy 1.14.1 and 1.18.1, with 10 tests passing in each environment. I have not tested a full build incorporating the upcoming SciPy changes.

I’ll leave Fixes #14379 out for now. Would you consider this audit sufficient to cover that issue, or is there anything else you’d like me to check?

@drammock

drammock commented Oct 9, 2026

Copy link
Copy Markdown
Member

Thanks for detailing your process @viranovskaya. It strikes me as odd that the rvs() API wouldn't be changing, so I've asked about it upstream. Let's see what they say. In the meantime I think we should pause work on this PR, probably until scipy/scipy#26277 gets merged (and maybe longer, in case there are follow-up PRs in SciPy that we'll need to wait for).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants