Source code for pypesto.objective.function

import numpy as np

from .base import ObjectiveBase, ResultDict
from typing import Callable, Sequence, Tuple, Union

from .constants import MODE_FUN, MODE_RES, FVAL, GRAD, HESS, RES, SRES


[docs]class Objective(ObjectiveBase): """ The objective class allows the user explicitely specify functions that compute the function value and/or residuals as well as respective derivatives. Parameters ---------- fun: The objective function to be minimized. If it only computes the objective function value, it should be of the form ``fun(x) -> float`` where x is an 1-D array with shape (n,), and n is the parameter space dimension. grad: Method for computing the gradient vector. If it is a callable, it should be of the form ``grad(x) -> array_like, shape (n,).`` If its value is True, then fun should return the gradient as a second output. hess: Method for computing the Hessian matrix. If it is a callable, it should be of the form ``hess(x) -> array, shape (n,n).`` If its value is True, then fun should return the gradient as a second, and the Hessian as a third output, and grad should be True as well. hessp: Method for computing the Hessian vector product, i.e. ``hessp(x, v) -> array_like, shape (n,)`` computes the product H*v of the Hessian of fun at x with v. res: Method for computing residuals, i.e. ``res(x) -> array_like, shape(m,).`` sres: Method for computing residual sensitivities. If its is a callable, it should be of the form ``sres(x) -> array, shape (m,n).`` If its value is True, then res should return the residual sensitivities as a second output. x_names: Parameter names. None if no names provided, otherwise a list of str, length dim_full (as in the Problem class). Can be read by the problem. """
[docs] def __init__(self, fun: Callable = None, grad: Union[Callable, bool] = None, hess: Callable = None, hessp: Callable = None, res: Callable = None, sres: Union[Callable, bool] = None, x_names: Sequence[str] = None): self.fun = fun self.grad = grad self.hess = hess self.hessp = hessp self.res = res self.sres = sres super().__init__(x_names)
@property def has_fun(self) -> bool: return callable(self.fun) @property def has_grad(self) -> bool: return callable(self.grad) or self.grad is True @property def has_hess(self) -> bool: return callable(self.hess) or self.hess is True @property def has_hessp(self) -> bool: # Not supported yet return False @property def has_res(self) -> bool: return callable(self.res) @property def has_sres(self) -> bool: return callable(self.sres) or self.sres is True
[docs] def check_sensi_orders(self, sensi_orders, mode): if (mode is MODE_FUN and (0 in sensi_orders and not self.has_fun or 1 in sensi_orders and not self.has_grad or 2 in sensi_orders and not self.has_hess) ) or (mode is MODE_RES and (0 in sensi_orders and not self.has_res or 1 in sensi_orders and not self.has_sres) ): return False return True
[docs] def check_mode(self, mode): if mode == MODE_FUN and not self.has_fun: return False if mode == MODE_RES and not self.has_res: return False return True
[docs] def call_unprocessed(self, x, sensi_orders, mode): """ Call objective function without pre- or post-processing and formatting. Returns ------- result: A dict containing the results. """ if mode == MODE_FUN: result = self._call_mode_fun(x, sensi_orders) elif mode == MODE_RES: result = self._call_mode_res(x, sensi_orders) else: raise ValueError("This mode is not supported.") return result
def _call_mode_fun( self, x: np.ndarray, sensi_orders: Tuple[int, ...] ) -> ResultDict: if sensi_orders == (0,): if self.grad is True: fval = self.fun(x)[0] else: fval = self.fun(x) result = {FVAL: fval} elif sensi_orders == (1,): if self.grad is True: grad = self.fun(x)[1] else: grad = self.grad(x) result = {GRAD: grad} elif sensi_orders == (2,): if self.hess is True: hess = self.fun(x)[2] else: hess = self.hess(x) result = {HESS: hess} elif sensi_orders == (0, 1): if self.grad is True: fval, grad = self.fun(x)[0:2] else: fval = self.fun(x) grad = self.grad(x) result = {FVAL: fval, GRAD: grad} elif sensi_orders == (1, 2): if self.hess is True: grad, hess = self.fun(x)[1:3] else: hess = self.hess(x) if self.grad is True: grad = self.fun(x)[1] else: grad = self.grad(x) result = {GRAD: grad, HESS: hess} elif sensi_orders == (0, 1, 2): if self.hess is True: fval, grad, hess = self.fun(x)[0:3] else: hess = self.hess(x) if self.grad is True: fval, grad = self.fun(x)[0:2] else: fval = self.fun(x) grad = self.grad(x) result = {FVAL: fval, GRAD: grad, HESS: hess} else: raise ValueError("These sensitivity orders are not supported.") return result def _call_mode_res( self, x: np.ndarray, sensi_orders: Tuple[int, ...] ) -> ResultDict: if sensi_orders == (0,): if self.sres is True: res = self.res(x)[0] else: res = self.res(x) result = {RES: res} elif sensi_orders == (1,): if self.sres is True: sres = self.res(x)[1] else: sres = self.sres(x) result = {SRES: sres} elif sensi_orders == (0, 1): if self.sres is True: res, sres = self.res(x) else: res = self.res(x) sres = self.sres(x) result = {RES: res, SRES: sres} else: raise ValueError("These sensitivity orders are not supported.") return result