rsl_interpolation/spline.rs
1use crate::Accelerator;
2use crate::DomainError;
3use crate::DynInterpType;
4use crate::InterpType;
5use crate::Interpolation;
6use crate::InterpolationError;
7
8/// 1D Higher level interface.
9///
10/// A Spline owns the data it is constructed with, and provides the same evaluation methods as the
11/// lower-level Interpolator object, without needing to provide the data arrays in every call.
12///
13/// # Example
14/// ```
15/// # use rsl_interpolation::*;
16/// #
17/// # fn main() -> Result<(), InterpolationError>{
18/// let mut acc = Accelerator::new();
19///
20/// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
21/// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
22///
23/// let interp = Cubic.build(&xa, &ya)?;
24///
25/// let typ = Cubic;
26/// let spline = Spline::new(typ, &xa, &ya)?;
27///
28/// let x = 1.5;
29/// let y_interp = interp.eval(&xa, &ya, x, &mut acc)?;
30/// let y_spline = spline.eval(x, &mut acc)?;
31///
32/// assert_eq!(y_interp, y_spline);
33/// #
34/// # Ok(())
35/// # }
36/// ```
37pub struct Spline<I, T>
38where
39 I: InterpType<T> + Send + Sync + 'static,
40{
41 /// The lower-level [`Interpolator`].
42 ///
43 /// [`Interpolator`]: Interpolation#implementors
44 pub interp: I::Interpolation,
45 /// The owned x data.
46 pub xa: Box<[T]>,
47 /// The owned y data.
48 pub ya: Box<[T]>,
49 name: Box<str>,
50 min_size: usize,
51}
52
53impl<I, T> Spline<I, T>
54where
55 I: InterpType<T> + Send + Sync + 'static,
56{
57 /// Constructs a Spline of an Interpolation type `typ` from the data arrays `xa` and `ya`.
58 ///
59 /// # Example
60 /// ```
61 /// # use rsl_interpolation::*;
62 /// #
63 /// # fn main() -> Result<(), InterpolationError>{
64 /// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
65 /// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
66 /// let typ = Cubic;
67 ///
68 /// let spline = Spline::new(typ, &xa, &ya)?;
69 /// #
70 /// # Ok(())
71 /// # }
72 #[doc(alias = "gsl_spline_init")]
73 pub fn new(typ: I, xa: &[T], ya: &[T]) -> Result<Self, InterpolationError>
74 where
75 T: Clone,
76 {
77 Ok(Self {
78 interp: typ.build(xa, ya)?,
79 xa: xa.into(),
80 ya: ya.into(),
81 name: typ.name().into(),
82 min_size: typ.min_size(),
83 })
84 }
85
86 /// Returns the interpolated value `y` for a given point `x`, using the [`Accelerator`] `acc`.
87 ///
88 /// # Example
89 ///
90 /// ```
91 /// # use rsl_interpolation::*;
92 /// #
93 /// # fn main() -> Result<(), InterpolationError>{
94 /// let mut acc = Accelerator::new();
95 ///
96 /// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
97 /// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
98 /// let typ = Cubic;
99 /// let spline = Spline::new(typ, &xa, &ya)?;
100 /// #
101 /// let y = spline.eval(1.5, &mut acc)?;
102 ///
103 /// assert_eq!(y, 3.0);
104 /// # Ok(())
105 /// # }
106 /// ```
107 ///
108 /// # Errors
109 ///
110 /// Returns a [`DomainError`] if `x` is outside the range of `xa`.
111 #[doc(alias = "gsl_spline_eval")]
112 #[doc(alias = "gsl_spline_eval_e")]
113 pub fn eval(&self, x: T, acc: &mut Accelerator) -> Result<T, DomainError> {
114 self.interp.eval(&self.xa, &self.ya, x, acc)
115 }
116
117 /// Returns the derivative `dy/dx` of an interpolated function for a given point `x`, using the
118 /// [`Accelerator`] `acc`.
119 ///
120 /// # Example
121 ///
122 /// ```
123 /// # use rsl_interpolation::*;
124 /// #
125 /// # fn main() -> Result<(), InterpolationError>{
126 /// let mut acc = Accelerator::new();
127 ///
128 /// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
129 /// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
130 /// let typ = Cubic;
131 /// let spline = Spline::new(typ, &xa, &ya)?;
132 ///
133 /// let dydx = spline.eval_deriv(1.5, &mut acc)?;
134 ///
135 /// assert_eq!(dydx, 2.0);
136 /// # Ok(())
137 /// # }
138 /// ```
139 ///
140 /// # Errors
141 ///
142 /// Returns a [`DomainError`] if `x` is outside the range of `xa`.
143 #[doc(alias = "gsl_spline_eval_deriv")]
144 #[doc(alias = "gsl_spline_eval_deriv_e")]
145 pub fn eval_deriv(&self, x: T, acc: &mut Accelerator) -> Result<T, DomainError> {
146 self.interp.eval_deriv(&self.xa, &self.ya, x, acc)
147 }
148
149 /// Returns the second derivative `d²y/dx²` of an interpolated function for a given point `x`, using the
150 /// [`Accelerator`] `acc`.
151 ///
152 /// # Example
153 /// ```
154 /// # use rsl_interpolation::*;
155 /// #
156 /// # fn main() -> Result<(), InterpolationError>{
157 /// let mut acc = Accelerator::new();
158 ///
159 /// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
160 /// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
161 /// let typ = Cubic;
162 /// let spline = Spline::new(typ, &xa, &ya)?;
163 ///
164 /// let dydx = spline.eval_deriv2(1.5, &mut acc)?;
165 ///
166 /// assert_eq!(dydx, 0.0);
167 /// # Ok(())
168 /// # }
169 /// ```
170 ///
171 /// # Errors
172 ///
173 /// Returns a [`DomainError`] if `x` is outside the range of `xa`.
174 #[doc(alias = "gsl_spline_eval_deriv2")]
175 #[doc(alias = "gsl_spline_eval_deriv2_e")]
176 pub fn eval_deriv2(&self, x: T, acc: &mut Accelerator) -> Result<T, DomainError> {
177 self.interp.eval_deriv2(&self.xa, &self.ya, x, acc)
178 }
179
180 #[allow(rustdoc::broken_intra_doc_links)]
181 /// Returns the numerical integral of an interpolated function over the range [`a` ,`b`], using the
182 /// [`Accelerator`] `acc`.
183 ///
184 /// # Example
185 /// ```
186 /// # use rsl_interpolation::*;
187 /// #
188 /// # fn main() -> Result<(), InterpolationError>{
189 /// let mut acc = Accelerator::new();
190 ///
191 /// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
192 /// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
193 /// let typ = Cubic;
194 /// let spline = Spline::new(typ, &xa, &ya)?;
195 ///
196 /// let int = spline.eval_integ(0.0, 2.0, &mut acc)?;
197 ///
198 /// assert_eq!(int, 4.0);
199 /// # Ok(())
200 /// # }
201 /// ```
202 ///
203 /// # Errors
204 ///
205 /// Returns a [`DomainError`] if `a` or `b` is outside the range of xa.
206 #[doc(alias = "gsl_spline_eval_integ")]
207 #[doc(alias = "gsl_spline_eval_integ_e")]
208 pub fn eval_integ(&self, a: T, b: T, acc: &mut Accelerator) -> Result<T, DomainError> {
209 self.interp.eval_integ(&self.xa, &self.ya, a, b, acc)
210 }
211
212 /// Returns the name of the Interpolator.
213 #[doc(alias = "gsl_spline_name")]
214 pub fn name(&self) -> &str {
215 &self.name
216 }
217
218 /// Returns the minimum number of points required by the Interpolator.
219 #[doc(alias = "gsl_spline_min_size")]
220 pub fn min_size(&self) -> usize {
221 self.min_size
222 }
223}
224
225/// 2D Spline with runtime-determined Interpolation Type.
226pub type DynSpline<T> = Spline<DynInterpType<T>, T>;
227
228impl<T> DynSpline<T> {
229 /// Constructs a Spline of a dynamic Interpolation type `typ` from the data arrays `xa` and
230 /// `ya`.
231 ///
232 /// # Example
233 /// ```
234 /// # use rsl_interpolation::*;
235 /// #
236 /// # fn main() -> Result<(), InterpolationError> {
237 /// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
238 /// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
239 /// let typ = "cubic";
240 ///
241 /// let spline = match typ {
242 /// "cubic" => Spline::new_dyn(Cubic, &xa, &ya)?,
243 /// "akima" => Spline::new_dyn(Akima, &xa, &ya)?,
244 /// // ...
245 /// _ => unreachable!()
246 /// };
247 /// # Ok(())
248 /// # }
249 /// ```
250 #[doc(alias = "gsl_spline_init")]
251 pub fn new_dyn<I>(typ: I, xa: &[T], ya: &[T]) -> Result<Self, InterpolationError>
252 where
253 T: Clone,
254 I: InterpType<T> + Send + Sync + 'static,
255 I::Interpolation: Send + Sync + 'static,
256 {
257 Self::new(DynInterpType::new(typ), xa, ya)
258 }
259}
260
261/// Creates a [`DynSpline`] of `typ` type.
262///
263/// Useful when `typ` is not known at compile time.
264///
265/// # Example
266/// ```
267/// # use rsl_interpolation::*;
268/// #
269/// # fn main() -> Result<(), InterpolationError> {
270/// let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
271/// let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
272/// let typ = "cubic";
273///
274/// let spline = make_spline(typ, &xa, &ya)?;
275/// # Ok(())
276/// # }
277/// ```
278pub fn make_spline<T>(typ: &str, xa: &[T], ya: &[T]) -> Result<DynSpline<T>, InterpolationError>
279where
280 T: crate::Num + ndarray_linalg::Lapack,
281{
282 use crate::*;
283
284 match typ.to_lowercase().as_str() {
285 "linear" => Ok(DynSpline::new_dyn(Linear, xa, ya)?),
286 "cubic" => Ok(DynSpline::new_dyn(Cubic, xa, ya)?),
287 "akima" => Ok(DynSpline::new_dyn(Akima, xa, ya)?),
288 "cubicperiodic" | "cubic periodic" => Ok(DynSpline::new_dyn(CubicPeriodic, xa, ya)?),
289 "akimaperiodic" | "akima periodic" => Ok(DynSpline::new_dyn(AkimaPeriodic, xa, ya)?),
290 "steffen" => Ok(DynSpline::new_dyn(Steffen, xa, ya)?),
291 _ => Err(InterpolationError::InvalidType(typ.into())),
292 }
293}
294
295#[cfg(test)]
296mod test {
297 use super::*;
298 use crate::*;
299
300 #[test]
301 fn test_spline_creation() {
302 let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
303 let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
304
305 let spline = Spline::new(Cubic, &xa, &ya).unwrap();
306 let _: &str = spline.name();
307 let _: usize = spline.min_size();
308 }
309
310 #[test]
311 fn test_spline_eval() {
312 let xa = [0.0, 1.0, 2.0];
313 let ya = [0.0, 1.0, 2.0];
314 let spline = Spline::new(Cubic, &xa, &ya).unwrap();
315 let mut acc = Accelerator::new();
316
317 let x = 0.5;
318 let y = spline.eval(x, &mut acc).unwrap();
319 let dy = spline.eval_deriv(x, &mut acc).unwrap();
320 let dy2 = spline.eval_deriv2(x, &mut acc).unwrap();
321 let int = spline.eval_integ(0.0, x, &mut acc).unwrap();
322
323 assert_eq!(y, 0.5);
324 assert_eq!(dy, 1.0);
325 assert_eq!(dy2, 0.0);
326 assert_eq!(int, 0.125);
327 }
328
329 #[test]
330 fn test_dyn_spline() {
331 let xa = [0.0, 1.0, 2.0, 3.0, 4.0];
332 let ya = [0.0, 2.0, 4.0, 6.0, 8.0];
333
334 Spline::new(Cubic, &xa, &ya).unwrap();
335 Spline::new_dyn(Cubic, &xa, &ya).unwrap();
336 }
337}