use multicalc::numerical_integration::mode::*;
use multicalc::numerical_integration::gaussian_integration;
use multicalc::numerical_integration::integrator::*;
use multicalc::numerical_integration::iterative_integration;
use multicalc::utils::error_codes::*;
#[test]
fn test_booles_integration_1() {
let func = |args: f64| -> f64 { 2.0 * args };
let integration_limit = [0.0, 2.0];
let integrator =
iterative_integration::IterativeSingle::from_parameters(100, IterativeMethod::Booles);
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 4.0) < 1e-14);
}
#[test]
fn test_booles_integration_2() {
let func = |args: &[f64; 3]| -> f64 { 2.0 * args[0] + args[1] * args[2] };
let integration_limit = [0.0, 1.0];
let point = [1.0, 2.0, 3.0];
let integrator =
iterative_integration::IterativeMulti::from_parameters(100, IterativeMethod::Booles);
let val = integrator
.get_single_partial(&func, 0, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 7.0) < 1e-25);
let integration_limit = [0.0, 2.0];
let val = integrator
.get_single_partial(&func, 1, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 10.0) < 0.00001);
let integration_limit = [0.0, 3.0];
let val = integrator
.get_single_partial(&func, 2, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 15.0) < 0.00001);
}
#[test]
fn test_booles_integration_3() {
let func = |args: f64| -> f64 { 6.0 * args };
let integration_limits = [[0.0, 2.0], [0.0, 2.0]];
let integrator =
iterative_integration::IterativeSingle::from_parameters(20, IterativeMethod::Booles);
let val = integrator.get_double(&func, &integration_limits).unwrap();
assert!(f64::abs(val - 24.0) < 0.00001);
}
#[test]
fn test_gauss_legendre_quadrature_integration_1() {
let func = |args: f64| -> f64 { 4.0 * args * args * args - 3.0 * args * args };
let integration_limit = [0.0, 2.0];
let integrator = gaussian_integration::GaussianSingle::from_parameters(
4,
GaussianQuadratureMethod::GaussLegendre,
);
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 8.0) < 1e-14);
}
#[test]
fn test_gauss_legendre_quadrature_integration_2() {
let func = |args: &[f64; 3]| -> f64 { 2.0 * args[0] + args[1] * args[2] };
let integration_limit = [0.0, 1.0];
let point = [1.0, 2.0, 3.0];
let integrator = gaussian_integration::GaussianMulti::from_parameters(
2,
GaussianQuadratureMethod::GaussLegendre,
);
let val = integrator
.get_single_partial(&func, 0, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 7.0) < 1e-14);
let integration_limit = [0.0, 2.0];
let val = integrator
.get_single_partial(&func, 1, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 10.0) < 1e-14);
let integration_limit = [0.0, 3.0];
let val = integrator
.get_single_partial(&func, 2, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 15.0) < 1e-14);
}
#[test]
fn test_gauss_legendre_quadrature_integration_3() {
let func = |args: f64| -> f64 { 6.0 * args };
let integration_limits = [[0.0, 2.0], [0.0, 2.0]];
let integrator = gaussian_integration::GaussianSingle::from_parameters(
2,
GaussianQuadratureMethod::GaussLegendre,
);
let val = integrator.get_double(&func, &integration_limits).unwrap();
assert!(f64::abs(val - 24.0) < 1e-14);
}
#[test]
fn test_simpsons_integration_1() {
let func = |args: f64| -> f64 { 2.0 * args };
let integration_limit = [0.0, 2.0];
let integrator =
iterative_integration::IterativeSingle::from_parameters(200, IterativeMethod::Simpsons);
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 4.0) < 0.05);
}
#[test]
fn test_simpsons_integration_2() {
let func = |args: &[f64; 3]| -> f64 { 2.0 * args[0] + args[1] * args[2] };
let integration_limit = [0.0, 1.0];
let point = [1.0, 2.0, 3.0];
let integrator =
iterative_integration::IterativeMulti::from_parameters(200, IterativeMethod::Simpsons);
let val = integrator
.get_single_partial(&func, 0, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 7.0) < 0.05);
let integration_limit = [0.0, 2.0];
let val = integrator
.get_single_partial(&func, 1, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 10.0) < 0.05);
let integration_limit = [0.0, 3.0];
let val = integrator
.get_single_partial(&func, 2, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 15.0) < 0.05);
}
#[test]
fn test_simpsons_integration_3() {
let func = |args: f64| -> f64 { 6.0 * args };
let integration_limits = [[0.0, 2.0], [0.0, 2.0]];
let integrator =
iterative_integration::IterativeSingle::from_parameters(200, IterativeMethod::Simpsons);
let val = integrator.get_double(&func, &integration_limits).unwrap();
assert!(f64::abs(val - 24.0) < 0.05);
}
#[test]
fn test_simpsons_integration_4() {
let func = |args: &[f64; 3]| -> f64 { 2.0 * args[0] + args[1] * args[2] };
let integration_limits = [[0.0, 1.0], [0.0, 1.0]];
let point = [1.0, 1.0, 1.0];
let integrator =
iterative_integration::IterativeMulti::from_parameters(200, IterativeMethod::Simpsons);
let val = integrator
.get_double_partial(&func, [0, 1], &integration_limits, &point)
.unwrap();
assert!(f64::abs(val - 1.50) < 0.05);
}
#[test]
fn test_trapezoidal_integration_1() {
let func = |args: f64| -> f64 { 2.0 * args };
let integration_limit = [0.0, 2.0];
let iterator =
iterative_integration::IterativeSingle::from_parameters(100, IterativeMethod::Trapezoidal);
let val = iterator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 4.0) < 0.00001);
}
#[test]
fn test_trapezoidal_integration_2() {
let func = |args: &[f64; 3]| -> f64 { 2.0 * args[0] + args[1] * args[2] };
let integration_limit = [0.0, 1.0];
let point = [1.0, 2.0, 3.0];
let iterator =
iterative_integration::IterativeMulti::from_parameters(100, IterativeMethod::Trapezoidal);
let val = iterator
.get_single_partial(&func, 0, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 7.0) < 0.00001);
let integration_limit = [0.0, 2.0];
let val = iterator
.get_single_partial(&func, 1, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 10.0) < 0.00001);
let integration_limit = [0.0, 3.0];
let val = iterator
.get_single_partial(&func, 2, &integration_limit, &point)
.unwrap();
assert!(f64::abs(val - 15.0) < 0.00001);
}
#[test]
fn test_trapezoidal_integration_3() {
let func = |args: f64| -> f64 { 6.0 * args };
let integration_limits = [[0.0, 2.0], [0.0, 2.0]];
let integrator =
iterative_integration::IterativeSingle::from_parameters(10, IterativeMethod::Trapezoidal);
let val = integrator.get_double(&func, &integration_limits).unwrap();
assert!(f64::abs(val - 24.0) < 0.00001);
}
#[test]
fn test_trapezoidal_integration_4() {
let func = |args: &[f64; 3]| -> f64 { 2.0 * args[0] + args[1] * args[2] };
let integration_limits = [[0.0, 1.0], [0.0, 2.0]];
let point = [1.0, 2.0, 3.0];
let integrator =
iterative_integration::IterativeMulti::from_parameters(10, IterativeMethod::Trapezoidal);
let val = integrator
.get_double_partial(&func, [0, 1], &integration_limits, &point)
.unwrap();
assert!(f64::abs(val - 8.0) < 0.00001);
}
#[test]
fn test_error_checking_1() {
let func = |args: f64| -> f64 { 2.0 * args };
let integration_limit = [10.0, 1.0];
let integrator = iterative_integration::IterativeSingle::default();
let result = integrator.get_single(&func, &integration_limit);
assert!(result.is_err());
assert!(result.unwrap_err() == CalcError::IntegrationLimitsIllDefined);
}
#[test]
fn test_error_checking_2() {
let func = |args: f64| -> f64 { 2.0 * args };
let integration_limit = [0.0, 1.0];
let integrator =
iterative_integration::IterativeSingle::from_parameters(0, IterativeMethod::Booles);
let result = integrator.get_single(&func, &integration_limit);
assert!(result.is_err());
assert!(result.unwrap_err() == CalcError::IterationsZero);
}
#[test]
fn test_error_checking_3() {
let func = |args: f64| -> f64 { 4.0 * args * args * args - 3.0 * args * args };
let integration_limit = [0.0, 2.0];
let integrator = gaussian_integration::GaussianSingle::from_parameters(
0,
GaussianQuadratureMethod::GaussLegendre,
);
let result = integrator.get_single(&func, &integration_limit);
assert!(result.is_err());
assert!(result.unwrap_err() == CalcError::QuadratureOrderOutOfRange);
}
#[test]
fn test_error_checking_4() {
let func = |args: f64| -> f64 { 4.0 * args * args * args - 3.0 * args * args };
let integration_limit = [0.0, 2.0];
let integrator = gaussian_integration::GaussianSingle::from_parameters(
31,
GaussianQuadratureMethod::GaussLegendre,
);
let result = integrator.get_single(&func, &integration_limit);
assert!(result.is_err());
assert!(result.unwrap_err() == CalcError::QuadratureOrderOutOfRange);
}
#[test]
fn test_gauss_hermite_single() {
let func = |x: f64| -> f64 { x * x };
let integrator = gaussian_integration::GaussianSingle::from_parameters(
5,
GaussianQuadratureMethod::GaussHermite,
);
let integration_limit = [f64::NEG_INFINITY, f64::INFINITY];
let val = integrator.get_single(&func, &integration_limit).unwrap();
let expected = core::f64::consts::PI.sqrt() / 2.0;
assert!(f64::abs(val - expected) < 1e-10);
}
#[test]
fn test_gauss_laguerre_single() {
let func = |x: f64| -> f64 { x * x };
let integrator = gaussian_integration::GaussianSingle::from_parameters(
5,
GaussianQuadratureMethod::GaussLaguerre,
);
let integration_limit = [0.0, f64::INFINITY];
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 2.0) < 1e-9);
}
#[test]
fn test_gauss_hermite_multivariable() {
let func = |args: &[f64; 2]| -> f64 { args[0] * args[0] * args[1] * args[1] };
let integrator = gaussian_integration::GaussianMulti::from_parameters(
5,
GaussianQuadratureMethod::GaussHermite,
);
let integration_limits = [
[f64::NEG_INFINITY, f64::INFINITY],
[f64::NEG_INFINITY, f64::INFINITY],
];
let point = [0.0, 0.0];
let val = integrator
.get([0, 1], &func, &integration_limits, &point)
.unwrap();
let sqrt_pi_half = core::f64::consts::PI.sqrt() / 2.0;
assert!(f64::abs(val - sqrt_pi_half * sqrt_pi_half) < 1e-10);
}
#[test]
fn test_gauss_laguerre_multivariable() {
let func = |args: &[f64; 2]| -> f64 { args[0] * args[0] * args[1] * args[1] };
let integrator = gaussian_integration::GaussianMulti::from_parameters(
5,
GaussianQuadratureMethod::GaussLaguerre,
);
let integration_limits = [[0.0, f64::INFINITY], [0.0, f64::INFINITY]];
let point = [0.0, 0.0];
let val = integrator
.get([0, 1], &func, &integration_limits, &point)
.unwrap();
assert!(f64::abs(val - 4.0) < 1e-8);
}
#[test]
fn test_iterative_infinite_gaussian() {
let func = |x: f64| -> f64 { f64::exp(-x * x) };
let integrator = iterative_integration::IterativeSingle::default();
let integration_limit = [f64::NEG_INFINITY, f64::INFINITY];
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - core::f64::consts::PI.sqrt()) < 1e-3);
}
#[test]
fn test_iterative_semi_infinite_exp() {
let func = |x: f64| -> f64 { f64::exp(-x) };
let integrator = iterative_integration::IterativeSingle::default();
let integration_limit = [0.0, f64::INFINITY];
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 1.0) < 1e-3);
}
#[test]
fn test_iterative_semi_infinite_inverse_square() {
let func = |x: f64| -> f64 { 1.0 / (x * x) };
let integrator = iterative_integration::IterativeSingle::default();
let integration_limit = [1.0, f64::INFINITY];
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 1.0) < 1e-3);
}
#[test]
fn test_iterative_negative_limits() {
let func = |x: f64| -> f64 { 2.0 * x };
let integrator = iterative_integration::IterativeSingle::default();
let integration_limit = [-2.0, 1.0];
let val = integrator.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - (-3.0)) < 1e-9);
}
#[test]
fn test_composite_rule_degree_3_polynomial() {
let func = |x: f64| -> f64 { x * x * x };
let integration_limit = [0.0, 2.0];
let simpson =
iterative_integration::IterativeSingle::from_parameters(120, IterativeMethod::Simpsons);
let val = simpson.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 4.0) < 1e-9);
let boole =
iterative_integration::IterativeSingle::from_parameters(120, IterativeMethod::Booles);
let val = boole.get_single(&func, &integration_limit).unwrap();
assert!(f64::abs(val - 4.0) < 1e-9);
}
fn naive_trapezoidal<F: Fn(f64) -> f64>(iterations: u64, lo: f64, hi: f64, f: F) -> f64 {
let delta = (hi - lo) / iterations as f64;
let mut point = lo;
let mut ans = f(point);
for _ in 0..iterations - 1 {
point += delta;
ans += 2.0 * f(point);
}
ans += f(hi);
0.5 * delta * ans
}
#[test]
fn pairwise_integration_is_accurate_on_long_sum() {
let func = |x: f64| -> f64 { 1.0 / (1.0 + x * x) };
let exact = core::f64::consts::PI / 4.0;
let iterations: u64 = 1 << 23;
let integrator = iterative_integration::IterativeSingle::from_parameters(
iterations,
IterativeMethod::Trapezoidal,
);
let pairwise = integrator.get_single(&func, &[0.0, 1.0]).unwrap();
let naive = naive_trapezoidal(iterations, 0.0, 1.0, func);
let pairwise_err = f64::abs(pairwise - exact);
let naive_err = f64::abs(naive - exact);
assert!(
pairwise_err < 1e-12,
"pairwise error {pairwise_err:e} too large"
);
assert!(
pairwise_err < naive_err,
"pairwise ({pairwise_err:e}) should be closer than naive ({naive_err:e})"
);
}
#[test]
fn test_booles_integration_f32() {
let func = |x: f32| -> f32 { 2.0 * x };
let integrator = iterative_integration::IterativeSingle::<f32>::from_parameters(
100,
IterativeMethod::Booles,
);
let val = integrator.get_single(&func, &[0.0, 2.0]).unwrap();
assert!(f32::abs(val - 4.0) < 1e-3, "got {val}");
}
#[test]
fn test_gauss_legendre_integration_f32() {
let func = |x: f32| -> f32 { 4.0 * x * x * x - 3.0 * x * x };
let integrator = gaussian_integration::GaussianSingle::<f32>::from_parameters(
4,
GaussianQuadratureMethod::GaussLegendre,
);
let val = integrator.get_single(&func, &[0.0, 2.0]).unwrap();
assert!(f32::abs(val - 8.0) < 1e-2, "got {val}");
}