You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

rng.py 22 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. # -*- coding: utf-8 -*-
  2. # MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  3. #
  4. # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
  5. #
  6. # Unless required by applicable law or agreed to in writing,
  7. # software distributed under the License is distributed on an
  8. # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. import collections
  10. import time
  11. from typing import Iterable, Optional, Union
  12. from numpy.random import MT19937
  13. from .. import Tensor
  14. from ..core._imperative_rt.core2 import apply
  15. from ..core._imperative_rt.core2 import sync as _sync
  16. from ..core._imperative_rt.ops import delete_rng_handle as _delete_rng_handle
  17. from ..core._imperative_rt.ops import get_global_rng_seed as _get_global_rng_seed
  18. from ..core._imperative_rt.ops import (
  19. get_rng_handle_compnode as _get_rng_handle_compnode,
  20. )
  21. from ..core._imperative_rt.ops import new_rng_handle as _new_rng_handle
  22. from ..core._imperative_rt.ops import set_global_rng_seed as _set_global_rng_seed
  23. from ..core.ops.builtin import (
  24. BetaRNG,
  25. GammaRNG,
  26. GaussianRNG,
  27. PermutationRNG,
  28. PoissonRNG,
  29. ShuffleRNG,
  30. UniformRNG,
  31. )
  32. from ..core.tensor import utils
  33. from ..device import get_default_device
  34. __all__ = [
  35. "seed",
  36. "RNG",
  37. "uniform",
  38. "normal",
  39. "gamma",
  40. "beta",
  41. "poisson",
  42. "permutation",
  43. "shuffle",
  44. ]
  45. _rng = None
  46. def _infer_broadcasted_shape(inps: Iterable[Tensor]) -> tuple:
  47. broadcasted_ndim = inps[0].ndim
  48. broadcasted_shape = list(inps[0]._tuple_shape)
  49. for i in range(1, len(inps)):
  50. cur_ndim = inps[i].ndim
  51. cur_shape = list(inps[i]._tuple_shape)
  52. n_dim = max(cur_ndim, broadcasted_ndim)
  53. for j in range(n_dim - 1, -1, -1):
  54. cur_dim = cur_ndim + j - n_dim
  55. broad_dim = broadcasted_ndim + j - n_dim
  56. cur_size = cur_shape[cur_dim] if cur_dim >= 0 else 1
  57. broad_size = broadcasted_shape[broad_dim] if broad_dim >= 0 else 1
  58. assert cur_size == broad_size or cur_size == 1 or broad_size == 1, (
  59. "The size of inps[{}] ({}) must match the size ({}) at "
  60. "dim {}".format(i, cur_size, broad_size, j)
  61. )
  62. broad_size = max(cur_size, broad_size)
  63. if broad_dim < 0:
  64. broadcasted_shape = [broad_size] + broadcasted_shape
  65. broadcasted_ndim += 1
  66. else:
  67. broadcasted_shape[broad_dim] = broad_size
  68. return tuple(broadcasted_shape)
  69. def _broadcast_tensors_with_size(
  70. inps: Iterable[Tensor], size: Iterable[int]
  71. ) -> Iterable[Tensor]:
  72. assert inps, "The inps cloud not be empty"
  73. target_shape = _infer_broadcasted_shape(inps)
  74. if isinstance(size, collections.abc.Iterable):
  75. target_shape = tuple(size) + target_shape
  76. target_ndim = len(target_shape)
  77. for i in range(len(inps)):
  78. if inps[i]._tuple_shape != target_shape:
  79. inps[i] = (
  80. inps[i]
  81. .reshape((1,) * (target_ndim - inps[i].ndim) + inps[i]._tuple_shape)
  82. ._broadcast(target_shape)
  83. )
  84. return inps
  85. def _uniform(
  86. low: float,
  87. high: float,
  88. size: Optional[Iterable[int]],
  89. seed: int,
  90. device: str,
  91. handle: int,
  92. ) -> Tensor:
  93. assert low < high, "Uniform is not defined when low >= high"
  94. if size is None:
  95. size = (1,)
  96. op = UniformRNG(seed=seed, handle=handle, dtype="float32")
  97. _ref = Tensor([], dtype="int32", device=device)
  98. shape = utils.astensor1d(size, _ref, dtype="int32", device=device)
  99. (output,) = apply(op, shape)
  100. if low == 0 and high == 1:
  101. return output
  102. return low + (high - low) * output
  103. def _normal(
  104. mean: float,
  105. std: float,
  106. size: Optional[Iterable[int]],
  107. seed: int,
  108. device: str,
  109. handle: int,
  110. ) -> Tensor:
  111. if size is None:
  112. size = (1,)
  113. op = GaussianRNG(seed=seed, mean=mean, std=std, handle=handle, dtype="float32")
  114. _ref = Tensor([], dtype="int32", device=device)
  115. shape = utils.astensor1d(size, _ref, dtype="int32", device=device)
  116. (output,) = apply(op, shape)
  117. return output
  118. def _gamma(
  119. shape: Union[Tensor, float],
  120. scale: Union[Tensor, float],
  121. size: Optional[Iterable[int]],
  122. seed: int,
  123. handle: int,
  124. ) -> Tensor:
  125. handle_cn = None if handle == 0 else _get_rng_handle_compnode(handle)
  126. if not isinstance(shape, Tensor):
  127. assert shape > 0, "Gamma is not defined when shape <= 0"
  128. shape = Tensor(shape, dtype="float32", device=handle_cn)
  129. if not isinstance(scale, Tensor):
  130. assert scale > 0, "Gamma is not defined when scale <= 0"
  131. scale = Tensor(scale, dtype="float32", device=handle_cn)
  132. assert (
  133. handle_cn is None or handle_cn == shape.device
  134. ), "The shape ({}) must be the same device with handle ({})".format(
  135. shape.device, handle_cn
  136. )
  137. assert (
  138. handle_cn is None or handle_cn == scale.device
  139. ), "The scale ({}) must be the same device with handle ({})".format(
  140. scale.device, handle_cn
  141. )
  142. if isinstance(size, int) and size != 0:
  143. size = (size,)
  144. shape, scale = _broadcast_tensors_with_size([shape, scale], size)
  145. op = GammaRNG(seed=seed, handle=handle)
  146. (output,) = apply(op, shape, scale)
  147. return output
  148. def _beta(
  149. alpha: Union[Tensor, float],
  150. beta: Union[Tensor, float],
  151. size: Optional[Iterable[int]],
  152. seed: int,
  153. handle: int,
  154. ) -> Tensor:
  155. handle_cn = None if handle == 0 else _get_rng_handle_compnode(handle)
  156. if not isinstance(alpha, Tensor):
  157. assert alpha > 0, "Beta is not defined when alpha <= 0"
  158. alpha = Tensor(alpha, dtype="float32", device=handle_cn)
  159. if not isinstance(beta, Tensor):
  160. assert beta > 0, "Beta is not defined when beta <= 0"
  161. beta = Tensor(beta, dtype="float32", device=handle_cn)
  162. assert (
  163. handle_cn is None or handle_cn == alpha.device
  164. ), "The alpha ({}) must be the same device with handle ({})".format(
  165. alpha.device, handle_cn
  166. )
  167. assert (
  168. handle_cn is None or handle_cn == beta.device
  169. ), "The beta ({}) must be the same device with handle ({})".format(
  170. beta.device, handle_cn
  171. )
  172. if isinstance(size, int) and size != 0:
  173. size = (size,)
  174. alpha, beta = _broadcast_tensors_with_size([alpha, beta], size)
  175. op = BetaRNG(seed=seed, handle=handle)
  176. (output,) = apply(op, alpha, beta)
  177. return output
  178. def _poisson(
  179. lam: Union[Tensor, float], size: Optional[Iterable[int]], seed: int, handle: int
  180. ) -> Tensor:
  181. handle_cn = None if handle == 0 else _get_rng_handle_compnode(handle)
  182. if not isinstance(lam, Tensor):
  183. assert lam > 0, "Poisson is not defined when lam <= 0"
  184. lam = Tensor(lam, dtype="float32", device=handle_cn)
  185. if isinstance(size, int) and size != 0:
  186. size = (size,)
  187. assert (
  188. handle_cn is None or handle_cn == lam.device
  189. ), "The lam ({}) must be the same device with handle ({})".format(
  190. lam.device, handle_cn
  191. )
  192. (lam,) = _broadcast_tensors_with_size([lam], size)
  193. op = PoissonRNG(seed=seed, handle=handle)
  194. (output,) = apply(op, lam)
  195. return output
  196. def _permutation(n: int, seed: int, device: str, handle: int, dtype: str) -> Tensor:
  197. assert isinstance(n, int)
  198. assert n >= 0, "Permutation is not defined when n < 0"
  199. size = (n,)
  200. op = PermutationRNG(seed=seed, handle=handle, dtype=dtype)
  201. _ref = Tensor([], dtype="int32", device=device)
  202. shape = utils.astensor1d(size, _ref, dtype="int32", device=device)
  203. (output,) = apply(op, shape)
  204. return output
  205. def _shuffle(inp: Tensor, seed: int, handle: int) -> Tensor:
  206. assert inp.size > 0, "size needs to be greater than 0"
  207. op = ShuffleRNG(seed=seed, handle=handle)
  208. output, _ = apply(op, inp)
  209. return output
  210. class RNG:
  211. r""":class:`RNG` exposes a number of methods for generating random numbers.
  212. Args:
  213. seed: random seed used to initialize the pseudo-random number generator. Default: None
  214. device: the device of generated tensor. Default: None
  215. Examples:
  216. .. testcode::
  217. import megengine.random as rand
  218. rng = rand.RNG(seed=100)
  219. x = rng.uniform(size=(2, 2))
  220. print(x.numpy())
  221. Outputs:
  222. .. testoutput::
  223. :options: +SKIP
  224. [[0.84811664 0.6147553 ]
  225. [0.59429836 0.64727545]]
  226. """
  227. def __init__(self, seed: int = None, device: str = None):
  228. self._device = device if device else get_default_device()
  229. if seed is not None:
  230. self._seed = seed
  231. self._handle = _new_rng_handle(self._device, self._seed)
  232. else:
  233. self._seed = _get_global_rng_seed
  234. self._handle = 0
  235. self._device = None
  236. def uniform(
  237. self, low: float = 0, high: float = 1, size: Optional[Iterable[int]] = None
  238. ):
  239. r"""Random variable with uniform distribution $U(0, 1)$.
  240. Args:
  241. low: lower range. Default: 0
  242. high: upper range. Default: 1
  243. size: the size of output tensor. Default: None
  244. Returns:
  245. the output tensor.
  246. Examples:
  247. .. testcode::
  248. import megengine as mge
  249. import megengine.random as rand
  250. x = rand.uniform(size=(2, 2))
  251. print(x.numpy())
  252. Outputs:
  253. .. testoutput::
  254. :options: +SKIP
  255. [[0.91600335 0.6680226 ]
  256. [0.2046729 0.2769141 ]]
  257. """
  258. _seed = self._seed() if callable(self._seed) else self._seed
  259. return _uniform(
  260. low=low,
  261. high=high,
  262. size=size,
  263. seed=_seed,
  264. device=self._device,
  265. handle=self._handle,
  266. )
  267. def normal(
  268. self, mean: float = 0, std: float = 1, size: Optional[Iterable[int]] = None
  269. ):
  270. r"""Random variable with Gaussian distribution :math:`N(\mu, \sigma)`.
  271. Args:
  272. mean: the mean or expectation of the distribution. Default: 0
  273. std: the standard deviation of the distribution (variance = :math:`\sigma ^ 2`).
  274. Default: 1
  275. size: the size of output tensor. Default: None
  276. Returns:
  277. the output tensor.
  278. Examples:
  279. .. testcode::
  280. import megengine as mge
  281. import megengine.random as rand
  282. x = rand.normal(mean=0, std=1, size=(2, 2))
  283. print(x.numpy())
  284. Outputs:
  285. .. testoutput::
  286. :options: +SKIP
  287. [[-1.4010863 -0.9874344 ]
  288. [ 0.56373274 0.79656655]]
  289. """
  290. _seed = self._seed() if callable(self._seed) else self._seed
  291. return _normal(
  292. mean=mean,
  293. std=std,
  294. size=size,
  295. seed=_seed,
  296. device=self._device,
  297. handle=self._handle,
  298. )
  299. def gamma(
  300. self,
  301. shape: Union[Tensor, float],
  302. scale: Union[Tensor, float] = 1,
  303. size: Optional[Iterable[int]] = None,
  304. ):
  305. r"""Random variable with Gamma distribution :math:`\Gamma(k, \theta)`.
  306. The corresponding probability density function is
  307. .. math::
  308. p(x)=x^{k-1} \frac{e^{-x / \theta}}{\theta^{k} \Gamma(k)}
  309. \quad \text { for } x>0 \quad k, \theta>0,
  310. where :math:`\Gamma(k)` is the gamma function,
  311. .. math::
  312. \Gamma(k)=(k-1) ! \quad \text { for } \quad k>0.
  313. Args:
  314. shape: the shape parameter (sometimes designated "k") of the distribution.
  315. Must be non-negative.
  316. scale: the scale parameter (sometimes designated "theta") of the distribution.
  317. Must be non-negative. Default: 1
  318. size: the size of output tensor. If shape and scale are scalars and given size is, e.g.,
  319. `(m, n)`, then the output shape is `(m, n)`. If shape or scale is a Tensor and given size
  320. is, e.g., `(m, n)`, then the output shape is `(m, n) + broadcast(shape, scale).shape`.
  321. The broadcast rules are consistent with `numpy.broadcast`. Default: None
  322. Returns:
  323. the output tensor.
  324. Examples:
  325. .. testcode::
  326. import megengine as mge
  327. import megengine.random as rand
  328. x = rand.gamma(shape=2, scale=1, size=(2, 2))
  329. print(x.numpy())
  330. shape = mge.Tensor([[ 1],
  331. [10]], dtype="float32")
  332. scale = mge.Tensor([1,5], dtype="float32")
  333. x = rand.gamma(shape=shape, scale=scale)
  334. print(x.numpy())
  335. x = rand.gamma(shape=shape, scale=scale, size=2)
  336. print(x.numpy())
  337. Outputs:
  338. .. testoutput::
  339. :options: +SKIP
  340. [[1.5064533 4.0689363 ]
  341. [0.71639484 1.4551026 ]]
  342. [[ 0.4352188 11.399335 ]
  343. [ 9.1888 52.009277 ]]
  344. [[[ 1.1726005 3.9654975 ]
  345. [13.656933 36.559006 ]]
  346. [[ 0.25848487 2.5540342 ]
  347. [11.960409 21.031536 ]]]
  348. """
  349. _seed = self._seed() if callable(self._seed) else self._seed
  350. return _gamma(
  351. shape=shape, scale=scale, size=size, seed=_seed, handle=self._handle
  352. )
  353. def beta(
  354. self,
  355. alpha: Union[Tensor, float],
  356. beta: Union[Tensor, float],
  357. size: Optional[Iterable[int]] = None,
  358. ):
  359. r"""Random variable with Beta distribution :math:`\operatorname{Beta}(\alpha, \beta)`.
  360. The corresponding probability density function is
  361. .. math::
  362. p(x)=\frac{1}{\mathrm{~B}(\alpha, \beta)} x^{\alpha-1}(1-x)^{\beta-1}
  363. \quad \text { for } \alpha, \beta>0,
  364. where :math:`\mathrm{~B}(\alpha, \beta)` is the beta function,
  365. .. math::
  366. \mathrm{~B}(\alpha, \beta)=\int_{0}^{1} t^{\alpha-1}(1-t)^{\beta-1} d t.
  367. Args:
  368. alpha: the alpha parameter of the distribution. Must be non-negative.
  369. beta: the beta parameter of the distribution. Must be non-negative.
  370. size: the size of output tensor. If alpha and beta are scalars and given size is, e.g.,
  371. `(m, n)`, then the output shape is `(m, n)`. If alpha or beta is a Tensor and given size
  372. is, e.g., `(m, n)`, then the output shape is `(m, n) + broadcast(alpha, beta).shape`.
  373. Returns:
  374. the output tensor.
  375. Examples:
  376. .. testcode::
  377. import megengine as mge
  378. import megengine.random as rand
  379. x = rand.beta(alpha=2, beta=1, size=(2, 2))
  380. print(x.numpy())
  381. alpha = mge.Tensor([[0.5],
  382. [ 3]], dtype="float32")
  383. beta = mge.Tensor([0.5,5], dtype="float32")
  384. x = rand.beta(alpha=alpha, beta=beta)
  385. print(x.numpy())
  386. x = rand.beta(alpha=alpha, beta=beta, size=2)
  387. print(x.numpy())
  388. Outputs:
  389. .. testoutput::
  390. :options: +SKIP
  391. [[0.582565 0.91763186]
  392. [0.86963767 0.6088103 ]]
  393. [[0.41503012 0.16438372]
  394. [0.90159506 0.47588003]]
  395. [[[0.55195075 0.01111084]
  396. [0.95298755 0.25048104]]
  397. [[0.11680304 0.13859665]
  398. [0.997879 0.43259275]]]
  399. """
  400. _seed = self._seed() if callable(self._seed) else self._seed
  401. return _beta(alpha=alpha, beta=beta, size=size, seed=_seed, handle=self._handle)
  402. def poisson(self, lam: Union[float, Tensor], size: Optional[Iterable[int]] = None):
  403. r"""Random variable with poisson distribution :math:`\operatorname{Poisson}(\lambda)`.
  404. The corresponding probability density function is
  405. .. math::
  406. f(k ; \lambda)=\frac{\lambda^{k} e^{-\lambda}}{k !},
  407. where k is the number of occurrences :math:`({\displaystyle k=0,1,2...})`.
  408. Args:
  409. lam: the lambda parameter of the distribution. Must be non-negative.
  410. size: the size of output tensor. If lam is a scalar and given size is, e.g., `(m, n)`,
  411. then the output shape is `(m, n)`. If lam is a Tensor with shape `(k, v)` and given
  412. size is, e.g., `(m, n)`, then the output shape is `(m, n, k, v)`. Default: None.
  413. Returns:
  414. the output tensor.
  415. Examples:
  416. .. testcode::
  417. import megengine as mge
  418. import megengine.random as rand
  419. x = rand.poisson(lam=2., size=(1, 3))
  420. print(x.numpy())
  421. lam = mge.Tensor([[1.,1.],
  422. [10,10]], dtype="float32")
  423. x = rand.poisson(lam=lam)
  424. print(x.numpy())
  425. x = rand.poisson(lam=lam, size=(1,3))
  426. print(x.numpy())
  427. Outputs:
  428. .. testoutput::
  429. :options: +SKIP
  430. [[3. 1. 3.]]
  431. [[ 2. 2.]
  432. [12. 11.]]
  433. [[[[ 1. 1.]
  434. [11. 4.]]
  435. [[ 0. 0.]
  436. [ 9. 13.]]
  437. [[ 0. 1.]
  438. [ 7. 12.]]]]
  439. """
  440. _seed = self._seed() if callable(self._seed) else self._seed
  441. return _poisson(lam=lam, size=size, seed=_seed, handle=self._handle)
  442. def permutation(self, n: Union[int, Tensor], *, dtype: str = "int32"):
  443. r"""Randomly permute a sequence, or return a permuted range.
  444. If ``n`` is a multi-dimensional tensor, it is only shuffled along its first index.
  445. Args:
  446. n: If ``n`` is an integer, random permutation of integers from :math:`0` to :math:`n - 1`.
  447. If ``n`` is an tensor, make a copy and shuffle the elements randomly.
  448. dtype: the output data type when ``n`` is an integer.
  449. int32, int16 and float32 are supported. Default: int32
  450. Returns:
  451. the output tensor.
  452. Examples:
  453. .. testcode::
  454. import numpy as np
  455. import megengine as mge
  456. import megengine.random as rand
  457. x = rand.permutation(10, dtype="int32")
  458. print(x.numpy())
  459. x = rand.permutation(10, dtype="float32")
  460. print(x.numpy())
  461. x = mge.tensor(np.arange(18)).reshape(6,3)
  462. x = rand.permutation(x)
  463. print(x.numpy())
  464. Outputs:
  465. .. testoutput::
  466. :options: +SKIP
  467. [4 5 0 7 3 8 6 1 9 2]
  468. [3. 4. 9. 0. 6. 8. 7. 1. 5. 2.]
  469. [[12 13 14]
  470. [ 3 4 5]
  471. [15 16 17]
  472. [ 0 1 2]
  473. [ 9 10 11]
  474. [ 6 7 8]]
  475. """
  476. _seed = self._seed() if callable(self._seed) else self._seed
  477. if isinstance(n, int):
  478. return _permutation(
  479. n=n, seed=_seed, device=self._device, handle=self._handle, dtype=dtype
  480. )
  481. assert isinstance(n, Tensor)
  482. return _shuffle(inp=n, seed=_seed, handle=self._handle)
  483. def shuffle(self, inp: Tensor):
  484. r"""Modify a sequence in-place by shuffling its contents.
  485. This function only shuffles the Tensor along the first axis of a multi-dimensional Tensor.
  486. The order of sub-Tensors is changed but their contents remains the same.
  487. Args:
  488. inp: input tensor.
  489. Examples:
  490. .. testcode::
  491. import numpy as np
  492. import megengine as mge
  493. import megengine.random as rand
  494. x = mge.tensor(np.arange(10))
  495. rand.shuffle(x)
  496. print(x.numpy())
  497. y = mge.tensor(np.arange(18)).reshape(6,3)
  498. rand.shuffle(y)
  499. print(y.numpy())
  500. Outputs:
  501. .. testoutput::
  502. :options: +SKIP
  503. [7 9 3 0 8 2 4 5 6 1]
  504. [[12. 13. 14.]
  505. [ 3. 4. 5.]
  506. [15. 16. 17.]
  507. [ 0. 1. 2.]
  508. [ 9. 10. 11.]
  509. [ 6. 7. 8.]]
  510. """
  511. _seed = self._seed() if callable(self._seed) else self._seed
  512. inp._reset(_shuffle(inp=inp, seed=_seed, handle=self._handle))
  513. def __del__(self):
  514. if self._handle != 0:
  515. # RNG op might execute after handle released due to async dispatch, so
  516. # we need sync before delete a handle to avoid memory leak or
  517. # use-after-free
  518. _sync()
  519. _delete_rng_handle(self._handle)
  520. def _default_rng():
  521. r"""Default constructor for :class:`RNG`."""
  522. return RNG(seed=None, device=None)
  523. _default_handle = _default_rng()
  524. uniform = _default_handle.uniform
  525. normal = _default_handle.normal
  526. gamma = _default_handle.gamma
  527. beta = _default_handle.beta
  528. poisson = _default_handle.poisson
  529. permutation = _default_handle.permutation
  530. shuffle = _default_handle.shuffle
  531. def _random_seed_generator():
  532. assert _rng
  533. while True:
  534. yield _rng.random_raw()
  535. def seed(seed: int):
  536. global _rng # pylint: disable=global-statement
  537. _rng = MT19937(seed=seed)
  538. _set_global_rng_seed(seed)
  539. seed(int(time.time()))