ndarray_interp/lib.rs
1// Copyright (c) 2023 Jonas Bosse
2//
3// Licensed under the MIT license
4
5//! The ndarray-interp crate provides interpolation algorithms
6//! for interpolating _n_-dimesional data.
7//!
8//! # 1D Interpolation
9//! The [interp1d] module provides the [`Interp1D`](interp1d::Interp1D) interpolator
10//! and different interpolation strategies
11//!
12//! **1D Strategies**
13//! - [`interp1d::Linear`] - Linear interpolation and extrapolation
14//! - [`interp1d::cubic_spline`] - Cubic Spline interpolation with different boundary conditions.
15//!
16//! # 2D Interpolation
17//! The [interp2d] module provides the [`Interp2D`](interp2d::Interp2D) interpolator
18//! and different interpolation strategies
19//!
20//! **2D Strategies**
21//! - [`interp2d::Bilinear`] - Bilinear interpolation and extrapolation
22//!
23//! # Custom interpolation strategy
24//! This crate defines traits to allow implementation of user
25//! defined interpolation algorithms.
26//! A 1D interpolation strategy can be created by implementing the
27//! [`Interp1DStrategy`](interp1d::Interp1DStrategy) and
28//! [`Interp1DStrategyBuilder`](interp1d::Interp1DStrategyBuilder) traits.
29//! A 2D interpolation strategy can be created by implementing the
30//! [`Interp2DStrategy`](interp2d::Interp2DStrategy) and
31//! [`Interp2DStrategyBuilder`](interp2d::Interp2DStrategyBuilder) traits.
32//!
33//! See also the `custom_strategy.rs` example.
34//!
35//! # Examples
36//! **1D Example**
37//! ``` rust
38//! use ndarray_interp::interp1d::*;
39//! use ndarray::*;
40//!
41//! let data = array![0.0, 1.0, 1.5, 1.0, 0.0 ];
42//! let interp = Interp1DBuilder::new(data).build().unwrap();
43//!
44//! let result = interp.interp_scalar(3.5).unwrap();
45//! assert!(result == 0.5);
46//! let result = interp.interp_array(&array![0.0, 0.5, 1.5]).unwrap();
47//! assert!(result == array![0.0, 0.5, 1.25])
48//! ```
49//!
50//! **1D Example with multidimensional data**
51//! ```rust
52//! use ndarray_interp::interp1d::*;
53//! use ndarray::*;
54//!
55//! let data = array![
56//! [0.0, 1.0],
57//! [1.0, 2.0],
58//! [1.5, 2.5],
59//! [1.0, 2.0],
60//! ];
61//! let x = array![1.0, 2.0, 3.0, 4.0];
62//!
63//! let interp = Interp1D::builder(data)
64//! .strategy(Linear::new().extrapolate(true))
65//! .x(x)
66//! .build().unwrap();
67//!
68//! let result = interp.interp(0.5).unwrap();
69//! assert!(result == array![-0.5, 0.5]);
70//! let result = interp.interp_array(&array![0.5, 4.0]).unwrap();
71//! assert!(result == array![[-0.5, 0.5], [1.0, 2.0]]);
72//! ```
73//!
74//! **2D Example**
75//! ```rust
76//! use ndarray_interp::interp2d::*;
77//! use ndarray::*;
78//!
79//! let data = array![
80//! [1.0, 2.0, 2.5],
81//! [3.0, 4.0, 3.5],
82//! ];
83//! let interp = Interp2D::builder(data).build().unwrap();
84//!
85//! let result = interp.interp_scalar(0.0, 0.5).unwrap();
86//! assert!(result == 1.5);
87//! let result = interp.interp_array(&array![0.0, 1.0], &array![0.5, 2.0]).unwrap();
88//! assert!(result == array![1.5, 3.5]);
89//! ```
90//!
91//! **1D Example with multidimensional data**
92//! ``` rust
93//! use ndarray_interp::interp2d::*;
94//! use ndarray::*;
95//!
96//! let data = array![
97//! // ---------------------------------> y
98//! [[1.0, -1.0], [2.0, -2.0], [3.0, -3.0]], // |
99//! [[4.0, -4.0], [5.0, -5.0], [6.0, -6.0]], // |
100//! [[7.0, -7.0], [8.0, -8.0], [9.0, -9.0]], // V
101//! [[7.5, -7.5], [8.5, -8.5], [9.5, -9.5]], // x
102//! ];
103//! let x = array![1.0, 2.0, 3.0, 4.0];
104//! let y = array![1.0, 2.0, 3.0];
105//!
106//! let interp = Interp2D::builder(data)
107//! .x(x)
108//! .y(y)
109//! .build().unwrap();
110//!
111//! let result = interp.interp(1.5, 2.0).unwrap();
112//! assert!(result == array![3.5, -3.5]);
113//! let result = interp.interp_array(&array![1.5, 1.5], &array![2.0, 2.5]).unwrap();
114//! assert!(result == array![[3.5, -3.5],[4.0, -4.0]]);
115//! ```
116
117use std::mem::ManuallyDrop;
118
119use thiserror::Error;
120
121mod dim_extensions;
122pub mod interp1d;
123pub mod interp2d;
124pub mod vector_extensions;
125
126/// Errors during Interpolator creation
127#[derive(Debug, Error)]
128pub enum BuilderError {
129 /// Insufficient data for the chosen interpolation strategy
130 #[error("{0}")]
131 NotEnoughData(String),
132 /// A interpolation axis is not strict monotonic rising
133 #[error("{0}")]
134 Monotonic(String),
135 #[error("{0}")]
136 ShapeError(String),
137 #[error("{0}")]
138 ValueError(String),
139}
140
141/// Errors during Interpolation
142#[derive(Debug, Error)]
143pub enum InterpolateError {
144 #[error("{0}")]
145 OutOfBounds(String),
146}
147
148/// cast `a` from type `A` to type `B` without any safety checks
149///
150/// ## Safety
151/// - The caller must guarantee that `A` and `B` are the same types
152/// - Types should be annotated to ensure type inference does not break
153/// the contract by accident
154unsafe fn cast_unchecked<A, B>(a: A) -> B {
155 let ptr = &*ManuallyDrop::new(a) as *const A as *const B;
156 unsafe { ptr.read() }
157}