Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/_lib/_util.py: 26%

250 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1from contextlib import contextmanager 

2import functools 

3import operator 

4import warnings 

5import numbers 

6from collections import namedtuple 

7import inspect 

8import math 

9from typing import ( 

10 Optional, 

11 Union, 

12 TYPE_CHECKING, 

13 TypeVar, 

14) 

15 

16import numpy as np 

17 

18IntNumber = Union[int, np.integer] 

19DecimalNumber = Union[float, np.floating, np.integer] 

20 

21# Since Generator was introduced in numpy 1.17, the following condition is needed for 

22# backward compatibility 

23if TYPE_CHECKING: 

24 SeedType = Optional[Union[IntNumber, np.random.Generator, 

25 np.random.RandomState]] 

26 GeneratorType = TypeVar("GeneratorType", bound=Union[np.random.Generator, 

27 np.random.RandomState]) 

28 

29try: 

30 from numpy.random import Generator as Generator 

31except ImportError: 

32 class Generator(): # type: ignore[no-redef] 

33 pass 

34 

35 

36def _lazywhere(cond, arrays, f, fillvalue=None, f2=None): 

37 """ 

38 np.where(cond, x, fillvalue) always evaluates x even where cond is False. 

39 This one only evaluates f(arr1[cond], arr2[cond], ...). 

40 

41 Examples 

42 -------- 

43 >>> import numpy as np 

44 >>> a, b = np.array([1, 2, 3, 4]), np.array([5, 6, 7, 8]) 

45 >>> def f(a, b): 

46 ... return a*b 

47 >>> _lazywhere(a > 2, (a, b), f, np.nan) 

48 array([ nan, nan, 21., 32.]) 

49 

50 Notice, it assumes that all `arrays` are of the same shape, or can be 

51 broadcasted together. 

52 

53 """ 

54 cond = np.asarray(cond) 

55 if fillvalue is None: 

56 if f2 is None: 

57 raise ValueError("One of (fillvalue, f2) must be given.") 

58 else: 

59 fillvalue = np.nan 

60 else: 

61 if f2 is not None: 

62 raise ValueError("Only one of (fillvalue, f2) can be given.") 

63 

64 args = np.broadcast_arrays(cond, *arrays) 

65 cond, arrays = args[0], args[1:] 

66 temp = tuple(np.extract(cond, arr) for arr in arrays) 

67 tcode = np.mintypecode([a.dtype.char for a in arrays]) 

68 out = np.full(np.shape(arrays[0]), fill_value=fillvalue, dtype=tcode) 

69 np.place(out, cond, f(*temp)) 

70 if f2 is not None: 

71 temp = tuple(np.extract(~cond, arr) for arr in arrays) 

72 np.place(out, ~cond, f2(*temp)) 

73 

74 return out 

75 

76 

77def _lazyselect(condlist, choicelist, arrays, default=0): 

78 """ 

79 Mimic `np.select(condlist, choicelist)`. 

80 

81 Notice, it assumes that all `arrays` are of the same shape or can be 

82 broadcasted together. 

83 

84 All functions in `choicelist` must accept array arguments in the order 

85 given in `arrays` and must return an array of the same shape as broadcasted 

86 `arrays`. 

87 

88 Examples 

89 -------- 

90 >>> import numpy as np 

91 >>> x = np.arange(6) 

92 >>> np.select([x <3, x > 3], [x**2, x**3], default=0) 

93 array([ 0, 1, 4, 0, 64, 125]) 

94 

95 >>> _lazyselect([x < 3, x > 3], [lambda x: x**2, lambda x: x**3], (x,)) 

96 array([ 0., 1., 4., 0., 64., 125.]) 

97 

98 >>> a = -np.ones_like(x) 

99 >>> _lazyselect([x < 3, x > 3], 

100 ... [lambda x, a: x**2, lambda x, a: a * x**3], 

101 ... (x, a), default=np.nan) 

102 array([ 0., 1., 4., nan, -64., -125.]) 

103 

104 """ 

105 arrays = np.broadcast_arrays(*arrays) 

106 tcode = np.mintypecode([a.dtype.char for a in arrays]) 

107 out = np.full(np.shape(arrays[0]), fill_value=default, dtype=tcode) 

108 for func, cond in zip(choicelist, condlist): 

109 if np.all(cond is False): 

110 continue 

111 cond, _ = np.broadcast_arrays(cond, arrays[0]) 

112 temp = tuple(np.extract(cond, arr) for arr in arrays) 

113 np.place(out, cond, func(*temp)) 

114 return out 

115 

116 

117def _aligned_zeros(shape, dtype=float, order="C", align=None): 

118 """Allocate a new ndarray with aligned memory. 

119 

120 Primary use case for this currently is working around a f2py issue 

121 in NumPy 1.9.1, where dtype.alignment is such that np.zeros() does 

122 not necessarily create arrays aligned up to it. 

123 

124 """ 

125 dtype = np.dtype(dtype) 

126 if align is None: 

127 align = dtype.alignment 

128 if not hasattr(shape, '__len__'): 

129 shape = (shape,) 

130 size = functools.reduce(operator.mul, shape) * dtype.itemsize 

131 buf = np.empty(size + align + 1, np.uint8) 

132 offset = buf.__array_interface__['data'][0] % align 

133 if offset != 0: 

134 offset = align - offset 

135 # Note: slices producing 0-size arrays do not necessarily change 

136 # data pointer --- so we use and allocate size+1 

137 buf = buf[offset:offset+size+1][:-1] 

138 data = np.ndarray(shape, dtype, buf, order=order) 

139 data.fill(0) 

140 return data 

141 

142 

143def _prune_array(array): 

144 """Return an array equivalent to the input array. If the input 

145 array is a view of a much larger array, copy its contents to a 

146 newly allocated array. Otherwise, return the input unchanged. 

147 """ 

148 if array.base is not None and array.size < array.base.size // 2: 

149 return array.copy() 

150 return array 

151 

152 

153def prod(iterable): 

154 """ 

155 Product of a sequence of numbers. 

156 

157 Faster than np.prod for short lists like array shapes, and does 

158 not overflow if using Python integers. 

159 """ 

160 product = 1 

161 for x in iterable: 

162 product *= x 

163 return product 

164 

165 

166def float_factorial(n: int) -> float: 

167 """Compute the factorial and return as a float 

168 

169 Returns infinity when result is too large for a double 

170 """ 

171 return float(math.factorial(n)) if n < 171 else np.inf 

172 

173 

174# copy-pasted from scikit-learn utils/validation.py 

175# change this to scipy.stats._qmc.check_random_state once numpy 1.16 is dropped 

176def check_random_state(seed): 

177 """Turn `seed` into a `np.random.RandomState` instance. 

178 

179 Parameters 

180 ---------- 

181 seed : {None, int, `numpy.random.Generator`, `numpy.random.RandomState`}, optional 

182 If `seed` is None (or `np.random`), the `numpy.random.RandomState` 

183 singleton is used. 

184 If `seed` is an int, a new ``RandomState`` instance is used, 

185 seeded with `seed`. 

186 If `seed` is already a ``Generator`` or ``RandomState`` instance then 

187 that instance is used. 

188 

189 Returns 

190 ------- 

191 seed : {`numpy.random.Generator`, `numpy.random.RandomState`} 

192 Random number generator. 

193 

194 """ 

195 if seed is None or seed is np.random: 

196 return np.random.mtrand._rand 

197 if isinstance(seed, (numbers.Integral, np.integer)): 

198 return np.random.RandomState(seed) 

199 if isinstance(seed, (np.random.RandomState, np.random.Generator)): 

200 return seed 

201 

202 raise ValueError('%r cannot be used to seed a numpy.random.RandomState' 

203 ' instance' % seed) 

204 

205 

206def _asarray_validated(a, check_finite=True, 

207 sparse_ok=False, objects_ok=False, mask_ok=False, 

208 as_inexact=False): 

209 """ 

210 Helper function for SciPy argument validation. 

211 

212 Many SciPy linear algebra functions do support arbitrary array-like 

213 input arguments. Examples of commonly unsupported inputs include 

214 matrices containing inf/nan, sparse matrix representations, and 

215 matrices with complicated elements. 

216 

217 Parameters 

218 ---------- 

219 a : array_like 

220 The array-like input. 

221 check_finite : bool, optional 

222 Whether to check that the input matrices contain only finite numbers. 

223 Disabling may give a performance gain, but may result in problems 

224 (crashes, non-termination) if the inputs do contain infinities or NaNs. 

225 Default: True 

226 sparse_ok : bool, optional 

227 True if scipy sparse matrices are allowed. 

228 objects_ok : bool, optional 

229 True if arrays with dype('O') are allowed. 

230 mask_ok : bool, optional 

231 True if masked arrays are allowed. 

232 as_inexact : bool, optional 

233 True to convert the input array to a np.inexact dtype. 

234 

235 Returns 

236 ------- 

237 ret : ndarray 

238 The converted validated array. 

239 

240 """ 

241 if not sparse_ok: 

242 import scipy.sparse 

243 if scipy.sparse.issparse(a): 

244 msg = ('Sparse matrices are not supported by this function. ' 

245 'Perhaps one of the scipy.sparse.linalg functions ' 

246 'would work instead.') 

247 raise ValueError(msg) 

248 if not mask_ok: 

249 if np.ma.isMaskedArray(a): 

250 raise ValueError('masked arrays are not supported') 

251 toarray = np.asarray_chkfinite if check_finite else np.asarray 

252 a = toarray(a) 

253 if not objects_ok: 

254 if a.dtype is np.dtype('O'): 

255 raise ValueError('object arrays are not supported') 

256 if as_inexact: 

257 if not np.issubdtype(a.dtype, np.inexact): 

258 a = toarray(a, dtype=np.float_) 

259 return a 

260 

261 

262def _validate_int(k, name, minimum=None): 

263 """ 

264 Validate a scalar integer. 

265 

266 This functon can be used to validate an argument to a function 

267 that expects the value to be an integer. It uses `operator.index` 

268 to validate the value (so, for example, k=2.0 results in a 

269 TypeError). 

270 

271 Parameters 

272 ---------- 

273 k : int 

274 The value to be validated. 

275 name : str 

276 The name of the parameter. 

277 minimum : int, optional 

278 An optional lower bound. 

279 """ 

280 try: 

281 k = operator.index(k) 

282 except TypeError: 

283 raise TypeError(f'{name} must be an integer.') from None 

284 if minimum is not None and k < minimum: 

285 raise ValueError(f'{name} must be an integer not less ' 

286 f'than {minimum}') from None 

287 return k 

288 

289 

290# Add a replacement for inspect.getfullargspec()/ 

291# The version below is borrowed from Django, 

292# https://github.com/django/django/pull/4846. 

293 

294# Note an inconsistency between inspect.getfullargspec(func) and 

295# inspect.signature(func). If `func` is a bound method, the latter does *not* 

296# list `self` as a first argument, while the former *does*. 

297# Hence, cook up a common ground replacement: `getfullargspec_no_self` which 

298# mimics `inspect.getfullargspec` but does not list `self`. 

299# 

300# This way, the caller code does not need to know whether it uses a legacy 

301# .getfullargspec or a bright and shiny .signature. 

302 

303FullArgSpec = namedtuple('FullArgSpec', 

304 ['args', 'varargs', 'varkw', 'defaults', 

305 'kwonlyargs', 'kwonlydefaults', 'annotations']) 

306 

307 

308def getfullargspec_no_self(func): 

309 """inspect.getfullargspec replacement using inspect.signature. 

310 

311 If func is a bound method, do not list the 'self' parameter. 

312 

313 Parameters 

314 ---------- 

315 func : callable 

316 A callable to inspect 

317 

318 Returns 

319 ------- 

320 fullargspec : FullArgSpec(args, varargs, varkw, defaults, kwonlyargs, 

321 kwonlydefaults, annotations) 

322 

323 NOTE: if the first argument of `func` is self, it is *not*, I repeat 

324 *not*, included in fullargspec.args. 

325 This is done for consistency between inspect.getargspec() under 

326 Python 2.x, and inspect.signature() under Python 3.x. 

327 

328 """ 

329 sig = inspect.signature(func) 

330 args = [ 

331 p.name for p in sig.parameters.values() 

332 if p.kind in [inspect.Parameter.POSITIONAL_OR_KEYWORD, 

333 inspect.Parameter.POSITIONAL_ONLY] 

334 ] 

335 varargs = [ 

336 p.name for p in sig.parameters.values() 

337 if p.kind == inspect.Parameter.VAR_POSITIONAL 

338 ] 

339 varargs = varargs[0] if varargs else None 

340 varkw = [ 

341 p.name for p in sig.parameters.values() 

342 if p.kind == inspect.Parameter.VAR_KEYWORD 

343 ] 

344 varkw = varkw[0] if varkw else None 

345 defaults = tuple( 

346 p.default for p in sig.parameters.values() 

347 if (p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD and 

348 p.default is not p.empty) 

349 ) or None 

350 kwonlyargs = [ 

351 p.name for p in sig.parameters.values() 

352 if p.kind == inspect.Parameter.KEYWORD_ONLY 

353 ] 

354 kwdefaults = {p.name: p.default for p in sig.parameters.values() 

355 if p.kind == inspect.Parameter.KEYWORD_ONLY and 

356 p.default is not p.empty} 

357 annotations = {p.name: p.annotation for p in sig.parameters.values() 

358 if p.annotation is not p.empty} 

359 return FullArgSpec(args, varargs, varkw, defaults, kwonlyargs, 

360 kwdefaults or None, annotations) 

361 

362 

363class _FunctionWrapper: 

364 """ 

365 Object to wrap user's function, allowing picklability 

366 """ 

367 def __init__(self, f, args): 

368 self.f = f 

369 self.args = [] if args is None else args 

370 

371 def __call__(self, x): 

372 return self.f(x, *self.args) 

373 

374 

375class MapWrapper: 

376 """ 

377 Parallelisation wrapper for working with map-like callables, such as 

378 `multiprocessing.Pool.map`. 

379 

380 Parameters 

381 ---------- 

382 pool : int or map-like callable 

383 If `pool` is an integer, then it specifies the number of threads to 

384 use for parallelization. If ``int(pool) == 1``, then no parallel 

385 processing is used and the map builtin is used. 

386 If ``pool == -1``, then the pool will utilize all available CPUs. 

387 If `pool` is a map-like callable that follows the same 

388 calling sequence as the built-in map function, then this callable is 

389 used for parallelization. 

390 """ 

391 def __init__(self, pool=1): 

392 self.pool = None 

393 self._mapfunc = map 

394 self._own_pool = False 

395 

396 if callable(pool): 

397 self.pool = pool 

398 self._mapfunc = self.pool 

399 else: 

400 from multiprocessing import Pool 

401 # user supplies a number 

402 if int(pool) == -1: 

403 # use as many processors as possible 

404 self.pool = Pool() 

405 self._mapfunc = self.pool.map 

406 self._own_pool = True 

407 elif int(pool) == 1: 

408 pass 

409 elif int(pool) > 1: 

410 # use the number of processors requested 

411 self.pool = Pool(processes=int(pool)) 

412 self._mapfunc = self.pool.map 

413 self._own_pool = True 

414 else: 

415 raise RuntimeError("Number of workers specified must be -1," 

416 " an int >= 1, or an object with a 'map' " 

417 "method") 

418 

419 def __enter__(self): 

420 return self 

421 

422 def terminate(self): 

423 if self._own_pool: 

424 self.pool.terminate() 

425 

426 def join(self): 

427 if self._own_pool: 

428 self.pool.join() 

429 

430 def close(self): 

431 if self._own_pool: 

432 self.pool.close() 

433 

434 def __exit__(self, exc_type, exc_value, traceback): 

435 if self._own_pool: 

436 self.pool.close() 

437 self.pool.terminate() 

438 

439 def __call__(self, func, iterable): 

440 # only accept one iterable because that's all Pool.map accepts 

441 try: 

442 return self._mapfunc(func, iterable) 

443 except TypeError as e: 

444 # wrong number of arguments 

445 raise TypeError("The map-like callable must be of the" 

446 " form f(func, iterable)") from e 

447 

448 

449def rng_integers(gen, low, high=None, size=None, dtype='int64', 

450 endpoint=False): 

451 """ 

452 Return random integers from low (inclusive) to high (exclusive), or if 

453 endpoint=True, low (inclusive) to high (inclusive). Replaces 

454 `RandomState.randint` (with endpoint=False) and 

455 `RandomState.random_integers` (with endpoint=True). 

456 

457 Return random integers from the "discrete uniform" distribution of the 

458 specified dtype. If high is None (the default), then results are from 

459 0 to low. 

460 

461 Parameters 

462 ---------- 

463 gen : {None, np.random.RandomState, np.random.Generator} 

464 Random number generator. If None, then the np.random.RandomState 

465 singleton is used. 

466 low : int or array-like of ints 

467 Lowest (signed) integers to be drawn from the distribution (unless 

468 high=None, in which case this parameter is 0 and this value is used 

469 for high). 

470 high : int or array-like of ints 

471 If provided, one above the largest (signed) integer to be drawn from 

472 the distribution (see above for behavior if high=None). If array-like, 

473 must contain integer values. 

474 size : array-like of ints, optional 

475 Output shape. If the given shape is, e.g., (m, n, k), then m * n * k 

476 samples are drawn. Default is None, in which case a single value is 

477 returned. 

478 dtype : {str, dtype}, optional 

479 Desired dtype of the result. All dtypes are determined by their name, 

480 i.e., 'int64', 'int', etc, so byteorder is not available and a specific 

481 precision may have different C types depending on the platform. 

482 The default value is np.int_. 

483 endpoint : bool, optional 

484 If True, sample from the interval [low, high] instead of the default 

485 [low, high) Defaults to False. 

486 

487 Returns 

488 ------- 

489 out: int or ndarray of ints 

490 size-shaped array of random integers from the appropriate distribution, 

491 or a single such random int if size not provided. 

492 """ 

493 if isinstance(gen, Generator): 

494 return gen.integers(low, high=high, size=size, dtype=dtype, 

495 endpoint=endpoint) 

496 else: 

497 if gen is None: 

498 # default is RandomState singleton used by np.random. 

499 gen = np.random.mtrand._rand 

500 if endpoint: 

501 # inclusive of endpoint 

502 # remember that low and high can be arrays, so don't modify in 

503 # place 

504 if high is None: 

505 return gen.randint(low + 1, size=size, dtype=dtype) 

506 if high is not None: 

507 return gen.randint(low, high=high + 1, size=size, dtype=dtype) 

508 

509 # exclusive 

510 return gen.randint(low, high=high, size=size, dtype=dtype) 

511 

512 

513@contextmanager 

514def _fixed_default_rng(seed=1638083107694713882823079058616272161): 

515 """Context with a fixed np.random.default_rng seed.""" 

516 orig_fun = np.random.default_rng 

517 np.random.default_rng = lambda seed=seed: orig_fun(seed) 

518 try: 

519 yield 

520 finally: 

521 np.random.default_rng = orig_fun 

522 

523 

524def _argmin(a, keepdims=False, axis=None): 

525 """ 

526 argmin with a `keepdims` parameter. 

527 

528 See https://github.com/numpy/numpy/issues/8710 

529 

530 If axis is not None, a.shape[axis] must be greater than 0. 

531 """ 

532 res = np.argmin(a, axis=axis) 

533 if keepdims and axis is not None: 

534 res = np.expand_dims(res, axis=axis) 

535 return res 

536 

537 

538def _first_nonnan(a, axis): 

539 """ 

540 Return the first non-nan value along the given axis. 

541 

542 If a slice is all nan, nan is returned for that slice. 

543 

544 The shape of the return value corresponds to ``keepdims=True``. 

545 

546 Examples 

547 -------- 

548 >>> import numpy as np 

549 >>> nan = np.nan 

550 >>> a = np.array([[ 3., 3., nan, 3.], 

551 [ 1., nan, 2., 4.], 

552 [nan, nan, 9., -1.], 

553 [nan, 5., 4., 3.], 

554 [ 2., 2., 2., 2.], 

555 [nan, nan, nan, nan]]) 

556 >>> _first_nonnan(a, axis=0) 

557 array([[3., 3., 2., 3.]]) 

558 >>> _first_nonnan(a, axis=1) 

559 array([[ 3.], 

560 [ 1.], 

561 [ 9.], 

562 [ 5.], 

563 [ 2.], 

564 [nan]]) 

565 """ 

566 k = _argmin(np.isnan(a), axis=axis, keepdims=True) 

567 return np.take_along_axis(a, k, axis=axis) 

568 

569 

570def _nan_allsame(a, axis, keepdims=False): 

571 """ 

572 Determine if the values along an axis are all the same. 

573 

574 nan values are ignored. 

575 

576 `a` must be a numpy array. 

577 

578 `axis` is assumed to be normalized; that is, 0 <= axis < a.ndim. 

579 

580 For an axis of length 0, the result is True. That is, we adopt the 

581 convention that ``allsame([])`` is True. (There are no values in the 

582 input that are different.) 

583 

584 `True` is returned for slices that are all nan--not because all the 

585 values are the same, but because this is equivalent to ``allsame([])``. 

586 

587 Examples 

588 -------- 

589 >>> import numpy as np 

590 >>> a = np.array([[ 3., 3., nan, 3.], 

591 [ 1., nan, 2., 4.], 

592 [nan, nan, 9., -1.], 

593 [nan, 5., 4., 3.], 

594 [ 2., 2., 2., 2.], 

595 [nan, nan, nan, nan]]) 

596 >>> _nan_allsame(a, axis=1, keepdims=True) 

597 array([[ True], 

598 [False], 

599 [False], 

600 [False], 

601 [ True], 

602 [ True]]) 

603 """ 

604 if axis is None: 

605 if a.size == 0: 

606 return True 

607 a = a.ravel() 

608 axis = 0 

609 else: 

610 shp = a.shape 

611 if shp[axis] == 0: 

612 shp = shp[:axis] + (1,)*keepdims + shp[axis + 1:] 

613 return np.full(shp, fill_value=True, dtype=bool) 

614 a0 = _first_nonnan(a, axis=axis) 

615 return ((a0 == a) | np.isnan(a)).all(axis=axis, keepdims=keepdims) 

616 

617 

618def _contains_nan(a, nan_policy='propagate', use_summation=True): 

619 if not isinstance(a, np.ndarray): 

620 use_summation = False # some array_likes ignore nans (e.g. pandas) 

621 policies = ['propagate', 'raise', 'omit'] 

622 if nan_policy not in policies: 

623 raise ValueError("nan_policy must be one of {%s}" % 

624 ', '.join("'%s'" % s for s in policies)) 

625 

626 if np.issubdtype(a.dtype, np.inexact): 

627 # The summation method avoids creating a (potentially huge) array. 

628 if use_summation: 

629 with np.errstate(invalid='ignore', over='ignore'): 

630 contains_nan = np.isnan(np.sum(a)) 

631 else: 

632 contains_nan = np.isnan(a).any() 

633 elif np.issubdtype(a.dtype, object): 

634 contains_nan = False 

635 for el in a.ravel(): 

636 # isnan doesn't work on non-numeric elements 

637 if np.issubdtype(type(el), np.number) and np.isnan(el): 

638 contains_nan = True 

639 break 

640 else: 

641 # Only `object` and `inexact` arrays can have NaNs 

642 contains_nan = False 

643 

644 if contains_nan and nan_policy == 'raise': 

645 raise ValueError("The input contains nan values") 

646 

647 return contains_nan, nan_policy 

648 

649 

650def _rename_parameter(old_name, new_name, dep_version=None): 

651 """ 

652 Generate decorator for backward-compatible keyword renaming. 

653 

654 Apply the decorator generated by `_rename_parameter` to functions with a 

655 recently renamed parameter to maintain backward-compatibility. 

656 

657 After decoration, the function behaves as follows: 

658 If only the new parameter is passed into the function, behave as usual. 

659 If only the old parameter is passed into the function (as a keyword), raise 

660 a DeprecationWarning if `dep_version` is provided, and behave as usual 

661 otherwise. 

662 If both old and new parameters are passed into the function, raise a 

663 DeprecationWarning if `dep_version` is provided, and raise the appropriate 

664 TypeError (function got multiple values for argument). 

665 

666 Parameters 

667 ---------- 

668 old_name : str 

669 Old name of parameter 

670 new_name : str 

671 New name of parameter 

672 dep_version : str, optional 

673 Version of SciPy in which old parameter was deprecated in the format 

674 'X.Y.Z'. If supplied, the deprecation message will indicate that 

675 support for the old parameter will be removed in version 'X.Y+2.Z' 

676 

677 Notes 

678 ----- 

679 Untested with functions that accept *args. Probably won't work as written. 

680 

681 """ 

682 def decorator(fun): 

683 @functools.wraps(fun) 

684 def wrapper(*args, **kwargs): 

685 if old_name in kwargs: 

686 if dep_version: 

687 end_version = dep_version.split('.') 

688 end_version[1] = str(int(end_version[1]) + 2) 

689 end_version = '.'.join(end_version) 

690 message = (f"Use of keyword argument `{old_name}` is " 

691 f"deprecated and replaced by `{new_name}`. " 

692 f"Support for `{old_name}` will be removed " 

693 f"in SciPy {end_version}.") 

694 warnings.warn(message, DeprecationWarning, stacklevel=2) 

695 if new_name in kwargs: 

696 message = (f"{fun.__name__}() got multiple values for " 

697 f"argument now known as `{new_name}`") 

698 raise TypeError(message) 

699 kwargs[new_name] = kwargs.pop(old_name) 

700 return fun(*args, **kwargs) 

701 return wrapper 

702 return decorator 

703 

704 

705def _rng_spawn(rng, n_children): 

706 # spawns independent RNGs from a parent RNG 

707 bg = rng._bit_generator 

708 ss = bg._seed_seq 

709 child_rngs = [np.random.Generator(type(bg)(child_ss)) 

710 for child_ss in ss.spawn(n_children)] 

711 return child_rngs