Source code for csnlp.core.derivatives
"""A collection of two methods for computing higher-order sensitivities (i.e., Jacobian
and Hessian) w.r.t. CasADi symbolic variables. Natively, CasADi does not support
jacobian or hessian for matrices (or at least, they will be flattened). These
"higher-order" functions allows to compute the jacobian and hessian of a matrix w.r.t.
another matrix."""
from itertools import product as _product
from typing import Union
import casadi as cs
import numpy as np
from .data import array2cs as _array2cs
from .data import cs2array as _cs2array
[docs]
def hojacobian(ex: Union[cs.MX, cs.SX], x: Union[cs.MX, cs.SX]) -> np.ndarray:
"""Computes jacobian on higher-order matrices, not just vectors.
Parameters
----------
ex : casadi.MX or SX
The expression to compute the jacobian for. Can be a matrix.
x : casadi.MX or SX
The variable to differentiate with respect to. Can be a matrix.
Returns
-------
numpy array of symbolic variables
A 4D array of objects, where each entry ``(i,j,k,m)`` is the derivative
of ``ex[i,j]`` w.r.t. ``x[k,m]``.
"""
return _cs2array(cs.jacobian(cs.vec(ex), cs.vec(x))).reshape(
ex.shape + x.shape, order="F"
)
[docs]
def hohessian(
ex: Union[cs.MX, cs.SX], x: Union[cs.MX, cs.SX], y: Union[cs.MX, cs.SX, None] = None
) -> tuple[np.ndarray, np.ndarray]:
"""Computes hessian on higher-order matrices, similar to :func:`hojacobian`.
Parameters
----------
ex : casadi.MX or SX
The expression to compute the hessin for. Can be a matrix.
x : casadi.MX or SX
The variable to differentiate with respect to. Can be a matrix.
y : casadi.MX or SX, optional
Use this argument to specify the second partial derivative, i.e., if
the output requested is not the hessian of ``ex`` w.r.t. ``x``, but rather
the second-order partial derivatives of ``ex`` w.r.t. ``x`` and ``y``.
Returns
-------
tuple of 2 numpy arrays of symbolic variables
The first element is a 6D array of objects, where each entry
``(i,j,k,m,l,p)`` is the hessian of ``ex[i,j]`` w.r.t. ``x[k,m], y[l,p]``,
while the second element contains the jacobian instead (see :func:`hojacobian`
for details).
"""
if y is None:
y = x
J = hojacobian(ex, x)
H = np.empty(ex.shape + x.shape + y.shape, object)
for i in _product(*map(range, ex.shape)):
H[i] = hojacobian(_array2cs(J[i]), y)
return H, J