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}