mathhook_core/calculus/derivatives/
higher_order.rs

1//! Higher-order derivative utilities
2use crate::calculus::derivatives::Derivative;
3use crate::core::{Expression, Symbol};
4use crate::simplify::Simplify;
5use crate::symbol;
6/// Higher-order derivative operations
7pub struct HigherOrderDerivatives;
8impl HigherOrderDerivatives {
9    /// Compute nth-order derivative
10    ///
11    /// # Examples
12    ///
13    /// ```rust
14    /// use mathhook_core::simplify::Simplify;
15    /// use mathhook_core::calculus::derivatives::Derivative;
16    /// use mathhook_core::{Expression};
17    /// use mathhook_core::symbol;
18    /// use mathhook_core::calculus::derivatives::HigherOrderDerivatives;
19    ///
20    /// let x = symbol!(x);
21    /// let expr = Expression::pow(Expression::symbol(x.clone()), Expression::integer(4));
22    /// let second_derivative = HigherOrderDerivatives::compute(&expr, x, 2);
23    /// ```
24    pub fn compute(expr: &Expression, variable: Symbol, order: u32) -> Expression {
25        match order {
26            0 => expr.clone(),
27            1 => expr.derivative(variable).simplify(),
28            n => {
29                let mut result = expr.clone();
30                for _ in 0..n {
31                    result = result.derivative(variable.clone()).simplify();
32                }
33                result
34            }
35        }
36    }
37    /// Compute mixed partial derivatives
38    ///
39    /// # Examples
40    ///
41    /// ```rust
42    /// use mathhook_core::{Expression, symbol};
43    /// use mathhook_core::calculus::derivatives::HigherOrderDerivatives;
44    ///
45    /// let x = symbol!(x);
46    /// let y = symbol!(y);
47    /// let expr = Expression::mul(vec![
48    ///     Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
49    ///     Expression::symbol(y.clone())
50    /// ]);
51    /// let mixed_partial = HigherOrderDerivatives::mixed_partial(&expr, vec![(x.clone(), 1), (y.clone(), 1)]);
52    /// ```
53    pub fn mixed_partial(expr: &Expression, derivatives: Vec<(Symbol, u32)>) -> Expression {
54        let mut result = expr.clone();
55        for (variable, order) in derivatives {
56            if order > 0 {
57                result = Self::compute(&result, variable, order);
58            }
59        }
60        result.simplify()
61    }
62    /// Check if higher-order derivative exists
63    ///
64    /// # Examples
65    ///
66    /// ```rust
67    /// use mathhook_core::simplify::Simplify;
68    /// use mathhook_core::calculus::derivatives::Derivative;
69    /// use mathhook_core::{Expression, symbol};
70    /// use mathhook_core::calculus::derivatives::HigherOrderDerivatives;
71    ///
72    /// let x = symbol!(x);
73    /// let expr = Expression::function("sin", vec![Expression::symbol(x.clone())]);
74    /// let exists = HigherOrderDerivatives::exists(&expr, x.clone(), 5);
75    /// ```
76    pub fn exists(expr: &Expression, variable: Symbol, order: u32) -> bool {
77        if order == 0 {
78            return true;
79        }
80        if !expr.is_differentiable(variable.clone()) {
81            return false;
82        }
83        if order == 1 {
84            return true;
85        }
86        let first_derivative = expr.derivative(variable.clone());
87        Self::exists(&first_derivative, variable, order - 1)
88    }
89    /// Compute derivative table up to specified order
90    ///
91    /// # Examples
92    ///
93    /// ```rust
94    /// use mathhook_core::{Expression, symbol};
95    /// use mathhook_core::calculus::derivatives::HigherOrderDerivatives;
96    ///
97    /// let x = symbol!(x);
98    /// let expr = Expression::function("sin", vec![Expression::symbol(x.clone())]);
99    /// let table = HigherOrderDerivatives::derivative_table(&expr, &x, 4);
100    /// ```
101    pub fn derivative_table(
102        expr: &Expression,
103        variable: &Symbol,
104        max_order: u32,
105    ) -> Vec<Expression> {
106        let mut table = Vec::with_capacity((max_order + 1) as usize);
107        let mut current = expr.clone();
108        table.push(current.clone());
109        for _ in 1..=max_order {
110            current = current.derivative(variable.clone()).simplify();
111            table.push(current.clone());
112        }
113        table
114    }
115    /// Find pattern in higher-order derivatives
116    ///
117    /// # Examples
118    ///
119    /// ```rust
120    /// use mathhook_core::{Expression, symbol};
121    /// use mathhook_core::calculus::derivatives::HigherOrderDerivatives;
122    ///
123    /// let x = symbol!(x);
124    /// let expr = Expression::function("sin", vec![Expression::symbol(x.clone())]);
125    /// let pattern = HigherOrderDerivatives::find_pattern(&expr, x.clone(), 8);
126    /// ```
127    pub fn find_pattern(expr: &Expression, variable: Symbol, check_orders: u32) -> Option<u32> {
128        let derivatives = Self::derivative_table(expr, &variable, check_orders);
129        (1..=(check_orders / 2)).find(|&period| Self::has_period(&derivatives, period as usize))
130    }
131    /// Check if derivative sequence has a given period
132    fn has_period(derivatives: &[Expression], period: usize) -> bool {
133        if derivatives.len() < 2 * period {
134            return false;
135        }
136        for i in 0..period {
137            if derivatives[i] != derivatives[i + period] {
138                return false;
139            }
140        }
141        true
142    }
143    /// Compute Leibniz rule for nth derivative of product
144    ///
145    /// # Examples
146    ///
147    /// ```rust
148    /// use mathhook_core::simplify::Simplify;
149    /// use mathhook_core::{Expression, symbol};
150    /// use mathhook_core::calculus::derivatives::HigherOrderDerivatives;
151    ///
152    /// let x = symbol!(x);
153    /// let u = Expression::symbol(x.clone());
154    /// let v = Expression::function("sin", vec![Expression::symbol(x.clone())]);
155    /// let nth_product = HigherOrderDerivatives::leibniz_rule(&u, &v, &x, 3);
156    /// ```
157    pub fn leibniz_rule(u: &Expression, v: &Expression, variable: &Symbol, n: u32) -> Expression {
158        let mut terms = Vec::with_capacity((n + 1) as usize);
159        for k in 0..=n {
160            let binomial_coeff = Self::binomial_coefficient(n, k);
161            let u_derivative = Self::compute(u, variable.clone(), k);
162            let v_derivative = Self::compute(v, variable.clone(), n - k);
163            let term = Expression::mul(vec![
164                Expression::integer(binomial_coeff),
165                u_derivative,
166                v_derivative,
167            ]);
168            terms.push(term);
169        }
170        Expression::add(terms).simplify()
171    }
172    /// Compute binomial coefficient C(n,k)
173    fn binomial_coefficient(n: u32, k: u32) -> i64 {
174        if k > n {
175            return 0;
176        }
177        if k == 0 || k == n {
178            return 1;
179        }
180        let k = k.min(n - k);
181        let mut result = 1i64;
182        for i in 0..k {
183            result = result * (n - i) as i64 / (i + 1) as i64;
184        }
185        result
186    }
187    /// Compute FaĆ  di Bruno's formula for chain rule higher derivatives
188    ///
189    /// # Examples
190    ///
191    /// ```rust
192    /// use mathhook_core::{Expression, symbol};
193    /// use mathhook_core::calculus::derivatives::HigherOrderDerivatives;
194    ///
195    /// let x = symbol!(x);
196    /// let inner = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
197    /// let outer_name = "sin";
198    /// let chain_derivative = HigherOrderDerivatives::faa_di_bruno(outer_name, &inner, x.clone(), 2);
199    /// ```
200    pub fn faa_di_bruno(
201        outer_function: &str,
202        inner_function: &Expression,
203        variable: Symbol,
204        n: u32,
205    ) -> Expression {
206        if n == 0 {
207            return Expression::function(outer_function, vec![inner_function.clone()]);
208        }
209        if n == 1 {
210            let outer_derivative = Self::get_function_derivative(outer_function, inner_function);
211            let inner_derivative = inner_function.derivative(variable);
212            return Expression::mul(vec![outer_derivative, inner_derivative]).simplify();
213        }
214        Expression::derivative(
215            Expression::function(outer_function, vec![inner_function.clone()]),
216            variable,
217            n,
218        )
219    }
220    /// Get derivative of standard function using UniversalFunctionRegistry
221    ///
222    /// Uses registry-based derivative lookup for all registered functions.
223    /// Falls back to symbolic derivative for unknown functions.
224    fn get_function_derivative(function_name: &str, arg: &Expression) -> Expression {
225        use crate::functions::intelligence::get_universal_registry;
226        let registry = get_universal_registry();
227        if let Some(props) = registry.get_properties(function_name) {
228            if let Some(derivative) = props.get_derivative_expression(arg) {
229                return derivative;
230            }
231        }
232        Expression::derivative(
233            Expression::function(function_name, vec![arg.clone()]),
234            symbol!(x),
235            1,
236        )
237    }
238}
239#[cfg(test)]
240mod tests {
241    use super::*;
242    use crate::symbol;
243    #[test]
244    fn test_polynomial_higher_derivatives() {
245        let x = symbol!(x);
246        let x4 = Expression::pow(Expression::symbol(x.clone()), Expression::integer(4));
247        let first = HigherOrderDerivatives::compute(&x4, x.clone(), 1);
248        assert!(!first.is_zero());
249        let second = HigherOrderDerivatives::compute(&x4, x.clone(), 2);
250        assert!(!second.is_zero());
251        let third = HigherOrderDerivatives::compute(&x4, x.clone(), 3);
252        assert!(!third.is_zero());
253        let fourth = HigherOrderDerivatives::compute(&x4, x.clone(), 4);
254        assert!(!fourth.is_zero());
255        let fifth = HigherOrderDerivatives::compute(&x4, x.clone(), 5);
256        assert_eq!(fifth.simplify(), Expression::integer(0));
257    }
258    #[test]
259    fn test_exponential_derivatives() {
260        let x = symbol!(x);
261        let exp_x = Expression::function("exp", vec![Expression::symbol(x.clone())]);
262        for order in 1..=5 {
263            let derivative = HigherOrderDerivatives::compute(&exp_x, x.clone(), order);
264            assert!(!derivative.is_zero());
265        }
266    }
267    #[test]
268    fn test_trigonometric_patterns() {
269        let x = symbol!(x);
270        let sin_x = Expression::function("sin", vec![Expression::symbol(x.clone())]);
271        let cos_x = Expression::function("cos", vec![Expression::symbol(x.clone())]);
272        let sin_table = HigherOrderDerivatives::derivative_table(&sin_x, &x, 8);
273        assert_eq!(sin_table.len(), 9);
274        let cos_table = HigherOrderDerivatives::derivative_table(&cos_x, &x, 8);
275        assert_eq!(cos_table.len(), 9);
276        let sin_pattern = HigherOrderDerivatives::find_pattern(&sin_x, x.clone(), 8);
277        assert!(sin_pattern.is_some());
278        let cos_pattern = HigherOrderDerivatives::find_pattern(&cos_x, x.clone(), 8);
279        assert!(cos_pattern.is_some());
280    }
281    #[test]
282    fn test_mixed_partial_derivatives() {
283        let x = symbol!(x);
284        let y = symbol!(y);
285        let xy = Expression::mul(vec![
286            Expression::symbol(x.clone()),
287            Expression::symbol(y.clone()),
288        ]);
289        let mixed_xy =
290            HigherOrderDerivatives::mixed_partial(&xy, vec![(x.clone(), 1), (y.clone(), 1)]);
291        assert!(!mixed_xy.is_zero());
292        let x2y = Expression::mul(vec![
293            Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
294            Expression::symbol(y.clone()),
295        ]);
296        let mixed_x2y =
297            HigherOrderDerivatives::mixed_partial(&x2y, vec![(x.clone(), 2), (y.clone(), 1)]);
298        assert!(!mixed_x2y.is_zero());
299    }
300    #[test]
301    fn test_derivative_existence() {
302        let x = symbol!(x);
303        let smooth_expr = Expression::function("sin", vec![Expression::symbol(x.clone())]);
304        assert!(HigherOrderDerivatives::exists(&smooth_expr, x.clone(), 0));
305        assert!(HigherOrderDerivatives::exists(&smooth_expr, x.clone(), 1));
306        assert!(HigherOrderDerivatives::exists(&smooth_expr, x.clone(), 10));
307        let polynomial = Expression::pow(Expression::symbol(x.clone()), Expression::integer(3));
308        assert!(HigherOrderDerivatives::exists(&polynomial, x.clone(), 3));
309        assert!(HigherOrderDerivatives::exists(&polynomial, x.clone(), 4));
310    }
311    #[test]
312    fn test_leibniz_rule() {
313        let x = symbol!(x);
314        let u = Expression::symbol(x.clone());
315        let v = Expression::function("sin", vec![Expression::symbol(x.clone())]);
316        let leibniz_2 = HigherOrderDerivatives::leibniz_rule(&u, &v, &x, 2);
317        assert!(!leibniz_2.is_zero());
318        let leibniz_3 = HigherOrderDerivatives::leibniz_rule(&u, &v, &x, 3);
319        assert!(!leibniz_3.is_zero());
320        let u_poly = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
321        let v_exp = Expression::function("exp", vec![Expression::symbol(x.clone())]);
322        let leibniz_poly = HigherOrderDerivatives::leibniz_rule(&u_poly, &v_exp, &x, 2);
323        assert!(!leibniz_poly.is_zero());
324    }
325    #[test]
326    fn test_binomial_coefficients() {
327        assert_eq!(HigherOrderDerivatives::binomial_coefficient(0, 0), 1);
328        assert_eq!(HigherOrderDerivatives::binomial_coefficient(1, 0), 1);
329        assert_eq!(HigherOrderDerivatives::binomial_coefficient(1, 1), 1);
330        assert_eq!(HigherOrderDerivatives::binomial_coefficient(2, 1), 2);
331        assert_eq!(HigherOrderDerivatives::binomial_coefficient(3, 2), 3);
332        assert_eq!(HigherOrderDerivatives::binomial_coefficient(4, 2), 6);
333        assert_eq!(HigherOrderDerivatives::binomial_coefficient(5, 3), 10);
334        assert_eq!(HigherOrderDerivatives::binomial_coefficient(10, 5), 252);
335        assert_eq!(HigherOrderDerivatives::binomial_coefficient(5, 6), 0);
336    }
337    #[test]
338    fn test_faa_di_bruno_formula() {
339        let x = symbol!(x);
340        let inner = Expression::pow(Expression::symbol(x.clone()), Expression::integer(2));
341        let chain_1 = HigherOrderDerivatives::faa_di_bruno("sin", &inner, x.clone(), 1);
342        assert!(!chain_1.is_zero());
343        let chain_2 = HigherOrderDerivatives::faa_di_bruno("sin", &inner, x.clone(), 2);
344        assert!(!chain_2.is_zero());
345        let exp_inner = Expression::function("exp", vec![Expression::symbol(x.clone())]);
346        let exp_chain = HigherOrderDerivatives::faa_di_bruno("ln", &exp_inner, x.clone(), 1);
347        assert!(!exp_chain.is_zero());
348    }
349    #[test]
350    fn test_zero_order_derivative() {
351        let x = symbol!(x);
352        let expr = Expression::add(vec![
353            Expression::pow(Expression::symbol(x.clone()), Expression::integer(3)),
354            Expression::function("sin", vec![Expression::symbol(x.clone())]),
355            Expression::integer(5),
356        ]);
357        let zero_order = HigherOrderDerivatives::compute(&expr, x.clone(), 0);
358        assert_eq!(zero_order, expr);
359    }
360    #[test]
361    fn test_multivariate_higher_derivatives() {
362        let x = symbol!(x);
363        let y = symbol!(y);
364        let multivar = Expression::add(vec![
365            Expression::mul(vec![
366                Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
367                Expression::pow(Expression::symbol(y.clone()), Expression::integer(3)),
368            ]),
369            Expression::function(
370                "sin",
371                vec![Expression::mul(vec![
372                    Expression::symbol(x.clone()),
373                    Expression::symbol(y.clone()),
374                ])],
375            ),
376        ]);
377        let dx2 = HigherOrderDerivatives::compute(&multivar, x.clone(), 2);
378        assert!(!dx2.is_zero());
379        let dy3 = HigherOrderDerivatives::compute(&multivar, y.clone(), 3);
380        assert!(!dy3.is_zero());
381        let mixed =
382            HigherOrderDerivatives::mixed_partial(&multivar, vec![(x.clone(), 1), (y.clone(), 2)]);
383        assert!(!mixed.is_zero());
384    }
385    #[test]
386    fn test_edge_cases() {
387        let x = symbol!(x);
388        let constant = Expression::integer(42);
389        let const_deriv = HigherOrderDerivatives::compute(&constant, x.clone(), 5);
390        assert_eq!(const_deriv.simplify(), Expression::integer(0));
391        let linear = Expression::symbol(x.clone());
392        let linear_second = HigherOrderDerivatives::compute(&linear, x.clone(), 2);
393        assert_eq!(linear_second.simplify(), Expression::integer(0));
394        let empty_mixed = HigherOrderDerivatives::mixed_partial(&linear, vec![]);
395        assert_eq!(empty_mixed, linear);
396    }
397}