File size: 2,846 Bytes
6c3f19f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import math
import torch
from gpytorch.priors import Prior
from torch.distributions import HalfCauchy,HalfNormal,constraints
from torch.distributions.utils import broadcast_all
from numbers import Number

class LogHalfHorseshoePrior(Prior):
    """Prior for the log-noise variance hyperparameter for GPs. 



    This is parameterized by `scale` and `lb`. `lb` is the lower bound on the noise variance. 

    The `scale` parameter is more important. The default value for `scale` - 0.01 - works well 

    for deterministic and low-noise situations. A larger value may be need in noisy sitations 

    and small training datasets. A larger scale implies more noisy data as the prior.



    To change the scale for this prior for a model to say 0.1,

        >>> model.likelihood.register(

        >>>     'noise_prior',LogHalfHorseshoePrior(0.1,model.likelihood.noise_prior.lb),

        >>>     'raw_noise'

        >>> )



    .. note::

        The `log_prob` method is only approximate and unnormalized. There is no closed form

        expression for the underlying horseshoe distribution. The lower and upper bounds on

        its' density are, however, known. Here, we use the same approximate density value that 

        the spearmint package uses.

    

    :param scale: scale parameter of the Horseshoe distribution

    :type scale: float or torch.Tensor



    :param lb: lower bound on the original scale. Defaults to 1e-6

    :type lb: float or torch.Tensor, optional

    """
    arg_constraints = {"scale": constraints.positive,"lb":constraints.positive}
    support = constraints.real
    def __init__(self, scale, lb=1e-6,validate_args=None):
        self.scale,self.lb = broadcast_all(scale,lb)
        if isinstance(scale,Number):
            batch_shape = torch.Size()
        else:
            batch_shape = self.scale.size()
        super().__init__(batch_shape,validate_args=validate_args)
    
    def transform(self, x):
        return self.lb + torch.exp(x)

    def log_prob(self, X):
        # first term is the density in the original scale
        # the second term is for the transformation
        return torch.log(torch.log(1+3*(self.scale / self.transform(X)) ** 2))+ X

    def rsample(self, sample_shape=torch.Size([])):
        local_shrinkage = HalfCauchy(1).rsample(self.scale.shape).to(self.lb)
        param_sample = HalfNormal(local_shrinkage * self.scale).rsample(sample_shape).to(self.lb)
        if len(self.lb) > 1:
            param_sample[param_sample<self.lb[0]] = self.lb[0]
        else:
            param_sample[param_sample<self.lb] = self.lb
        return param_sample.log()

    def expand(self,expand_shape, _instance=None):
        batch_shape = torch.Size(expand_shape)
        return LogHalfHorseshoePrior(self.scale.expand(batch_shape))