rgsl/types/interpolation.rs
1//
2// A rust binding for the GSL library by Guillaume Gomez (guillaume1.gomez@gmail.com)
3//
4
5/*!
6# Interpolation
7
8This chapter describes functions for performing interpolation. The library provides a variety of interpolation methods, including Cubic splines
9and Akima splines. The interpolation types are interchangeable, allowing different methods to be used without recompiling. Interpolations can
10be defined for both normal and periodic boundary conditions. Additional functions are available for computing derivatives and integrals of
11interpolating functions.
12
13These interpolation methods produce curves that pass through each datapoint. To interpolate noisy data with a smoothing curve see Basis Splines.
14
15## Introduction
16
17Given a set of data points (x_1, y_1) \dots (x_n, y_n) the routines described in this section compute a continuous interpolating function
18y(x) such that y(x_i) = y_i. The interpolation is piecewise smooth, and its behavior at the end-points is determined by the type of
19interpolation used.
20
21## Index Look-up and Acceleration
22
23The state of searches can be stored in a gsl_interp_accel object, which is a kind of iterator for interpolation lookups. It caches the previous
24value of an index lookup. When the subsequent interpolation point falls in the same interval its index value can be returned immediately.
25
26## Higher-level Interface
27
28The functions described in the previous sections required the user to supply pointers to the x and y arrays on each call. The following
29functions are equivalent to the corresponding gsl_interp functions but maintain a copy of this data in the gsl_spline object. This removes
30the need to pass both xa and ya as arguments on each evaluation.
31
32## References and Further Reading
33
34Descriptions of the interpolation algorithms and further references can be found in the following books:
35
36C.W. Ueberhuber, Numerical Computation (Volume 1), Chapter 9 “Interpolation”, Springer (1997), ISBN 3-540-62058-3.
37D.M. Young, R.T. Gregory A Survey of Numerical Mathematics (Volume 1), Chapter 6.8, Dover (1988), ISBN 0-486-65691-8.
38!*/
39
40use crate::Value;
41use ffi::FFI;
42
43/// Evaluation accelerator.
44#[derive(Clone)]
45pub struct InterpAccel(pub sys::gsl_interp_accel);
46
47impl InterpAccel {
48 /// This function returns a pointer to an accelerator object, which is a kind of iterator for
49 /// interpolation lookups. It tracks the state of lookups, thus allowing for application of
50 /// various acceleration strategies.
51 #[allow(clippy::new_without_default)]
52 pub fn new() -> InterpAccel {
53 InterpAccel(sys::gsl_interp_accel {
54 cache: 0,
55 miss_count: 0,
56 hit_count: 0,
57 })
58 }
59
60 /// This function reinitializes the accelerator object acc. It should be used when the cached
61 /// information is no longer applicable-for example, when switching to a new dataset.
62 pub fn reset(&mut self) {
63 self.0.cache = 0;
64 self.0.miss_count = 0;
65 self.0.hit_count = 0;
66 }
67
68 /// This function performs a lookup action on the data array x_array of size size, using the
69 /// given accelerator a. This is how lookups are performed during evaluation of an
70 /// interpolation. The function returns an index i such that `x_array[i] <= x < x_array[i+1]`.
71 #[doc(alias = "gsl_interp_accel_find")]
72 pub fn find(&mut self, x_array: &[f64], x: f64) -> usize {
73 unsafe { sys::gsl_interp_accel_find(&mut self.0, x_array.as_ptr(), x_array.len() as _, x) }
74 }
75}
76
77ffi_wrapper!(Interp, *mut sys::gsl_interp, gsl_interp_free);
78
79impl Interp {
80 /// This function returns a pointer to a newly allocated interpolation object of type T for
81 /// size data-points.
82 ///
83 /// ```
84 /// use rgsl::{Interp, InterpType};
85 ///
86 /// let interp_type = InterpType::linear();
87 /// let interp = Interp::new(interp_type, 2).expect("Failed to initialize `Interp`...");
88 /// ```
89 #[doc(alias = "gsl_interp_alloc")]
90 pub fn new(t: InterpType, size: usize) -> Option<Interp> {
91 let tmp = unsafe { sys::gsl_interp_alloc(t.unwrap_shared(), size) };
92
93 if tmp.is_null() {
94 None
95 } else {
96 Some(Self::wrap(tmp))
97 }
98 }
99
100 /// This function initializes the interpolation object interp for the data (xa,ya) where xa and
101 /// ya are arrays of size size. The interpolation object (gsl_interp) does not save the data
102 /// arrays xa and ya and only stores the static state computed from the data. The xa data array
103 /// is always assumed to be strictly ordered, with increasing x values; the behavior for other
104 /// arrangements is not defined.
105 ///
106 /// Asserts that `ya.len() >= xa.len()`.
107 #[doc(alias = "gsl_interp_init")]
108 pub fn init(&mut self, xa: &[f64], ya: &[f64]) -> Result<(), Value> {
109 assert!(ya.len() >= xa.len());
110 let ret = unsafe {
111 sys::gsl_interp_init(
112 self.unwrap_unique(),
113 xa.as_ptr(),
114 ya.as_ptr(),
115 xa.len() as _,
116 )
117 };
118 result_handler!(ret, ())
119 }
120
121 /// This function returns the name of the interpolation type used by interp. For example,
122 ///
123 /// ```
124 /// use rgsl::{Interp, InterpType};
125 ///
126 /// let interp_type = InterpType::linear();
127 /// let interp = Interp::new(interp_type, 2).expect("Failed to initialize `Interp`...");
128 /// println!("interp uses '{}' interpolation.", interp.name());
129 /// ```
130 ///
131 /// would print something like :
132 ///
133 /// ```Shell
134 /// interp uses 'cspline' interpolation.
135 /// ```
136 #[doc(alias = "gsl_interp_name")]
137 pub fn name(&self) -> String {
138 let tmp = unsafe { sys::gsl_interp_name(self.unwrap_shared()) };
139
140 if tmp.is_null() {
141 String::new()
142 } else {
143 unsafe {
144 String::from_utf8_lossy(::std::ffi::CStr::from_ptr(tmp).to_bytes()).to_string()
145 }
146 }
147 }
148
149 /// This function returns the minimum number of points required by the interpolation object
150 /// interp or interpolation type T. For example, Akima spline interpolation requires a minimum
151 /// of 5 points.
152 #[doc(alias = "gsl_interp_min_size")]
153 pub fn min_size(&self) -> u32 {
154 unsafe { sys::gsl_interp_min_size(self.unwrap_shared()) }
155 }
156}
157
158ffi_wrapper!(InterpType, *const sys::gsl_interp_type);
159
160impl InterpType {
161 /// This function returns the minimum number of points required by the interpolation object
162 /// interp or interpolation type T. For example, Akima spline interpolation requires a minimum
163 /// of 5 points.
164 #[doc(alias = "gsl_interp_type_min_size")]
165 pub fn min_size(&self) -> u32 {
166 unsafe { sys::gsl_interp_type_min_size(self.unwrap_shared()) }
167 }
168
169 /// Linear interpolation. This interpolation method does not require any additional memory.
170 #[doc(alias = "gsl_interp_linear")]
171 pub fn linear() -> InterpType {
172 ffi_wrap!(gsl_interp_linear)
173 }
174
175 /// Polynomial interpolation. This method should only be used for interpolating small numbers
176 /// of points because polynomial interpolation introduces large oscillations, even for
177 /// well-behaved datasets. The number of terms in the interpolating polynomial is equal to the
178 /// number of points.
179 #[doc(alias = "gsl_interp_polynomial")]
180 pub fn polynomial() -> InterpType {
181 ffi_wrap!(gsl_interp_polynomial)
182 }
183
184 /// Cubic spline with natural boundary conditions. The resulting curve is piecewise cubic on
185 /// each interval, with matching first and second derivatives at the supplied data-points. The
186 /// second derivative is chosen to be zero at the first point and last point.
187 #[doc(alias = "gsl_interp_cspline")]
188 pub fn cspline() -> InterpType {
189 ffi_wrap!(gsl_interp_cspline)
190 }
191
192 /// Cubic spline with periodic boundary conditions. The resulting curve is piecewise cubic on
193 /// each interval, with matching first and second derivatives at the supplied data-points. The
194 /// derivatives at the first and last points are also matched. Note that the last point in the
195 /// data must have the same y-value as the first point, otherwise the resulting periodic
196 /// interpolation will have a discontinuity at the boundary.
197 #[doc(alias = "gsl_interp_cspline_periodic")]
198 pub fn cspline_periodic() -> InterpType {
199 ffi_wrap!(gsl_interp_cspline_periodic)
200 }
201
202 /// Non-rounded Akima spline with natural boundary conditions. This method uses the non-rounded
203 /// corner algorithm of Wodicka.
204 #[doc(alias = "gsl_interp_akima")]
205 pub fn akima() -> InterpType {
206 ffi_wrap!(gsl_interp_akima)
207 }
208
209 /// Non-rounded Akima spline with periodic boundary conditions. This method uses the non-rounded
210 /// corner algorithm of Wodicka.
211 #[doc(alias = "gsl_interp_akima_periodic")]
212 pub fn akima_periodic() -> InterpType {
213 ffi_wrap!(gsl_interp_akima_periodic)
214 }
215}
216
217ffi_wrapper!(
218 Spline,
219 *mut sys::gsl_spline,
220 gsl_spline_free,
221 "General interpolation object."
222);
223
224impl Spline {
225 #[doc(alias = "gsl_spline_alloc")]
226 pub fn new(t: InterpType, size: usize) -> Option<Spline> {
227 let tmp = unsafe { sys::gsl_spline_alloc(t.unwrap_shared(), size) };
228
229 if tmp.is_null() {
230 None
231 } else {
232 Some(Self::wrap(tmp))
233 }
234 }
235
236 #[doc(alias = "gsl_spline_init")]
237 pub fn init(&mut self, xa: &[f64], ya: &[f64]) -> Result<(), Value> {
238 let ret = unsafe {
239 sys::gsl_spline_init(
240 self.unwrap_unique(),
241 xa.as_ptr(),
242 ya.as_ptr(),
243 xa.len() as _,
244 )
245 };
246 result_handler!(ret, ())
247 }
248
249 #[doc(alias = "gsl_spline_name")]
250 pub fn name(&self) -> String {
251 let tmp = unsafe { sys::gsl_spline_name(self.unwrap_shared()) };
252
253 if tmp.is_null() {
254 String::new()
255 } else {
256 unsafe {
257 String::from_utf8_lossy(::std::ffi::CStr::from_ptr(tmp).to_bytes()).to_string()
258 }
259 }
260 }
261
262 #[doc(alias = "gsl_spline_min_size")]
263 pub fn min_size(&self) -> u32 {
264 unsafe { sys::gsl_spline_min_size(self.unwrap_shared()) }
265 }
266
267 #[doc(alias = "gsl_spline_eval")]
268 pub fn eval(&self, x: f64, acc: &mut InterpAccel) -> f64 {
269 unsafe { sys::gsl_spline_eval(self.unwrap_shared(), x, &mut acc.0) }
270 }
271
272 /// Returns `y`.
273 #[doc(alias = "gsl_spline_eval_e")]
274 pub fn eval_e(&self, x: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
275 let mut y = 0.;
276 let ret = unsafe { sys::gsl_spline_eval_e(self.unwrap_shared(), x, &mut acc.0, &mut y) };
277 result_handler!(ret, y)
278 }
279
280 #[doc(alias = "gsl_spline_eval_deriv")]
281 pub fn eval_deriv(&self, x: f64, acc: &mut InterpAccel) -> f64 {
282 unsafe { sys::gsl_spline_eval_deriv(self.unwrap_shared(), x, &mut acc.0) }
283 }
284
285 /// Returns `d`.
286 #[doc(alias = "gsl_spline_eval_deriv_e")]
287 pub fn eval_deriv_e(&self, x: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
288 let mut d = 0.;
289 let ret =
290 unsafe { sys::gsl_spline_eval_deriv_e(self.unwrap_shared(), x, &mut acc.0, &mut d) };
291 result_handler!(ret, d)
292 }
293
294 #[doc(alias = "gsl_spline_eval_deriv2")]
295 pub fn eval_deriv2(&self, x: f64, acc: &mut InterpAccel) -> f64 {
296 unsafe { sys::gsl_spline_eval_deriv2(self.unwrap_shared(), x, &mut acc.0) }
297 }
298
299 /// Returns `d2`.
300 #[doc(alias = "gsl_spline_eval_deriv2_e")]
301 pub fn eval_deriv2_e(&self, x: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
302 let mut d2 = 0.;
303 let ret =
304 unsafe { sys::gsl_spline_eval_deriv2_e(self.unwrap_shared(), x, &mut acc.0, &mut d2) };
305 result_handler!(ret, d2)
306 }
307
308 #[doc(alias = "gsl_spline_eval_integ")]
309 pub fn eval_integ(&self, a: f64, b: f64, acc: &mut InterpAccel) -> f64 {
310 unsafe { sys::gsl_spline_eval_integ(self.unwrap_shared(), a, b, &mut acc.0) }
311 }
312
313 /// Returns `d2`.
314 #[doc(alias = "gsl_spline_eval_integ_e")]
315 pub fn eval_integ_e(&self, a: f64, b: f64, acc: &mut InterpAccel) -> Result<f64, Value> {
316 let mut result = 0.;
317 let ret = unsafe {
318 sys::gsl_spline_eval_integ_e(self.unwrap_shared(), a, b, &mut acc.0, &mut result)
319 };
320 result_handler!(ret, result)
321 }
322}