Skip to main content

get_spartial_derivative

Macro get_spartial_derivative 

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

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

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

§Arguments

  • f - Multivariate, scalar-valued function, $f:\mathbb{R}^{n}\to\mathbb{R}$.
  • func_name - Name of the function that will return the partial derivative of $f(\mathbf{x})$ with respect to $x_{k}$ at any point $\mathbf{x}\in\mathbb{R}^{n}$.
  • param_type (optional) - Type of the extra runtime parameter p that is passed to f. Defaults to [f64] (implying that f accepts p: &[f64]).

§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 $f(\mathbf{x})$ to evaluate its partial derivative with respect to $x_{k}$.

§Examples

§Basic Example

Compute the partial derivative of

$$f(x)=x^{3}\sin{y}$$

with respect to $y$ at $(x,y)=(5,1)$, and compare the result to the true result of

$$\frac{\partial f}{\partial y}\bigg\rvert_{(x,y)=(5,1)}=5^{3}\cos{(1)}$$

First, note that we can rewrite this function as

$$f(\mathbf{x})=x_{0}^{3}\sin{x_{1}}$$

where $\mathbf{x}=(x_{0},x_{1})^{T}$ (note that we use 0-based indexing to aid with the computational implementation). We are then trying to find

$$\frac{\partial f}{\partial x_{1}}\bigg\rvert_{\mathbf{x}=\mathbf{x}_{0}}$$

where $\mathbf{x}_{0}=(5,1)^{T}$.

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

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

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

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

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

// 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_spartial_derivative!(f, dfk);

// Verify that the partial derivative function obtained using get_spartial_derivative! computes
// the partial derivative correctly.
assert_eq!(dfk(&x0, k, &[]), 5.0_f64.powi(3) * 1.0_f64.cos());
§Using other vector types

The function produced by get_spartial_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_spartial_derivative, Dual, DualVector};

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

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

// 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_spartial_derivative!(f, dfk);

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

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

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

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

§Example Passing Runtime Parameters

Compute the partial derivative of a parameterized function

$$f(\mathbf{x})=ax_{0}^{2}+bx_{1}^{2}+cx_{0}x_{1}+d\sin(ex_{0})$$

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

  • $\dfrac{\partial f}{\partial x_{0}}=2ax_{0}+cx_{1}+de\cos(ex_{0})$
  • $\dfrac{\partial f}{\partial x_{1}}=2bx_{1}+cx_{0}$
use linalg_traits::{Scalar, Vector};
use numtest::*;

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

// Define the function, f(x).
fn f<S: Scalar, V: Vector<S>>(x: &V, p: &[f64]) -> 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]);
    a * x.vget(0).powi(2)
        + b * x.vget(1).powi(2)
        + c * x.vget(0) * x.vget(1)
        + d * (e * x.vget(0)).sin()
}

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

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

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

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

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

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

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

§Example Passing Custom Parameter Types

Use a custom parameter struct instead of f64 values.

use linalg_traits::{Scalar, Vector};
use numtest::*;

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

struct Data {
    a: f64,
    b: f64,
    c: f64,
    d: f64,
    e: f64,
}

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

// Runtime parameter struct.
let p = Data {
    a: 1.5,
    b: 2.0,
    c: 0.8,
    d: 3.0,
    e: 0.5,
};

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

// Autogenerate the partial derivative function, telling the macro to expect a runtime parameter
// of type &Data.
get_spartial_derivative!(f, dfk, Data);

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

// Compute the partial derivatives using both the automatically generated partial derivative
// function and the true partial derivative functions, and compare the results.
let df_dx0: f64 = dfk(&x0, 0, &p);
let df_dx1: f64 = dfk(&x0, 1, &p);
assert_equal_to_decimal!(df_dx0, df_dx0_true(&x0), 14);
assert_equal_to_decimal!(df_dx1, df_dx1_true(&x0), 15);