Source code for cofi.utils._lik_base

"""Base class for likelihood functions in CoFI."""

from abc import abstractmethod, ABCMeta
from numbers import Number
from functools import reduce
from typing import Optional, Callable, Tuple
import numpy as np
from scipy import sparse

# Assuming these exist in the cofi package
# from .._exceptions import DimensionMismatchError

# For now, let's define this locally
class DimensionMismatchError(Exception):
    def __init__(self, entered_name, entered_dimension, expected_source, expected_dimension):
        super().__init__(
            f"{entered_name} has dimension {entered_dimension}, but {expected_source} "
            f"expects dimension {expected_dimension}"
        )


EPS = 1e-154


[docs] class BaseLikelihood(metaclass=ABCMeta): r"""Base class for a likelihood function This base class provides a consistent interface for likelihood functions used in Bayesian inverse problems. Concrete implementations should define how to evaluate the log-likelihood and its derivatives. .. rubric:: Basic interface The basic properties / methods for a likelihood function in ``cofi.utils`` include the following: .. autosummary:: BaseLikelihood.model_shape BaseLikelihood.model_size BaseLikelihood.log_likelihood BaseLikelihood.gradient BaseLikelihood.hessian BaseLikelihood.__call__ BaseLikelihood.get_ml_cov """ def __init__(self): """Initialize the base likelihood class.""" pass @property @abstractmethod def model_shape(self) -> tuple: """The shape of models that current likelihood function accepts. Returns ------- tuple The shape of the model parameter array """ raise NotImplementedError @property def model_size(self) -> Number: """The number of unknowns that current likelihood function accepts. Returns ------- Number The total number of model parameters """ return reduce(lambda a, b: a * b, np.array(self.model_shape), 1)
[docs] def __call__(self, model: np.ndarray) -> float: r"""A class instance itself can also be called as a function. It works exactly the same as :meth:`log_likelihood`. In other words, the following two usages are exactly the same:: >>> my_likelihood = GaussianLikelihood(data, forward_func) >>> log_p = my_likelihood(np.array([1,2,3])) # usage 1 >>> log_p = my_likelihood.log_likelihood(np.array([1,2,3])) # usage 2 Parameters ---------- model : np.ndarray The model parameters to evaluate Returns ------- float The log-likelihood value """ return self.log_likelihood(model)
[docs] @abstractmethod def log_likelihood(self, model: np.ndarray) -> float: """Evaluate the log-likelihood function at a given model. Parameters ---------- model : np.ndarray The model parameters at which to evaluate the log-likelihood Returns ------- float The log-likelihood value log p(d|m) """ raise NotImplementedError
[docs] @abstractmethod def gradient(self, model: np.ndarray) -> np.ndarray: """The gradient of log-likelihood with respect to model parameters. The gradient has shape :math:`(M,)` where :math:`M` is the number of model parameters. Parameters ---------- model : np.ndarray The model parameters at which to evaluate the gradient Returns ------- np.ndarray The gradient vector of shape (M,) """ raise NotImplementedError
[docs] @abstractmethod def hessian(self, model: np.ndarray) -> np.ndarray: """The Hessian of log-likelihood with respect to model parameters. The Hessian has shape :math:`(M,M)` where :math:`M` is the number of model parameters. Parameters ---------- model : np.ndarray The model parameters at which to evaluate the Hessian Returns ------- np.ndarray The Hessian matrix of shape (M, M) """ raise NotImplementedError
[docs] def get_ml_cov(self, model: np.ndarray) -> Optional[np.ndarray]: """Get maximum likelihood estimate of data covariance (if applicable). This is an optional method that some likelihood implementations may provide to return the maximum likelihood estimate of the data covariance matrix for a given model. Parameters ---------- model : np.ndarray The model parameters Returns ------- Optional[np.ndarray] The estimated data covariance matrix, or None if not applicable """ return None