convinterp

High-order convolution interpolation, derivatives and integrals on uniform grids, in any number of dimensions

convinterp interpolates data sampled on uniform grids, in one dimension or many, and differentiates and integrates the interpolant analytically. Its kernels are piecewise polynomials, evaluated in a compiled Rust core; the Python interface works with NumPy arrays.

convinterp is a port of the Julia package ConvolutionInterpolations.jl, by the same author, and is tested against it.

This documentation has four more pages:

NoteAlpha release

Interpolation, derivatives and integrals on uniform grids work in any number of dimensions. Scattered data are coming, and this documentation grows with the package.

Installation

pip install convinterp

Prebuilt wheels are available for Linux, macOS and Windows, on x86_64 and ARM.

A first example

import numpy as np                                     # arrays
from convinterp import convolution_interpolation       # the one function you need

# 1D: data on a uniform grid
x = np.linspace(0.0, 2 * np.pi, 50)                    # 50 uniformly spaced knots
itp = convolution_interpolation(x, np.sin(x))          # the interpolant of sin(x)
print(f"itp(1.0)  = {itp(1.0):.12f}")                  # the interpolated value at x = 1
print(f"sin(1.0)  = {np.sin(1.0):.12f}")               # the exact value, for comparison

# its first derivative, evaluated analytically from the kernel
d_itp = convolution_interpolation(x, np.sin(x), derivative=1)
print(f"d_itp(1.0) = {d_itp(1.0):.12f}")               # the interpolated derivative at x = 1
print(f"cos(1.0)   = {np.cos(1.0):.12f}")              # the exact derivative

# its integral from the first knot x[0] = 0, also evaluated analytically
i_itp = convolution_interpolation(x, np.sin(x), derivative=-1)
print(f"i_itp(1.0)   = {i_itp(1.0):.12f}")             # the interpolated integral from 0 to 1
print(f"1 - cos(1.0) = {1 - np.cos(1.0):.12f}")        # the exact integral
itp(1.0)  = 0.841470984880
sin(1.0)  = 0.841470984808
d_itp(1.0) = 0.540302305515
cos(1.0)   = 0.540302305868
i_itp(1.0)   = 0.459697694148
1 - cos(1.0) = 0.459697694132

In more dimensions, give one knot array per axis. The data array’s axis d runs along knots[d]:

y = np.linspace(0.0, 1.0, 30)                          # knots of the second axis
data = np.sin(x)[:, None] * np.exp(y)[None, :]         # sin(x)·exp(y) on the 50 × 30 grid
itp2 = convolution_interpolation((x, y), data)         # the 2D interpolant

# a mixed derivative ∂²/∂x∂y, with one derivative order per axis
dxy = convolution_interpolation((x, y), data, derivative=(1, 1))
print(f"dxy(1.0, 0.5)        = {dxy(1.0, 0.5):.10f}")  # the interpolated mixed derivative
print(f"cos(1.0)·exp(0.5)    = {np.cos(1.0) * np.exp(0.5):.10f}")  # the exact value
dxy(1.0, 0.5)        = 0.8908079037
cos(1.0)·exp(0.5)    = 0.8908079043

Features

  • High order. The kernels "b7" to "b13" converge at 7th order on smooth data, and "b5" at 6th, with exact polynomial reproduction up to the boundaries.
  • Derivatives. Every derivative order a kernel supports, up to 7 for the widest kernels, is evaluated analytically from the kernel’s polynomial pieces. Orders can differ per axis.
  • Integrals. Integrals up to order 8, anchored at the first knot, are evaluated analytically as well, in constant time per point whatever the grid size. They can be mixed with derivatives, one order per axis.
  • Any number of dimensions. The same function covers 1D curves, 2D images and fields, and higher-dimensional tables.
  • Fast. Locating a point on a uniform grid takes one division, and the kernel weights come from exact polynomials evaluated in compiled, vectorized Rust.
  • Boundary handling. The data are extended beyond each boundary by polynomial extrapolation where the data allow it, chosen automatically per boundary, or set by hand.