numdiff/lib.rs
1//! [![github]](https://github.com/tamaskis/numdiff) [![crates-io]](https://crates.io/crates/numdiff) [![docs-rs]](https://docs.rs/numdiff)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! Automatic and numerical differentiation.
8//!
9//! # Overview
10//!
11//! This crate implements two different methods for evaluating derivatives in Rust:
12//!
13//! 1. Automatic differentiation (forward-mode using first-order dual numbers).
14//! 2. Numerical differentiation (using forward difference and central difference approximations).
15//!
16//! This crate provides generic functions (for numerical differentiation) and macros (for automatic
17//! differentiation) to evaluate various types of derivatives of the following types of functions:
18//!
19//! * Univariate, scalar-valued functions ($f:\mathbb{R}\to\mathbb{R}$)
20//! * Univariate, vector-valued functions ($\mathbf{f}:\mathbb{R}\to\mathbb{R}^{m}$)
21//! * Multivariate, scalar-valued functions ($f:\mathbb{R}^{n}\to\mathbb{R}$)
22//! * Multivariate, vector-valued functions ($\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$)
23//!
24//! These functions and macros are made generic over the choice of vector representation, as long as
25//! the vector type implements the `linalg_traits::Vector` trait. See the
26//! [`linalg_traits` documentation](https://docs.rs/linalg-traits/latest/linalg_traits/) for more
27//! information.
28//!
29//! # Automatic Differentiation (Forward-Mode)
30//!
31//! | Derivative Type | Function Type | Macro to Generate Derivative Function |
32//! | --------------- | ------------- | ---------------------------------- |
33//! | derivative | $f:\mathbb{R}\to\mathbb{R}$ | [`get_sderivative!`] |
34//! | derivative | $\mathbf{f}:\mathbb{R}\to\mathbb{R}^{m}$ | [`get_vderivative!`] |
35//! | partial derivative | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`get_spartial_derivative!`] |
36//! | partial derivative | $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`get_vpartial_derivative!`] |
37//! | gradient | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`get_gradient!`] |
38//! | directional derivative | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`get_directional_derivative!`] |
39//! | Jacobian| $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`get_jacobian!`] |
40//!
41//! ## Limitations
42//!
43//! * These macros only work on functions that are generic both over the type of scalar and the type
44//! of vector.
45//! * Consequently, these macros do _not_ work on closures.
46//! - Currently, this also means we can't "pass extra parameters" to a function.
47//! * Constants (e.g. `5.0_f64`) need to be defined using `linalg_traits::Scalar::new` (e.g. if a
48//! function has the generic parameter `S: Scalar`, then instead of defining a constant number
49//! such as `5.0_f64`, we need to do `S::new(5.0)`).
50//! - This is also the case for some functions that can take constants are arguments, such as
51//! [`num_traits::Float::powf`].
52//! * When defining functions that operate on generic scalars (to make them compatible with
53//! automatic differentiation), we cannot do an assignment operation such as `1.0 += x` if
54//! `x: S` where `S: Scalar`.
55//!
56//! ## Alternatives
57//!
58//! There are already some alternative crates in the Rust ecosystem that already implement dual
59//! numbers. Originally, I intended to implement the autodifferentiation functions in this crate
60//! using one of those other dual number implementations in the backend. However, each crate had
61//! certain shortcomings that ultimately led to me providing a custom implementation of dual numbers
62//! in this crate. The alternative crates implementing dual numbers are described below.
63//!
64//! ##### [`num-dual`](https://docs.rs/num-dual/latest/num_dual/)
65//!
66//! * This crate _can_ be used to differentiate functions of generic types that implement the
67//! [`DualNum`](https://docs.rs/num-dual/latest/num_dual/trait.DualNum.html) trait. Since this
68//! trait is implemented for [`f32`] and [`f64`], it would allow us to write generic functions
69//! that can be simply evaluated using [`f64`]s, but can also be automatically differentiated if
70//! needed.
71//! * However, there are some notable shortcomings that are described below.
72//! * The [`Dual`](https://github.com/itt-ustutt/num-dual/blob/master/src/dual.rs) struct panics in
73//! its implementations of the following standard functions which are quite common in engineering:
74//! - [`num_traits::Float::floor`]
75//! - [`num_traits::Float::ceil`]
76//! - [`num_traits::Float::round`]
77//! - [`num_traits::Float::trunc`]
78//! - [`num_traits::Float::fract`]
79//! * `num-dual` has a required dependency on
80//! [`nalgebra`](https://docs.rs/nalgebra/latest/nalgebra/), which is quite a heavy dependency for
81//! those who do not need it.
82//!
83//! ##### [`autodj`](https://docs.rs/autodj/latest/autodj/)
84//!
85//! * Can only differentiate functions written using custom types, such as
86//! [`DualF64`](https://docs.rs/autodj/latest/autodj/solid/single/type.DualF64.html).
87//! * Multivariate functions, especially those with a dynamic number of variables, can be extremely
88//! clunky (see
89//! [this example](https://docs.rs/autodj/latest/autodj/index.html#dynamic-number-of-variables)).
90//!
91//! ##### [`autodiff`](https://docs.rs/autodiff/latest/autodiff/)
92//!
93//! * Can only differentiate functions written using the custom type
94//! [`FT<T>`](https://docs.rs/autodiff/latest/autodiff/forward_autodiff/type.FT.html).
95//! * Incorrect implementation of certain functions, such as [`num_traits::Float::floor`] (see the
96//! [source code](https://github.com/elrnv/autodiff/blob/master/src/forward_autodiff.rs)).
97//!
98//! # Central Difference Approximations
99//!
100//! | Derivative Type | Function Type | Function to Approximate Derivative |
101//! | --------------- | ------------- | ---------------------------------- |
102//! | derivative | $f:\mathbb{R}\to\mathbb{R}$ | [`central_difference::sderivative()`] |
103//! | derivative | $\mathbf{f}:\mathbb{R}\to\mathbb{R}^{m}$ | [`central_difference::vderivative()`] |
104//! | partial derivative | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`central_difference::spartial_derivative()`] |
105//! | partial derivative | $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`central_difference::vpartial_derivative()`] |
106//! | gradient | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`central_difference::gradient()`] |
107//! | directional derivative | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`central_difference::directional_derivative()`] |
108//! | Jacobian | $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`central_difference::jacobian()`] |
109//! | Hessian | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`central_difference::shessian()`] |
110//! | Hessian | $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`central_difference::vhessian()`] |
111//!
112//! # Forward Difference Approximations
113//!
114//! | Derivative Type | Function Type | Function to Approximate Derivative |
115//! | --------------- | ------------- | ---------------------------------- |
116//! | derivative | $f:\mathbb{R}\to\mathbb{R}$ | [`forward_difference::sderivative()`] |
117//! | derivative | $\mathbf{f}:\mathbb{R}\to\mathbb{R}^{m}$ | [`forward_difference::vderivative()`] |
118//! | partial derivative | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`forward_difference::spartial_derivative()`] |
119//! | partial derivative | $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`forward_difference::vpartial_derivative()`] |
120//! | gradient | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`forward_difference::gradient()`] |
121//! | directional derivative | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`forward_difference::directional_derivative()`] |
122//! | Jacobian | $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`forward_difference::jacobian()`] |
123//! | Hessian | $f:\mathbb{R}^{n}\to\mathbb{R}$ | [`forward_difference::shessian()`] |
124//! | Hessian | $\mathbf{f}:\mathbb{R}^{n}\to\mathbb{R}^{m}$ | [`forward_difference::vhessian()`] |
125
126// Linter setup.
127#![warn(missing_docs)]
128
129// Module declarations.
130pub(crate) mod automatic_differentiation;
131pub mod central_difference;
132pub mod constants;
133pub mod forward_difference;
134
135// Module declarations for utils used for testing only.
136#[cfg(test)]
137pub(crate) mod test_utils;
138
139// Re-exports.
140pub use automatic_differentiation::dual::Dual;
141pub use automatic_differentiation::dual_vector::DualVector;