Source code for binf.pdf.posteriors
"""
This module contains implementations of posterior distribution interfaces
"""
from abc import ABCMeta, abstractmethod
import numpy
from csb.numeric import log, exp
from binf import AbstractBinfNamedCallable
from binf.pdf import AbstractBinfPDF
[docs]class Posterior(AbstractBinfPDF):
def __init__(self, likelihoods, priors, name='the one and only posterior'):
"""
A PDF object implementing a posterior distribution consisting of
multiple likelihoods and priors
:param likelihoods: name / object pairs of likelihoods modeling
different data-generating processes
:type likelihoods: dict
:param priors: name / object pairs of priors modeling prior
information about the modeling parameters
:param name: some name for this object
:type name: str
"""
super(Posterior, self).__init__(name)
self._likelihoods = likelihoods
self._priors = priors
self._setup_parameters()
self._components = dict(**self.priors)
self._components.update(**self.likelihoods)
self._register_component_variables(*self._get_component_variables())
self._set_original_variables()
[docs] def _setup_parameters(self):
"""
Sets up ('inherits') parameters from likelihoods and priors
"""
for comps in (self.likelihoods, self.priors):
for comp in comps.values():
for p in comp.parameters:
if p not in self.parameters:
self._register(p)
self[p] = comp[p].__class__(comp[p].value,
comp[p].name)
comp[p].bind_to(self[p])
[docs] def _get_component_variables(self):
"""
Retrieves variables from likelihoods and priors
:returns: sets of variables, fixed variables, variables the
posterior distribution can be differentiated w.r.t.
and the variable parameter types
:rtype: (set, set, set, dict)
"""
vars = []
fixed_vars = []
diff_vars = []
var_param_types = []
for c in self._components:
for v in self._components[c].variables:
vars.append(v)
var_param_types.append(self._components[c].var_param_types[v])
if v in self._components[c].differentiable_variables:
diff_vars.append(v)
for p in self._components[c].parameters:
if p in self._components[c]._original_variables:
fixed_vars.append(p)
## Might or might not work, atm I'm not clear about the status
## of the differentiable_variable thing:
# if p in self._components[c].differentiable_variables:
# diff_vars.append(v)
return set(vars), set(fixed_vars), set(diff_vars), var_param_types
[docs] def _register_component_variables(self, vars, fixed_vars, diff_vars,
var_param_types):
"""
Registers variables of likelihoods and priors as variables of
this object and updates the variable parameter type dictionary
:param vars: set of variables
:type vars: set
:param fixed_vars: set of fixed variables
:type fixed_vars: set
:param diff_vars: set of variables the posterior distribution can
be differentiated w.r.t.
:type diff_vars: set
:param var_param_types: the parameter type for each variable
:type var_param_types: dict
"""
for var in vars:
self._register_variable(str(var), differentiable=var in diff_vars)
for fixed_var in fixed_vars:
self._original_variables.update({fixed_var})
self.update_var_param_types(**{var: _type for var, _type
in zip(vars, var_param_types)})
[docs] def _get_component_variables_list(self):
"""
For each component (likelihood or prior), returns its variables
:returns: set of variables for each component
:rtype: dict
"""
return {c: c.variables for c in self._components.values()}
[docs] def _evaluate_components(self, **model_parameters):
r"""
Evaluates the log-probabilities of all components
:param \**model_parameters: parameters of the model. To be consistent,
this should probably called variables
:type \**model_parameters: dict
:returns: list of log-probabilities
:rtype: list
"""
mps = model_parameters
results = []
comp_vars = self._get_component_variables_list()
for c in comp_vars:
single_result = c.log_prob(**{v: mps[v] for v in comp_vars[c]})
results.append(single_result)
return results
[docs] def _evaluate_log_prob(self, **model_parameters):
single_results = self._evaluate_components(**model_parameters)
return numpy.sum(single_results)
@property
def likelihoods(self):
"""
Returns the likelihoods of this posterior distribution
:returns: likelihoods
:rtype: dict
"""
return self._likelihoods
@property
def priors(self):
"""
Returns the priors of this posterior distribution
:returns: priors
:rtype: dict
"""
return self._priors
[docs] def _evaluate_gradient(self, **variables):
vars = variables
res = numpy.zeros(sum([len(variables[v])
if hasattr(variables[v], '__len__') else 1
for v in variables
if v in self.differentiable_variables]))
for n, f in self._components.iteritems():
if len(f.variables) > 0 and len(f.differentiable_variables) > 0:
res += f.gradient(**{x: vars[x] for x in vars
if x in f.variables})
return res
[docs] def clone(self):
copy = self.__class__({L: self.likelihoods[L].clone()
for L in self.likelihoods},
{P: self.priors[P].clone()
for P in self.priors},
self.name)
copy.set_fixed_variables_from_pdf(self)
return copy
[docs] def conditional_factory(self, **fixed_vars):
cond_likelihoods = {L: self.likelihoods[L].conditional_factory(**fixed_vars)
for L in self.likelihoods}
cond_priors = {P: self.priors[P].conditional_factory(**fixed_vars)
for P in self.priors}
copy = self.__class__(cond_likelihoods, cond_priors, self.name)
return copy