1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use crate::scalar::{Numeric, ScalarFn, ScalarFnN};
use crate::utils::error_codes::CalcError;
/// Base trait for single-variable differentiation.
pub trait DerivatorSingleVariable {
/// The scalar the derivative is computed in.
type Scalar: Numeric;
/// Computes the `order`-th derivative of `func` at `point`.
///
/// # Errors
/// [`CalcError::DerivativeOrderZero`] if `order` is zero, or
/// [`CalcError::StepSizeZero`] if the configured step size is zero.
///
/// # Examples
/// ```
/// use multicalc::numerical_derivative::derivator::DerivatorSingleVariable;
/// use multicalc::numerical_derivative::finite_difference::FiniteDifferenceSingle;
/// use multicalc::scalar_fn;
///
/// let func = scalar_fn!(|x| x * x * x);
/// let derivator = FiniteDifferenceSingle::default();
///
/// let val = derivator.get(1, &func, 2.0).unwrap();
/// assert!(f64::abs(val - 12.0) < 1e-7);
/// let val = derivator.get(2, &func, 2.0).unwrap();
/// assert!(f64::abs(val - 12.0) < 1e-5);
/// ```
fn get<F: ScalarFn>(
&self,
order: usize,
func: &F,
point: Self::Scalar,
) -> Result<Self::Scalar, CalcError>;
/// Convenience wrapper for the first derivative.
fn get_single<F: ScalarFn>(
&self,
func: &F,
point: Self::Scalar,
) -> Result<Self::Scalar, CalcError> {
self.get(1, func, point)
}
/// Convenience wrapper for the second derivative.
fn get_double<F: ScalarFn>(
&self,
func: &F,
point: Self::Scalar,
) -> Result<Self::Scalar, CalcError> {
self.get(2, func, point)
}
}
/// Base trait for multi-variable differentiation.
pub trait DerivatorMultiVariable {
/// The scalar the derivative is computed in.
type Scalar: Numeric;
/// Computes the partial derivative of `func` at `point`, differentiating once
/// with respect to each variable index listed in `idx_to_differentiate`. The
/// derivative order equals the length of that array.
///
/// # Errors
/// [`CalcError::DerivativeOrderZero`] if `idx_to_differentiate` is empty,
/// [`CalcError::StepSizeZero`] if the step size is zero, or
/// [`CalcError::IndexOutOfRange`] if any index is `>= NUM_VARS`.
///
/// # Examples
/// ```
/// use multicalc::numerical_derivative::derivator::DerivatorMultiVariable;
/// use multicalc::numerical_derivative::finite_difference::FiniteDifferenceMulti;
/// use multicalc::scalar_fn;
///
/// // f(x, y, z) = y*sin(x) + x*cos(y) + x*y*e^z
/// let func = scalar_fn!(|v: &[f64; 3]| v[1] * v[0].sin() + v[0] * v[1].cos() + v[0] * v[1] * v[2].exp());
/// let derivator = FiniteDifferenceMulti::default();
///
/// // mixed partial d(df/dx)/dy
/// let val = derivator.get(&func, &[0, 1], &[1.0, 2.0, 3.0]).unwrap();
/// let expected = f64::cos(1.0) - f64::sin(2.0) + f64::exp(3.0);
/// assert!(f64::abs(val - expected) < 0.001);
/// ```
fn get<F: ScalarFnN<NUM_VARS>, const NUM_VARS: usize, const NUM_ORDER: usize>(
&self,
func: &F,
idx_to_differentiate: &[usize; NUM_ORDER],
point: &[Self::Scalar; NUM_VARS],
) -> Result<Self::Scalar, CalcError>;
/// Convenience wrapper for a single partial derivative.
fn get_single_partial<F: ScalarFnN<NUM_VARS>, const NUM_VARS: usize>(
&self,
func: &F,
idx_to_differentiate: usize,
point: &[Self::Scalar; NUM_VARS],
) -> Result<Self::Scalar, CalcError> {
self.get(func, &[idx_to_differentiate], point)
}
/// Convenience wrapper for a second partial derivative.
fn get_double_partial<F: ScalarFnN<NUM_VARS>, const NUM_VARS: usize>(
&self,
func: &F,
idx_to_differentiate: &[usize; 2],
point: &[Self::Scalar; NUM_VARS],
) -> Result<Self::Scalar, CalcError> {
self.get(func, idx_to_differentiate, point)
}
}