Skip to content

sampling

Classes for efficient random sampling from discrete distributions

VoseAliasSampling

Implementation of fast biased sampling of discrete values [0, ..., n]

For a concise explanation see https://www.keithschwarz.com/darts-dice-coins/

Parameters

weights: Union[np.array, list]

relative weights of the n events, where weights[i] is the relative 
statistical weight of event i. The weights do not need to be 
normalized.

For an array with length n, generated random values 
will be from range(n).

See Also

RandomWalk

Examples

Create a VoseAliasSampling instance

from pathpy.processes import VoseAliasSampling sampler = VoseAliasSampling([1,1,2])

Fast biased sampling in O(1)

[ sampler.sample() for i in range(10) ] [ 0 2 0 1 2 1 2 1 2 0 2 2 ]

Source code in src/pathpyG/processes/sampling.py
class VoseAliasSampling:
    """
    Implementation of fast biased sampling of discrete values [0, ..., n]

    For a concise explanation see https://www.keithschwarz.com/darts-dice-coins/

    Parameters
    ----------

    weights: Union[np.array, list]

        relative weights of the n events, where weights[i] is the relative 
        statistical weight of event i. The weights do not need to be 
        normalized. 

        For an array with length n, generated random values 
        will be from range(n).

    See Also
    --------
    RandomWalk

    Examples
    --------

    Create a VoseAliasSampling instance

    >>> from pathpy.processes import VoseAliasSampling
    >>> sampler = VoseAliasSampling([1,1,2])

    Fast biased sampling in O(1)

    >>> [ sampler.sample() for i in range(10) ]
    [ 0 2 0 1 2 1 2 1 2 0 2 2 ] 

    """

    def __init__(self, weights: Union[np.array, list]) -> None:
        """
        Initializes probability and alias tables
        """
        self.n = len(weights)
        self.probs = dict()
        self.scaled_probs = dict()
        self.aliases = dict()

        small = list()
        large = list()

        for i in range(1, self.n+1):
            self.probs[i] = weights[i-1]
            self.scaled_probs[i] = self.n*weights[i-1]
            if self.scaled_probs[i]>1:
                large.append(i)
            elif self.scaled_probs[i]<=1:
                small.append(i)

        while small and large:
            l = small.pop()
            g = large.pop()

            self.probs[l] = self.scaled_probs[l]
            self.aliases[l] = g
            self.scaled_probs[g] = self.scaled_probs[l] + self.scaled_probs[g] -1

            if self.scaled_probs[g] < 1:
                small.append(g)
            else:
                large.append(g)
        while large:
            g = large.pop()
            self.probs[g] = 1
        while small:
            l = small.pop()
            self.probs[l] = 1

    def sample(self) -> int:
        """
        Biased sampling of discrete value in O(1)

        Returns
        -------
            integer value from range(n), where n is the length 
            of the weight array used to create the instance.

        """
        i = np.random.randint(1, self.n+1)
        x = np.random.rand()
        if x < self.probs[i]:
            return i-1
        else:
            return self.aliases[i]-1

__init__

Initializes probability and alias tables

Source code in src/pathpyG/processes/sampling.py
def __init__(self, weights: Union[np.array, list]) -> None:
    """
    Initializes probability and alias tables
    """
    self.n = len(weights)
    self.probs = dict()
    self.scaled_probs = dict()
    self.aliases = dict()

    small = list()
    large = list()

    for i in range(1, self.n+1):
        self.probs[i] = weights[i-1]
        self.scaled_probs[i] = self.n*weights[i-1]
        if self.scaled_probs[i]>1:
            large.append(i)
        elif self.scaled_probs[i]<=1:
            small.append(i)

    while small and large:
        l = small.pop()
        g = large.pop()

        self.probs[l] = self.scaled_probs[l]
        self.aliases[l] = g
        self.scaled_probs[g] = self.scaled_probs[l] + self.scaled_probs[g] -1

        if self.scaled_probs[g] < 1:
            small.append(g)
        else:
            large.append(g)
    while large:
        g = large.pop()
        self.probs[g] = 1
    while small:
        l = small.pop()
        self.probs[l] = 1

sample

Biased sampling of discrete value in O(1)

Returns
integer value from range(n), where n is the length 
of the weight array used to create the instance.
Source code in src/pathpyG/processes/sampling.py
def sample(self) -> int:
    """
    Biased sampling of discrete value in O(1)

    Returns
    -------
        integer value from range(n), where n is the length 
        of the weight array used to create the instance.

    """
    i = np.random.randint(1, self.n+1)
    x = np.random.rand()
    if x < self.probs[i]:
        return i-1
    else:
        return self.aliases[i]-1