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.0

The 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.0

Keyword Arguments

integrate(itp, a, b; search=AutoSearch(), hint=nothing)
nothing #hide
  • search: Search policy for locating the bounds in the grid. Defaults to AutoSearch(), which adapts automatically. See Search & Hints for details.
  • hint: A Ref{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)
Performance

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 quadrature

The 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)
Matrix Output

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)
ModeEffect 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

FunctionDescriptionReturn Type
integrate(itp, a, b)Definite integral over $[a, b]$Scalar (or Vector for Series)
integrate(itp)Full-domain integrationScalar (or Vector for Series)
integrate(x, y; method)One-shot full-domain integral from raw dataScalar
cumulative_integrate(itp)Grid-aligned indefinite integralsVector (Scalar) / Matrix (Series)

API Reference

FastInterpolations.integrateFunction
# 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-domain
source
FastInterpolations.cumulative_integrateFunction
cumulative_integrate(itp)          # persistent — Vector (Matrix for a Series)
cumulative_integrate(x, y; method) # one-shot   — build from raw data

Running 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.

source