#![cfg(feature = "taylor")]
use approx::assert_relative_eq;
use echidna::{Scalar, Taylor, TaylorDyn, TaylorDynGuard};
#[test]
fn exp_taylor_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.exp();
assert_relative_eq!(result.coeffs[0], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.5, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 1.0 / 6.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 1.0 / 24.0, epsilon = 1e-12);
}
#[test]
fn sin_taylor_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.sin();
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], -1.0 / 6.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 0.0, epsilon = 1e-12);
}
#[test]
fn cos_taylor_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.cos();
assert_relative_eq!(result.coeffs[0], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], -0.5, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 1.0 / 24.0, epsilon = 1e-12);
}
#[test]
fn ln_1_plus_x_taylor_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let one = Taylor::<f64, 5>::constant(1.0);
let result = (one + x).ln();
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], -0.5, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 1.0 / 3.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], -0.25, epsilon = 1e-12);
}
#[test]
fn geometric_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let one = Taylor::<f64, 5>::constant(1.0);
let result = one / (one - x);
for k in 0..5 {
assert_relative_eq!(result.coeffs[k], 1.0, epsilon = 1e-12);
}
}
#[test]
fn taylor_k2_matches_dual_exp() {
let x0 = 1.5;
let t = Taylor::<f64, 2>::variable(x0);
let result = t.exp();
let expected_val = x0.exp();
let expected_deriv = x0.exp(); assert_relative_eq!(result.coeffs[0], expected_val, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], expected_deriv, epsilon = 1e-12);
}
#[test]
fn taylor_k2_matches_dual_sin() {
let x0 = 0.7;
let t = Taylor::<f64, 2>::variable(x0);
let result = t.sin();
assert_relative_eq!(result.coeffs[0], x0.sin(), epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], x0.cos(), epsilon = 1e-12);
}
#[test]
fn taylor_k2_matches_dual_ln() {
let x0 = 2.0;
let t = Taylor::<f64, 2>::variable(x0);
let result = t.ln();
assert_relative_eq!(result.coeffs[0], x0.ln(), epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0 / x0, epsilon = 1e-12);
}
#[test]
fn taylor_k2_matches_dual_sqrt() {
let x0 = 4.0;
let t = Taylor::<f64, 2>::variable(x0);
let result = t.sqrt();
assert_relative_eq!(result.coeffs[0], x0.sqrt(), epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 0.5 / x0.sqrt(), epsilon = 1e-12);
}
#[test]
fn taylor_k2_matches_dual_atan() {
let x0 = 0.5;
let t = Taylor::<f64, 2>::variable(x0);
let result = t.atan();
assert_relative_eq!(result.coeffs[0], x0.atan(), epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0 / (1.0 + x0 * x0), epsilon = 1e-12);
}
#[test]
fn taylor_k2_matches_dual_tanh() {
let x0 = 0.3;
let t = Taylor::<f64, 2>::variable(x0);
let result = t.tanh();
let c = x0.cosh();
assert_relative_eq!(result.coeffs[0], x0.tanh(), epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0 / (c * c), epsilon = 1e-12);
}
#[test]
fn cauchy_product_known_polynomials() {
let a = Taylor::<f64, 3>::new([1.0, 1.0, 0.0]);
let b = Taylor::<f64, 3>::new([1.0, 1.0, 0.0]);
let c = a * b;
assert_relative_eq!(c.coeffs[0], 1.0, epsilon = 1e-12);
assert_relative_eq!(c.coeffs[1], 2.0, epsilon = 1e-12);
assert_relative_eq!(c.coeffs[2], 1.0, epsilon = 1e-12);
}
#[test]
fn recursive_division() {
let a = Taylor::<f64, 3>::new([1.0, 2.0, 1.0]);
let b = Taylor::<f64, 3>::new([1.0, 1.0, 0.0]);
let c = a / b;
assert_relative_eq!(c.coeffs[0], 1.0, epsilon = 1e-12);
assert_relative_eq!(c.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(c.coeffs[2], 0.0, epsilon = 1e-12);
}
fn ad_generic_rosenbrock<T: Scalar>(x: T, y: T) -> T {
let one = T::one();
let hundred = T::from(100.0).unwrap();
let t1 = one - x;
let t2 = y - x * x;
t1 * t1 + hundred * t2 * t2
}
#[test]
fn taylor_through_scalar_generic() {
let x = Taylor::<f64, 2>::variable(1.0);
let y = Taylor::<f64, 2>::constant(1.0);
let result = ad_generic_rosenbrock(x, y);
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[1], 0.0, epsilon = 1e-10);
}
#[cfg(feature = "bytecode")]
mod bytecode_tests {
use super::*;
fn simple_function<T: echidna::Scalar>(x: &[T]) -> T {
x[0].exp() + x[0].sin()
}
#[test]
fn forward_tangent_with_taylor() {
let (tape, _) = echidna::record(|x| simple_function(x), &[1.0]);
let mut buf = Vec::new();
let x0 = 1.0_f64;
let inputs = [Taylor::<f64, 3>::variable(x0)];
tape.forward_tangent(&inputs, &mut buf);
let output = buf[tape.output_index()];
assert_relative_eq!(output.coeffs[0], x0.exp() + x0.sin(), epsilon = 1e-10);
assert_relative_eq!(output.coeffs[1], x0.exp() + x0.cos(), epsilon = 1e-10);
assert_relative_eq!(
output.coeffs[2],
(x0.exp() - x0.sin()) / 2.0,
epsilon = 1e-10
);
}
#[test]
fn forward_tangent_with_taylor_dyn() {
let (tape, _) = echidna::record(|x| simple_function(x), &[1.0]);
let mut buf = Vec::new();
let x0 = 1.0_f64;
let _guard = TaylorDynGuard::<f64>::new(3);
let inputs = [TaylorDyn::variable(x0)];
tape.forward_tangent(&inputs, &mut buf);
let output = buf[tape.output_index()];
let coeffs = output.coeffs();
assert_relative_eq!(coeffs[0], x0.exp() + x0.sin(), epsilon = 1e-10);
assert_relative_eq!(coeffs[1], x0.exp() + x0.cos(), epsilon = 1e-10);
assert_relative_eq!(coeffs[2], (x0.exp() - x0.sin()) / 2.0, epsilon = 1e-10);
}
fn multivar_function<T: echidna::Scalar>(x: &[T]) -> T {
x[0] * x[1] + x[0].sin()
}
#[test]
fn forward_tangent_multivar_taylor() {
let (tape, _) = echidna::record(|x| multivar_function(x), &[1.0, 2.0]);
let mut buf = Vec::new();
let inputs = [
Taylor::<f64, 3>::variable(1.0),
Taylor::<f64, 3>::constant(2.0),
];
tape.forward_tangent(&inputs, &mut buf);
let output = buf[tape.output_index()];
assert_relative_eq!(output.coeffs[0], 2.0 + 1.0_f64.sin(), epsilon = 1e-10);
assert_relative_eq!(output.coeffs[1], 2.0 + 1.0_f64.cos(), epsilon = 1e-10);
assert_relative_eq!(output.coeffs[2], -1.0_f64.sin() / 2.0, epsilon = 1e-10);
}
fn sum_of_squares<T: echidna::Scalar>(x: &[T]) -> T {
x[0] * x[0] + x[1] * x[1] + x[2] * x[2]
}
fn cubic_mix<T: echidna::Scalar>(x: &[T]) -> T {
x[0] * x[0] * x[1] + x[1] * x[1] * x[0] + x[0] * x[1] * x[2]
}
fn cube_1d<T: echidna::Scalar>(x: &[T]) -> T {
x[0] * x[0] * x[0]
}
fn linear_fn<T: echidna::Scalar>(x: &[T]) -> T {
let three = T::from(3.0).unwrap();
let two = T::from(2.0).unwrap();
three * x[0] + two * x[1]
}
fn exp_plus_sin<T: echidna::Scalar>(x: &[T]) -> T {
x[0].exp() + x[1].sin()
}
#[test]
fn taylor_grad_k2_gradient_matches_hvp_sum_of_squares() {
let x = [1.0, 2.0, 3.0];
let v = [0.5, -1.0, 0.3];
let (tape, _) = echidna::record(|x| sum_of_squares(x), &x);
let (hvp_grad, _) = tape.hvp(&x, &v);
let (_, adj) = tape.taylor_grad::<2>(&x, &v);
for i in 0..3 {
assert_relative_eq!(adj[i].coeff(0), hvp_grad[i], epsilon = 1e-10);
}
}
#[test]
fn taylor_grad_k2_gradient_matches_hvp_cubic_mix() {
let x = [1.5, -0.5, 2.0];
let v = [1.0, 0.0, -1.0];
let (tape, _) = echidna::record(|x| cubic_mix(x), &x);
let (hvp_grad, _) = tape.hvp(&x, &v);
let (_, adj) = tape.taylor_grad::<2>(&x, &v);
for i in 0..3 {
assert_relative_eq!(adj[i].coeff(0), hvp_grad[i], epsilon = 1e-10);
}
}
#[test]
fn taylor_grad_k2_hvp_matches_hvp() {
let x = [1.0, 2.0, 3.0];
let v = [0.5, -1.0, 0.3];
let (tape, _) = echidna::record(|x| sum_of_squares(x), &x);
let (_, hvp_vec) = tape.hvp(&x, &v);
let (_, adj) = tape.taylor_grad::<2>(&x, &v);
for i in 0..3 {
assert_relative_eq!(adj[i].coeff(1), hvp_vec[i], epsilon = 1e-10);
}
}
#[test]
fn taylor_grad_k3_matches_third_order_hvvp_same_direction() {
let x = [1.5, -0.5, 2.0];
let v = [1.0, 0.5, -1.0];
let (tape, _) = echidna::record(|x| cubic_mix(x), &x);
let (grad_ref, hvp_ref, third_ref) = tape.third_order_hvvp(&x, &v, &v);
let (_, adj) = tape.taylor_grad::<3>(&x, &v);
for i in 0..3 {
assert_relative_eq!(adj[i].coeff(0), grad_ref[i], epsilon = 1e-10);
assert_relative_eq!(adj[i].coeff(1), hvp_ref[i], epsilon = 1e-10);
assert_relative_eq!(adj[i].derivative(2), third_ref[i], epsilon = 1e-10);
}
}
#[test]
fn taylor_grad_k3_gradient_matches_standard_gradient() {
let x = [1.5, -0.5, 2.0];
let v = [1.0, 0.0, -1.0];
let (mut tape, _) = echidna::record(|x| cubic_mix(x), &x);
let grad_ref = tape.gradient(&x);
let (_, adj) = tape.taylor_grad::<3>(&x, &v);
for i in 0..3 {
assert_relative_eq!(adj[i].coeff(0), grad_ref[i], epsilon = 1e-10);
}
}
#[test]
fn taylor_grad_k2_full_hessian_via_basis() {
let x = [1.0, 2.0, 3.0];
let (tape, _) = echidna::record(|x| sum_of_squares(x), &x);
let n = 3;
let (_, _, hess_ref) = tape.hessian(&x);
for j in 0..n {
let mut e_j = vec![0.0; n];
e_j[j] = 1.0;
let (_, adj) = tape.taylor_grad::<2>(&x, &e_j);
for i in 0..n {
assert_relative_eq!(adj[i].coeff(1), hess_ref[i][j], epsilon = 1e-10);
}
}
}
#[test]
fn taylor_grad_linear_higher_order_zero() {
let x = [2.0, 3.0];
let v = [1.0, -1.0];
let (tape, _) = echidna::record(|x| linear_fn(x), &x);
let (output, adj) = tape.taylor_grad::<3>(&x, &v);
assert_relative_eq!(adj[0].coeff(0), 3.0, epsilon = 1e-10);
assert_relative_eq!(adj[1].coeff(0), 2.0, epsilon = 1e-10);
for i in 0..2 {
for k in 1..3 {
assert_relative_eq!(adj[i].coeff(k), 0.0, epsilon = 1e-10);
}
}
assert_relative_eq!(output.coeff(0), 12.0, epsilon = 1e-10);
assert_relative_eq!(output.coeff(1), 1.0, epsilon = 1e-10);
assert_relative_eq!(output.coeff(2), 0.0, epsilon = 1e-10);
}
#[test]
fn taylor_grad_cube_1d_k4() {
let x = [2.0];
let v = [1.0];
let (tape, _) = echidna::record(|x| cube_1d(x), &x);
let (output, adj) = tape.taylor_grad::<4>(&x, &v);
assert_relative_eq!(output.coeff(0), 8.0, epsilon = 1e-10);
assert_relative_eq!(output.coeff(1), 12.0, epsilon = 1e-10);
assert_relative_eq!(output.coeff(2), 6.0, epsilon = 1e-10);
assert_relative_eq!(output.coeff(3), 1.0, epsilon = 1e-10);
assert_relative_eq!(adj[0].coeff(0), 12.0, epsilon = 1e-10); assert_relative_eq!(adj[0].coeff(1), 12.0, epsilon = 1e-10); assert_relative_eq!(adj[0].derivative(2), 6.0, epsilon = 1e-10); assert_relative_eq!(adj[0].derivative(3), 0.0, epsilon = 1e-10); }
#[test]
fn taylor_grad_transcendental() {
let x = [1.0, 0.5];
let v = [1.0, 0.0]; let (tape, _) = echidna::record(|x| exp_plus_sin(x), &x);
let (_, adj) = tape.taylor_grad::<2>(&x, &v);
assert_relative_eq!(adj[0].coeff(0), 1.0_f64.exp(), epsilon = 1e-10);
assert_relative_eq!(adj[1].coeff(0), 0.5_f64.cos(), epsilon = 1e-10);
assert_relative_eq!(adj[0].coeff(1), 1.0_f64.exp(), epsilon = 1e-10);
assert_relative_eq!(adj[1].coeff(1), 0.0, epsilon = 1e-10);
let (_, hvp_vec) = tape.hvp(&x, &v);
assert_relative_eq!(adj[0].coeff(1), hvp_vec[0], epsilon = 1e-10);
assert_relative_eq!(adj[1].coeff(1), hvp_vec[1], epsilon = 1e-10);
}
#[test]
fn taylor_grad_with_buf_reuse() {
let x = [1.0, 2.0, 3.0];
let v = [0.5, -1.0, 0.3];
let (tape, _) = echidna::record(|x| sum_of_squares(x), &x);
let mut fwd_buf = Vec::new();
let mut adj_buf = Vec::new();
let (out1, adj1) = tape.taylor_grad_with_buf::<2>(&x, &v, &mut fwd_buf, &mut adj_buf);
let (out2, adj2) = tape.taylor_grad_with_buf::<2>(&x, &v, &mut fwd_buf, &mut adj_buf);
assert_relative_eq!(out1.coeff(0), out2.coeff(0), epsilon = 1e-14);
assert_relative_eq!(out1.coeff(1), out2.coeff(1), epsilon = 1e-14);
for i in 0..3 {
assert_relative_eq!(adj1[i].coeff(0), adj2[i].coeff(0), epsilon = 1e-14);
assert_relative_eq!(adj1[i].coeff(1), adj2[i].coeff(1), epsilon = 1e-14);
}
}
#[test]
fn taylor_grad_zero_direction() {
let x = [1.0, 2.0, 3.0];
let v = [0.0, 0.0, 0.0]; let (mut tape, _) = echidna::record(|x| sum_of_squares(x), &x);
let (_, adj) = tape.taylor_grad::<3>(&x, &v);
let grad_ref = tape.gradient(&x);
for i in 0..3 {
assert_relative_eq!(adj[i].coeff(0), grad_ref[i], epsilon = 1e-10);
}
for i in 0..3 {
for k in 1..3 {
assert_relative_eq!(adj[i].coeff(k), 0.0, epsilon = 1e-10);
}
}
}
#[test]
fn ode_taylor_exp_growth() {
let (tape, _) = echidna::record_multi(|x| vec![x[0]], &[1.0]);
let result = tape.ode_taylor_step::<6>(&[1.0]);
assert_eq!(result.len(), 1);
let y = &result[0];
let mut factorial = 1.0;
for k in 0..6 {
assert_relative_eq!(y.coeff(k), 1.0 / factorial, epsilon = 1e-12);
factorial *= (k + 1) as f64;
}
}
#[test]
fn ode_taylor_exp_decay() {
let (tape, _) = echidna::record_multi(|x| vec![-x[0]], &[1.0]);
let result = tape.ode_taylor_step::<6>(&[1.0]);
assert_eq!(result.len(), 1);
let y = &result[0];
let mut factorial = 1.0;
for k in 0..6 {
let sign = if k % 2 == 0 { 1.0 } else { -1.0 };
assert_relative_eq!(y.coeff(k), sign / factorial, epsilon = 1e-12);
factorial *= (k + 1) as f64;
}
}
#[test]
fn ode_taylor_quadratic_blowup() {
let (tape, _) = echidna::record_multi(|x| vec![x[0] * x[0]], &[1.0]);
let result = tape.ode_taylor_step::<6>(&[1.0]);
assert_eq!(result.len(), 1);
let y = &result[0];
for k in 0..6 {
assert_relative_eq!(y.coeff(k), 1.0, epsilon = 1e-10);
}
}
#[test]
fn ode_taylor_rotation() {
let (tape, _) = echidna::record_multi(|x| vec![-x[1], x[0]], &[1.0, 0.0]);
let result = tape.ode_taylor_step::<6>(&[1.0, 0.0]);
assert_eq!(result.len(), 2);
assert_relative_eq!(result[0].coeff(0), 1.0, epsilon = 1e-12);
assert_relative_eq!(result[0].coeff(1), 0.0, epsilon = 1e-12);
assert_relative_eq!(result[0].coeff(2), -0.5, epsilon = 1e-12);
assert_relative_eq!(result[0].coeff(3), 0.0, epsilon = 1e-12);
assert_relative_eq!(result[0].coeff(4), 1.0 / 24.0, epsilon = 1e-12);
assert_relative_eq!(result[0].coeff(5), 0.0, epsilon = 1e-12);
assert_relative_eq!(result[1].coeff(0), 0.0, epsilon = 1e-12);
assert_relative_eq!(result[1].coeff(1), 1.0, epsilon = 1e-12);
assert_relative_eq!(result[1].coeff(2), 0.0, epsilon = 1e-12);
assert_relative_eq!(result[1].coeff(3), -1.0 / 6.0, epsilon = 1e-12);
assert_relative_eq!(result[1].coeff(4), 0.0, epsilon = 1e-12);
assert_relative_eq!(result[1].coeff(5), 1.0 / 120.0, epsilon = 1e-12);
}
#[test]
fn ode_taylor_step_eval() {
let (tape, _) = echidna::record_multi(|x| vec![-x[0]], &[1.0]);
let result = tape.ode_taylor_step::<8>(&[1.0]);
let y_at_h = result[0].eval_at(0.1);
assert_relative_eq!(y_at_h, (-0.1_f64).exp(), epsilon = 1e-12);
}
#[test]
fn ode_taylor_k2_minimal() {
let (tape, _) = echidna::record_multi(|x| vec![x[0]], &[1.0]);
let result = tape.ode_taylor_step::<2>(&[1.0]);
assert_eq!(result.len(), 1);
assert_relative_eq!(result[0].coeff(0), 1.0, epsilon = 1e-12);
assert_relative_eq!(result[0].coeff(1), 1.0, epsilon = 1e-12);
}
#[test]
fn ode_taylor_buffer_reuse() {
let (tape, _) = echidna::record_multi(|x| vec![x[0] * x[0]], &[1.0]);
let mut buf = Vec::new();
let result1 = tape.ode_taylor_step_with_buf::<6>(&[1.0], &mut buf);
let result2 = tape.ode_taylor_step_with_buf::<6>(&[1.0], &mut buf);
for k in 0..6 {
assert_relative_eq!(result1[0].coeff(k), result2[0].coeff(k), epsilon = 1e-14);
}
}
}
#[test]
fn eval_at_known_polynomial() {
let p = Taylor::<f64, 3>::new([1.0, 2.0, 3.0]);
assert_relative_eq!(p.eval_at(0.0), 1.0, epsilon = 1e-14);
assert_relative_eq!(p.eval_at(0.5), 2.75, epsilon = 1e-14);
assert_relative_eq!(p.eval_at(1.0), 6.0, epsilon = 1e-14);
}
#[test]
fn taylor_dyn_matches_taylor_exp() {
let x0 = 1.5;
let ts = Taylor::<f64, 4>::variable(x0).exp();
let _guard = TaylorDynGuard::<f64>::new(4);
let td = TaylorDyn::<f64>::variable(x0).exp();
let td_coeffs = td.coeffs();
for k in 0..4 {
assert_relative_eq!(ts.coeffs[k], td_coeffs[k], epsilon = 1e-12);
}
}
#[test]
fn taylor_dyn_matches_taylor_sin() {
let x0 = 0.7;
let ts = Taylor::<f64, 4>::variable(x0).sin();
let _guard = TaylorDynGuard::<f64>::new(4);
let td = TaylorDyn::<f64>::variable(x0).sin();
let td_coeffs = td.coeffs();
for k in 0..4 {
assert_relative_eq!(ts.coeffs[k], td_coeffs[k], epsilon = 1e-12);
}
}
#[test]
fn taylor_dyn_matches_taylor_arithmetic() {
let _guard = TaylorDynGuard::<f64>::new(4);
let ta = Taylor::<f64, 4>::variable(2.0);
let tb = Taylor::<f64, 4>::variable(3.0);
let ts_result = (ta * tb + ta.exp()) / tb.ln();
let da = TaylorDyn::<f64>::variable(2.0);
let db = TaylorDyn::<f64>::variable(3.0);
let td_result = (da * db + da.exp()) / db.ln();
let td_coeffs = td_result.coeffs();
for k in 0..4 {
assert_relative_eq!(ts_result.coeffs[k], td_coeffs[k], epsilon = 1e-10);
}
}
#[test]
fn taylor_k1_is_scalar() {
let x = Taylor::<f64, 1>::variable(3.0);
let result = x.exp();
assert_relative_eq!(result.coeffs[0], 3.0_f64.exp(), epsilon = 1e-12);
}
#[test]
fn taylor_constant_propagation() {
let c = Taylor::<f64, 4>::constant(5.0);
let result = c.exp();
assert_relative_eq!(result.coeffs[0], 5.0_f64.exp(), epsilon = 1e-12);
for k in 1..4 {
assert_relative_eq!(result.coeffs[k], 0.0, epsilon = 1e-12);
}
}
#[test]
fn taylor_discontinuous_floor() {
let x = Taylor::<f64, 3>::variable(2.7);
let result = x.floor();
assert_relative_eq!(result.coeffs[0], 2.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_derivative_extraction() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.exp();
assert_relative_eq!(result.derivative(0), 1.0, epsilon = 1e-12); assert_relative_eq!(result.derivative(1), 1.0, epsilon = 1e-12); assert_relative_eq!(result.derivative(2), 1.0, epsilon = 1e-12); assert_relative_eq!(result.derivative(3), 1.0, epsilon = 1e-12); assert_relative_eq!(result.derivative(4), 1.0, epsilon = 1e-12); }
#[test]
fn taylor_dyn_guard_lifecycle() {
{
let _guard = TaylorDynGuard::<f64>::new(3);
let x = TaylorDyn::<f64>::variable(1.0);
let result = x.exp();
assert_relative_eq!(result.value(), 1.0_f64.exp(), epsilon = 1e-12);
}
{
let _guard = TaylorDynGuard::<f64>::new(5);
let x = TaylorDyn::<f64>::variable(2.0);
let result = x.ln();
assert_relative_eq!(result.value(), 2.0_f64.ln(), epsilon = 1e-12);
let coeffs = result.coeffs();
assert_eq!(coeffs.len(), 5);
}
}
#[test]
fn taylor_dyn_constant_no_arena_alloc() {
let _guard = TaylorDynGuard::<f64>::new(3);
let c = TaylorDyn::<f64>::constant(42.0);
assert_eq!(c.index(), echidna::taylor_dyn::CONSTANT);
assert_relative_eq!(c.value(), 42.0, epsilon = 1e-12);
let coeffs = c.coeffs();
assert_relative_eq!(coeffs[0], 42.0, epsilon = 1e-12);
assert_relative_eq!(coeffs[1], 0.0, epsilon = 1e-12);
assert_relative_eq!(coeffs[2], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_tan_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.tan();
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 1.0 / 3.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_sinh_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.sinh();
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 1.0 / 6.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_cosh_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.cosh();
assert_relative_eq!(result.coeffs[0], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.5, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 1.0 / 24.0, epsilon = 1e-12);
}
#[test]
fn taylor_atan_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.atan();
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], -1.0 / 3.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_asin_at_zero() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.asin();
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 1.0 / 6.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_tanh_series() {
let x = Taylor::<f64, 5>::variable(0.0);
let result = x.tanh();
assert_relative_eq!(result.coeffs[0], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 0.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], -1.0 / 3.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[4], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_dyn_through_scalar_generic() {
let _guard = TaylorDynGuard::<f64>::new(3);
let x = TaylorDyn::<f64>::variable(1.0);
let y = TaylorDyn::<f64>::constant(1.0);
let result = ad_generic_rosenbrock(x, y);
assert_relative_eq!(result.value(), 0.0, epsilon = 1e-10);
}
#[test]
fn taylor_powi_negative_base_squared() {
let x = Taylor::<f64, 4>::variable(-2.0);
let result = x.powi(2);
assert_relative_eq!(result.coeffs[0], 4.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], -4.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], 1.0, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], 0.0, epsilon = 1e-12);
}
#[test]
fn taylor_powi_negative_base_cubed() {
let x = Taylor::<f64, 4>::variable(-2.0);
let result = x.powi(3);
assert_relative_eq!(result.coeffs[0], -8.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[1], 12.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[2], -6.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[3], 1.0, epsilon = 1e-10);
}
#[test]
fn taylor_powi_negative_base_fourth_power() {
let x = Taylor::<f64, 5>::variable(-3.0);
let result = x.powi(4);
assert_relative_eq!(result.coeffs[0], 81.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[1], -108.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[2], 54.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[3], -12.0, epsilon = 1e-10);
assert_relative_eq!(result.coeffs[4], 1.0, epsilon = 1e-10);
}
#[test]
fn taylor_powi_negative_base_negative_exponent() {
let x = Taylor::<f64, 4>::variable(-2.0);
let result = x.powi(-1);
assert_relative_eq!(result.coeffs[0], -0.5, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[1], -0.25, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[2], -0.125, epsilon = 1e-12);
assert_relative_eq!(result.coeffs[3], -0.0625, epsilon = 1e-12);
}
#[test]
fn taylor_powi_positive_base_still_works() {
let x = Taylor::<f64, 3>::variable(2.0);
let result = x.powi(10);
assert_relative_eq!(result.coeffs[0], 1024.0, epsilon = 1e-8);
assert_relative_eq!(result.coeffs[1], 5120.0, epsilon = 1e-6);
}