| import torch |
| |
| |
| class SobolEngine(object): |
| r""" |
| The :class:`torch.quasirandom.SobolEngine` is an engine for generating |
| (scrambled) Sobol sequences. Sobol sequences are an example of low |
| discrepancy quasi-random sequences. |
| |
| This implementation of an engine for Sobol sequences is capable of |
| sampling sequences up to a maximum dimension of 1111. It uses direction |
| numbers to generate these sequences, and these numbers have been adapted |
| from `here <http://web.maths.unsw.edu.au/~fkuo/sobol/joe-kuo-old.1111>`_. |
| |
| References: |
| - Art B. Owen. Scrambling Sobol and Niederreiter-Xing points. |
| Journal of Complexity, 14(4):466-489, December 1998. |
| |
| - I. M. Sobol. The distribution of points in a cube and the accurate |
| evaluation of integrals. |
| Zh. Vychisl. Mat. i Mat. Phys., 7:784-802, 1967. |
| |
| Args: |
| dimension (Int): The dimensionality of the sequence to be drawn |
| scramble (bool, optional): Setting this to ``True`` will produce |
| scrambled Sobol sequences. Scrambling is |
| capable of producing better Sobol |
| sequences. Default: ``False``. |
| seed (Int, optional): This is the seed for the scrambling. The seed |
| of the random number generator is set to this, |
| if specified. Otherwise, it uses a random seed. |
| Default: ``None`` |
| |
| Examples:: |
| |
| >>> soboleng = torch.quasirandom.SobolEngine(dimension=5) |
| >>> soboleng.draw(3) |
| tensor([[0.5000, 0.5000, 0.5000, 0.5000, 0.5000], |
| [0.7500, 0.2500, 0.7500, 0.2500, 0.7500], |
| [0.2500, 0.7500, 0.2500, 0.7500, 0.2500]]) |
| """ |
| MAXBIT = 30 |
| MAXDIM = 1111 |
| |
| def __init__(self, dimension, scramble=False, seed=None): |
| if dimension > self.MAXDIM or dimension < 1: |
| raise ValueError("Supported range of dimensionality " |
| "for SobolEngine is [1, {}]".format(self.MAXDIM)) |
| |
| self.seed = seed |
| self.scramble = scramble |
| self.dimension = dimension |
| |
| cpu = torch.device("cpu") |
| |
| self.sobolstate = torch.zeros(dimension, self.MAXBIT, device=cpu, dtype=torch.long) |
| torch._sobol_engine_initialize_state_(self.sobolstate, self.dimension) |
| |
| if self.scramble: |
| if self.seed is not None: |
| g = torch.Generator() |
| g.manual_seed(self.seed) |
| else: |
| g = None |
| |
| shift_ints = torch.randint(2, (self.dimension, self.MAXBIT), device=cpu, generator=g) |
| self.shift = torch.mv(shift_ints, torch.pow(2, torch.arange(0, self.MAXBIT, device=cpu))) |
| |
| ltm_dims = (self.dimension, self.MAXBIT, self.MAXBIT) |
| ltm = torch.randint(2, ltm_dims, device=cpu, generator=g).tril() |
| |
| torch._sobol_engine_scramble_(self.sobolstate, ltm, self.dimension) |
| else: |
| self.shift = torch.zeros(self.dimension, device=cpu, dtype=torch.long) |
| |
| self.quasi = self.shift.clone(memory_format=torch.contiguous_format) |
| self.num_generated = 0 |
| |
| def draw(self, n=1, out=None, dtype=torch.float32): |
| r""" |
| Function to draw a sequence of :attr:`n` points from a Sobol sequence. |
| Note that the samples are dependent on the previous samples. The size |
| of the result is :math:`(n, dimension)`. |
| |
| Args: |
| n (Int, optional): The length of sequence of points to draw. |
| Default: 1 |
| out (Tensor, optional): The output tensor |
| dtype (:class:`torch.dtype`, optional): the desired data type of the |
| returned tensor. |
| Default: ``torch.float32`` |
| """ |
| result, self.quasi = torch._sobol_engine_draw(self.quasi, n, self.sobolstate, |
| self.dimension, self.num_generated, dtype=dtype) |
| self.num_generated += n |
| if out is not None: |
| out.resize_as_(result).copy_(result) |
| return out |
| return result |
| |
| def reset(self): |
| r""" |
| Function to reset the ``SobolEngine`` to base state. |
| """ |
| self.quasi.copy_(self.shift) |
| self.num_generated = 0 |
| return self |
| |
| def fast_forward(self, n): |
| r""" |
| Function to fast-forward the state of the ``SobolEngine`` by |
| :attr:`n` steps. This is equivalent to drawing :attr:`n` samples |
| without using the samples. |
| |
| Args: |
| n (Int): The number of steps to fast-forward by. |
| """ |
| torch._sobol_engine_ff_(self.quasi, n, self.sobolstate, self.dimension, self.num_generated) |
| self.num_generated += n |
| return self |
| |
| def __repr__(self): |
| fmt_string = ['dimension={}'.format(self.dimension)] |
| if self.scramble: |
| fmt_string += ['scramble=True'] |
| if self.seed is not None: |
| fmt_string += ['seed={}'.format(self.seed)] |
| return self.__class__.__name__ + '(' + ', '.join(fmt_string) + ')' |