rsl_interpolation/
accel.rs

1/// Index Look-up Acceration
2///
3/// This object caches the previous value of an index lookup. When the subsequent interpolation
4/// point falls in the same interval, its index value can be returned immediately.
5///
6/// The performance boost can be significant when continuously evaluating splines around the same
7/// area as the previous point. Moreover, the same Accelerator can be shared across multiple
8/// Splines, if they are defined over the same x points. This is especially useful in ODE solvers,
9/// as the solver's step size is usually much smaller that the xarray spacing.
10///
11/// See [`GSL's Acceleration section`].
12///
13/// ## Example
14///
15/// ```
16/// # use rsl_interpolation::*;
17/// #
18/// # fn main() {
19/// let mut acc = Accelerator::new();
20/// # }
21/// ```
22/// [`GSL's Acceleration section`]: https://www.gnu.org/software/gsl/doc/html/interp.html#d-index-look-up-and-acceleration
23#[doc(alias = "gsl_interp_accel")]
24#[derive(Clone, Copy)]
25pub struct Accelerator {
26    /// The current cached index.
27    pub(crate) cache: usize,
28    /// The total cache hits.
29    pub(crate) hits: usize,
30    /// The total cache misses.
31    pub(crate) misses: usize,
32}
33
34impl Accelerator {
35    /// Creates a new Accelerator.
36    pub fn new() -> Self {
37        Accelerator {
38            cache: 0,
39            hits: 0,
40            misses: 0,
41        }
42    }
43
44    /// This function returns the index i of the array `xarray` such that
45    /// `xarray[i] <= x <= xarray[i+1]`. The index is searched for in the range [ilo, ihi].
46    #[doc(alias = "gsl_interp_bsearch")]
47    pub(crate) fn bsearch<T>(&self, xarray: &[T], x: T, ilo: usize, ihi: usize) -> usize
48    where
49        T: num::Float,
50    {
51        let mut ilo = ilo;
52        let mut ihi = ihi;
53        while ihi > ilo + 1 {
54            let i = (ihi + ilo) / 2;
55            if xarray[i] > x {
56                ihi = i;
57            } else {
58                ilo = i;
59            }
60        }
61        ilo
62    }
63
64    /// Performs a lookup action on the data array. Returns an index i such that
65    /// xarray[i] <= x < xarray[i+1].
66    #[doc(alias = "gsl_interp_accel_find")]
67    pub(crate) fn find<T>(&mut self, xarray: &[T], x: T) -> usize
68    where
69        T: num::Float,
70    {
71        if x < xarray[self.cache] {
72            self.misses += 1;
73            self.cache = self.bsearch(xarray, x, 0, self.cache);
74        } else if x >= xarray[self.cache + 1] {
75            self.misses += 1;
76            self.cache = self.bsearch(xarray, x, self.cache, xarray.len() - 1);
77        } else {
78            self.hits += 1;
79        }
80        self.cache
81    }
82
83    /// Resets the Accelerator's stats to 0.
84    #[doc(alias = "gsl_interp_accel_reset")]
85    pub fn reset(&mut self) {
86        self.cache = 0;
87        self.hits = 0;
88        self.misses = 0;
89    }
90}
91
92impl std::fmt::Debug for Accelerator {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        f.debug_struct("Accelerator")
95            .field("cache", &self.cache)
96            .field("hits  ", &self.hits)
97            .field("misses", &self.misses)
98            .finish()
99    }
100}
101
102impl Default for Accelerator {
103    fn default() -> Self {
104        Self::new()
105    }
106}
107
108#[cfg(test)]
109mod test {
110    use super::*;
111
112    #[test]
113    fn test_instantiation() {
114        let _ = Accelerator::new();
115    }
116
117    #[test]
118    fn test_reset() {
119        let mut acc = Accelerator::new();
120        acc.cache = 1;
121        acc.hits = 1;
122        acc.misses = 1;
123        acc.reset();
124
125        assert_eq!(acc.cache, 0);
126        assert_eq!(acc.hits, 0);
127        assert_eq!(acc.misses, 0);
128    }
129}