-
Notifications
You must be signed in to change notification settings - Fork 13
/
asymmetric_laplace.py
68 lines (52 loc) · 2.35 KB
/
asymmetric_laplace.py
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
65
66
67
68
"""
:code:`asymmetric_laplace.py`
Asymmetric Laplace Distribution, with location parameter 0. This is used as our NPI Effectiveness prior.
See also: https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution
"""
import pymc3.distributions.continuous as continuous
import theano.tensor as tt
import numpy as np
class AsymmetricLaplace(continuous.Continuous):
"""
Assymetric Laplace Distribution
See also: https://en.wikipedia.org/wiki/Asymmetric_Laplace_distribution
"""
def __init__(self, scale, symmetry, testval=0.0, *args, **kwargs):
"""
Constructor
:param scale: scale parameter
:param symmetry: asymmetry parameter. Reduces to a normal laplace distribution with value 1
"""
self.scale = tt.as_tensor_variable(scale)
self.symmetry = tt.as_tensor_variable(symmetry)
super().__init__(*args, **kwargs, testval=testval)
def random(self, point=None, size=None):
"""
Draw random samples from this distribution, using the inverse CDF method.
:param point: not used
:param size: size of sample to draw
:return: Samples
"""
if point is not None:
raise NotImplementedError('Random not implemented with point specified')
if size is not None:
u = np.random.uniform(size=size)
x = - tt.log((1 - u) * (1 + self.symmetry ** 2)) / (self.symmetry * self.scale) * (
u > ((self.symmetry ** 2) / (1 + self.symmetry ** 2))) + self.symmetry * tt.log(
u * (1 + self.symmetry ** 2) / (self.symmetry ** 2)) / self.scale * (
u < ((self.symmetry ** 2) / (1 + self.symmetry ** 2)))
return x
u = np.random.uniform()
if u > (self.symmetry ** 2) / (1 + self.symmetry ** 2):
x = - tt.log((1 - u) * (1 + self.symmetry ** 2)) / (self.symmetry * self.scale)
else:
x = self.symmetry * tt.log(u * (1 + self.symmetry ** 2) / (self.symmetry ** 2)) / self.scale
return x
def logp(self, value):
"""
Compute logp.
:param value: evaluation point
:return: log probability at evaluation point
"""
return tt.log(self.scale / (self.symmetry + (self.symmetry ** -1))) + (
-value * self.scale * tt.sgn(value) * (self.symmetry ** tt.sgn(value)))