Source code for graphid.util.util_random

import numpy as np
import random
import itertools as it
import ubelt as ub  # NOQA

_SEED_MAX = (2 ** 32 - 1)


[docs] def shuffle(items, rng=None): """ Shuffles a list inplace and then returns it for convinience Args: items (list or ndarray): list to shuffl rng (RandomState or int): seed or random number gen Returns: list: this is the input, but returned for convinience 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] """ rng = ensure_rng(rng) rng.shuffle(items) return items
[docs] def random_combinations(items, size, num=None, rng=None): """ Yields `num` combinations of length `size` from items in random order Args: 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: combo Example: >>> import ubelt as ub # NOQA >>> items = list(range(10)) >>> size = 3 >>> num = 5 >>> rng = 0 >>> combos = list(random_combinations(items, size, num, rng)) >>> result = ('combos = %s' % (ub.urepr(combos),)) >>> print(result) Example: >>> import ubelt as ub # NOQA >>> items = list(zip(range(10), range(10))) >>> size = 3 >>> num = 5 >>> rng = 0 >>> combos = list(random_combinations(items, size, num, rng)) >>> result = ('combos = %s' % (ub.urepr(combos),)) >>> print(result) """ import scipy.special rng = ensure_rng(rng, api='python') num_ = np.inf if num is None else num # Ensure we dont request more than is possible n_max = int(scipy.special.comb(len(items), size)) num_ = min(n_max, num_) if num is not None and num_ > n_max // 2: # If num is too big just generate all combinations and shuffle them combos = list(it.combinations(items, size)) rng.shuffle(combos) for combo in combos[:num]: yield combo else: # Otherwise yield randomly until we get something we havent seen items = list(items) combos = set() while len(combos) < num_: # combo = tuple(sorted(rng.choice(items, size, replace=False))) combo = tuple(sorted(rng.sample(items, size))) if combo not in combos: # TODO: store indices instead of combo values combos.add(combo) yield combo
[docs] def random_product(items, num=None, rng=None): """ Yields `num` items from the cartesian product of items in a random order. Args: items (list of sequences): items to get caresian product of packed in a list or tuple. (note this deviates from api of it.product) Example: >>> items = [(1, 2, 3), (4, 5, 6, 7)] >>> rng = 0 >>> list(random_product(items, rng=0)) >>> list(random_product(items, num=3, rng=0)) """ rng = ensure_rng(rng, 'python') seen = set() items = [list(g) for g in items] max_num = np.prod(np.array(list(map(len, items)))) if num is None: num = max_num if num > max_num: raise ValueError('num exceedes maximum number of products') # TODO: make this more efficient when num is large if num > max_num // 2: for prod in shuffle(list(it.product(*items)), rng=rng): yield prod else: while len(seen) < num: # combo = tuple(sorted(rng.choice(items, size, replace=False))) idxs = tuple(rng.randint(0, len(g) - 1) for g in items) if idxs not in seen: seen.add(idxs) prod = tuple(g[x] for g, x in zip(items, idxs)) yield prod
[docs] def _npstate_to_pystate(npstate): """ Convert state of a NumPy RandomState object to a state that can be used by Python's Random. References: https://stackoverflow.com/questions/44313620/converting-randomstate 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_VERSION = 3 version, keys, pos, has_gauss, cached_gaussian_ = npstate keys_pos = tuple(map(int, keys)) + (int(pos),) cached_gaussian_ = cached_gaussian_ if has_gauss else None pystate = (PY_VERSION, keys_pos, cached_gaussian_) return pystate
[docs] def _pystate_to_npstate(pystate): """ Convert state of a Python Random object to state usable by NumPy RandomState. References: https://stackoverflow.com/questions/44313620/converting-randomstate 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() """ NP_VERSION = 'MT19937' version, keys_pos_, cached_gaussian_ = pystate keys, pos = keys_pos_[:-1], keys_pos_[-1] keys = np.array(keys, dtype=np.uint32) has_gauss = cached_gaussian_ is not None cached_gaussian = cached_gaussian_ if has_gauss else 0.0 npstate = (NP_VERSION, keys, pos, has_gauss, cached_gaussian) return npstate
[docs] def ensure_rng(rng, api='numpy'): """ Returns a random number generator Args: seed: if None, then the rng is unseeded. Otherwise the seed can be an integer or a RandomState class Example: >>> rng = ensure_rng(None) >>> ensure_rng(0).randint(0, 1000) 684 >>> ensure_rng(np.random.RandomState(1)).randint(0, 1000) 37 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 """ if api == 'numpy': if rng is None: rng = np.random elif isinstance(rng, int): rng = np.random.RandomState(seed=rng % _SEED_MAX) elif isinstance(rng, random.Random): # Convert python to numpy random state py_rng = rng pystate = py_rng.getstate() npstate = _pystate_to_npstate(pystate) rng = np_rng = np.random.RandomState(seed=0) np_rng.set_state(npstate) elif api == 'python': if rng is None: rng = random elif isinstance(rng, int): rng = random.Random(rng % _SEED_MAX) elif isinstance(rng, np.random.RandomState): # Convert numpy to python random state np_rng = rng npstate = np_rng.get_state() pystate = _npstate_to_pystate(npstate) rng = py_rng = random.Random(0) py_rng.setstate(pystate) else: raise KeyError('unknown rng api={}'.format(api)) return rng
if __name__ == '__main__': """ CommandLine: python -m netharn.util.util_random all """ import xdoctest xdoctest.doctest_module(__file__)