Skip to content

Commit a77debb

Browse files
authored
API: sparse.linalg.svds: transition to rng (SPEC 7) (scipy#21899)
* API: sparse.linalg.svds: transition to rng (SPEC 7) * TST: sparse.linalg.svds: make tests deterministic
1 parent abf159d commit a77debb

File tree

5 files changed

+152
-197
lines changed

5 files changed

+152
-197
lines changed

scipy/sparse/linalg/_eigen/_svds.py

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .arpack import _arpack # type: ignore[attr-defined]
55
from . import eigsh
66

7-
from scipy._lib._util import check_random_state
7+
from scipy._lib._util import check_random_state, _transition_to_rng
88
from scipy.sparse.linalg._interface import LinearOperator, aslinearoperator
99
from scipy.sparse.linalg._eigen.lobpcg import lobpcg # type: ignore[no-redef]
1010
from scipy.sparse.linalg._svdp import _svdp
@@ -19,7 +19,7 @@ def _herm(x):
1919

2020

2121
def _iv(A, k, ncv, tol, which, v0, maxiter,
22-
return_singular, solver, random_state):
22+
return_singular, solver, rng):
2323

2424
# input validation/standardization for `solver`
2525
# out of order because it's needed for other parameters
@@ -90,15 +90,16 @@ def _iv(A, k, ncv, tol, which, v0, maxiter,
9090
if return_singular not in rs_options:
9191
raise ValueError(f"`return_singular_vectors` must be in {rs_options}.")
9292

93-
random_state = check_random_state(random_state)
93+
rng = check_random_state(rng)
9494

9595
return (A, k, ncv, tol, which, v0, maxiter,
96-
return_singular, solver, random_state)
96+
return_singular, solver, rng)
9797

9898

99+
@_transition_to_rng("random_state", position_num=9)
99100
def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None,
100101
maxiter=None, return_singular_vectors=True,
101-
solver='arpack', random_state=None, options=None):
102+
solver='arpack', rng=None, options=None):
102103
"""
103104
Partial singular value decomposition of a sparse matrix.
104105
@@ -158,17 +159,11 @@ def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None,
158159
:ref:`'lobpcg' <sparse.linalg.svds-lobpcg>`, and
159160
:ref:`'propack' <sparse.linalg.svds-propack>` are supported.
160161
Default: `'arpack'`.
161-
random_state : {None, int, `numpy.random.Generator`,
162-
`numpy.random.RandomState`}, optional
163-
164-
Pseudorandom number generator state used to generate resamples.
165-
166-
If `random_state` is ``None`` (or `np.random`), the
167-
`numpy.random.RandomState` singleton is used.
168-
If `random_state` is an int, a new ``RandomState`` instance is used,
169-
seeded with `random_state`.
170-
If `random_state` is already a ``Generator`` or ``RandomState``
171-
instance then that instance is used.
162+
rng : `numpy.random.Generator`, optional
163+
Pseudorandom number generator state. When `rng` is None, a new
164+
`numpy.random.Generator` is created using entropy from the
165+
operating system. Types other than `numpy.random.Generator` are
166+
passed to `numpy.random.default_rng` to instantiate a ``Generator``.
172167
options : dict, optional
173168
A dictionary of solver-specific options. No solver-specific options
174169
are currently supported; this parameter is reserved for future use.
@@ -263,7 +258,7 @@ def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None,
263258
>>> vT = v.T
264259
>>> A = u @ np.diag(s) @ vT
265260
>>> A = A.astype(np.float32)
266-
>>> u2, s2, vT2 = svds(A, k=2, random_state=rng)
261+
>>> u2, s2, vT2 = svds(A, k=2, rng=rng)
267262
>>> np.allclose(s2, s)
268263
True
269264
@@ -290,21 +285,21 @@ def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None,
290285
291286
The next example follows that of 'sklearn.decomposition.TruncatedSVD'.
292287
293-
>>> rng = np.random.RandomState(0)
288+
>>> rng = np.random.default_rng(0)
294289
>>> X_dense = rng.random(size=(100, 100))
295290
>>> X_dense[:, 2 * np.arange(50)] = 0
296291
>>> X = sparse.csr_array(X_dense)
297-
>>> _, singular_values, _ = svds(X, k=5, random_state=rng)
292+
>>> _, singular_values, _ = svds(X, k=5, rng=rng)
298293
>>> print(singular_values)
299-
[ 4.3293... 4.4491... 4.5420... 4.5987... 35.2410...]
294+
[ 4.3221... 4.4043... 4.4907... 4.5858... 35.4549...]
300295
301296
The function can be called without the transpose of the input matrix
302297
ever explicitly constructed.
303298
304299
>>> rng = np.random.default_rng(102524723947864966825913730119128190974)
305300
>>> G = sparse.random_array((8, 9), density=0.5, random_state=rng)
306301
>>> Glo = aslinearoperator(G)
307-
>>> _, singular_values_svds, _ = svds(Glo, k=5, random_state=rng)
302+
>>> _, singular_values_svds, _ = svds(Glo, k=5, rng=rng)
308303
>>> _, singular_values_svd, _ = linalg.svd(G.toarray())
309304
>>> np.allclose(singular_values_svds, singular_values_svd[-4::-1])
310305
True
@@ -436,9 +431,9 @@ def svds(A, k=6, ncv=None, tol=0, which='LM', v0=None,
436431
437432
"""
438433
args = _iv(A, k, ncv, tol, which, v0, maxiter, return_singular_vectors,
439-
solver, random_state)
434+
solver, rng)
440435
(A, k, ncv, tol, which, v0, maxiter,
441-
return_singular_vectors, solver, random_state) = args
436+
return_singular_vectors, solver, rng) = args
442437

443438
largest = (which == 'LM')
444439
n, m = A.shape
@@ -477,7 +472,7 @@ def matmat_XH_X(x):
477472
if k == 1 and v0 is not None:
478473
X = np.reshape(v0, (-1, 1))
479474
else:
480-
X = random_state.standard_normal(size=(min(A.shape), k))
475+
X = rng.standard_normal(size=(min(A.shape), k))
481476

482477
_, eigvec = lobpcg(XH_X, X, tol=tol ** 2, maxiter=maxiter,
483478
largest=largest)
@@ -488,7 +483,7 @@ def matmat_XH_X(x):
488483
irl_mode = (which == 'SM')
489484
res = _svdp(A, k=k, tol=tol**2, which=which, maxiter=None,
490485
compute_u=jobu, compute_v=jobv, irl_mode=irl_mode,
491-
kmax=maxiter, v0=v0, random_state=random_state)
486+
kmax=maxiter, v0=v0, rng=rng)
492487

493488
u, s, vh, _ = res # but we'll ignore bnd, the last output
494489

@@ -510,7 +505,7 @@ def matmat_XH_X(x):
510505

511506
elif solver == 'arpack' or solver is None:
512507
if v0 is None:
513-
v0 = random_state.standard_normal(size=(min(A.shape),))
508+
v0 = rng.standard_normal(size=(min(A.shape),))
514509
_, eigvec = eigsh(XH_X, k=k, tol=tol ** 2, maxiter=maxiter,
515510
ncv=ncv, which=which, v0=v0)
516511
# arpack do not guarantee exactly orthonormal eigenvectors

scipy/sparse/linalg/_eigen/_svds_doc.py

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
def _svds_arpack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
22
maxiter=None, return_singular_vectors=True,
3-
solver='arpack', random_state=None):
3+
solver='arpack', rng=None):
44
"""
55
Partial singular value decomposition of a sparse matrix using ARPACK.
66
@@ -54,17 +54,11 @@ def _svds_arpack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
5454
:ref:`'lobpcg' <sparse.linalg.svds-lobpcg>` and
5555
:ref:`'propack' <sparse.linalg.svds-propack>`
5656
are also supported.
57-
random_state : {None, int, `numpy.random.Generator`,
58-
`numpy.random.RandomState`}, optional
59-
60-
Pseudorandom number generator state used to generate resamples.
61-
62-
If `random_state` is ``None`` (or `np.random`), the
63-
`numpy.random.RandomState` singleton is used.
64-
If `random_state` is an int, a new ``RandomState`` instance is used,
65-
seeded with `random_state`.
66-
If `random_state` is already a ``Generator`` or ``RandomState``
67-
instance then that instance is used.
57+
rng : `numpy.random.Generator`, optional
58+
Pseudorandom number generator state. When `rng` is None, a new
59+
`numpy.random.Generator` is created using entropy from the
60+
operating system. Types other than `numpy.random.Generator` are
61+
passed to `numpy.random.default_rng` to instantiate a ``Generator``.
6862
options : dict, optional
6963
A dictionary of solver-specific options. No solver-specific options
7064
are currently supported; this parameter is reserved for future use.
@@ -134,7 +128,7 @@ def _svds_arpack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
134128

135129
def _svds_lobpcg_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
136130
maxiter=None, return_singular_vectors=True,
137-
solver='lobpcg', random_state=None):
131+
solver='lobpcg', rng=None):
138132
"""
139133
Partial singular value decomposition of a sparse matrix using LOBPCG.
140134
@@ -184,17 +178,11 @@ def _svds_lobpcg_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
184178
:ref:`'arpack' <sparse.linalg.svds-arpack>` and
185179
:ref:`'propack' <sparse.linalg.svds-propack>`
186180
are also supported.
187-
random_state : {None, int, `numpy.random.Generator`,
188-
`numpy.random.RandomState`}, optional
189-
190-
Pseudorandom number generator state used to generate resamples.
191-
192-
If `random_state` is ``None`` (or `np.random`), the
193-
`numpy.random.RandomState` singleton is used.
194-
If `random_state` is an int, a new ``RandomState`` instance is used,
195-
seeded with `random_state`.
196-
If `random_state` is already a ``Generator`` or ``RandomState``
197-
instance then that instance is used.
181+
rng : `numpy.random.Generator`, optional
182+
Pseudorandom number generator state. When `rng` is None, a new
183+
`numpy.random.Generator` is created using entropy from the
184+
operating system. Types other than `numpy.random.Generator` are
185+
passed to `numpy.random.default_rng` to instantiate a ``Generator``.
198186
options : dict, optional
199187
A dictionary of solver-specific options. No solver-specific options
200188
are currently supported; this parameter is reserved for future use.
@@ -265,7 +253,7 @@ def _svds_lobpcg_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
265253

266254
def _svds_propack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
267255
maxiter=None, return_singular_vectors=True,
268-
solver='propack', random_state=None):
256+
solver='propack', rng=None):
269257
"""
270258
Partial singular value decomposition of a sparse matrix using PROPACK.
271259
@@ -314,17 +302,11 @@ def _svds_propack_doc(A, k=6, ncv=None, tol=0, which='LM', v0=None,
314302
:ref:`'arpack' <sparse.linalg.svds-arpack>` and
315303
:ref:`'lobpcg' <sparse.linalg.svds-lobpcg>`
316304
are also supported.
317-
random_state : {None, int, `numpy.random.Generator`,
318-
`numpy.random.RandomState`}, optional
319-
320-
Pseudorandom number generator state used to generate resamples.
321-
322-
If `random_state` is ``None`` (or `np.random`), the
323-
`numpy.random.RandomState` singleton is used.
324-
If `random_state` is an int, a new ``RandomState`` instance is used,
325-
seeded with `random_state`.
326-
If `random_state` is already a ``Generator`` or ``RandomState``
327-
instance then that instance is used.
305+
rng : `numpy.random.Generator`, optional
306+
Pseudorandom number generator state. When `rng` is None, a new
307+
`numpy.random.Generator` is created using entropy from the
308+
operating system. Types other than `numpy.random.Generator` are
309+
passed to `numpy.random.default_rng` to instantiate a ``Generator``.
328310
options : dict, optional
329311
A dictionary of solver-specific options. No solver-specific options
330312
are currently supported; this parameter is reserved for future use.

0 commit comments

Comments
 (0)