Files
2026-07-13 13:30:25 +08:00

85 lines
2.5 KiB
Python
Executable File

import numpy as np
from prml.nn.array.broadcast import broadcast_to
from prml.nn.math.abs import abs
from prml.nn.math.exp import exp
from prml.nn.math.log import log
from prml.nn.random.random import RandomVariable
from prml.nn.tensor.constant import Constant
from prml.nn.tensor.tensor import Tensor
class Laplace(RandomVariable):
"""
Laplace distribution
p(x|loc, scale)
= exp(-|x - loc|/scale) / (2 * scale)
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):
eps = 0.5 - np.random.uniform(size=self.loc.shape)
self.eps = np.sign(eps) * np.log(1 - 2 * np.abs(eps))
self.output = self.loc.value - self.scale.value * self.eps
if isinstance(self.loc, Constant) and isinstance(self.scale, 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 0.5 * exp(-abs(x - self.loc) / self.scale) / self.scale
def _log_pdf(self, x):
return np.log(0.5) - abs(x - self.loc) / self.scale - log(self.scale)