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

110 lines
3.2 KiB
Python
Executable File

import numpy as np
import scipy.special as sp
from prml.nn.array.broadcast import broadcast_to
from prml.nn.math.exp import exp
from prml.nn.math.gamma import gamma
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 Gamma(RandomVariable):
"""
Gamma distribution
p(x|a(shape), b(rate))
= b^a * x^(a - 1) * e^(-bx) / Gamma(a)
Parameters
----------
shape : tensor_like
shape parameter
rate : tensor_like
rate parameter
data : tensor_like
realization
p : RandomVariable
original distribution of a model
"""
def __init__(self, shape, rate, data=None, p=None):
super().__init__(data, p)
shape, rate = self._check_input(shape, rate)
self.shape = shape
self.rate = rate
def _check_input(self, shape, rate):
shape = self._convert2tensor(shape)
rate = self._convert2tensor(rate)
if shape.shape != rate.shape:
shape_ = np.broadcast(shape.value, rate.value).shape
if shape.shape != shape_:
shape = broadcast_to(shape, shape_)
if rate.shape != shape_:
rate = broadcast_to(rate, shape_)
return shape, rate
@property
def shape(self):
return self.parameter["shape"]
@shape.setter
def shape(self, shape):
try:
ispositive = (shape.value > 0).all()
except AttributeError:
ispositive = (shape.value > 0)
if not ispositive:
raise ValueError("value of shape must be positive")
self.parameter["shape"] = shape
@property
def rate(self):
return self.parameter["rate"]
@rate.setter
def rate(self, rate):
try:
ispositive = (rate.value > 0).all()
except AttributeError:
ispositive = (rate.value > 0)
if not ispositive:
raise ValueError("value of rate must be positive")
self.parameter["rate"] = rate
def forward(self):
self.output = np.random.gamma(self.shape.value, 1 / self.rate.value)
if isinstance(self.shape, Constant) and isinstance(self.rate, Constant):
return Constant(self.output)
return Tensor(self.output, function=self)
def backward(self, delta):
a = self.shape.value
psia = sp.digamma(a)
psi1a = sp.polygamma(1, a)
sqrtpsi1a = np.sqrt(psi1a)
psi2a = sp.polygamma(2, a)
b = self.rate.value
eps = (np.log(self.output) - psia + np.log(b)) / sqrtpsi1a
dshape = self.output * (0.5 * eps * psi2a / sqrtpsi1a + psi1a) * delta
drate = -delta * self.output / b
self.shape.backward(dshape)
self.rate.backward(drate)
def _pdf(self, x):
return (
self.rate ** self.shape
* x ** (self.shape - 1)
* exp(-self.rate * x)
/ gamma(self.shape)
)
def _log_pdf(self, x):
return (
self.shape * log(self.rate)
+ (self.shape - 1) * log(x)
- self.rate * x
- log(gamma(self.shape))
)