Numerical Integration
Numra provides a complete suite of numerical integration (quadrature) methods, from adaptive general-purpose routines to specialized Gaussian rules for infinite intervals and composite rules for sampled data.
use numra::integrate::{ quad, QuadOptions, QuadResult, // adaptive Gauss-Kronrod trapezoid, simpson, romberg, // composite rules cumulative_trapezoid, // running integral trapezoid_nonuniform, // non-uniform spacing gauss_legendre, // finite intervals gauss_laguerre, // semi-infinite [0, inf) gauss_hermite, // infinite (-inf, inf) dblquad, // double integrals};Adaptive Quadrature — quad
Section titled “Adaptive Quadrature — quad”The quad function is the workhorse integration routine, analogous to SciPy’s
scipy.integrate.quad. It uses a 15-point Kronrod rule with an embedded 7-point
Gauss rule (G7K15) for error estimation, combined with adaptive bisection of
subintervals ordered by error magnitude.
use numra::integrate::{quad, QuadOptions};
// Integrate sin(x) from 0 to pi -- exact answer is 2.0let result = quad( |x: f64| x.sin(), 0.0, std::f64::consts::PI, &QuadOptions::default(),).unwrap();
assert!((result.value - 2.0).abs() < 1e-10);println!("Value: {:.15}", result.value);println!("Error estimate: {:.2e}", result.error_estimate);println!("Function evaluations: {}", result.n_evaluations);QuadOptions
Section titled “QuadOptions”Control the accuracy and behavior of quad via QuadOptions:
let opts = QuadOptions::default() .atol(1e-14) // absolute tolerance .rtol(1e-14) // relative tolerance .max_subdivisions(200) // max adaptive bisections .points(vec![0.5]); // pre-split at known trouble pointsThe convergence criterion is:
where is the current integral estimate.
QuadResult
Section titled “QuadResult”Every call to quad returns a QuadResult containing:
| Field | Type | Description |
|---|---|---|
value | S | Estimated integral value |
error_estimate | S | Estimated absolute error |
n_evaluations | usize | Total function evaluations |
n_subdivisions | usize | Number of bisections performed |
Handling Singularities
Section titled “Handling Singularities”For integrands with known singularities or discontinuities, use the points
option to pre-split the interval. This prevents the adaptive algorithm from
wasting evaluations near the problematic location.
// Integrate 1/sqrt(x) from 0 to 1 -- singular at x = 0, exact answer is 2.0let opts = QuadOptions::default() .max_subdivisions(100) .points(vec![0.0]);
let result = quad( |x: f64| if x.abs() < 1e-300 { 0.0 } else { 1.0 / x.sqrt() }, 0.0, 1.0, &opts,).unwrap();
assert!((result.value - 2.0).abs() < 1e-6);Composite Rules for Sampled Data
Section titled “Composite Rules for Sampled Data”When you have tabulated data (arrays of y-values at uniform spacing), use the composite rules directly.
Trapezoid Rule
Section titled “Trapezoid Rule”The composite trapezoidal rule approximates the integral using linear interpolation between adjacent points:
use numra::integrate::trapezoid;
// Uniformly-spaced data: y = [0, 1, 4, 9] at x = 0, 1, 2, 3let y = vec![0.0, 1.0, 4.0, 9.0];let integral = trapezoid(&y, 1.0); // dx = 1.0println!("Trapezoid result: {}", integral);For non-uniformly-spaced data, use trapezoid_nonuniform:
use numra::integrate::trapezoid_nonuniform;
let x = vec![0.0, 0.5, 1.5, 3.0];let y = vec![0.0, 0.25, 2.25, 9.0];let integral = trapezoid_nonuniform(&x, &y);Simpson’s Rule
Section titled “Simpson’s Rule”Simpson’s 1/3 rule uses quadratic interpolation through groups of three points. It is exact for polynomials up to degree 3:
use numra::integrate::simpson;
// Simpson's rule is exact for x^2 on uniform gridslet y: Vec<f64> = (0..5).map(|i| (i as f64).powi(2)).collect();let integral = simpson(&y, 1.0); // dx = 1.0// Exact: integral of x^2 from 0 to 4 = 64/3assert!((integral - 64.0 / 3.0).abs() < 1e-12);If the number of points is even (odd number of panels), Simpson’s rule is applied to the first n-1 points, with a trapezoid correction for the last panel.
Cumulative Trapezoid
Section titled “Cumulative Trapezoid”cumulative_trapezoid returns the running integral at each point, useful
for computing antiderivatives from sampled data:
use numra::integrate::cumulative_trapezoid;
let y = vec![0.0, 1.0, 2.0, 3.0, 4.0];let running = cumulative_trapezoid(&y, 1.0);// running[i] = integral from y[0] to y[i+1]// [0.5, 2.0, 4.5, 8.0]Romberg Integration
Section titled “Romberg Integration”Romberg integration applies Richardson extrapolation to successive refinements of the trapezoidal rule, achieving high accuracy with smooth integrands:
use numra::integrate::romberg;
// Integrate e^x from 0 to 1 (exact: e - 1)let result = romberg(|x: f64| x.exp(), 0.0, 1.0, 1e-12).unwrap();assert!((result.value - (std::f64::consts::E - 1.0)).abs() < 1e-12);Romberg integration is particularly effective for smooth (infinitely differentiable) integrands where the trapezoidal rule’s error expansion has only even powers of .
Gaussian Quadrature
Section titled “Gaussian Quadrature”Gaussian quadrature rules use optimally-chosen nodes and weights to achieve exact integration of polynomials up to degree with points.
Gauss-Legendre — Finite Intervals
Section titled “Gauss-Legendre — Finite Intervals”Integrates over a finite interval . Numra provides precomputed tables for to :
use numra::integrate::gauss_legendre;
// 5-point rule is exact for polynomials up to degree 9let result: f64 = gauss_legendre(|x: f64| x * x, 0.0, 1.0, 5).unwrap();assert!((result - 1.0 / 3.0).abs() < 1e-14);
// High-order rule for transcendental functionslet result: f64 = gauss_legendre(|x: f64| x.exp(), -1.0, 1.0, 20).unwrap();let exact = std::f64::consts::E - 1.0 / std::f64::consts::E;assert!((result - exact).abs() < 1e-14);Gauss-Laguerre — Semi-Infinite Intervals
Section titled “Gauss-Laguerre — Semi-Infinite Intervals”Approximates integrals of the form . The weight function is built in; you provide only . Supports to :
use numra::integrate::gauss_laguerre;
// integral_0^inf x^2 * e^(-x) dx = Gamma(3) = 2! = 2let result: f64 = gauss_laguerre(|x: f64| x * x, 5).unwrap();assert!((result - 2.0).abs() < 1e-10);Gauss-Hermite — Infinite Intervals
Section titled “Gauss-Hermite — Infinite Intervals”Approximates integrals of the form . The weight function is built in. Supports to :
use numra::integrate::gauss_hermite;
// integral_{-inf}^{inf} 1 * e^(-x^2) dx = sqrt(pi)let result: f64 = gauss_hermite(|_x: f64| 1.0, 5).unwrap();assert!((result - std::f64::consts::PI.sqrt()).abs() < 1e-12);
// integral_{-inf}^{inf} x^2 * e^(-x^2) dx = sqrt(pi)/2let result: f64 = gauss_hermite(|x: f64| x * x, 5).unwrap();assert!((result - std::f64::consts::PI.sqrt() / 2.0).abs() < 1e-12);Multi-Dimensional Integration — dblquad
Section titled “Multi-Dimensional Integration — dblquad”dblquad computes double integrals via iterated 1D adaptive quadrature:
The y-limits can be functions of x, enabling integration over non-rectangular regions.
use numra::integrate::{dblquad, QuadOptions};
// Integral of x*y over the unit square [0,1] x [0,1] = 0.25let result = dblquad( |x: f64, y: f64| x * y, 0.0, 1.0, // x limits |_x| 0.0, |_x| 1.0, // y limits (constant = rectangle) &QuadOptions::default(),).unwrap();assert!((result.value - 0.25).abs() < 1e-10);
// Integral over a triangle: 0 <= x <= 1, 0 <= y <= xlet result = dblquad( |_x: f64, _y: f64| 1.0, 0.0, 1.0, |_x| 0.0, |x| x, // y goes from 0 to x &QuadOptions::default(),).unwrap();assert!((result.value - 0.5).abs() < 1e-10);Method Comparison
Section titled “Method Comparison”| Method | Best For | Accuracy | Notes |
|---|---|---|---|
quad | General-purpose adaptive | Default choice for function-based integration | |
trapezoid | Sampled data (uniform) | Simple, robust, works with tabulated data | |
simpson | Sampled data (uniform) | Exact for cubics; needs 3+ points | |
romberg | Smooth functions | Richardson extrapolation of trapezoid rule | |
gauss_legendre | Smooth functions, finite interval | Exponential in | Best when you know the smoothness |
gauss_laguerre | Exponential in | Weight function built in | |
gauss_hermite | Exponential in | Weight function built in | |
dblquad | 2D integrals | Inherits from quad | Variable y-limits for non-rectangular regions |
When to Use Each
Section titled “When to Use Each”-
“I have a function and want the integral” — Use
quad. It handles singularities, oscillatory integrands, and automatically adapts to the required accuracy. -
“I have tabulated data at uniform spacing” — Use
simpsonfor best accuracy,trapezoidfor simplicity, orcumulative_trapezoidif you need the running integral. -
“I have non-uniformly-spaced data” — Use
trapezoid_nonuniform. -
“My integral is over a semi-infinite or infinite domain” — Use
gauss_laguerreorgauss_hermitewith the appropriate weight function. -
“I need a 2D integral” — Use
dblquadwith constant or variable limits. -
“I need maximum accuracy on a smooth function” — Use
gauss_legendrewith —, orrombergwith a tight tolerance.
Generic Over Scalar Type
Section titled “Generic Over Scalar Type”All integration routines are generic over S: Scalar, working with both
f32 and f64:
// f32 integrationlet opts = QuadOptions::<f32>::default().atol(1e-4).rtol(1e-4);let result = quad(|x: f32| x.sin(), 0.0f32, std::f32::consts::PI, &opts).unwrap();assert!((result.value - 2.0).abs() < 1e-4);