mathhook_core/functions/polynomials/
evaluation.rs1use crate::core::Expression;
7use crate::functions::properties::PolynomialProperties;
8use std::collections::HashMap;
9
10pub fn evaluate_recurrence(properties: &PolynomialProperties, n: usize, x: f64) -> f64 {
48 if n == 0 {
49 return eval_expr_at_x_internal(&properties.recurrence.initial_conditions.0, x);
50 }
51 if n == 1 {
52 return eval_expr_at_x_internal(&properties.recurrence.initial_conditions.1, x);
53 }
54
55 let mut p_prev = eval_expr_at_x_internal(&properties.recurrence.initial_conditions.0, x);
56 let mut p_curr = eval_expr_at_x_internal(&properties.recurrence.initial_conditions.1, x);
57
58 for i in 1..n {
59 let alpha = eval_coeff_at_n_internal(&properties.recurrence.alpha_coeff, i, x);
60 let beta = eval_coeff_at_n_internal(&properties.recurrence.beta_coeff, i, x);
61 let gamma = eval_coeff_at_n_internal(&properties.recurrence.gamma_coeff, i, x);
62
63 let p_next = (alpha * x + beta) * p_curr + gamma * p_prev;
64 p_prev = p_curr;
65 p_curr = p_next;
66 }
67
68 p_curr
69}
70
71fn eval_coeff_at_n_internal(expr: &Expression, n: usize, x: f64) -> f64 {
89 let mut substitutions = HashMap::new();
90 substitutions.insert("n".to_owned(), Expression::integer(n as i64));
91 substitutions.insert("x".to_owned(), Expression::float(x));
92
93 expr.substitute(&substitutions)
94 .evaluate_to_f64()
95 .unwrap_or_else(|_| eval_func_coeff_internal(expr, n))
96}
97
98fn eval_func_coeff_internal(expr: &Expression, n: usize) -> f64 {
103 match expr {
104 Expression::Function { name, .. } => {
105 let n_f64 = n as f64;
106 match name.as_ref() {
107 "legendre_alpha" => (2.0 * n_f64 + 1.0) / (n_f64 + 1.0),
108 "legendre_gamma" => -n_f64 / (n_f64 + 1.0),
109 "laguerre_alpha" => -(1.0 / (n_f64 + 1.0)),
110 "laguerre_beta" => (2.0 * n_f64 + 1.0) / (n_f64 + 1.0),
111 "laguerre_gamma" => -n_f64 / (n_f64 + 1.0),
112 _ => 0.0,
113 }
114 }
115 _ => 0.0,
116 }
117}
118
119fn eval_expr_at_x_internal(expr: &Expression, x: f64) -> f64 {
133 let mut substitutions = HashMap::new();
134 substitutions.insert("x".to_owned(), Expression::float(x));
135
136 expr.substitute(&substitutions)
137 .evaluate_to_f64()
138 .unwrap_or(0.0)
139}
140
141pub fn evaluate_legendre_numerical(args: &[f64]) -> Vec<f64> {
145 if args.len() != 2 {
146 return vec![0.0];
147 }
148 let n = args[0] as usize;
149 let x = args[1];
150
151 vec![evaluate_legendre(n, x)]
152}
153
154fn evaluate_legendre(n: usize, x: f64) -> f64 {
158 if n == 0 {
159 return 1.0;
160 }
161 if n == 1 {
162 return x;
163 }
164
165 let mut p_prev = 1.0;
166 let mut p_curr = x;
167
168 for i in 1..n {
169 let i_f64 = i as f64;
170 let alpha = (2.0 * i_f64 + 1.0) / (i_f64 + 1.0);
171 let gamma = -i_f64 / (i_f64 + 1.0);
172
173 let p_next = alpha * x * p_curr + gamma * p_prev;
174 p_prev = p_curr;
175 p_curr = p_next;
176 }
177
178 p_curr
179}
180
181pub fn evaluate_hermite_numerical(args: &[f64]) -> Vec<f64> {
185 if args.len() != 2 {
186 return vec![0.0];
187 }
188 let n = args[0] as usize;
189 let x = args[1];
190
191 vec![evaluate_hermite(n, x)]
192}
193
194fn evaluate_hermite(n: usize, x: f64) -> f64 {
198 if n == 0 {
199 return 1.0;
200 }
201 if n == 1 {
202 return 2.0 * x;
203 }
204
205 let mut p_prev = 1.0;
206 let mut p_curr = 2.0 * x;
207
208 for i in 1..n {
209 let i_f64 = i as f64;
210 let alpha = 2.0 * x;
211 let gamma = -2.0 * i_f64;
212
213 let p_next = alpha * p_curr + gamma * p_prev;
214 p_prev = p_curr;
215 p_curr = p_next;
216 }
217
218 p_curr
219}
220
221pub fn evaluate_laguerre_numerical(args: &[f64]) -> Vec<f64> {
225 if args.len() != 2 {
226 return vec![0.0];
227 }
228 let n = args[0] as usize;
229 let x = args[1];
230
231 vec![evaluate_laguerre(n, x)]
232}
233
234fn evaluate_laguerre(n: usize, x: f64) -> f64 {
238 if n == 0 {
239 return 1.0;
240 }
241 if n == 1 {
242 return 1.0 - x;
243 }
244
245 let mut p_prev = 1.0;
246 let mut p_curr = 1.0 - x;
247
248 for i in 1..n {
249 let i_f64 = i as f64;
250 let alpha = -(1.0 / (i_f64 + 1.0));
251 let beta = (2.0 * i_f64 + 1.0) / (i_f64 + 1.0);
252 let gamma = -i_f64 / (i_f64 + 1.0);
253
254 let p_next = (alpha * x + beta) * p_curr + gamma * p_prev;
255 p_prev = p_curr;
256 p_curr = p_next;
257 }
258
259 p_curr
260}
261
262pub fn evaluate_chebyshev_first_numerical(args: &[f64]) -> Vec<f64> {
266 if args.len() != 2 {
267 return vec![0.0];
268 }
269 let n = args[0] as usize;
270 let x = args[1];
271
272 vec![evaluate_chebyshev_first(n, x)]
273}
274
275fn evaluate_chebyshev_first(n: usize, x: f64) -> f64 {
279 if n == 0 {
280 return 1.0;
281 }
282 if n == 1 {
283 return x;
284 }
285
286 let mut p_prev = 1.0;
287 let mut p_curr = x;
288
289 for _ in 1..n {
290 let p_next = 2.0 * x * p_curr - p_prev;
291 p_prev = p_curr;
292 p_curr = p_next;
293 }
294
295 p_curr
296}
297
298pub fn evaluate_chebyshev_second_numerical(args: &[f64]) -> Vec<f64> {
302 if args.len() != 2 {
303 return vec![0.0];
304 }
305 let n = args[0] as usize;
306 let x = args[1];
307
308 vec![evaluate_chebyshev_second(n, x)]
309}
310
311fn evaluate_chebyshev_second(n: usize, x: f64) -> f64 {
315 if n == 0 {
316 return 1.0;
317 }
318 if n == 1 {
319 return 2.0 * x;
320 }
321
322 let mut p_prev = 1.0;
323 let mut p_curr = 2.0 * x;
324
325 for _ in 1..n {
326 let p_next = 2.0 * x * p_curr - p_prev;
327 p_prev = p_curr;
328 p_curr = p_next;
329 }
330
331 p_curr
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use crate::functions::polynomials::legendre::LegendreIntelligence;
338 use crate::functions::properties::FunctionProperties;
339 use crate::{expr, symbol};
340
341 #[test]
342 fn test_evaluate_recurrence_legendre_low_order() {
343 let legendre = LegendreIntelligence::new();
344 let props = legendre.get_properties();
345
346 if let Some(FunctionProperties::Polynomial(legendre_props)) = props.get("legendre_p") {
347 assert!((evaluate_recurrence(legendre_props, 0, 0.5) - 1.0).abs() < 1e-10);
348 assert!((evaluate_recurrence(legendre_props, 1, 0.5) - 0.5).abs() < 1e-10);
349 }
350 }
351
352 #[test]
353 fn test_coefficient_evaluation() {
354 let n_sym = symbol!(n);
355 let expr_2n_plus_1 = expr!((2 * n) + 1);
356
357 assert!((eval_coeff_at_n_internal(&Expression::symbol(n_sym), 5, 0.0) - 5.0).abs() < 1e-10);
358 assert!((eval_coeff_at_n_internal(&expr_2n_plus_1, 5, 0.0) - 11.0).abs() < 1e-10);
359 }
360
361 #[test]
362 fn test_expression_evaluation() {
363 let x_sym = symbol!(x);
364 let expr_1 = expr!(1);
365 let expr_x = Expression::symbol(x_sym);
366 let expr_2x = expr!(2 * x);
367
368 assert!((eval_expr_at_x_internal(&expr_1, 0.5) - 1.0).abs() < 1e-10);
369 assert!((eval_expr_at_x_internal(&expr_x, 0.5) - 0.5).abs() < 1e-10);
370 assert!((eval_expr_at_x_internal(&expr_2x, 0.5) - 1.0).abs() < 1e-10);
371 }
372}