get_vpartial_derivative

Macro get_vpartial_derivative 

Source
macro_rules! get_vpartial_derivative {
    ($f:ident, $func_name:ident) => { ... };
}
Expand description

Get a function that returns the partial derivative of the provided multivariate, vector-valued function.

The partial derivative is computed using forward-mode automatic differentiation.

§Arguments

  • f - Multivariate, vector-valued function, $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$.
  • func_name - Name of the function that will return the partial derivative of $\mathbf{f}(\mathbf{x})$ with respect to $x_{k}$ at any point $\mathbf{x}\in\mathbb{R}^{n}$.

§Defining f

The multivariate, vector-valued function f must have the following function signature:

fn f<S: Scalar, V: Vector<S>>(x: &V) -> V::DVectorT<S> {
    // place function contents here
}

For the automatic differentiation to work, f must be fully generic over the types of scalars and vectors used. Additionally, the function must return an instance of V::DVector (a dynamically-sized vector type that is compatible with V, see the linalg-traits docs for more information) since we did not want to burden the user with having to specify the size of the output vector (i.e. $m$, where $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$) at compile time, especially since users may be using this crate exclusively with dynamically-sized types.

§Warning

f cannot be defined as closure. It must be defined as a function.

§Note

The function produced by this macro will perform 1 evaluation of $\mathbf{f}(\mathbf{x})$ to evaluate its partial derivative with respect to $x_{k}$.

§Examples

§Basic Example

Compute the partial derivative of

$$\mathbf{f}(\mathbf{x})=\begin{bmatrix}\sin{x_{0}}\sin{x_{1}}\\\cos{x_{0}}\cos{x_{1}}\end{bmatrix}$$

with respect to $x_{0}$ at $\mathbf{x}=(1,2)^{T}$, and compare the result to the true result of

$$ \frac{\partial\mathbf{f}}{\partial x_{0}}\bigg\rvert_{\mathbf{x}=(1,2)^{T}}= \begin{bmatrix} \cos{(1)}\sin{(2)} \\ -\sin{(1)}\cos{(2)} \end{bmatrix} $$

§Using standard vectors
use linalg_traits::{Scalar, Vector};

use numdiff::{get_vpartial_derivative, Dual, DualVector};

// Define the function, f(x).
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> V::DVectorT<S> {
    V::DVectorT::from_slice(&[
        x.vget(0).sin() * x.vget(1).sin(),
        x.vget(0).cos() * x.vget(1).cos(),
    ])
}

// Define the evaluation point.
let x0 = vec![1.0, 2.0];

// Define the element of the vector (using 0-based indexing) we are differentiating with respect
// to.
let k = 0;

// Autogenerate the function "dfk" that can be used to compute the partial derivative of f(x)
// with respect to xₖ at any point x.
get_vpartial_derivative!(f, dfk);

// Verify that the partial derivative function obtained using get_vpartial_derivative! computes
// the partial derivative correctly.
assert_eq!(
    dfk(&x0, k, &[]),
    vec![1.0_f64.cos() * 2.0_f64.sin(), -1.0_f64.sin() * 2.0_f64.cos()]
);
§Using other vector types

The function produced by get_vpartial_derivative! can accept any type for x0, as long as it implements the linalg_traits::Vector trait.

use faer::Mat;
use linalg_traits::{Scalar, Vector};
use nalgebra::{dvector, DVector, SVector};
use ndarray::{array, Array1};

use numdiff::{get_vpartial_derivative, Dual, DualVector};

// Define the function, f(x).
fn f<S: Scalar, V: Vector<S>>(x: &V, _p: &[f64]) -> V::DVectorT<S> {
    V::DVectorT::from_slice(&[
        x.vget(0).sin() * x.vget(1).sin(),
        x.vget(0).cos() * x.vget(1).cos(),
    ])
}

// Define the element of the vector (using 0-based indexing) we are differentiating with respect
// to.
let k = 0;

// Autogenerate the function "dfk" that can be used to compute the partial derivative of f(x)
// with respect to xₖ at any point x.
get_vpartial_derivative!(f, dfk);

// nalgebra::DVector
let x0: DVector<f64> = dvector![5.0, 1.0];
let dfk_eval: DVector<f64> = dfk(&x0, k, &[]);

// nalgebra::SVector
let x0: SVector<f64, 2> = SVector::from_slice(&[5.0, 1.0]);
let dfk_eval: DVector<f64> = dfk(&x0, k, &[]);

// ndarray::Array1
let x0: Array1<f64> = array![5.0, 1.0];
let dfk_eval: Array1<f64> = dfk(&x0, k, &[]);

// faer::Mat
let x0: Mat<f64> = Mat::from_slice(&[5.0, 1.0]);
let dfk_eval: Mat<f64> = dfk(&x0, k, &[]);

§Example Passing Runtime Parameters

Compute the partial derivative of a parameterized vector function

$$\mathbf{f}(\mathbf{x})=\begin{bmatrix}ax_{0}^{2}+bx_{1}\\c\sin(dx_{0})+ex_{1}^{2}\end{bmatrix}$$

where $a$, $b$, $c$, $d$, and $e$ are runtime parameters. The partial derivatives are:

  • $\dfrac{\partial \mathbf{f}}{\partial x_{0}}=\begin{bmatrix}2ax_{0}\\cd\cos(dx_{0})\end{bmatrix}$
  • $\dfrac{\partial \mathbf{f}}{\partial x_{1}}=\begin{bmatrix}b\\2ex_{1}\end{bmatrix}$
use linalg_traits::{Scalar, Vector};
use numtest::*;

use numdiff::{get_vpartial_derivative, Dual, DualVector};

// Define the function, f(x).
fn f<S: Scalar, V: Vector<S>>(x: &V, p: &[f64]) -> V::DVectorT<S> {
    let a = S::new(p[0]);
    let b = S::new(p[1]);
    let c = S::new(p[2]);
    let d = S::new(p[3]);
    let e = S::new(p[4]);
    V::DVectorT::from_slice(&[
        a * x.vget(0).powi(2) + b * x.vget(1),
        c * (d * x.vget(0)).sin() + e * x.vget(1).powi(2)
    ])
}

// Define individual parameters.
let a = 1.5;
let b = -2.0;
let c = 3.0;
let d = 0.5;
let e = 2.5;

// Parameter vector.
let p = [a, b, c, d, e];

// Evaluation point.
let x0 = vec![1.0, 0.8];

// Autogenerate the partial derivative function.
get_vpartial_derivative!(f, dfk);

// True partial derivative functions.
let df_dx0_true = |x: &[f64]| vec![
    2.0 * a * x[0],
    c * d * (d * x[0]).cos()
];
let df_dx1_true = |x: &[f64]| vec![
    b,
    2.0 * e * x[1]
];

// Compute ∂f/∂x₀ at x₀ and compare with true function.
let df_dx0 = dfk(&x0, 0, &p);
let expected_df_dx0 = df_dx0_true(&x0);
assert_arrays_equal_to_decimal!(df_dx0, expected_df_dx0, 14);

// Compute ∂f/∂x₁ at x₀ and compare with true function.
let df_dx1 = dfk(&x0, 1, &p);
let expected_df_dx1 = df_dx1_true(&x0);
assert_arrays_equal_to_decimal!(df_dx1, expected_df_dx1, 15);