mod common;
use symplex::prelude::*;
fn eval_series_at(series: &Ex, var: &Ex, p: i64, q: i64) -> f64 {
let ctx = series.context();
let pt = ctx.rational(p, q);
series
.subs(var, &pt)
.eval()
.eval_f64()
.expect("series numerical evaluation should succeed")
}
#[test]
fn taylor_exp_at_0() {
let ctx = Context::new();
let x = ctx.symbol("x");
let series = x.exp().maclaurin(&x, 6);
assert!(!series.has_unevaluated(), "exp(x) maclaurin failed");
let expanded = series.expand().eval();
let val = eval_series_at(&expanded, &x, 1, 2);
let exact = 0.5_f64.exp();
assert!(
(val - exact).abs() < 1e-3,
"exp series at x=0.5: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_1 = eval_series_at(&expanded, &x, 1, 1);
let exact_1 = 1.0_f64.exp();
assert!(
(val_1 - exact_1).abs() < 0.01,
"exp series at x=1: got {val_1}, expected {exact_1}, err={}",
(val_1 - exact_1).abs()
);
}
#[test]
fn taylor_sin_at_0() {
let ctx = Context::new();
let x = ctx.symbol("x");
let series = x.sin().maclaurin(&x, 8);
assert!(!series.has_unevaluated(), "sin(x) maclaurin failed");
let expanded = series.expand().eval();
let val = eval_series_at(&expanded, &x, 1, 2);
let exact = 0.5_f64.sin();
assert!(
(val - exact).abs() < 1e-6,
"sin series at x=0.5: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_1 = eval_series_at(&expanded, &x, 1, 1);
let exact_1 = 1.0_f64.sin();
assert!(
(val_1 - exact_1).abs() < 1e-4,
"sin series at x=1: got {val_1}, expected {exact_1}, err={}",
(val_1 - exact_1).abs()
);
}
#[test]
fn taylor_cos_at_0() {
let ctx = Context::new();
let x = ctx.symbol("x");
let series = x.cos().maclaurin(&x, 8);
assert!(!series.has_unevaluated(), "cos(x) maclaurin failed");
let expanded = series.expand().eval();
let val = eval_series_at(&expanded, &x, 1, 2);
let exact = 0.5_f64.cos();
assert!(
(val - exact).abs() < 1e-6,
"cos series at x=0.5: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_1 = eval_series_at(&expanded, &x, 1, 1);
let exact_1 = 1.0_f64.cos();
assert!(
(val_1 - exact_1).abs() < 1e-4,
"cos series at x=1: got {val_1}, expected {exact_1}, err={}",
(val_1 - exact_1).abs()
);
}
#[test]
fn taylor_ln_at_1() {
let ctx = Context::new();
let x = ctx.symbol("x");
let series = x.ln().series(&x, &ctx.int(1), 8);
assert!(!series.has_unevaluated(), "ln(x) series at 1 failed");
let expanded = series.expand().eval();
let val = eval_series_at(&expanded, &x, 11, 10);
let exact = 1.1_f64.ln();
assert!(
(val - exact).abs() < 1e-6,
"ln series at x=1.1: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_center = eval_series_at(&expanded, &x, 1, 1);
assert!(
val_center.abs() < 1e-12,
"ln series at x=1 (expansion point) should be 0: got {val_center}"
);
}
#[test]
fn taylor_exp_at_1() {
let ctx = Context::new();
let x = ctx.symbol("x");
let series = x.exp().series(&x, &ctx.int(1), 6);
assert!(!series.has_unevaluated(), "exp(x) series at 1 failed");
let expanded = series.expand().eval();
let val = eval_series_at(&expanded, &x, 11, 10);
let exact = 1.1_f64.exp();
assert!(
(val - exact).abs() < 1e-4,
"exp series(at 1) at x=1.1: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_center = eval_series_at(&expanded, &x, 1, 1);
let exact_center = 1.0_f64.exp();
assert!(
(val_center - exact_center).abs() < 1e-10,
"exp series at x=1 (expansion point) should be e: got {val_center}, expected {exact_center}"
);
}
#[test]
fn taylor_sinh_at_0() {
let ctx = Context::new();
let x = ctx.symbol("x");
let series = x.sinh().maclaurin(&x, 8);
assert!(!series.has_unevaluated(), "sinh(x) maclaurin failed");
let expanded = series.expand().eval();
let val = eval_series_at(&expanded, &x, 1, 2);
let exact = 0.5_f64.sinh();
assert!(
(val - exact).abs() < 1e-6,
"sinh series at x=0.5: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_1 = eval_series_at(&expanded, &x, 1, 1);
let exact_1 = 1.0_f64.sinh();
assert!(
(val_1 - exact_1).abs() < 1e-4,
"sinh series at x=1: got {val_1}, expected {exact_1}, err={}",
(val_1 - exact_1).abs()
);
}
#[test]
fn taylor_cosh_at_0() {
let ctx = Context::new();
let x = ctx.symbol("x");
let series = x.cosh().maclaurin(&x, 8);
assert!(!series.has_unevaluated(), "cosh(x) maclaurin failed");
let expanded = series.expand().eval();
let val = eval_series_at(&expanded, &x, 1, 2);
let exact = 0.5_f64.cosh();
assert!(
(val - exact).abs() < 1e-6,
"cosh series at x=0.5: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_1 = eval_series_at(&expanded, &x, 1, 1);
let exact_1 = 1.0_f64.cosh();
assert!(
(val_1 - exact_1).abs() < 1e-4,
"cosh series at x=1: got {val_1}, expected {exact_1}, err={}",
(val_1 - exact_1).abs()
);
}
#[test]
fn taylor_composition_exp_sin() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expr = x.sin().exp();
let series = expr.maclaurin(&x, 6);
assert!(!series.has_unevaluated(), "exp(sin(x)) maclaurin failed");
let expanded = series.expand().eval();
let val_0 = eval_series_at(&expanded, &x, 0, 1);
assert!(
(val_0 - 1.0).abs() < 1e-10,
"exp(sin(x)) series at x=0 should be 1: got {val_0}"
);
let val = eval_series_at(&expanded, &x, 1, 2);
let exact = 0.5_f64.sin().exp();
assert!(
(val - exact).abs() < 1e-3,
"exp(sin(x)) series at x=0.5: got {val}, expected {exact}, err={}",
(val - exact).abs()
);
let val_small = eval_series_at(&expanded, &x, 1, 5);
let exact_small = 0.2_f64.sin().exp();
assert!(
(val_small - exact_small).abs() < 1e-5,
"exp(sin(x)) series at x=0.2: got {val_small}, expected {exact_small}, err={}",
(val_small - exact_small).abs()
);
}
#[test]
fn series_numerical_accuracy_sin() {
let ctx = Context::new();
let x = ctx.symbol("x");
let exact = 0.5_f64.sin();
let orders = [3u32, 5, 7, 9, 11];
let mut prev_err = f64::MAX;
for &order in &orders {
let series = x.sin().maclaurin(&x, order);
let expanded = series.expand().eval();
if let Ok(val) = expanded.subs(&x, &ctx.rational(1, 2)).eval().eval_f64() {
let err = (val - exact).abs();
assert!(
err < prev_err,
"sin series error should decrease: order {order} err={err} >= prev_err={prev_err}"
);
prev_err = err;
}
}
assert!(
prev_err < 1e-6,
"sin series at order 11 should be very accurate, err={prev_err}"
);
}
#[test]
fn series_numerical_accuracy_exp() {
let ctx = Context::new();
let x = ctx.symbol("x");
let exact = 0.5_f64.exp();
let orders = [2u32, 4, 6, 8, 10];
let mut prev_err = f64::MAX;
for &order in &orders {
let series = x.exp().maclaurin(&x, order);
let expanded = series.expand().eval();
if let Ok(val) = expanded.subs(&x, &ctx.rational(1, 2)).eval().eval_f64() {
let err = (val - exact).abs();
assert!(
err < prev_err,
"exp series error should decrease: order {order} err={err} >= prev_err={prev_err}"
);
prev_err = err;
}
}
assert!(
prev_err < 1e-8,
"exp series at order 10 should be very accurate, err={prev_err}"
);
}