89 lines
2.5 KiB
Python
Executable File
89 lines
2.5 KiB
Python
Executable File
import numpy as np
|
|
from prml.nn.array.broadcast import broadcast_to
|
|
from prml.nn.math.log import log
|
|
from prml.nn.math.square import square
|
|
from prml.nn.random.random import RandomVariable
|
|
from prml.nn.tensor.constant import Constant
|
|
from prml.nn.tensor.tensor import Tensor
|
|
|
|
|
|
class Cauchy(RandomVariable):
|
|
"""
|
|
Cauchy distribution aka Lorentz distribution
|
|
p(x|x0(loc), scale)
|
|
= 1 / [pi*scale * {1 + (x - x0)^2 / scale^2}]
|
|
Parameters
|
|
----------
|
|
loc : tensor_like
|
|
location parameter
|
|
scale : tensor_like
|
|
scale parameter
|
|
data : tensor_like
|
|
realization
|
|
p : RandomVariable
|
|
original distribution of a model
|
|
"""
|
|
|
|
def __init__(self, loc, scale, data=None, p=None):
|
|
super().__init__(data, p)
|
|
self.loc, self.scale = self._check_input(loc, scale)
|
|
|
|
def _check_input(self, loc, scale):
|
|
loc = self._convert2tensor(loc)
|
|
scale = self._convert2tensor(scale)
|
|
if loc.shape != scale.shape:
|
|
shape = np.broadcast(loc.value, scale.value).shape
|
|
if loc.shape != shape:
|
|
loc = broadcast_to(loc, shape)
|
|
if scale.shape != shape:
|
|
scale = broadcast_to(scale, shape)
|
|
return loc, scale
|
|
|
|
@property
|
|
def loc(self):
|
|
return self.parameter["loc"]
|
|
|
|
@loc.setter
|
|
def loc(self, loc):
|
|
self.parameter["loc"] = loc
|
|
|
|
@property
|
|
def scale(self):
|
|
return self.parameter["scale"]
|
|
|
|
@scale.setter
|
|
def scale(self, scale):
|
|
try:
|
|
ispositive = (scale.value > 0).all()
|
|
except AttributeError:
|
|
ispositive = (scale.value > 0)
|
|
|
|
if not ispositive:
|
|
raise ValueError("value of scale must be positive")
|
|
self.parameter["scale"] = scale
|
|
|
|
def forward(self):
|
|
self.eps = np.random.standard_cauchy(size=self.loc.shape)
|
|
self.output = self.scale.value * self.eps + self.loc.value
|
|
if isinstance(self.loc, Constant):
|
|
return Constant(self.output)
|
|
return Tensor(self.output, function=self)
|
|
|
|
def backward(self, delta):
|
|
dloc = delta
|
|
dscale = delta * self.eps
|
|
self.loc.backward(dloc)
|
|
self.scale.backward(dscale)
|
|
|
|
def _pdf(self, x):
|
|
return (
|
|
1 / (np.pi * self.scale * (1 + square((x - self.loc) / self.scale)))
|
|
)
|
|
|
|
def _log_pdf(self, x):
|
|
return (
|
|
-np.log(np.pi)
|
|
- log(self.scale)
|
|
- log(1 + square((x - self.loc) / self.scale))
|
|
)
|