Integration
FastInterpolations.jl provides exact analytical integration computed directly from spline coefficients. No numerical quadrature is used.
Basic Usage
The primary function is integrate(itp, a, b), which computes the definite integral $\int_{a}^{b} f(x)\,dx$.
using FastInterpolations
x = range(0.0, 2π, 50)
y = sin.(x)
itp = cubic_interp(x, y)
# Compute ∫₀^π sin(x) dx
integrate(itp, 0.0, π) # ≈ 2.0The bounds $a$ and $b$ can be any real numbers within the domain (or outside, if extrapolation is enabled). They do not need to be grid points.
Swapping bounds behaves consistently with calculus rules: $\int_a^b = -\int_b^a$.
integrate(itp, π, 0.0) # ≈ -2.0Keyword Arguments
integrate(itp, a, b; search=AutoSearch(), hint=nothing)
nothing #hidesearch: Search policy for locating the bounds in the grid. Defaults toAutoSearch(), which adapts automatically. See Search & Hints for details.hint: ARef{Int}to speed up sequential queries by remembering the last search index.
Full-Domain Integration
To integrate over the entire domain $[x_1, x_n]$, simply omit the bounds:
integrate(itp)integrate(itp) is faster than integrate(itp, x[1], x[end]). It uses a specialized summation path that avoids search operations and bound checks entirely.
One-Shot Integration
When you need the full-domain integral once and not the interpolant itself, pass the raw data with a method directly. This mirrors the unified interp(x, y; method=…) API and builds the interpolant internally with no copies and no per-cell axis caches, so the call allocates nothing:
xs = range(0.0, π, 50)
ys = sin.(xs)
integrate(xs, ys; method = LinearInterp()) # trapezoidal (exact for the linear interpolant)
integrate(xs, ys; method = CubicInterp()) # spline quadratureThe method keyword is required; its options (bc, side, tension) are forwarded to the underlying interpolant.
Series Interpolants
For multi-channel data (Series Interpolants), integration works channel-wise.
Scalar Integration
Returns a Vector containing the integral for each series.
x = range(0.0, 1.0, 30)
# Data with 2 channels
Y = hcat(sin.(π .* x), cos.(π .* x))
sitp = cubic_interp(x, Series(Y))
# Returns [∫ sin, ∫ cos]
integrate(sitp, 0.0, 1.0)Cumulative Integration
cumulative_integrate(sitp) returns the prefix sums of integrals at grid points.
- Scalar Interpolant: Returns
Vector{T}(length $N$). - Series Interpolant: Returns
Matrix{T}(size $N \times K$).
# Result is (30 × 2) matrix, matching logic of sitp.y
cum = cumulative_integrate(sitp)For Series, cumulative_integrate returns a Matrix rather than a Vector{Vector}. This keeps the output memory layout consistent with the input data layout.
Extrapolation Behavior
Integration respects the extrap keyword used during interpolant creation.
x = range(0.0, 2π, 50)
y = sin.(x)
# Constant extrapolation means f(x) = f(x₁) for x < x₁
itp_flat = cubic_interp(x, y; extrap=ClampExtrap())
# Integrates the constant value outside the domain
integrate(itp_flat, -1.0, 10.0)| Mode | Effect on Integration |
|---|---|
NoExtrap() | Error if bounds are outside domain. |
ClampExtrap() | Linearly adds area (value × distance). |
ExtendExtrap() | Evaluation continues using the polynomial of the boundary cell. |
WrapExtrap() | Periodic integration. |
API Summary
| Function | Description | Return Type |
|---|---|---|
integrate(itp, a, b) | Definite integral over $[a, b]$ | Scalar (or Vector for Series) |
integrate(itp) | Full-domain integration | Scalar (or Vector for Series) |
integrate(x, y; method) | One-shot full-domain integral from raw data | Scalar |
cumulative_integrate(itp) | Grid-aligned indefinite integrals | Vector (Scalar) / Matrix (Series) |
API Reference
FastInterpolations.integrate — Function
# persistent — integrate an interpolant you already built
integrate(itp) # full-domain
integrate(itp, a, b) # 1-D, over [a, b]
integrate(itp, lo::NTuple, hi::NTuple) # ND, over the box [lo, hi]
# one-shot — build the `method` interpolant from raw data, then integrate
integrate(x, y; method) # 1-D full-domain
integrate(x, y, a, b; method) # 1-D, over [a, b]
integrate(grids, data; method) # ND full-domain
integrate(grids, data, lo, hi; method) # ND, over the box [lo, hi]Definite integral of an interpolant.
The persistent forms integrate an interpolant you built earlier. integrate(itp) covers the whole domain through a specialized search-free summation; the bounded forms integrate over [a, b] (1-D) or the hyper-rectangle [lo, hi] (ND). ND covers every tensor-product interpolant — homogeneous (linear_interp, cubic_interp, …) and heterogeneous mixes (interp(grids, data; method=(CubicInterp(), LinearInterp()))); only the Hermite family (local-slope / user-slope) has no ND integral.
The one-shot forms build the method interpolant from raw data, then integrate in a single call, storing the input by reference where the method allows (copy=false): the trivial families (Linear/Constant) never copy, the 1-D coefficient builds reference the data (Cubic copies only its grid), and the ND PreCompute methods (Cubic/Quadratic) copy grids and data. 1-D integrates every method; ND takes a single tensor-product method (Linear, Cubic, Quadratic, Constant).
integrate(cubic_interp(x, y)) # persistent, full-domain
integrate(x, y; method = CubicInterp()) # one-shot, full-domain
integrate(x, y, 0.2, 1.5; method = LinearInterp()) # one-shot, ∫ from 0.2 to 1.5
integrate((xs, ys), data; method = LinearInterp()) # one-shot, 2-D full-domainFastInterpolations.cumulative_integrate — Function
cumulative_integrate(itp) # persistent — Vector (Matrix for a Series)
cumulative_integrate(x, y; method) # one-shot — build from raw dataRunning integral at every grid node: out[i] is the integral from the first node up to node i, so out[1] == 0 and out[end] == integrate(itp). The one-shot form builds the method interpolant (reference storage) first. 1-D only — ND cumulative integration has no unambiguous definition.
FastInterpolations.cumulative_integrate! — Function
cumulative_integrate!(out, itp) -> outIn-place cumulative_integrate: fills out with the running integral. out must satisfy length(out) == length(grid).