mathhook_core/calculus/
series.rs

1//! Series expansions and analysis
2//!
3//! Implements Taylor series, Laurent series, Maclaurin series,
4//! and other infinite series expansions for symbolic computation.
5//!
6//! For noncommutative expressions (matrices, operators, quaternions):
7//! - (A+B)^n expansion preserves order: A^2 + AB + BA + B^2 (NOT A^2 + 2AB + B^2)
8//! - Taylor series terms maintain factor order
9//! - Power series coefficients respect noncommutativity
10
11use crate::calculus::derivatives::Derivative;
12use crate::core::{Expression, Symbol};
13use crate::simplify::Simplify;
14
15/// Types of series expansions
16#[derive(Debug, Clone, Copy, PartialEq)]
17pub enum SeriesType {
18    /// Taylor series expansion
19    Taylor,
20    /// Laurent series (includes negative powers)
21    Laurent,
22    /// Maclaurin series (Taylor around 0)
23    Maclaurin,
24    /// Fourier series
25    Fourier,
26    /// Power series
27    Power,
28}
29
30/// Trait for series expansion operations
31pub trait SeriesExpansion {
32    /// Compute Taylor series expansion
33    ///
34    /// # Examples
35    ///
36    /// ```rust
37    /// use mathhook_core::{Expression, symbol};
38    /// use mathhook_core::calculus::SeriesExpansion;
39    ///
40    /// let x = symbol!(x);
41    /// let expr = Expression::function("exp", vec![Expression::symbol(x.clone())]);
42    /// let point = Expression::integer(0);
43    /// let result = expr.taylor_series(&x, &point, 5);
44    /// ```
45    fn taylor_series(&self, variable: &Symbol, point: &Expression, order: u32) -> Expression;
46
47    /// Compute Laurent series expansion
48    ///
49    /// # Examples
50    ///
51    /// ```rust
52    /// use mathhook_core::{Expression, symbol};
53    /// use mathhook_core::calculus::SeriesExpansion;
54    ///
55    /// let x = symbol!(x);
56    /// let expr = Expression::pow(
57    ///     Expression::function("sin", vec![Expression::symbol(x.clone())]),
58    ///     Expression::integer(-1)
59    /// );
60    /// let point = Expression::integer(0);
61    /// let result = expr.laurent_series(&x, &point, 5);
62    /// ```
63    fn laurent_series(&self, variable: &Symbol, point: &Expression, order: u32) -> Expression;
64
65    /// Compute Maclaurin series (Taylor around 0)
66    ///
67    /// # Examples
68    ///
69    /// ```rust
70    /// use mathhook_core::{Expression, symbol};
71    /// use mathhook_core::calculus::SeriesExpansion;
72    ///
73    /// let x = symbol!(x);
74    /// let expr = Expression::function("cos", vec![Expression::symbol(x.clone())]);
75    /// let result = expr.maclaurin_series(&x, 6);
76    /// ```
77    fn maclaurin_series(&self, variable: &Symbol, order: u32) -> Expression;
78
79    /// Compute power series coefficients
80    ///
81    /// # Examples
82    ///
83    /// ```rust
84    /// use mathhook_core::{Expression, symbol};
85    /// use mathhook_core::calculus::SeriesExpansion;
86    ///
87    /// let x = symbol!(x);
88    /// let expr = Expression::function("exp", vec![Expression::symbol(x.clone())]);
89    /// let point = Expression::integer(0);
90    /// let result = expr.power_series_coefficients(&x, &point, 5);
91    /// ```
92    fn power_series_coefficients(
93        &self,
94        variable: &Symbol,
95        point: &Expression,
96        order: u32,
97    ) -> Vec<Expression>;
98}
99
100/// Series expansion methods and utilities
101pub struct SeriesMethods;
102
103impl SeriesMethods {
104    /// Compute factorial
105    pub fn factorial(n: u32) -> Expression {
106        if n == 0 || n == 1 {
107            Expression::integer(1)
108        } else {
109            let mut result = 1i64;
110            for i in 2..=n {
111                result *= i as i64;
112            }
113            Expression::integer(result)
114        }
115    }
116
117    /// Compute binomial coefficient
118    pub fn binomial_coefficient(n: u32, k: u32) -> Expression {
119        if k > n {
120            Expression::integer(0)
121        } else if k == 0 || k == n {
122            Expression::integer(1)
123        } else {
124            let numerator = Self::factorial(n);
125            let denominator = Expression::mul(vec![Self::factorial(k), Self::factorial(n - k)]);
126            Expression::mul(vec![
127                numerator,
128                Expression::pow(denominator, Expression::integer(-1)),
129            ])
130            .simplify()
131        }
132    }
133
134    /// Get known series expansion for common functions
135    pub fn known_series(
136        function_name: &str,
137        variable: &Symbol,
138        point: &Expression,
139        order: u32,
140    ) -> Option<Expression> {
141        match function_name {
142            "exp" if point.is_zero() => {
143                // e^x = 1 + x + x²/2! + x³/3! + ...
144                let mut terms = vec![Expression::integer(1)];
145                for n in 1..=order {
146                    let term = Expression::mul(vec![
147                        Expression::pow(
148                            Expression::symbol(variable.clone()),
149                            Expression::integer(n as i64),
150                        ),
151                        Expression::pow(Self::factorial(n), Expression::integer(-1)),
152                    ])
153                    .simplify();
154                    terms.push(term);
155                }
156                Some(Expression::add(terms))
157            }
158            "sin" if point.is_zero() => {
159                // sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + ...
160                let mut terms = Vec::new();
161                for n in 0..=order {
162                    let power = 2 * n + 1;
163                    if power > order {
164                        break;
165                    }
166
167                    let sign = if n % 2 == 0 { 1 } else { -1 };
168                    let term = Expression::mul(vec![
169                        Expression::integer(sign),
170                        Expression::pow(
171                            Expression::symbol(variable.clone()),
172                            Expression::integer(power as i64),
173                        ),
174                        Expression::pow(Self::factorial(power), Expression::integer(-1)),
175                    ])
176                    .simplify();
177                    terms.push(term);
178                }
179                Some(Expression::add(terms))
180            }
181            "cos" if point.is_zero() => {
182                // cos(x) = 1 - x²/2! + x⁴/4! - x⁶/6! + ...
183                let mut terms = Vec::new();
184                for n in 0..=order {
185                    let power = 2 * n;
186                    if power > order {
187                        break;
188                    }
189
190                    let sign = if n % 2 == 0 { 1 } else { -1 };
191                    let term = if power == 0 {
192                        Expression::integer(sign)
193                    } else {
194                        Expression::mul(vec![
195                            Expression::integer(sign),
196                            Expression::pow(
197                                Expression::symbol(variable.clone()),
198                                Expression::integer(power as i64),
199                            ),
200                            Expression::pow(Self::factorial(power), Expression::integer(-1)),
201                        ])
202                        .simplify()
203                    };
204                    terms.push(term);
205                }
206                Some(Expression::add(terms))
207            }
208            "ln" if *point == Expression::integer(1) => {
209                // ln(1+x) = x - x²/2 + x³/3 - x⁴/4 + ...
210                let mut terms = Vec::new();
211                for n in 1..=order {
212                    let sign = if n % 2 == 1 { 1 } else { -1 };
213                    let term = Expression::mul(vec![
214                        Expression::integer(sign),
215                        Expression::pow(
216                            Expression::symbol(variable.clone()),
217                            Expression::integer(n as i64),
218                        ),
219                        Expression::pow(Expression::integer(n as i64), Expression::integer(-1)),
220                    ]);
221                    terms.push(term);
222                }
223                Some(Expression::add(terms))
224            }
225            _ => None,
226        }
227    }
228
229    /// Compute general Taylor series using derivatives
230    ///
231    /// Taylor series: f(x) = Σ [f^(n)(a) / n!] * (x-a)^n
232    ///
233    /// For noncommutative expressions, order is preserved:
234    /// - Derivative f^(n)(a) comes first
235    /// - Power (x-a)^n comes second
236    /// - Division by n! comes last
237    pub fn general_taylor_series(
238        expr: &Expression,
239        variable: &Symbol,
240        point: &Expression,
241        order: u32,
242    ) -> Expression {
243        let mut terms = Vec::new();
244
245        for n in 0..=order {
246            let nth_derivative = expr.nth_derivative(variable.clone(), n);
247            let derivative_at_point = Self::evaluate_at_point(&nth_derivative, variable, point);
248
249            let x_minus_a = if point.is_zero() {
250                Expression::symbol(variable.clone())
251            } else {
252                Expression::add(vec![
253                    Expression::symbol(variable.clone()),
254                    Expression::mul(vec![Expression::integer(-1), point.clone()]),
255                ])
256            };
257
258            let term = if n == 0 {
259                derivative_at_point
260            } else {
261                // Order: f^(n)(a) * (x-a)^n * (1/n!)
262                // This preserves order for noncommutative derivatives
263                Expression::mul(vec![
264                    derivative_at_point,
265                    Expression::pow(x_minus_a, Expression::integer(n as i64)),
266                    Expression::pow(Self::factorial(n), Expression::integer(-1)),
267                ])
268            };
269
270            terms.push(term);
271        }
272
273        Expression::add(terms).simplify()
274    }
275
276    /// Evaluate expression at a specific point
277    pub fn evaluate_at_point(
278        expr: &Expression,
279        variable: &Symbol,
280        point: &Expression,
281    ) -> Expression {
282        match expr {
283            Expression::Symbol(sym) => {
284                if sym == variable {
285                    point.clone()
286                } else {
287                    expr.clone()
288                }
289            }
290            Expression::Add(terms) => {
291                let evaluated: Vec<Expression> = terms
292                    .iter()
293                    .map(|term| Self::evaluate_at_point(term, variable, point))
294                    .collect();
295                Expression::add(evaluated).simplify()
296            }
297            Expression::Mul(factors) => {
298                let evaluated: Vec<Expression> = factors
299                    .iter()
300                    .map(|factor| Self::evaluate_at_point(factor, variable, point))
301                    .collect();
302                Expression::mul(evaluated).simplify()
303            }
304            Expression::Pow(base, exp) => {
305                let eval_base = Self::evaluate_at_point(base, variable, point);
306                let eval_exp = Self::evaluate_at_point(exp, variable, point);
307                Expression::pow(eval_base, eval_exp).simplify()
308            }
309            Expression::Function { name, args } => {
310                let evaluated_args: Vec<Expression> = args
311                    .iter()
312                    .map(|arg| Self::evaluate_at_point(arg, variable, point))
313                    .collect();
314                Expression::function(name.clone(), evaluated_args)
315            }
316            _ => expr.clone(),
317        }
318    }
319}
320
321impl SeriesExpansion for Expression {
322    fn taylor_series(&self, variable: &Symbol, point: &Expression, order: u32) -> Expression {
323        // Try known series first
324        if let Expression::Function { name, args } = self {
325            if args.len() == 1 {
326                if let Expression::Symbol(sym) = &args[0] {
327                    if sym == variable {
328                        if let Some(known) =
329                            SeriesMethods::known_series(name, variable, point, order)
330                        {
331                            return known;
332                        }
333                    }
334                }
335            }
336        }
337
338        // Fall back to general Taylor series computation
339        SeriesMethods::general_taylor_series(self, variable, point, order)
340    }
341
342    fn laurent_series(&self, variable: &Symbol, point: &Expression, order: u32) -> Expression {
343        // Laurent series includes negative powers for functions with poles
344        // For now, delegate to function call
345        Expression::function(
346            "laurent_series",
347            vec![
348                self.clone(),
349                Expression::symbol(variable.clone()),
350                point.clone(),
351                Expression::integer(order as i64),
352            ],
353        )
354    }
355
356    fn maclaurin_series(&self, variable: &Symbol, order: u32) -> Expression {
357        self.taylor_series(variable, &Expression::integer(0), order)
358    }
359
360    fn power_series_coefficients(
361        &self,
362        variable: &Symbol,
363        point: &Expression,
364        order: u32,
365    ) -> Vec<Expression> {
366        let mut coefficients = Vec::new();
367
368        for n in 0..=order {
369            let nth_derivative = self.nth_derivative(variable.clone(), n);
370            let derivative_at_point =
371                SeriesMethods::evaluate_at_point(&nth_derivative, variable, point);
372            let coefficient = Expression::mul(vec![
373                derivative_at_point,
374                Expression::pow(SeriesMethods::factorial(n), Expression::integer(-1)),
375            ])
376            .simplify();
377            coefficients.push(coefficient);
378        }
379
380        coefficients
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387    use crate::symbol;
388
389    #[test]
390    fn test_exponential_maclaurin() {
391        let x = symbol!(x);
392        let expr = Expression::function("exp", vec![Expression::symbol(x.clone())]);
393        let result = expr.maclaurin_series(&x, 3);
394
395        // e^x ≈ 1 + x + x²/2 + x³/6
396        let expected = Expression::add(vec![
397            Expression::integer(1),
398            Expression::symbol(x.clone()),
399            Expression::mul(vec![
400                Expression::rational(1, 2),
401                Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
402            ]),
403            Expression::mul(vec![
404                Expression::rational(1, 6),
405                Expression::pow(Expression::symbol(x.clone()), Expression::integer(3)),
406            ]),
407        ]);
408
409        assert_eq!(result.simplify(), expected.simplify());
410    }
411
412    #[test]
413    fn test_sine_maclaurin() {
414        let x = symbol!(x);
415        let expr = Expression::function("sin", vec![Expression::symbol(x.clone())]);
416        let result = expr.maclaurin_series(&x, 3);
417
418        // sin(x) ≈ x - x³/6
419        let expected = Expression::add(vec![
420            Expression::symbol(x.clone()),
421            Expression::mul(vec![
422                Expression::rational(-1, 6),
423                Expression::pow(Expression::symbol(x.clone()), Expression::integer(3)),
424            ]),
425        ]);
426
427        assert_eq!(result.simplify(), expected.simplify());
428    }
429
430    #[test]
431    fn test_cosine_maclaurin() {
432        let x = symbol!(x);
433        let expr = Expression::function("cos", vec![Expression::symbol(x.clone())]);
434        let result = expr.maclaurin_series(&x, 2);
435
436        // cos(x) ≈ 1 - x²/2
437        let expected = Expression::add(vec![
438            Expression::integer(1),
439            Expression::mul(vec![
440                Expression::rational(-1, 2),
441                Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
442            ]),
443        ]);
444
445        assert_eq!(result.simplify(), expected.simplify());
446    }
447
448    #[test]
449    fn test_factorial() {
450        assert_eq!(SeriesMethods::factorial(0), Expression::integer(1));
451        assert_eq!(SeriesMethods::factorial(1), Expression::integer(1));
452        assert_eq!(SeriesMethods::factorial(4), Expression::integer(24));
453        assert_eq!(SeriesMethods::factorial(5), Expression::integer(120));
454    }
455}