use crate::adaptive::AdaptiveIntegrator;
use crate::error::QuadratureError;
use crate::gauss_kronrod::GKPair;
use crate::result::QuadratureResult;
pub fn integrate_semi_infinite_upper<G>(
f: G,
a: f64,
tol: f64,
) -> Result<QuadratureResult<f64>, QuadratureError>
where
G: Fn(f64) -> f64,
{
if !a.is_finite() {
return Err(QuadratureError::InvalidInput(
"lower bound must be finite for semi-infinite integration",
));
}
let transformed = move |t: f64| {
if t >= 1.0 {
return 0.0;
}
let one_minus_t = 1.0 - t;
let x = a + t / one_minus_t;
let jacobian = 1.0 / (one_minus_t * one_minus_t);
f(x) * jacobian
};
AdaptiveIntegrator::default()
.with_pair(GKPair::G10K21)
.with_abs_tol(tol)
.with_rel_tol(tol)
.with_max_evals(50_000)
.integrate(0.0, 1.0, transformed)
}
pub fn integrate_semi_infinite_lower<G>(
f: G,
b: f64,
tol: f64,
) -> Result<QuadratureResult<f64>, QuadratureError>
where
G: Fn(f64) -> f64,
{
if !b.is_finite() {
return Err(QuadratureError::InvalidInput(
"upper bound must be finite for semi-infinite integration",
));
}
let transformed = move |t: f64| {
if t >= 1.0 {
return 0.0;
}
let one_minus_t = 1.0 - t;
let x = b - t / one_minus_t;
let jacobian = 1.0 / (one_minus_t * one_minus_t);
f(x) * jacobian
};
AdaptiveIntegrator::default()
.with_pair(GKPair::G10K21)
.with_abs_tol(tol)
.with_rel_tol(tol)
.with_max_evals(50_000)
.integrate(0.0, 1.0, transformed)
}
pub fn integrate_infinite<G>(f: G, tol: f64) -> Result<QuadratureResult<f64>, QuadratureError>
where
G: Fn(f64) -> f64,
{
let transformed = move |t: f64| {
let t2 = t * t;
if t2 >= 1.0 {
return 0.0;
}
let one_minus_t2 = 1.0 - t2;
let x = t / one_minus_t2;
let jacobian = (1.0 + t2) / (one_minus_t2 * one_minus_t2);
f(x) * jacobian
};
AdaptiveIntegrator::default()
.with_pair(GKPair::G10K21)
.with_abs_tol(tol)
.with_rel_tol(tol)
.with_max_evals(50_000)
.integrate(-1.0, 1.0, transformed)
}
#[cfg(test)]
mod tests {
use super::*;
use core::f64::consts::PI;
#[test]
fn exp_decay_upper() {
let r = integrate_semi_infinite_upper(|x: f64| (-x).exp(), 0.0, 1e-10).unwrap();
assert!(r.converged, "err={}", r.error_estimate);
assert!((r.value - 1.0).abs() < 1e-8, "value={}", r.value);
}
#[test]
fn gaussian_half_line() {
let expected = PI.sqrt() / 2.0;
let r = integrate_semi_infinite_upper(|x: f64| (-x * x).exp(), 0.0, 1e-10).unwrap();
assert!(r.converged, "err={}", r.error_estimate);
assert!(
(r.value - expected).abs() < 1e-8,
"value={}, expected={}",
r.value,
expected
);
}
#[test]
fn lorentzian_upper() {
let r = integrate_semi_infinite_upper(|x: f64| 1.0 / (1.0 + x * x), 0.0, 1e-10).unwrap();
assert!(r.converged, "err={}", r.error_estimate);
assert!(
(r.value - PI / 2.0).abs() < 1e-8,
"value={}, expected={}",
r.value,
PI / 2.0
);
}
#[test]
fn exp_growth_lower() {
let r = integrate_semi_infinite_lower(f64::exp, 0.0, 1e-10).unwrap();
assert!(r.converged, "err={}", r.error_estimate);
assert!((r.value - 1.0).abs() < 1e-8, "value={}", r.value);
}
#[test]
fn gaussian_full_line() {
let expected = PI.sqrt();
let r = integrate_infinite(|x: f64| (-x * x).exp(), 1e-10).unwrap();
assert!(r.converged, "err={}", r.error_estimate);
assert!(
(r.value - expected).abs() < 1e-8,
"value={}, expected={}",
r.value,
expected
);
}
#[test]
fn lorentzian_full_line() {
let r = integrate_infinite(|x: f64| 1.0 / (1.0 + x * x), 1e-10).unwrap();
assert!(r.converged, "err={}", r.error_estimate);
assert!(
(r.value - PI).abs() < 1e-8,
"value={}, expected={}",
r.value,
PI
);
}
#[test]
fn non_finite_bound() {
let r = integrate_semi_infinite_upper(f64::exp, f64::INFINITY, 1e-10);
assert!(r.is_err());
let r = integrate_semi_infinite_lower(f64::exp, f64::NEG_INFINITY, 1e-10);
assert!(r.is_err());
}
#[test]
fn shifted_semi_infinite() {
let expected = (-2.0_f64).exp();
let r = integrate_semi_infinite_upper(|x: f64| (-x).exp(), 2.0, 1e-10).unwrap();
assert!(r.converged, "err={}", r.error_estimate);
assert!(
(r.value - expected).abs() < 1e-8,
"value={}, expected={}",
r.value,
expected
);
}
}