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