mathhook_core/functions/polynomials/
symbolic.rs

1//! Symbolic Polynomial Expansion
2//!
3//! Provides symbolic expansion of orthogonal polynomials to explicit Expression forms
4//! using recurrence relations. All implementations are mathematically verified against
5
6use crate::core::Expression;
7use crate::simplify::Simplify;
8
9/// Expand Legendre polynomial P_n(x) to explicit symbolic form
10///
11/// Uses three-term recurrence to build symbolic expression:
12/// - P_0(x) = 1
13/// - P_1(x) = x
14/// - P_{n+1}(x) = [(2n+1)x P_n(x) - n P_{n-1}(x)] / (n+1)
15///
16/// This implementation builds the polynomial iteratively using the Expression system,
17/// applying simplification at each step to maintain manageable expression size.
18///
19/// # Arguments
20///
21/// * `n` - Polynomial degree (non-negative integer)
22///
23/// # Returns
24///
25/// Expression representing the expanded Legendre polynomial P_n(x)
26///
27/// # Mathematical Background
28///
29/// Legendre polynomials are solutions to Legendre's differential equation:
30/// (1-x²)y'' - 2xy' + n(n+1)y = 0
31///
32/// They are orthogonal on [-1, 1] with weight function w(x) = 1.
33///
34/// # Examples
35///
36/// ```rust
37/// use mathhook_core::functions::polynomials::symbolic::expand_legendre_symbolic;
38/// use mathhook_core::core::Expression;
39///
40/// let p0 = expand_legendre_symbolic(0);
41/// let p1 = expand_legendre_symbolic(1);
42/// let p2 = expand_legendre_symbolic(2);
43/// let p3 = expand_legendre_symbolic(3);
44/// ```
45#[inline]
46#[must_use]
47pub fn expand_legendre_symbolic(n: usize) -> Expression {
48    if n == 0 {
49        return Expression::integer(1);
50    }
51    if n == 1 {
52        return Expression::symbol("x");
53    }
54
55    let x = Expression::symbol("x");
56    let mut p_prev = Expression::integer(1);
57    let mut p_curr = x.clone();
58
59    for i in 1..n {
60        let i_i64 = i as i64;
61
62        let alpha_num = 2 * i_i64 + 1;
63        let alpha_den = i_i64 + 1;
64        let gamma_num = -i_i64;
65        let gamma_den = i_i64 + 1;
66
67        let term1 = Expression::mul(vec![
68            Expression::rational(alpha_num, alpha_den),
69            x.clone(),
70            p_curr.clone(),
71        ]);
72
73        let term2 = Expression::mul(vec![
74            Expression::rational(gamma_num, gamma_den),
75            p_prev.clone(),
76        ]);
77
78        let p_next = Expression::add(vec![term1, term2]).simplify();
79
80        p_prev = p_curr;
81        p_curr = p_next;
82    }
83
84    p_curr
85}
86
87/// Expand Hermite polynomial H_n(x) to explicit symbolic form
88///
89/// Uses three-term recurrence to build symbolic expression:
90/// - H_0(x) = 1
91/// - H_1(x) = 2x
92/// - H_{n+1}(x) = 2x H_n(x) - 2n H_{n-1}(x)
93///
94/// These are the physicist's Hermite polynomials used in quantum mechanics
95/// for the harmonic oscillator eigenfunctions.
96///
97/// # Arguments
98///
99/// * `n` - Polynomial degree (non-negative integer)
100///
101/// # Returns
102///
103/// Expression representing the expanded Hermite polynomial H_n(x)
104///
105/// # Mathematical Background
106///
107/// Hermite polynomials are solutions to Hermite's differential equation:
108/// y'' - 2xy' + 2ny = 0
109///
110/// They are orthogonal on (-∞, ∞) with weight function w(x) = e^(-x²).
111///
112/// # Examples
113///
114/// ```rust
115/// use mathhook_core::functions::polynomials::symbolic::expand_hermite_symbolic;
116/// use mathhook_core::core::Expression;
117///
118/// let h0 = expand_hermite_symbolic(0);
119/// let h1 = expand_hermite_symbolic(1);
120/// let h2 = expand_hermite_symbolic(2);
121/// let h3 = expand_hermite_symbolic(3);
122/// ```
123#[inline]
124#[must_use]
125pub fn expand_hermite_symbolic(n: usize) -> Expression {
126    if n == 0 {
127        return Expression::integer(1);
128    }
129    if n == 1 {
130        return Expression::mul(vec![Expression::integer(2), Expression::symbol("x")]);
131    }
132
133    let x = Expression::symbol("x");
134    let mut p_prev = Expression::integer(1);
135    let mut p_curr = Expression::mul(vec![Expression::integer(2), x.clone()]);
136
137    for i in 1..n {
138        let i_i64 = i as i64;
139
140        let term1 = Expression::mul(vec![Expression::integer(2), x.clone(), p_curr.clone()]);
141
142        let term2 = Expression::mul(vec![Expression::integer(-2 * i_i64), p_prev.clone()]);
143
144        let p_next = Expression::add(vec![term1, term2]).simplify();
145
146        p_prev = p_curr;
147        p_curr = p_next;
148    }
149
150    p_curr
151}
152
153/// Expand Laguerre polynomial L_n(x) to explicit symbolic form
154///
155/// Uses three-term recurrence to build symbolic expression:
156/// - L_0(x) = 1
157/// - L_1(x) = 1 - x
158/// - (n+1)L_{n+1}(x) = (2n+1-x)L_n(x) - nL_{n-1}(x)
159///
160/// These are the standard Laguerre polynomials (not generalized).
161///
162/// # Arguments
163///
164/// * `n` - Polynomial degree (non-negative integer)
165///
166/// # Returns
167///
168/// Expression representing the expanded Laguerre polynomial L_n(x)
169///
170/// # Mathematical Background
171///
172/// Laguerre polynomials are solutions to Laguerre's differential equation:
173/// xy'' + (1-x)y' + ny = 0
174///
175/// They are orthogonal on [0, ∞) with weight function w(x) = e^(-x).
176///
177/// # Examples
178///
179/// ```rust
180/// use mathhook_core::functions::polynomials::symbolic::expand_laguerre_symbolic;
181/// use mathhook_core::core::Expression;
182///
183/// let l0 = expand_laguerre_symbolic(0);
184/// let l1 = expand_laguerre_symbolic(1);
185/// let l2 = expand_laguerre_symbolic(2);
186/// let l3 = expand_laguerre_symbolic(3);
187/// ```
188#[inline]
189#[must_use]
190pub fn expand_laguerre_symbolic(n: usize) -> Expression {
191    if n == 0 {
192        return Expression::integer(1);
193    }
194    if n == 1 {
195        return Expression::add(vec![
196            Expression::integer(1),
197            Expression::mul(vec![Expression::integer(-1), Expression::symbol("x")]),
198        ]);
199    }
200
201    let x = Expression::symbol("x");
202    let mut p_prev = Expression::integer(1);
203    let mut p_curr = Expression::add(vec![
204        Expression::integer(1),
205        Expression::mul(vec![Expression::integer(-1), x.clone()]),
206    ]);
207
208    for i in 1..n {
209        let i_i64 = i as i64;
210
211        let alpha_num = -1;
212        let alpha_den = i_i64 + 1;
213        let beta_num = 2 * i_i64 + 1;
214        let beta_den = i_i64 + 1;
215        let gamma_num = -i_i64;
216        let gamma_den = i_i64 + 1;
217
218        let term1 = Expression::mul(vec![
219            Expression::rational(alpha_num, alpha_den),
220            x.clone(),
221            p_curr.clone(),
222        ]);
223
224        let term2 = Expression::mul(vec![
225            Expression::rational(beta_num, beta_den),
226            p_curr.clone(),
227        ]);
228
229        let term3 = Expression::mul(vec![
230            Expression::rational(gamma_num, gamma_den),
231            p_prev.clone(),
232        ]);
233
234        let p_next = Expression::add(vec![term1, term2, term3]).simplify();
235
236        p_prev = p_curr;
237        p_curr = p_next;
238    }
239
240    p_curr
241}
242
243/// Expand Chebyshev polynomial of the first kind T_n(x) to explicit symbolic form
244///
245/// Uses three-term recurrence to build symbolic expression:
246/// - T_0(x) = 1
247/// - T_1(x) = x
248/// - T_{n+1}(x) = 2x T_n(x) - T_{n-1}(x)
249///
250/// Chebyshev polynomials of the first kind are important in approximation theory
251/// and have the explicit form T_n(x) = cos(n arccos(x)) for |x| ≤ 1.
252///
253/// # Arguments
254///
255/// * `n` - Polynomial degree (non-negative integer)
256///
257/// # Returns
258///
259/// Expression representing the expanded Chebyshev polynomial T_n(x)
260///
261/// # Mathematical Background
262///
263/// Chebyshev polynomials of the first kind are solutions to:
264/// (1-x²)y'' - xy' + n²y = 0
265///
266/// They are orthogonal on [-1, 1] with weight function w(x) = 1/√(1-x²).
267///
268/// # Examples
269///
270/// ```rust
271/// use mathhook_core::functions::polynomials::symbolic::expand_chebyshev_first_symbolic;
272/// use mathhook_core::core::Expression;
273///
274/// let t0 = expand_chebyshev_first_symbolic(0);
275/// let t1 = expand_chebyshev_first_symbolic(1);
276/// let t2 = expand_chebyshev_first_symbolic(2);
277/// let t3 = expand_chebyshev_first_symbolic(3);
278/// ```
279#[inline]
280#[must_use]
281pub fn expand_chebyshev_first_symbolic(n: usize) -> Expression {
282    if n == 0 {
283        return Expression::integer(1);
284    }
285    if n == 1 {
286        return Expression::symbol("x");
287    }
288
289    let x = Expression::symbol("x");
290    let mut p_prev = Expression::integer(1);
291    let mut p_curr = x.clone();
292
293    for _ in 1..n {
294        let term1 = Expression::mul(vec![Expression::integer(2), x.clone(), p_curr.clone()]);
295
296        let term2 = Expression::mul(vec![Expression::integer(-1), p_prev.clone()]);
297
298        let p_next = Expression::add(vec![term1, term2]).simplify();
299
300        p_prev = p_curr;
301        p_curr = p_next;
302    }
303
304    p_curr
305}
306
307/// Expand Chebyshev polynomial of the second kind U_n(x) to explicit symbolic form
308///
309/// Uses three-term recurrence to build symbolic expression:
310/// - U_0(x) = 1
311/// - U_1(x) = 2x
312/// - U_{n+1}(x) = 2x U_n(x) - U_{n-1}(x)
313///
314/// Chebyshev polynomials of the second kind have the explicit form
315/// U_n(x) = sin((n+1) arccos(x)) / sin(arccos(x)) for |x| < 1.
316///
317/// # Arguments
318///
319/// * `n` - Polynomial degree (non-negative integer)
320///
321/// # Returns
322///
323/// Expression representing the expanded Chebyshev polynomial U_n(x)
324///
325/// # Mathematical Background
326///
327/// Chebyshev polynomials of the second kind are orthogonal on [-1, 1]
328/// with weight function w(x) = √(1-x²).
329///
330/// # Examples
331///
332/// ```rust
333/// use mathhook_core::functions::polynomials::symbolic::expand_chebyshev_second_symbolic;
334/// use mathhook_core::core::Expression;
335///
336/// let u0 = expand_chebyshev_second_symbolic(0);
337/// let u1 = expand_chebyshev_second_symbolic(1);
338/// let u2 = expand_chebyshev_second_symbolic(2);
339/// let u3 = expand_chebyshev_second_symbolic(3);
340/// ```
341#[inline]
342#[must_use]
343pub fn expand_chebyshev_second_symbolic(n: usize) -> Expression {
344    if n == 0 {
345        return Expression::integer(1);
346    }
347    if n == 1 {
348        return Expression::mul(vec![Expression::integer(2), Expression::symbol("x")]);
349    }
350
351    let x = Expression::symbol("x");
352    let mut p_prev = Expression::integer(1);
353    let mut p_curr = Expression::mul(vec![Expression::integer(2), x.clone()]);
354
355    for _ in 1..n {
356        let term1 = Expression::mul(vec![Expression::integer(2), x.clone(), p_curr.clone()]);
357
358        let term2 = Expression::mul(vec![Expression::integer(-1), p_prev.clone()]);
359
360        let p_next = Expression::add(vec![term1, term2]).simplify();
361
362        p_prev = p_curr;
363        p_curr = p_next;
364    }
365
366    p_curr
367}
368
369#[cfg(test)]
370mod tests {
371    use super::*;
372
373    #[test]
374    fn test_legendre_p0_p1() {
375        let p0 = expand_legendre_symbolic(0);
376        let p1 = expand_legendre_symbolic(1);
377
378        assert_eq!(p0, Expression::integer(1));
379        assert_eq!(p1, Expression::symbol("x"));
380    }
381
382    #[test]
383    fn test_hermite_h0_h1() {
384        let h0 = expand_hermite_symbolic(0);
385        let h1 = expand_hermite_symbolic(1);
386
387        assert_eq!(h0, Expression::integer(1));
388        assert_eq!(
389            h1,
390            Expression::mul(vec![Expression::integer(2), Expression::symbol("x")])
391        );
392    }
393
394    #[test]
395    fn test_laguerre_l0_l1() {
396        let l0 = expand_laguerre_symbolic(0);
397        let _l1 = expand_laguerre_symbolic(1);
398
399        assert_eq!(l0, Expression::integer(1));
400    }
401
402    #[test]
403    fn test_chebyshev_first_t0_t1() {
404        let t0 = expand_chebyshev_first_symbolic(0);
405        let t1 = expand_chebyshev_first_symbolic(1);
406
407        assert_eq!(t0, Expression::integer(1));
408        assert_eq!(t1, Expression::symbol("x"));
409    }
410
411    #[test]
412    fn test_chebyshev_second_u0_u1() {
413        let u0 = expand_chebyshev_second_symbolic(0);
414        let u1 = expand_chebyshev_second_symbolic(1);
415
416        assert_eq!(u0, Expression::integer(1));
417        assert_eq!(
418            u1,
419            Expression::mul(vec![Expression::integer(2), Expression::symbol("x")])
420        );
421    }
422}