Source code for cuqi.distribution._lognormal
import numpy as np
from cuqi.geometry import _get_identity_geometries
from cuqi.distribution import Distribution
from cuqi.distribution import Gaussian
import warnings
[docs]
class Lognormal(Distribution):
"""
Multivariate Lognormal distribution
Parameters
------------
mean: np.ndarray
Mean of the normal distribution used to define the lognormal distribution
cov: np.ndarray
Covariance matrix of the normal distribution used to define the lognormal distribution
Example
-------
.. code-block:: python
# Generate a lognormal distribution
mean = np.array([1.5,1])
cov = np.array([[3, 0],[0, 1]])
x = cuqi.distribution.Lognormal(mean, cov)
samples = x.sample(10000)
samples.hist_chain(1, bins=70)
"""
[docs]
def __init__(self, mean, cov, is_symmetric=False, **kwargs):
super().__init__(is_symmetric=is_symmetric, **kwargs)
self.mean = mean
self.cov = cov
self._normal = Gaussian(self.mean, self.cov)
@property
def _normal(self):
if not np.all(self._Gaussian.mean == self.mean):
self._Gaussian.mean = self.mean
if not np.all(self._Gaussian.cov == self.cov):
self._Gaussian.cov = self.cov
return self._Gaussian
@_normal.setter
def _normal(self, value):
self._Gaussian = value
@property
def dim(self):
return self._normal.dim
[docs]
def pdf(self, x):
if np.any(x<=0):
return 0
else:
return self._normal.pdf(np.log(x))*np.prod(1/x)
[docs]
def logpdf(self, x):
return np.log(self.pdf(x))
def _gradient(self, val, *args, **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))
elif not callable(self._normal.mean): # for prior
return np.diag(1/val)@(-1+self._normal.gradient(np.log(val)))
elif hasattr(self.mean,"gradient"): # for likelihood
model = self._normal.mean
dev = np.log(val) - model.forward(*args, **kwargs)
return model.gradient(self._normal.prec@dev, *args, **kwargs) # Jac(x).T@(self._normal.prec@dev)
else:
warnings.warn('Gradient not implemented for {}'.format(type(self._normal.mean)))
def _sample(self, N=1, rng=None):
return np.exp(self._normal._sample(N,rng))