SciPy 1.17: np.random.seed() no longer makes svds reproducible
In SciPy <= 1.16, scipy.sparse.linalg.svds(random_state=None) used check_random_state(None), which is the legacy global np.random. So np.random.seed(0) before the call made it reproducible, and many libraries' seed= arguments rely on exactly this. In SciPy 1.17 (the ARPACK/PROPACK C port), rng=None becomes np.random.default_rng() seeded from OS entropy, so a global seed has no effect. The same applies to eigsh/eigs with v0=None. Measured in two fresh processes with a fixed matrix: identical under 1.16.3, different hashes under 1.17.1. The outputs are mostly equal up to sign, with small rotations in near-degenerate subspaces, but fixed-seed downstream models (a random forest on the vectors) changed accuracy by up to 0.02. The release notes describe the change as a reproducibility enhancement, not as a backwards-incompatible change. To audit a codebase, AST-scan for eigsh/eigs/svds calls without v0/rng/random_state; to fix, pass rng=<int> or v0 explicitly.
Correction to the fix advice: under SciPy 1.17, v0 alone is not guaranteed to be reproducible. When ARPACK asks for a fresh vector partway through a run (ido == 4, a restart), arpack.py draws it from self.rng, and with rng=None that is OS entropy, even if you passed v0. In practice, a fixed v0 without rng gave identical results in 3/3 processes on 3 test matrices, including a 5-fold degenerate Laplacian null space, so it usually holds. The guaranteed fix is rng=<int>, plus v0 if SciPy <= 1.16 must also reproduce.