Fix: seed sequence permutation tests and make results independent of n_jobs #1234
Conversation
The seeds for permutation/simulation tests are now spawned per permutation from a numpy.random.SeedSequence. This makes results independent of `n_jobs`/`backend`, but changes the results obtained with a given `seed` relative to earlier squidpy versions. Document this with a `.. versionchanged:: 1.8.4` note on the affected public functions (`ligrec`, `nhood_enrichment`, `spatial_autocorr`, `ripley`). The shared note for the permutation-based functions lives in a single docrep template (`seed_versionchanged`) to avoid duplication; `ripley` keeps a tailored note as it concerns simulations. Also add a release-notes entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9a6008e to
dbc04f7
Compare
please explain: why does the seed have to go inside of numba? Numba supports |
Oof, I didn't know about this. Thank you! Then we can fully modernize as well |
|
first check if the methods you need work, I tried before and it failed since whatever distribution I tried didn’t work |
|
now I remember why I discarded generators. It was because it was documented that Generator was not thread safe but I guess it should be fine if we have a generator for each permutation that's used once I guess... But another point is |
| rs = np.random.RandomState(None if seed is None else perms[0] + seed) | ||
|
|
||
| clustering = clustering.copy() | ||
| # used as a read-only base; each permutation shuffles its own copy (see the loop below) |
There was a problem hiding this comment.
I checked if this copy would be costly or not. From the run results it shouldn't be. Also if we want to make it n_jobs independent it's inevitable
for more information, see https://pre-commit.ci
| .. versionchanged:: 1.8.4 | ||
| Every simulation now uses an independent :class:`numpy.random.Generator` spawned from a | ||
| :class:`numpy.random.SeedSequence`, following `NumPy's guidance on parallel random | ||
| number generation <https://numpy.org/doc/stable/reference/random/parallel.html>`_. | ||
| Previously every simulation reused the same ``seed`` and therefore produced an identical | ||
| point pattern; the simulated statistics (and hence the p-values) are now genuinely | ||
| independent across simulations, but results obtained with a given ``seed`` differ from | ||
| those produced by squidpy < 1.8.4. |
There was a problem hiding this comment.
is it intentional that this differs from the templated version?
There was a problem hiding this comment.
It was, because this was a bug. But I can rephrase it perhaps to make it clear that the old version was broken statistically and this isn't just about a behaviour change warning.
There was a problem hiding this comment.
ok I remembered that I also link to the ripley issue so it should be fine to just use the template
Fixes: #1233 and fixes #1232 as a side effect.
For each permutation we have a separate seed. The seeds are generated by
SeeqSequence(root_seed).spawn(x)routine. This will prevent having correlated results from sequential seeds mentioned in the numpy docs. I tried to use the best practice but since we need to use numba in the future our seeds can be onlySequence[int],instead of Sequence[np.random.SeedSequence]. Hence:Once you confirm. I will write warnings in docstrings and in notebooks about this behavioral change to users.