#multi-dimensional #interpolation #linear #numerical #multilinear

ninterp

Numerical interpolation for N-dimensional rectilinear grids

19 releases (6 breaking)

new 0.7.0 May 2, 2025
0.6.3 Mar 24, 2025
0.1.0 Nov 27, 2024

#220 in Math

Download history 163/week @ 2025-01-12 428/week @ 2025-01-19 498/week @ 2025-01-26 240/week @ 2025-02-02 219/week @ 2025-02-09 321/week @ 2025-02-16 301/week @ 2025-02-23 514/week @ 2025-03-02 357/week @ 2025-03-09 672/week @ 2025-03-16 270/week @ 2025-03-23 191/week @ 2025-03-30 247/week @ 2025-04-06 215/week @ 2025-04-13 500/week @ 2025-04-20 468/week @ 2025-04-27

1,439 downloads per month
Used in 4 crates (2 directly)

BSD-3-Clause

150KB
3.5K SLoC

ninterp

docs.rs Crates.io Version GitHub

The ninterp crate provides multivariate interpolation over rectilinear grids of any dimensionality.

There are hard-coded interpolators for lower dimensionalities (up to N = 3) for better runtime performance. All interpolators work with both owned and borrowed arrays (array views) of various types.

A variety of interpolation strategies are implemented and exposed in the prelude module. Custom interpolation strategies can be defined in downstream crates.

cargo add ninterp

Cargo Features

  • serde: support for serde 1.x
    cargo add ninterp --features serde
    

Examples

See examples in new method documentation:

Also see the examples directory for advanced examples:

  • Swapping strategies at runtime: dynamic_strategy.rs

    • Using strategy enums (strategy::enums::Strategy1DEnum/etc.)
      • Compatible with serde
      • Incompatible with custom strategies
    • Using Box<dyn Strategy1D>/etc. (dynamic dispatch)
      • Incompatible with serde
      • Compatible with custom strategies
      • Runtime cost
  • Swapping interpolators at runtime: dynamic_interpolator.rs

    • Using InterpolatorEnum
      • Compatible with serde
      • Incompatible with custom strategies
    • Using Box<dyn Interpolator> (dynamic dispatch)
      • Incompatible with serde
      • Compatible with custom strategies
      • Runtime cost
  • Defining custom strategies: custom_strategy.rs

  • Using transmutable (transparent) types, such as uom::si::Quantity: uom.rs

Overview

A prelude module has been defined:

use ninterp::prelude::*;

This exposes all strategies and a variety of interpolators:

There is also a constant-value 'interpolator': Interp0D. This is useful when working with an InterpolatorEnum or Box<dyn Interpolator>

Instantiation is done by calling an interpolator's new method. For dimensionalities N ≥ 1, this executes a validation step, preventing runtime panics. After editing interpolator data, call the InterpData's validate method or Interpolator::validate to rerun these checks.

To change the extrapolation setting, call set_extrapolate.

To change the interpolation strategy, supply a Strategy1DEnum/etc. or Box<dyn Strategy1D>/etc. upon instantiation, and call set_strategy.

Strategies

An interpolation strategy (e.g. Linear, Nearest, LeftNearest, RightNearest) must be specified. Not all interpolation strategies are implemented for every dimensionality. Linear and Nearest are implemented for all dimensionalities.

Custom strategies can be defined. See examples/custom_strategy.rs for an example.

Extrapolation

An Extrapolate setting must be provided in the new method. This controls what happens when a point is beyond the range of supplied coordinates. The following settings are applicable for all interpolators:

  • Extrapolate::Fill(T)
  • Extrapolate::Clamp
  • Extrapolate::Wrap
  • Extrapolate::Error

Extrapolate::Enable is valid for Linear for all dimensionalities.

If you are unsure which variant to choose, Extrapolate::Error is likely what you want.

Interpolation

Interpolation is executed by calling Interpolator::interpolate.

The length of the interpolant point slice must be equal to the interpolator dimensionality. The interpolator dimensionality can be retrieved by calling Interpolator::ndim.

Using Owned and Borrowed (Viewed) Data

All interpolators work with both owned and borrowed data. This is accomplished by the generic D, which has a bound on the ndarray::Data trait.

Type aliases are provided in the prelude for convenience, e.g. for 1-D:

  • Interp1DOwned
    • Data is owned by the interpolator object
    • Useful for struct fields
    use ndarray::prelude::*;
    use ninterp::prelude::*;
    let interp: Interp1DOwned<f64, _> = Interp1D::new(
        array![0.0, 1.0, 2.0, 3.0],
        array![0.0, 1.0, 4.0, 9.0],
        strategy::Linear,
        Extrapolate::Error,
    )
    .unwrap();
    
  • Interp1DViewed
    • Data is borrowed by the interpolator object
    • Use when interpolator data should be owned by another object
    use ndarray::prelude::*;
    use ninterp::prelude::*;
    let x = array![0.0, 1.0, 2.0, 3.0];
    let f_x = array![0.0, 1.0, 4.0, 9.0];
    let interp: Interp1DViewed<&f64, _> = Interp1D::new(
        x.view(),
        f_x.view(),
        strategy::Linear,
        Extrapolate::Error,
    )
    .unwrap();
    

Typically, the compiler can determine concrete types using the arguments provided to new methods. Examples throughout this crate have type annotions for clarity purposes; they are often unnecessary.

Dependencies

~2–2.7MB
~54K SLoC