Writeup | Repo | Python Docs | Rust Docs
N-dimensional interpolation/extrapolation methods, no-std and no-alloc compatible, prioritizing correctness, performance, and compatibility with memory-constrained environments.
Available as a rust crate and python library.
| Feature → ↓ Interpolant Method |
Regular Grid |
Rectilinear Grid |
Json Serialization |
Thread Parallelism |
|---|---|---|---|---|
| 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
For larger jobs such as image processing, the non-allocating evaluation pattern results in a nearly-linear thread speedup, limited only by thread spawning overhead and memory bandwidth.
See here for more info about quality-of-fit, throughput, and memory usage.
pip install interpnTo build the extension with profile-guided optimization, do sh ./scripts/distr_pgo.sh
after installing this extra compiler dependency:
rustup component add llvm-tools-previewAn LLVM install matching the version used by rustc is also required for doing PGO;
see the ./scripts/distr_pgo.sh or CI workflows for the exact version.
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);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();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)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)Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.