Source code for cuqi.distribution._inverse_gamma
import numpy as np
import scipy.stats as sps
from cuqi.geometry import _get_identity_geometries
from cuqi.utilities import force_ndarray
from cuqi.distribution import Distribution
[docs]
class InverseGamma(Distribution):
"""
Multivariate inverse gamma distribution of independent random variables x_i. Each is distributed according to the PDF function
.. math::
f(x) = (x-\\beta)^{(-\\alpha-1)} * \exp(-\\gamma/(x-\\beta)) / (\\gamma^{(-\\alpha)}*\Gamma(\\alpha))
where shape :math:`\\alpha`, location :math:`\\beta` and scale :math:`\\gamma` are the shape, location and scale of x_i, respectively. And :math:`\Gamma` is the Gamma function.
Parameters
------------
shape: float or array_like
The shape parameter
location: float or array_like
The location of the inverse gamma distribution. The support of the pdf function is the Cartesian product of the open intervals (location_1, infinity), (location_2, infinity), ..., (location_dim, infinity).
scale: float or array_like
The scale of the inverse gamma distribution (non-negative)
Example
-------
.. code-block:: python
# Generate an InverseGamma distribution
import numpy as np
import cuqi
import matplotlib.pyplot as plt
shape = [1,2]
location = 0
scale = 1
rng = np.random.RandomState(1)
x = cuqi.distribution.InverseGamma(shape, location, scale)
samples = x.sample(1000, rng=rng)
samples.hist_chain(0, bins=70)
plt.figure()
samples.hist_chain(1, bins=70)
"""
[docs]
def __init__(self, shape=None, location=None, scale=None, is_symmetric=False, **kwargs):
super().__init__(is_symmetric=is_symmetric, **kwargs)
self.shape = force_ndarray(shape, flatten=True)
self.location = force_ndarray(location, flatten=True)
self.scale = force_ndarray(scale, flatten=True)
[docs]
def logpdf(self, x):
return np.sum(sps.invgamma.logpdf(x, a=self.shape, loc=self.location, scale=self.scale))
[docs]
def cdf(self, x):
return np.prod(sps.invgamma.cdf(x, a=self.shape, loc=self.location, scale=self.scale))
def _gradient(self, val, **kwargs):
#Avoid complicated geometries that change the gradient.
if not type(self.geometry) in _get_identity_geometries():
raise NotImplementedError("Gradient not implemented for distribution {} with geometry {}".format(self,self.geometry))
#Computing the gradient for conditional InverseGamma distribution is not supported yet
elif self.is_cond:
raise NotImplementedError(f"Gradient is not implemented for {self} with conditioning variables {self.get_conditioning_variables()}")
#Compute the gradient
if np.any(val <= self.location):
return val*np.nan
else:
return (-self.shape-1)/(val - self.location) +\
self.scale/(val - self.location)**2
def _sample(self, N=1, rng=None):
return sps.invgamma.rvs(a=self.shape, loc= self.location, scale = self.scale ,size=(N,self.dim), random_state=rng).T