pub mod discrete;
pub mod exponential_integrals;
pub mod expsinh;
pub mod filon;
pub mod gaussiannoncentralchisquaredpolynomial;
pub mod gaussianorthogonalpolynomial;
pub mod gaussianquadratures;
pub mod gausslaguerrecosinepolynomial;
pub mod kronrod;
pub mod lobatto;
pub mod momentbasedgaussianpolynomial;
pub mod piecewise;
pub mod segment;
pub mod simpson;
pub mod tabulatedgausslegendre;
pub mod tanhsinh;
pub mod trapezoid;
pub mod twodimensional;
use crate::errors::QlResult;
use crate::fail;
use crate::types::Real;
pub(crate) fn require_accuracy(accuracy: Real) -> QlResult<()> {
if !accuracy.is_finite() || accuracy <= Real::EPSILON {
fail!("required accuracy ({accuracy}) must be finite and exceed machine epsilon");
}
Ok(())
}
pub(crate) fn de_quadrature<F, N>(
f: &mut F,
node: N,
t_max: Real,
rel_tolerance: Real,
max_refinements: usize,
) -> QlResult<Real>
where
F: FnMut(Real) -> Real,
N: Fn(Real) -> Option<(Real, Real)>,
{
struct Tally {
sum: Real,
abs_sum: Real,
}
fn term<F, N>(f: &mut F, node: &N, t: Real) -> QlResult<Option<(Real, Real)>>
where
F: FnMut(Real) -> Real,
N: Fn(Real) -> Option<(Real, Real)>,
{
let Some((x, w)) = node(t) else {
return Ok(None);
};
let y = f(x);
if !y.is_finite() {
fail!("integrand returned a non-finite value ({y}) at x = {x}");
}
Ok(Some((w * y, (w * y).abs())))
}
fn walk_side<F, N>(
f: &mut F,
node: &N,
start: Real,
step: Real,
cutoff: &mut Real,
tally: &mut Tally,
) -> QlResult<()>
where
F: FnMut(Real) -> Real,
N: Fn(Real) -> Option<(Real, Real)>,
{
let mut t = start;
while t.abs() <= *cutoff {
let Some((v, av)) = term(f, node, t)? else {
break;
};
let before = tally.sum;
tally.sum += v;
tally.abs_sum += av;
if tally.sum == before && tally.sum != 0.0 {
*cutoff = t.abs();
break;
}
t += step;
}
Ok(())
}
let mut tally = Tally {
sum: 0.0,
abs_sum: 0.0,
};
if let Some((v, av)) = term(f, &node, 0.0)? {
tally.sum += v;
tally.abs_sum += av;
}
let mut pos_cutoff = t_max;
let mut neg_cutoff = t_max;
walk_side(f, &node, 1.0, 1.0, &mut pos_cutoff, &mut tally)?;
walk_side(f, &node, -1.0, -1.0, &mut neg_cutoff, &mut tally)?;
let mut h = 1.0;
let mut value = tally.sum;
for refinement in 0..max_refinements {
h *= 0.5;
walk_side(f, &node, h, 2.0 * h, &mut pos_cutoff, &mut tally)?;
walk_side(f, &node, -h, -2.0 * h, &mut neg_cutoff, &mut tally)?;
let refined = h * tally.sum;
if !refined.is_finite() {
fail!(
"double-exponential quadrature overflowed accumulating an integral near the top of the f64 range"
);
}
let error = (refined - value).abs();
value = refined;
if refinement > 0 && error <= rel_tolerance * (h * tally.abs_sum) {
return Ok(value);
}
}
fail!(
"double-exponential quadrature failed to reach relative tolerance {rel_tolerance} within {max_refinements} refinements"
);
}
pub trait Integrator {
fn integrate_impl<F>(&self, f: &mut F, a: Real, b: Real) -> QlResult<Real>
where
F: FnMut(Real) -> Real;
#[allow(clippy::float_cmp)]
fn integrate<F>(&self, mut f: F, a: Real, b: Real) -> QlResult<Real>
where
F: FnMut(Real) -> Real,
{
if !a.is_finite() || !b.is_finite() {
fail!("integration bounds must be finite, got [{a}, {b}]");
}
if a == b {
return Ok(0.0);
}
if b > a {
self.integrate_impl(&mut f, a, b)
} else {
Ok(-self.integrate_impl(&mut f, b, a)?)
}
}
}