ndarray/
logspace.rs

1// Copyright 2014-2016 bluss and ndarray developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8#![cfg(feature = "std")]
9use num_traits::Float;
10
11/// An iterator of a sequence of logarithmically spaced number.
12///
13/// Iterator element type is `F`.
14pub struct Logspace<F> {
15    sign: F,
16    base: F,
17    start: F,
18    step: F,
19    index: usize,
20    len: usize,
21}
22
23impl<F> Iterator for Logspace<F>
24where
25    F: Float,
26{
27    type Item = F;
28
29    #[inline]
30    fn next(&mut self) -> Option<F> {
31        if self.index >= self.len {
32            None
33        } else {
34            // Calculate the value just like numpy.linspace does
35            let i = self.index;
36            self.index += 1;
37            let exponent = self.start + self.step * F::from(i).unwrap();
38            Some(self.sign * self.base.powf(exponent))
39        }
40    }
41
42    #[inline]
43    fn size_hint(&self) -> (usize, Option<usize>) {
44        let n = self.len - self.index;
45        (n, Some(n))
46    }
47}
48
49impl<F> DoubleEndedIterator for Logspace<F>
50where
51    F: Float,
52{
53    #[inline]
54    fn next_back(&mut self) -> Option<F> {
55        if self.index >= self.len {
56            None
57        } else {
58            // Calculate the value just like numpy.linspace does
59            self.len -= 1;
60            let i = self.len;
61            let exponent = self.start + self.step * F::from(i).unwrap();
62            Some(self.sign * self.base.powf(exponent))
63        }
64    }
65}
66
67impl<F> ExactSizeIterator for Logspace<F> where Logspace<F>: Iterator {}
68
69/// An iterator of a sequence of logarithmically spaced numbers.
70///
71/// The `Logspace` has `n` elements, where the first element is `base.powf(a)`
72/// and the last element is `base.powf(b)`.  If `base` is negative, this
73/// iterator will return all negative values.
74///
75/// The iterator element type is `F`, where `F` must implement `Float`, e.g.
76/// `f32` or `f64`.
77///
78/// **Panics** if converting `n - 1` to type `F` fails.
79#[inline]
80pub fn logspace<F>(base: F, a: F, b: F, n: usize) -> Logspace<F>
81where
82    F: Float,
83{
84    let step = if n > 1 {
85        let num_steps = F::from(n - 1).expect("Converting number of steps to `A` must not fail.");
86        (b - a) / num_steps
87    } else {
88        F::zero()
89    };
90    Logspace {
91        sign: base.signum(),
92        base: base.abs(),
93        start: a,
94        step,
95        index: 0,
96        len: n,
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::logspace;
103
104    #[test]
105    #[cfg(feature = "approx")]
106    fn valid() {
107        use crate::{arr1, Array1};
108        use approx::assert_abs_diff_eq;
109
110        let array: Array1<_> = logspace(10.0, 0.0, 3.0, 4).collect();
111        assert_abs_diff_eq!(array, arr1(&[1e0, 1e1, 1e2, 1e3]));
112
113        let array: Array1<_> = logspace(10.0, 3.0, 0.0, 4).collect();
114        assert_abs_diff_eq!(array, arr1(&[1e3, 1e2, 1e1, 1e0]));
115
116        let array: Array1<_> = logspace(-10.0, 3.0, 0.0, 4).collect();
117        assert_abs_diff_eq!(array, arr1(&[-1e3, -1e2, -1e1, -1e0]));
118
119        let array: Array1<_> = logspace(-10.0, 0.0, 3.0, 4).collect();
120        assert_abs_diff_eq!(array, arr1(&[-1e0, -1e1, -1e2, -1e3]));
121    }
122
123    #[test]
124    fn iter_forward() {
125        let mut iter = logspace(10.0f64, 0.0, 3.0, 4);
126
127        assert!(iter.size_hint() == (4, Some(4)));
128
129        assert!((iter.next().unwrap() - 1e0).abs() < 1e-5);
130        assert!((iter.next().unwrap() - 1e1).abs() < 1e-5);
131        assert!((iter.next().unwrap() - 1e2).abs() < 1e-5);
132        assert!((iter.next().unwrap() - 1e3).abs() < 1e-5);
133        assert!(iter.next().is_none());
134
135        assert!(iter.size_hint() == (0, Some(0)));
136    }
137
138    #[test]
139    fn iter_backward() {
140        let mut iter = logspace(10.0f64, 0.0, 3.0, 4);
141
142        assert!(iter.size_hint() == (4, Some(4)));
143
144        assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5);
145        assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5);
146        assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5);
147        assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5);
148        assert!(iter.next_back().is_none());
149
150        assert!(iter.size_hint() == (0, Some(0)));
151    }
152}