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 parameterpthat is passed tof. Defaults to[f64](implying thatfacceptsp: &[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);