Source code for pybop._costs
import numpy as np
[docs]
class BaseCost:
"""
Base class for defining cost functions.
This class is intended to be subclassed to create specific cost functions
for evaluating model predictions against a set of data. The cost function
quantifies the goodness-of-fit between the model predictions and the
observed data, with a lower cost value indicating a better fit.
Parameters
----------
problem : object
A problem instance containing the data and functions necessary for
evaluating the cost function.
_target : array-like
An array containing the target data to fit.
x0 : array-like
The initial guess for the model parameters.
bounds : tuple
The bounds for the model parameters.
n_parameters : int
The number of parameters in the model.
"""
def __init__(self, problem):
self.problem = problem
if problem is not None:
self._target = problem._target
self.x0 = problem.x0
self.bounds = problem.bounds
self.n_parameters = problem.n_parameters
[docs]
def __call__(self, x, grad=None):
"""
Calculate the cost function value for a given set of parameters.
This method must be implemented by subclasses.
Parameters
----------
x : array-like
The parameters for which to evaluate the cost.
grad : array-like, optional
An array to store the gradient of the cost function with respect
to the parameters.
Returns
-------
float
The calculated cost function value.
Raises
------
NotImplementedError
If the method has not been implemented by the subclass.
"""
raise NotImplementedError
[docs]
class RootMeanSquaredError(BaseCost):
"""
Root mean square error cost function.
Computes the root mean square error between model predictions and the target
data, providing a measure of the differences between predicted values and
observed values.
Inherits all parameters and attributes from ``BaseCost``.
"""
def __init__(self, problem):
super(RootMeanSquaredError, self).__init__(problem)
[docs]
def __call__(self, x, grad=None):
"""
Calculate the root mean square error for a given set of parameters.
Parameters
----------
x : array-like
The parameters for which to evaluate the cost.
grad : array-like, optional
An array to store the gradient of the cost function with respect
to the parameters.
Returns
-------
float
The root mean square error.
Raises
------
ValueError
If an error occurs during the calculation of the cost.
"""
try:
prediction = self.problem.evaluate(x)
if len(prediction) < len(self._target):
return np.float64(np.inf) # simulation stopped early
else:
return np.sqrt(np.mean((prediction - self._target) ** 2))
except Exception as e:
raise ValueError(f"Error in cost calculation: {e}")
[docs]
class SumSquaredError(BaseCost):
"""
Sum of squared errors cost function.
Computes the sum of the squares of the differences between model predictions
and target data, which serves as a measure of the total error between the
predicted and observed values.
Inherits all parameters and attributes from ``BaseCost``.
Additional Attributes
---------------------
_de : float
The gradient of the cost function to use if an error occurs during
evaluation. Defaults to 1.0.
"""
def __init__(self, problem):
super(SumSquaredError, self).__init__(problem)
# Default fail gradient
self._de = 1.0
[docs]
def __call__(self, x, grad=None):
"""
Calculate the sum of squared errors for a given set of parameters.
Parameters
----------
x : array-like
The parameters for which to evaluate the cost.
grad : array-like, optional
An array to store the gradient of the cost function with respect
to the parameters.
Returns
-------
float
The sum of squared errors.
Raises
------
ValueError
If an error occurs during the calculation of the cost.
"""
try:
prediction = self.problem.evaluate(x)
if len(prediction) < len(self._target):
return np.float64(np.inf) # simulation stopped early
else:
return np.sum(
(np.sum(((prediction - self._target) ** 2), axis=0)),
axis=0,
)
except Exception as e:
raise ValueError(f"Error in cost calculation: {e}")
[docs]
def evaluateS1(self, x):
"""
Compute the cost and its gradient with respect to the parameters.
Parameters
----------
x : array-like
The parameters for which to compute the cost and gradient.
Returns
-------
tuple
A tuple containing the cost and the gradient. The cost is a float,
and the gradient is an array-like of the same length as `x`.
Raises
------
ValueError
If an error occurs during the calculation of the cost or gradient.
"""
try:
y, dy = self.problem.evaluateS1(x)
if len(y) < len(self._target):
e = np.float64(np.inf)
de = self._de * np.ones(self.problem.n_parameters)
else:
dy = dy.reshape(
(
self.problem.n_time_data,
self.problem.n_outputs,
self.problem.n_parameters,
)
)
r = y - self._target
e = np.sum(np.sum(r**2, axis=0), axis=0)
de = 2 * np.sum(np.sum((r.T * dy.T), axis=2), axis=1)
return e, de
except Exception as e:
raise ValueError(f"Error in cost calculation: {e}")
[docs]
def set_fail_gradient(self, de):
"""
Set the fail gradient to a specified value.
The fail gradient is used if an error occurs during the calculation
of the gradient. This method allows updating the default gradient value.
Parameters
----------
de : float
The new fail gradient value to be used.
"""
de = float(de)
self._de = de