Source code for chainer.utils.walker_alias

import numpy

from chainer import cuda


[docs]class WalkerAlias(object): """Implementation of Walker's alias method. This method generates a random sample from given probabilities :math:`p_1, \\dots, p_n` in :math:`O(1)` time. It is more efficient than :func:`~numpy.random.choice`. This class works on both CPU and GPU. Args: probs (float list): Probabilities of entries. They are normalized with `sum(probs)`. See: `Wikipedia article <https://en.wikipedia.org/wiki/Alias_method>`_ """ def __init__(self, probs): prob = numpy.array(probs, numpy.float32) prob /= numpy.sum(prob) threshold = numpy.ndarray(len(probs), numpy.float32) values = numpy.ndarray(len(probs) * 2, numpy.int32) il, ir = 0, 0 pairs = list(zip(prob, range(len(probs)))) pairs.sort() for prob, i in pairs: p = prob * len(probs) while p > 1 and ir < il: values[ir * 2 + 1] = i p -= 1.0 - threshold[ir] ir += 1 threshold[il] = p values[il * 2] = i il += 1 # fill the rest for i in range(ir, len(probs)): values[i * 2 + 1] = 0 assert((values < len(threshold)).all()) self.threshold = threshold self.values = values self.use_gpu = False
[docs] def to_gpu(self): """Make a sampler GPU mode. """ if not self.use_gpu: self.threshold = cuda.to_gpu(self.threshold) self.values = cuda.to_gpu(self.values) self.use_gpu = True
def to_cpu(self): """Make a sampler CPU mode. """ if self.use_gpu: self.threshold = cuda.to_cpu(self.threshold) self.values = cuda.to_cpu(self.values) self.use_gpu = False
[docs] def sample(self, shape): """Generates a random sample based on given probabilities. Args: shape (tuple of int): Shape of a return value. Returns: Returns a generated array with the given shape. If a sampler is in CPU mode the return value is a :class:`numpy.ndarray` object, and if it is in GPU mode the return value is a :class:`cupy.ndarray` object. """ if self.use_gpu: return self.sample_gpu(shape) else: return self.sample_cpu(shape)
def sample_cpu(self, shape): ps = numpy.random.uniform(0, 1, shape) pb = ps * len(self.threshold) index = pb.astype(numpy.int32) left_right = (self.threshold[index] < pb - index).astype(numpy.int32) return self.values[index * 2 + left_right] def sample_gpu(self, shape): ps = cuda.cupy.random.uniform(size=shape, dtype=numpy.float32) vs = cuda.elementwise( 'T ps, raw T threshold , raw S values, int32 b', 'int32 vs', ''' T pb = ps * b; int index = __float2int_rd(pb); // fill_uniform sometimes returns 1.0, so we need to check index if (index >= b) { index = 0; } int lr = threshold[index] < pb - index; vs = values[index * 2 + lr]; ''', 'walker_alias_sample' )(ps, self.threshold, self.values, len(self.threshold)) return vs