Source code for cuqi.distribution._posterior
from cuqi.geometry import _DefaultGeometry, _get_identity_geometries
from cuqi.distribution import Distribution
from cuqi.density import Density
# ========================================================================
[docs]
class Posterior(Distribution):
"""
Posterior probability distribution defined by likelihood and prior.
The geometry is automatically determined from the model and prior.
Generates instance of cuqi.distribution.Posterior
Parameters
------------
likelihood: Likelihood function, cuqi.likelihood.Likelihood.
prior: Prior distribution, cuqi.distribution.Distribution.
"""
[docs]
def __init__(self, likelihood, prior, **kwargs):
if len(likelihood.get_parameter_names()) > 1:
raise ValueError("Likelihood must only have one parameter.")
if prior.is_cond:
raise ValueError("Prior must not be a conditional distribution.")
self.likelihood = likelihood
self.prior = prior
super().__init__(**kwargs)
[docs]
def get_density(self, name) -> Density:
""" Return a density with the given name. """
if name == self.likelihood.name:
return self.likelihood
if name == self.prior.name:
return self.prior
raise ValueError(f"No density with name {name}.")
@property
def data(self):
return self.likelihood.data
@property
def dim(self):
return self.prior.dim
@property
def geometry(self):
return self._geometry
@geometry.setter
def geometry(self, value):
# Compare model and prior
if self.model is not None and self.model.domain_geometry != self.prior.geometry:
if isinstance(self.prior.geometry, _DefaultGeometry):
pass #We allow default geometry in prior
else:
raise ValueError("Geometry from likelihood (model.domain_geometry) does not match prior geometry")
# Compare value and prior
if self.model is None and value is not None and value != self.prior.geometry:
if isinstance(self.prior.geometry, _DefaultGeometry):
pass #We allow default geometry in prior
else:
raise ValueError("Posterior and prior geometries are inconsistent.")
# Compare model and value
if self.model is not None and value is not None and value != self.model.domain_geometry:
if isinstance(self.model.domain_geometry, _DefaultGeometry):
pass #Allow default model geometry
else:
raise ValueError("Set geometry does not match with model geometry.")
# Compare likelihood and prior
if self.likelihood.geometry != self.prior.geometry:
if isinstance(self.prior.geometry, _DefaultGeometry):
pass #We allow default geometry in prior
elif isinstance(self.likelihood.geometry, _DefaultGeometry):
pass #We allow default geometry in likelihood
else:
raise ValueError("Likelihood and prior geometries are inconsistent.")
# If value is set, its consistant with prior (and prior is consistant with model)
# Likelihood and prior are consistant.
# If value is not set, take from model (if exists) or from likelihood or prior as last resort
if value is not None and not isinstance(value, _DefaultGeometry):
self._geometry = value
elif self.model is not None and not isinstance(self.model.domain_geometry, _DefaultGeometry):
self._geometry = self.model.domain_geometry
elif not isinstance(self.likelihood.geometry, _DefaultGeometry):
self._geometry = self.likelihood.geometry
else:
self._geometry = self.prior.geometry
[docs]
def logpdf(self, *args, **kwargs):
""" Returns the logpdf of the posterior distribution"""
return self.likelihood.logd(*args, **kwargs)+ self.prior.logd(*args, **kwargs)
[docs]
def get_conditioning_variables(self):
return self.prior.get_conditioning_variables()
[docs]
def get_parameter_names(self):
return self.prior.get_parameter_names()
def _gradient(self, x):
#Avoid complicated geometries that change the gradient.
if not type(self.geometry) in _get_identity_geometries() and\
not hasattr(self.geometry, 'gradient'):
raise NotImplementedError("Gradient not implemented for distribution {} with geometry {}".format(self,self.geometry))
return self.likelihood.gradient(x)+ self.prior.gradient(x)
def _sample(self,N=1,rng=None):
raise Exception("'Posterior.sample' is not defined. Sampling can be performed with the 'sampler' module.")
@property
def model(self):
"""Extract the cuqi model from likelihood."""
return self.likelihood.model
def __repr__(self):
msg = f"Posterior(\n"
msg += " Equation:\n"
msg += f"\t p({self.prior.name}|{self.likelihood.name}) ∝ L({self.prior.name}|{self.likelihood.name})p({self.prior.name})\n"
msg += " Densities:\n"
msg += f"\t{self.likelihood.name} ~ {self.likelihood}\n "
msg += f"\t{self.prior.name} ~ {self.prior}\n "
msg += ")"
return msg