| import math |
| import torch |
| from gpytorch.priors import Prior |
| from torch.distributions import constraints,Uniform,Normal |
| from torch.distributions.utils import broadcast_all |
| from numbers import Number |
|
|
| class MollifiedUniformPrior(Prior): |
| r"""Uniform distribution that is differentiable everywhere |
| |
| This is an approximation to the Uniform distribution which maintains differentiability by placing a |
| Gaussian distribution over points away from the original support. The density for a single dimension is |
| |
| .. math:: |
| |
| p(x) &= \frac{M}{\sqrt{2\pi\sigma^2}}\exp\left[-\frac{d(x)^2}{2\sigma^2}\right], \\ |
| d(x) &= \begin{cases} |
| a-x & x < a \\ |
| 0 & a\leq x < b\\ |
| x-b & x \geq b |
| \end{cases} |
| |
| :param a: lower range (inclusive) |
| :type a: float or torch.Tensor |
| |
| :param b: upper range (exclusive) |
| :type b: float or torch.Tensor |
| |
| :param tail_sigma: Standard deviation of the Gaussian distributions on the tails. Lower values make the |
| approximation closer to Uniform, but may increase the optimization effort. Defaults to 0.1 |
| :type tail_sigma: float or torch.Tensor, optional |
| |
| .. note:: |
| The `rsample` method for this distribution returns uniformly distributed samples from the interval `[a,b)`, |
| and **not** from the Mollified distribution. The `log_prob` method, however, returns the correct probability. |
| The purpose of the priors in this package is to generate initial starting points for optimizing |
| hyperparameters and for MAP estimation. |
| """ |
| arg_constraints = {'a':constraints.real,'b':constraints.real,'tail_sigma':constraints.positive} |
| support = constraints.real |
| def __init__(self,a,b,tail_sigma=0.1): |
| self.a,self.b,self.tail_sigma = broadcast_all(a,b,tail_sigma) |
| |
| if isinstance(a,Number) or isinstance(b,Number): |
| batch_shape = torch.Size() |
| else: |
| batch_shape = self.a.size() |
|
|
| super().__init__(batch_shape) |
| |
| @property |
| def mean(self): |
| return (self.a+self.b)/2 |
| |
| @property |
| def _half_range(self): |
| return (self.b-self.a)/2 |
|
|
| @property |
| def _log_normalization_constant(self): |
| return -torch.log(1+(self.b-self.a)/(math.sqrt(2*math.pi)*self.tail_sigma)) |
|
|
| def log_prob(self,X): |
| |
| tail_dist = ((X-self.mean).abs()-self._half_range).clamp(min=0) |
| return Normal(loc=torch.zeros_like(self.a),scale=self.tail_sigma).log_prob(tail_dist)+self._log_normalization_constant |
| |
| def rsample(self,sample_shape=torch.Size([])): |
| return Uniform(self.a,self.b).rsample(sample_shape).to(self.a) |
|
|
| def expand(self,expand_shape): |
| batch_shape = torch.Size(expand_shape) |
| return MollifiedUniformPrior( |
| self.a.expand(batch_shape), |
| self.b.expand(batch_shape), |
| self.tail_sigma.expand(batch_shape) |
| ) |
|
|