Source code for cuqi.distribution._cauchy
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 Cauchy(Distribution):
""" Multivariate Cauchy distribution of independent random variables.
Each is distributed according to the PDF function
.. math::
\\frac{1}{\\pi\\gamma(1+\\frac{(x-\\mu)^2}{\\gamma^2})}
where :math:`\\mu` is the location parameter and :math:`\\gamma` is the scale parameter.
Parameters
----------
location: float or array_like
Location parameter
scale: float or array_like
Scale parameter
Example
-------
.. code-block:: python
# % Generate a Cauchy distribution
import cuqi
location = 0
scale = 1
x = cuqi.distribution.Cauchy(location, scale)
x.logpdf(0) # -1.1447298858494002
.. code-block:: python
# % Generate a multivariate Cauchy distribution
import cuqi
location = [0, 1]
scale = [1, 3]
x = cuqi.distribution.Cauchy(location, scale)
x.logpdf([0, 0]) # -3.4934325760247367
"""
[docs]
def __init__(self, location=None, scale=None, is_symmetric=True, **kwargs):
super().__init__(is_symmetric=is_symmetric, **kwargs)
self.location = location
self.scale = scale
@property
def location(self):
""" Location parameter """
return self._location
@location.setter
def location(self, value):
self._location = force_ndarray(value, flatten=True)
@property
def scale(self):
""" Scale parameter """
return self._scale
@scale.setter
def scale(self, value):
self._scale = force_ndarray(value, flatten=True)
def _is_out_of_bounds(self, x):
""" Check if x is out of bounds """
return np.any(self.scale <= 0)
[docs]
def logpdf(self, x):
if self._is_out_of_bounds(x):
return -np.inf
return np.sum(-np.log(np.pi*self.scale*(1+((x-self.location)/self.scale)**2)))
[docs]
def cdf(self, x):
if self._is_out_of_bounds(x):
return -np.inf
return np.sum(sps.cauchy.cdf(x, loc=self.location, scale=self.scale))
[docs]
def gradient(self, x):
#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 distribution is not supported yet
if self.is_cond:
raise NotImplementedError(f"Gradient is not implemented for {self} with conditioning variables {self.get_conditioning_variables()}")
# Check bounds (return nan if out of bounds)
if self._is_out_of_bounds(x):
return x*np.nan
#Compute the gradient
x_translated = x-self.location
return -2*x_translated/(self.scale**2*(1+(x_translated/self.scale)**2))
def _sample(self, N=1, rng=None):
return sps.cauchy.rvs(loc=self.location, scale=self.scale, size=(N,self.dim), random_state=rng).T