qualia_core_db/specialized_libs/
symbolic_series.rs1use super::symbolic_algebra::{differentiate, simplify, Expr};
5
6pub fn taylor_coefficients(f: &Expr, x: &str, a: f64, order: usize) -> Option<Vec<f64>> {
9 let mut coeffs = Vec::with_capacity(order + 1);
10 let mut deriv = f.clone();
11 let mut factorial = 1.0;
12 for k in 0..=order {
13 if k > 0 {
14 deriv = simplify(&differentiate(&deriv, x));
15 factorial *= k as f64;
16 }
17 let mut env = std::collections::HashMap::new();
18 env.insert(x.to_string(), a);
19 coeffs.push(deriv.eval(&env)? / factorial);
20 }
21 Some(coeffs)
22}
23
24pub fn taylor_eval(coeffs: &[f64], a: f64, x: f64) -> f64 {
26 let d = x - a;
27 coeffs.iter().rev().fold(0.0, |acc, &c| acc * d + c)
28}
29
30#[cfg(test)]
31mod tests {
32 use super::super::symbolic_algebra::{add, c, mul, pow, sqrt, var};
33 use super::*;
34
35 #[test]
36 fn series_of_polynomial_is_exact() {
37 let f = add(
39 super::super::symbolic_algebra::sub(pow(var("x"), 3), mul(c(2.0), var("x"))),
40 c(1.0),
41 );
42 let coeffs = taylor_coefficients(&f, "x", 0.0, 5).unwrap();
43 assert!((coeffs[0] - 1.0).abs() < 1e-9);
44 assert!((coeffs[1] + 2.0).abs() < 1e-9);
45 assert!(coeffs[2].abs() < 1e-9);
46 assert!((coeffs[3] - 1.0).abs() < 1e-9);
47 assert!(coeffs[4].abs() < 1e-9 && coeffs[5].abs() < 1e-9);
48 }
49
50 #[test]
51 fn series_of_sqrt_about_one() {
52 let coeffs = taylor_coefficients(&sqrt(var("x")), "x", 1.0, 3).unwrap();
54 assert!((coeffs[0] - 1.0).abs() < 1e-9);
55 assert!((coeffs[1] - 0.5).abs() < 1e-9);
56 assert!((coeffs[2] + 0.125).abs() < 1e-9);
57 assert!((coeffs[3] - 0.0625).abs() < 1e-9);
58 let approx = taylor_eval(&coeffs, 1.0, 1.1);
60 assert!((approx - 1.1_f64.sqrt()).abs() < 1e-4);
61 }
62
63 #[test]
64 fn singularity_fails_closed() {
65 let f = super::super::symbolic_algebra::div(c(1.0), var("x"));
67 assert!(taylor_coefficients(&f, "x", 0.0, 3).is_none());
68 }
69}