interpn 0.8.2

N-dimensional interpolation/extrapolation methods, no-std and no-alloc compatible.
Documentation

InterpN

Repo | Python Docs | Rust Docs

N-dimensional interpolation/extrapolation methods, no-std and no-alloc compatible, prioritizing correctness, performance, and compatiblity with memory-constrained environments.

Available as a rust crate and python library.

These methods perform zero allocation when evaluated (except, optionally, for the output). Because of this, they have minimal per-call overhead, and are particularly effective when examining small numbers of observation points. See the performance page for detailed benchmarks.

Features

Feature →↓ Interpolant Method RegularGrid RectilinearGrid JsonSerialization
Nearest-Neighbor
Linear
Cubic

The methods provided here, while more limited in scope than scipy's,

  • are significantly faster under most conditions
  • use almost no RAM (and perform no heap allocations at all)
  • produce significantly improved floating-point error (by several orders of magnitude)
  • are json-serializable using Pydantic
  • can also be used easily in web and embedded applications via the Rust library
  • are permissively licensed

ND throughput 1000 obs

See here for more info about quality-of-fit, throughput, and memory usage.

Installation

pip install interpn

Profile-Guided Optimization

To build the extension with profile-guided optimization using pre-built profiles, do sh ./scripts/distr_pgo_install.sh. You can also generate your own PGO profiles like sh ./scripts/distr_pgo_profile.sh. after installing this extra compiler dependency:

rustup component add llvm-tools-preview

Rust Examples

Regular Grid

use interpn::{multilinear, multicubic};

// Define a grid
let x = [1.0_f64, 2.0, 3.0, 4.0];
let y = [0.0_f64, 1.0, 2.0, 3.0];

// Grid input for rectilinear method
let grids = &[&x[..], &y[..]];

// Grid input for regular grid method
let dims = [x.len(), y.len()];
let starts = [x[0], y[0]];
let steps = [x[1] - x[0], y[1] - y[0]];

// Values at grid points
let z = [2.0; 16];

// Observation points to interpolate/extrapolate
let xobs = [0.0_f64, 5.0];
let yobs = [-1.0, 3.0];
let obs = [&xobs[..], &yobs[..]];

// Storage for output
let mut out = [0.0; 2];

// Do interpolation
multilinear::regular::interpn(&dims, &starts, &steps, &z, &obs, &mut out);
multicubic::regular::interpn(&dims, &starts, &steps, &z, false, &obs, &mut out);

Rectilinear Grid

use interpn::{multilinear, multicubic};

// Define a grid
let x = [1.0_f64, 2.0, 3.0, 4.0];
let y = [0.0_f64, 1.0, 2.0, 3.0];

// Grid input for rectilinear method
let grids = &[&x[..], &y[..]];

// Values at grid points
let z = [2.0; 16];

// Points to interpolate/extrapolate
let xobs = [0.0_f64, 5.0];
let yobs = [-1.0, 3.0];
let obs = [&xobs[..], &yobs[..]];

// Storage for output
let mut out = [0.0; 2];

// Do interpolation
multilinear::rectilinear::interpn(grids, &z, &obs, &mut out).unwrap();
multicubic::rectilinear::interpn(grids, &z, false, &obs, &mut out).unwrap();

Python Examples

Available Methods

import interpn
import numpy as np

# Build grid
x = np.linspace(0.0, 10.0, 5)
y = np.linspace(20.0, 30.0, 4)
grids = [x, y]

xgrid, ygrid = np.meshgrid(x, y, indexing="ij")
zgrid = (xgrid + 2.0 * ygrid)  # Values at grid points

# Grid inputs for true regular grid
dims = [x.size, y.size]
starts = np.array([x[0], y[0]])
steps = np.array([x[1] - x[0], y[1] - y[0]])

# Initialize different interpolators
# Call like `linear_regular.eval([xs, ys])`
linear_regular = interpn.MultilinearRegular.new(dims, starts, steps, zgrid)
cubic_regular = interpn.MulticubicRegular.new(dims, starts, steps, zgrid)
linear_rectilinear = interpn.MultilinearRectilinear.new(grids, zgrid)
cubic_rectilinear = interpn.MulticubicRectilinear.new(grids, zgrid)

Multilinear Interpolation

import interpn
import numpy as np

# Build grid
x = np.linspace(0.0, 10.0, 5)
y = np.linspace(20.0, 30.0, 4)

xgrid, ygrid = np.meshgrid(x, y, indexing="ij")
zgrid = (xgrid + 2.0 * ygrid)  # Values at grid points

# Grid inputs for true regular grid
dims = [x.size, y.size]
starts = np.array([x[0], y[0]])
steps = np.array([x[1] - x[0], y[1] - y[0]])

# Observation points pointed back at the grid
obs = [xgrid.flatten(), ygrid.flatten()]

# Initialize
interpolator = interpn.MultilinearRegular.new(dims, starts, steps, zgrid.flatten())

# Interpolate
out = interpolator.eval(obs)

# Check result
assert np.allclose(out, zgrid.flatten(), rtol=1e-13)

# Serialize and deserialize
roundtrip_interpolator = interpn.MultilinearRegular.model_validate_json(
    interpolator.model_dump_json()
)
out2 = roundtrip_interpolator.eval(obs)

# Check result from roundtrip serialized/deserialized interpolator
assert np.all(out == out2)

License

Licensed under either of

at your option.