rsl_interpolation/types/
steffen.rs

1use crate::Accelerator;
2use crate::DomainError;
3use crate::InterpType;
4use crate::Interpolation;
5use crate::InterpolationError;
6use crate::types::utils::{check_if_inbounds, check1d_data};
7
8const MIN_SIZE: usize = 3;
9
10/// Steffen Interpolation type.
11///
12/// Steffen’s method guarantees the monotonicity of the interpolating function between the given
13/// data points. Therefore, minima and maxima can only occur exactly at the data points, and there
14/// can never be spurious oscillations between data points. The interpolated function is piecewise
15/// cubic in each interval. The resulting curve and its first derivative are guaranteed to be
16/// continuous, but the second derivative may be discontinuous.
17pub struct Steffen;
18
19impl<T> InterpType<T> for Steffen
20where
21    T: crate::Num,
22{
23    type Interpolation = SteffenInterp<T>;
24
25    /// Constructs a Cubic Interpolator.
26    ///
27    /// ## Example
28    ///
29    /// ```
30    /// # use rsl_interpolation::*;
31    /// #
32    /// # fn main() -> Result<(), InterpolationError>{
33    /// let xa = [0.0, 1.0, 2.0];
34    /// let ya = [0.0, 2.0, 4.0];
35    /// let interp = Steffen.build(&xa, &ya)?;
36    /// # Ok(())
37    /// # }
38    /// ```
39    fn build(&self, xa: &[T], ya: &[T]) -> Result<SteffenInterp<T>, InterpolationError> {
40        check1d_data(xa, ya, MIN_SIZE)?;
41        let size = xa.len();
42
43        // First assign the interval and slopes for the left boundary. We use the "simplest
44        // possibility" method described in the paper in section 2.2.
45        let h0 = xa[1] - xa[0];
46        let s0 = (ya[1] - ya[0]) / h0;
47
48        // Stores the calculated y' values.
49        let mut y_prime = Vec::<T>::with_capacity(size);
50        y_prime.push(s0);
51
52        // Calculate all necessary s, h, p, y' variables form 1 to `size-2` (0 to `size-2`
53        // inclusive)
54        let one = T::one();
55        let half = T::from(0.5).unwrap();
56
57        for i in 1..(size - 1) {
58            // Equation 6 in paper
59            let hi = xa[i + 1] - xa[i];
60            let him1 = xa[i] - xa[i - 1];
61
62            // Equation 7 in paper
63            let si = (ya[i + 1] - ya[i]) / hi;
64            let sim1 = (ya[i] - ya[i - 1]) / him1;
65
66            // Equation 8 in paper
67            let pi = (sim1 * hi + si * him1) / (him1 + hi);
68
69            let min1 = si.abs().min(half * pi.abs());
70            let min2 = sim1.abs().min(min1);
71            y_prime.push((steffen_copysign(one, sim1) + steffen_copysign(one, si)) * min2);
72        }
73
74        // We also need y' for the rightmost boundary; we use the 'simplest possibility' method
75        // described in the paper in section 2.2
76        //
77        //y` = s_{n-1}`
78        y_prime.push((ya[size - 1] - ya[size - 2]) / (xa[size - 1] - xa[size - 2]));
79
80        // Now we can calculate all the coefficients for the whole range
81        let mut a = Vec::<T>::with_capacity(size - 1);
82        let mut b = Vec::<T>::with_capacity(size - 1);
83        let mut c = Vec::<T>::with_capacity(size - 1);
84        let mut d = Vec::<T>::with_capacity(size - 1);
85
86        let two = T::from(2.0).unwrap();
87        let three = T::from(3.0).unwrap();
88        for i in 0..(size - 1) {
89            let hi = xa[i + 1] - xa[i];
90            let si = (ya[i + 1] - ya[i]) / hi;
91
92            // These are from equations 2-5 in the paper
93            a.push((y_prime[i] + y_prime[i + 1] - two * si) / hi.powi(2));
94            b.push((three * si - two * y_prime[i] - y_prime[i + 1]) / hi);
95            c.push(y_prime[i]);
96            d.push(ya[i]);
97        }
98
99        let state = SteffenInterp {
100            a,
101            b,
102            c,
103            d,
104            y_prime,
105        };
106
107        Ok(state)
108    }
109
110    fn name(&self) -> &str {
111        "Steffen"
112    }
113
114    fn min_size(&self) -> usize {
115        MIN_SIZE
116    }
117}
118
119// ===============================================================================================
120
121/// Steffen Interpolator.
122///
123/// Provides all the evaluation methods.
124///
125/// Should be constructed through the [`Steffen`] type.
126#[allow(dead_code)]
127pub struct SteffenInterp<T>
128where
129    T: crate::Num,
130{
131    a: Vec<T>,
132    b: Vec<T>,
133    c: Vec<T>,
134    d: Vec<T>,
135    y_prime: Vec<T>,
136}
137
138impl<T> Interpolation<T> for SteffenInterp<T>
139where
140    T: crate::Num,
141{
142    #[allow(unused_variables)]
143    fn eval(&self, xa: &[T], ya: &[T], x: T, acc: &mut Accelerator) -> Result<T, DomainError> {
144        check_if_inbounds(xa, x)?;
145        let index = acc.find(xa, x);
146
147        let xlo = xa[index];
148        let delx = x - xlo;
149        let a = self.a[index];
150        let b = self.b[index];
151        let c = self.c[index];
152        let d = self.d[index];
153
154        Ok(d + delx * (c + delx * (b + delx * a)))
155    }
156
157    #[allow(unused_variables)]
158    fn eval_deriv(
159        &self,
160        xa: &[T],
161        ya: &[T],
162        x: T,
163        acc: &mut Accelerator,
164    ) -> Result<T, DomainError> {
165        check_if_inbounds(xa, x)?;
166        let index = acc.find(xa, x);
167
168        let xlo = xa[index];
169        let delx = x - xlo;
170        let a = self.a[index];
171        let b = self.b[index];
172        let c = self.c[index];
173
174        let two = T::from(2.0).unwrap();
175        let three = T::from(3.0).unwrap();
176
177        Ok(c + delx * (two * b + delx * three * a))
178    }
179
180    #[allow(unused_variables)]
181    fn eval_deriv2(
182        &self,
183        xa: &[T],
184        ya: &[T],
185        x: T,
186        acc: &mut Accelerator,
187    ) -> Result<T, DomainError> {
188        check_if_inbounds(xa, x)?;
189        let index = acc.find(xa, x);
190
191        let xlo = xa[index];
192        let delx = x - xlo;
193        let a = self.a[index];
194        let b = self.b[index];
195
196        let two = T::from(2.0).unwrap();
197        let six = T::from(6.0).unwrap();
198
199        Ok(six * delx * a + two * b)
200    }
201
202    #[allow(unused_variables)]
203    fn eval_integ(
204        &self,
205        xa: &[T],
206        ya: &[T],
207        a: T,
208        b: T,
209        acc: &mut Accelerator,
210    ) -> Result<T, DomainError> {
211        check_if_inbounds(xa, a)?;
212        check_if_inbounds(xa, b)?;
213        let index_a = acc.find(xa, a);
214        let index_b = acc.find(xa, b);
215
216        let quarter = T::from(0.25).unwrap();
217        let half = T::from(0.5).unwrap();
218        let third = T::from(1.0 / 3.0).unwrap();
219        let mut result = T::zero();
220
221        for i in index_a..=index_b {
222            let xlo = xa[i];
223            let xhi = xa[i + 1];
224
225            let dx = xhi - xlo;
226
227            // If two x points are the same
228            if dx.is_zero() {
229                continue;
230            }
231
232            let x1 = if i == index_a { a - xlo } else { T::zero() };
233            let x2 = if i == index_b { b - xlo } else { xhi - xlo };
234
235            let x12 = x1.powi(2);
236            let x22 = x2.powi(2);
237
238            result += quarter * self.a[i] * (x22.powi(2) - x12.powi(2));
239            result += third * self.b[i] * (x22 * x2 - x12 * x1);
240            result += half * self.c[i] * (x22 - x12);
241            result += self.d[i] * (x2 - x1);
242        }
243
244        Ok(result)
245    }
246}
247
248fn steffen_copysign<T>(x: T, y: T) -> T
249where
250    T: crate::Num,
251{
252    if (x.is_sign_negative() & y.is_sign_positive()) | (x.is_sign_positive() & y.is_sign_negative())
253    {
254        -x
255    } else {
256        x
257    }
258}