:py:mod:`kwarray.util_random` ============================= .. py:module:: kwarray.util_random .. autoapi-nested-parse:: Handle and interchange between different random number generators (numpy, python, torch, ...). Also defines useful random iterator functions and :func:`ensure_rng`. Module Contents --------------- Functions ~~~~~~~~~ .. autoapisummary:: kwarray.util_random.seed_global kwarray.util_random.shuffle kwarray.util_random.random_combinations kwarray.util_random.random_product kwarray.util_random._npstate_to_pystate kwarray.util_random._pystate_to_npstate kwarray.util_random._coerce_rng_type kwarray.util_random.ensure_rng Attributes ~~~~~~~~~~ .. autoapisummary:: kwarray.util_random._SEED_MAX .. py:data:: _SEED_MAX .. py:function:: seed_global(seed, offset=0) Seeds the python, numpy, and torch global random states :Parameters: * **seed** (*int*) -- seed to use * **offset** (*int, optional*) -- if specified, uses a different seed for each global random state separated by this offset. .. py:function:: shuffle(items, rng=None) Shuffles a list inplace and then returns it for convinience :Parameters: * **items** (*list or ndarray*) -- list to shuffle * **rng** (*RandomState or int*) -- seed or random number gen :returns: this is the input, but returned for convinience :rtype: list .. rubric:: Example >>> list1 = [1, 2, 3, 4, 5, 6] >>> list2 = shuffle(list(list1), rng=1) >>> assert list1 != list2 >>> result = str(list2) >>> print(result) [3, 2, 5, 1, 4, 6] .. py:function:: random_combinations(items, size, num=None, rng=None) Yields ``num`` combinations of length ``size`` from items in random order :Parameters: * **items** (*List*) -- pool of items to choose from * **size** (*int*) -- number of items in each combination * **num** (*None, default=None*) -- number of combinations to generate * **rng** (*int | RandomState, default=None*) -- seed or random number generator :Yields: *Tuple* -- a random combination of ``items`` of length ``size``. .. rubric:: Example >>> import ubelt as ub >>> items = list(range(10)) >>> size = 3 >>> num = 5 >>> rng = 0 >>> # xdoctest: +IGNORE_WANT >>> combos = list(random_combinations(items, size, num, rng)) >>> print('combos = {}'.format(ub.repr2(combos, nl=1))) combos = [ (0, 6, 9), (4, 7, 8), (4, 6, 7), (2, 3, 5), (1, 2, 4), ] .. rubric:: Example >>> import ubelt as ub >>> items = list(zip(range(10), range(10))) >>> # xdoctest: +IGNORE_WANT >>> combos = list(random_combinations(items, 3, num=5, rng=0)) >>> print('combos = {}'.format(ub.repr2(combos, nl=1))) combos = [ ((0, 0), (6, 6), (9, 9)), ((4, 4), (7, 7), (8, 8)), ((4, 4), (6, 6), (7, 7)), ((2, 2), (3, 3), (5, 5)), ((1, 1), (2, 2), (4, 4)), ] .. py:function:: random_product(items, num=None, rng=None) Yields ``num`` items from the cartesian product of items in a random order. :Parameters: * **items** (*List[Sequence]*) -- items to get caresian product of packed in a list or tuple. (note this deviates from api of :func:`itertools.product`) * **num** (*int, default=None*) -- maximum number of items to generate. If None, all * **rng** (*random.Random | np.random.RandomState | int*) -- random number generator :Yields: *Tuple* -- a random item in the cartesian product .. rubric:: Example >>> import ubelt as ub >>> items = [(1, 2, 3), (4, 5, 6, 7)] >>> rng = 0 >>> # xdoctest: +IGNORE_WANT >>> products = list(random_product(items, rng=0)) >>> print(ub.repr2(products, nl=0)) [(3, 4), (1, 7), (3, 6), (2, 7),... (1, 6), (2, 5), (2, 4)] >>> products = list(random_product(items, num=3, rng=0)) >>> print(ub.repr2(products, nl=0)) [(3, 4), (1, 7), (3, 6)] .. rubric:: Example >>> # xdoctest: +REQUIRES(--profile) >>> rng = ensure_rng(0) >>> items = [np.array([15, 14]), np.array([27, 26]), >>> np.array([21, 22]), np.array([32, 31])] >>> num = 2 >>> for _ in range(100): >>> list(random_product(items, num=num, rng=rng)) .. py:function:: _npstate_to_pystate(npstate) Convert state of a NumPy RandomState object to a state that can be used by Python's Random. Derived from [1]_. .. rubric:: References .. [1] https://stackoverflow.com/questions/44313620/convert-randomstate .. rubric:: Example >>> py_rng = random.Random(0) >>> np_rng = np.random.RandomState(seed=0) >>> npstate = np_rng.get_state() >>> pystate = _npstate_to_pystate(npstate) >>> py_rng.setstate(pystate) >>> assert np_rng.rand() == py_rng.random() .. py:function:: _pystate_to_npstate(pystate) Convert state of a Python Random object to state usable by NumPy RandomState. Derived from [2]_. .. rubric:: References .. [2] https://stackoverflow.com/questions/44313620/convert-randomstate .. rubric:: Example >>> py_rng = random.Random(0) >>> np_rng = np.random.RandomState(seed=0) >>> pystate = py_rng.getstate() >>> npstate = _pystate_to_npstate(pystate) >>> np_rng.set_state(npstate) >>> assert np_rng.rand() == py_rng.random() .. py:function:: _coerce_rng_type(rng) Internal method that transforms input seeds into an integer form. .. py:function:: ensure_rng(rng, api='numpy') Coerces input into a random number generator. This function is useful for ensuring that your code uses a controlled internal random state that is independent of other modules. If the input is None, then a global random state is returned. If the input is a numeric value, then that is used as a seed to construct a random state. If the input is a random number generator, then another random number generator with the same state is returned. Depending on the api, this random state is either return as-is, or used to construct an equivalent random state with the requested api. :Parameters: * **rng** (*int | float | numpy.random.RandomState | random.Random | None*) -- if None, then defaults to the global rng. Otherwise this can be an integer or a RandomState class * **api** (*str, default='numpy'*) -- specify the type of random number generator to use. This can either be 'numpy' for a :class:`numpy.random.RandomState` object or 'python' for a :class:`random.Random` object. :returns: rng - either a numpy or python random number generator, depending on the setting of ``api``. :rtype: (numpy.random.RandomState | random.Random) .. rubric:: Example >>> rng = ensure_rng(None) >>> ensure_rng(0).randint(0, 1000) 684 >>> ensure_rng(np.random.RandomState(1)).randint(0, 1000) 37 .. rubric:: Example >>> num = 4 >>> print('--- Python as PYTHON ---') >>> py_rng = random.Random(0) >>> pp_nums = [py_rng.random() for _ in range(num)] >>> print(pp_nums) >>> print('--- Numpy as PYTHON ---') >>> np_rng = ensure_rng(random.Random(0), api='numpy') >>> np_nums = [np_rng.rand() for _ in range(num)] >>> print(np_nums) >>> print('--- Numpy as NUMPY---') >>> np_rng = np.random.RandomState(seed=0) >>> nn_nums = [np_rng.rand() for _ in range(num)] >>> print(nn_nums) >>> print('--- Python as NUMPY---') >>> py_rng = ensure_rng(np.random.RandomState(seed=0), api='python') >>> pn_nums = [py_rng.random() for _ in range(num)] >>> print(pn_nums) >>> assert np_nums == pp_nums >>> assert pn_nums == nn_nums .. rubric:: Example >>> # Test that random modules can be coerced >>> import random >>> import numpy as np >>> ensure_rng(random, api='python') >>> ensure_rng(random, api='numpy') >>> ensure_rng(np.random, api='python') >>> ensure_rng(np.random, api='numpy') Ignore: >>> np.random.seed(0) >>> np.random.randint(0, 10000) 2732 >>> np.random.seed(0) >>> np.random.mtrand._rand.randint(0, 10000) 2732 >>> np.random.seed(0) >>> ensure_rng(None).randint(0, 10000) 2732 >>> np.random.randint(0, 10000) 9845 >>> ensure_rng(None).randint(0, 10000) 3264