Source code for pybop.costs._likelihoods
import numpy as np
from pybop.costs.base_cost import BaseCost
[docs]
class BaseLikelihood(BaseCost):
"""
Base class for likelihoods
"""
def __init__(self, problem, sigma=None):
super(BaseLikelihood, self).__init__(problem, sigma)
self.n_time_data = problem.n_time_data
[docs]
def set_sigma(self, sigma):
"""
Setter for sigma parameter
"""
if not isinstance(sigma, np.ndarray):
sigma = np.array(sigma)
if not np.issubdtype(sigma.dtype, np.number):
raise ValueError("Sigma must contain only numeric values")
if np.any(sigma <= 0):
raise ValueError("Sigma must not be negative")
else:
self.sigma0 = sigma
[docs]
def get_sigma(self):
"""
Getter for sigma parameter
"""
return self.sigma0
[docs]
def get_n_parameters(self):
"""
Returns the number of parameters
"""
return self._n_parameters
[docs]
class GaussianLogLikelihoodKnownSigma(BaseLikelihood):
"""
This class represents a Gaussian Log Likelihood with a known sigma,
which assumes that the data follows a Gaussian distribution and computes
the log-likelihood of observed data under this assumption.
Attributes:
_logpi (float): Precomputed offset value for the log-likelihood function.
"""
def __init__(self, problem, sigma):
super(GaussianLogLikelihoodKnownSigma, self).__init__(problem, sigma)
if sigma is not None:
self.set_sigma(sigma)
self._offset = -0.5 * self.n_time_data * np.log(2 * np.pi / self.sigma0)
self._multip = -1 / (2.0 * self.sigma0**2)
self.sigma2 = self.sigma0**-2
self._dl = np.ones(self._n_parameters)
[docs]
def _evaluate(self, x, grad=None):
"""
Calls the problem.evaluate method and calculates
the log-likelihood
"""
y = self.problem.evaluate(x)
for key in self.signal:
if len(y.get(key, [])) != len(self._target.get(key, [])):
return -np.float64(np.inf) # prediction doesn't match target
e = np.array(
[
np.sum(
self._offset
+ self._multip * np.sum((self._target[signal] - y[signal]) ** 2)
)
for signal in self.signal
]
)
if self.n_outputs == 1:
return e.item()
else:
return np.sum(e)
[docs]
def _evaluateS1(self, x, grad=None):
"""
Calls the problem.evaluateS1 method and calculates
the log-likelihood
"""
y, dy = self.problem.evaluateS1(x)
for key in self.signal:
if len(y.get(key, [])) != len(self._target.get(key, [])):
likelihood = np.float64(np.inf)
dl = self._dl * np.ones(self.n_parameters)
return -likelihood, -dl
r = np.array([self._target[signal] - y[signal] for signal in self.signal])
likelihood = self._evaluate(x)
dl = np.sum((self.sigma2 * np.sum((r * dy.T), axis=2)), axis=1)
return likelihood, dl
[docs]
class GaussianLogLikelihood(BaseLikelihood):
"""
This class represents a Gaussian Log Likelihood, which assumes that the
data follows a Gaussian distribution and computes the log-likelihood of
observed data under this assumption.
Attributes:
_logpi (float): Precomputed offset value for the log-likelihood function.
"""
def __init__(self, problem):
super(GaussianLogLikelihood, self).__init__(problem)
self._logpi = -0.5 * self.n_time_data * np.log(2 * np.pi)
self._dl = np.ones(self._n_parameters + self.n_outputs)
[docs]
def _evaluate(self, x, grad=None):
"""
Evaluates the Gaussian log-likelihood for the given parameters.
Args:
x (array_like): The parameters for which to evaluate the log-likelihood.
The last `self.n_outputs` elements are assumed to be the
standard deviations of the Gaussian distributions.
Returns:
float: The log-likelihood value, or -inf if the standard deviations are received as non-positive.
"""
sigma = np.asarray(x[-self.n_outputs :])
if np.any(sigma <= 0):
return -np.inf
y = self.problem.evaluate(x[: -self.n_outputs])
for key in self.signal:
if len(y.get(key, [])) != len(self._target.get(key, [])):
return -np.float64(np.inf) # prediction doesn't match target
e = np.array(
[
np.sum(
self._logpi
- self.n_time_data * np.log(sigma)
- np.sum((self._target[signal] - y[signal]) ** 2) / (2.0 * sigma**2)
)
for signal in self.signal
]
)
if self.n_outputs == 1:
return e.item()
else:
return np.sum(e)
[docs]
def _evaluateS1(self, x, grad=None):
"""
Calls the problem.evaluateS1 method and calculates
the log-likelihood
"""
sigma = np.asarray(x[-self.n_outputs :])
if np.any(sigma <= 0):
return -np.float64(np.inf), -self._dl * np.ones(self.n_parameters)
y, dy = self.problem.evaluateS1(x[: -self.n_outputs])
for key in self.signal:
if len(y.get(key, [])) != len(self._target.get(key, [])):
likelihood = np.float64(np.inf)
dl = self._dl * np.ones(self.n_parameters)
return -likelihood, -dl
r = np.array([self._target[signal] - y[signal] for signal in self.signal])
likelihood = self._evaluate(x)
dl = sigma ** (-2.0) * np.sum((r * dy.T), axis=2)
dsigma = -self.n_time_data / sigma + sigma**-(3.0) * np.sum(r**2, axis=1)
dl = np.concatenate((dl.flatten(), dsigma))
return likelihood, dl