Source code for anml.parameter.likelihood

"""
==========
Likelihood
==========

The :class:`~anml.parameter.likelihood.Likelihood` keeps track of all information
related to probability distributions that can be used for priors or data distributions.
"""

from typing import List, Union, Optional
import numpy as np
from scipy.stats import multivariate_normal

from anml.exceptions import ANMLError


[docs]class LikelihoodError(ANMLError): """Raised when there is a mismatch in a distribution's parameters.""" pass
[docs]class Likelihood: def __init__(self): self.parameters = None def _set_parameters(self, parameters: Union[List[float], List[np.ndarray]]): self.parameters = parameters @staticmethod def _likelihood(vals: Union[float, np.ndarray], parameters: Union[List[float], List[np.ndarray]]): raise NotImplementedError() @staticmethod def _neg_log_likelihood(vals: Union[float, np.ndarray], parameters: Union[List[float], List[np.ndarray]]): raise NotImplementedError() @staticmethod def _grad(vals: Union[float, np.ndarray], parameters: Union[List[float], List[np.ndarray]]): raise NotImplementedError()
[docs] def get_neg_log_likelihood(self, vals: Union[float, np.ndarray], parameters: Optional[Union[List[float], List[np.ndarray]]] = None): """Gets the objective value based on parameters of the distribution and current values in `vals`. Parameters ---------- vals location at which to evaluate the oracle parameters parameters of the oracle, optional. If not passed it will use the parameters attached to self, otherwise return an error if there are no parameters. Returns ------- float representing the objective value evaluated based on vals and parameters. """ if parameters is None: if self.parameters is None: raise LikelihoodError("Need to set parameters before calling the objective function.") parameters = self.parameters return self._neg_log_likelihood(vals=vals, parameters=parameters)
[docs] def get_grad(self, vals: Union[float, np.ndarray], parameters: Optional[Union[List[float], List[np.ndarray]]] = None): if parameters is None: if self.parameters is None: raise LikelihoodError("Need to set parameters before calling the objective function.") parameters = self.parameters return self._grad(vals=vals, parameters=parameters)
[docs]class GaussianLikelihood(Likelihood): def __init__(self, mean: List[float] = None, std: List[float] = None): super().__init__() if mean is None: mean = [0.0] if std is None: std = [1.0] if any([s <= 0.0 for s in std]): raise LikelihoodError("Cannot have negative variance for Gaussian Likelihood.") self.parameters = [mean, std] @staticmethod def _likelihood(vals, parameters): return multivariate_normal(mean=parameters[0], cov=np.diag(np.asarray(parameters[1])**2)).pdf(vals) @staticmethod def _neg_log_likelihood(vals, parameters): return -multivariate_normal(mean=parameters[0], cov=np.diag(np.asarray(parameters[1])**2)).logpdf(vals) @staticmethod def _grad(vals, parameters): return (vals - parameters[0])/np.power(parameters[1], 2)