Skip to content

Commit 3656ddf

Browse files
JP-EllisLukeMathWalker
authored andcommitted
Add logspace and geomspace constructors (#617)
* Add `logspace` constructor for Array1 Signed-off-by: JP-Ellis <josh@jpellis.me> * Add logspace tests Signed-off-by: JP-Ellis <josh@jpellis.me> * More extensive logspace tests Signed-off-by: JP-Ellis <josh@jpellis.me> * Remove print statements in doc tests Signed-off-by: JP-Ellis <josh@jpellis.me> * Add `geomspace` and make `logspace` compatible with NumPy Signed-off-by: JP-Ellis <josh@jpellis.me>
1 parent 61b4835 commit 3656ddf

File tree

5 files changed

+393
-23
lines changed

5 files changed

+393
-23
lines changed

src/geomspace.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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+
use num_traits::Float;
9+
10+
/// An iterator of a sequence of geometrically spaced floats.
11+
///
12+
/// Iterator element type is `F`.
13+
pub struct Geomspace<F> {
14+
sign: F,
15+
start: F,
16+
step: F,
17+
index: usize,
18+
len: usize,
19+
}
20+
21+
impl<F> Iterator for Geomspace<F>
22+
where
23+
F: Float,
24+
{
25+
type Item = F;
26+
27+
#[inline]
28+
fn next(&mut self) -> Option<F> {
29+
if self.index >= self.len {
30+
None
31+
} else {
32+
// Calculate the value just like numpy.linspace does
33+
let i = self.index;
34+
self.index += 1;
35+
let exponent = self.start + self.step * F::from(i).unwrap();
36+
Some(self.sign * exponent.exp())
37+
}
38+
}
39+
40+
#[inline]
41+
fn size_hint(&self) -> (usize, Option<usize>) {
42+
let n = self.len - self.index;
43+
(n, Some(n))
44+
}
45+
}
46+
47+
impl<F> DoubleEndedIterator for Geomspace<F>
48+
where
49+
F: Float,
50+
{
51+
#[inline]
52+
fn next_back(&mut self) -> Option<F> {
53+
if self.index >= self.len {
54+
None
55+
} else {
56+
// Calculate the value just like numpy.linspace does
57+
self.len -= 1;
58+
let i = self.len;
59+
let exponent = self.start + self.step * F::from(i).unwrap();
60+
Some(self.sign * exponent.exp())
61+
}
62+
}
63+
}
64+
65+
impl<F> ExactSizeIterator for Geomspace<F> where Geomspace<F>: Iterator {}
66+
67+
/// An iterator of a sequence of geometrically spaced values.
68+
///
69+
/// The `Geomspace` has `n` elements, where the first element is `a` and the
70+
/// last element is `b`.
71+
///
72+
/// Iterator element type is `F`, where `F` must be either `f32` or `f64`.
73+
///
74+
/// **Panics** if the interval `[a, b]` contains zero (including the end points).
75+
#[inline]
76+
pub fn geomspace<F>(a: F, b: F, n: usize) -> Geomspace<F>
77+
where
78+
F: Float,
79+
{
80+
assert!(
81+
a != F::zero() && b != F::zero(),
82+
"Start and/or end of geomspace cannot be zero.",
83+
);
84+
assert!(
85+
a.is_sign_negative() == b.is_sign_negative(),
86+
"Logarithmic interval cannot cross 0."
87+
);
88+
89+
let log_a = a.abs().ln();
90+
let log_b = b.abs().ln();
91+
let step = if n > 1 {
92+
let nf: F = F::from(n).unwrap();
93+
(log_b - log_a) / (nf - F::one())
94+
} else {
95+
F::zero()
96+
};
97+
Geomspace {
98+
sign: a.signum(),
99+
start: log_a,
100+
step: step,
101+
index: 0,
102+
len: n,
103+
}
104+
}
105+
106+
#[cfg(test)]
107+
mod tests {
108+
use super::geomspace;
109+
use crate::{arr1, Array1};
110+
111+
#[test]
112+
fn valid() {
113+
let array: Array1<_> = geomspace(1e0, 1e3, 4).collect();
114+
assert!(array.all_close(&arr1(&[1e0, 1e1, 1e2, 1e3]), 1e-5));
115+
116+
let array: Array1<_> = geomspace(1e3, 1e0, 4).collect();
117+
assert!(array.all_close(&arr1(&[1e3, 1e2, 1e1, 1e0]), 1e-5));
118+
119+
let array: Array1<_> = geomspace(-1e3, -1e0, 4).collect();
120+
assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));
121+
122+
let array: Array1<_> = geomspace(-1e0, -1e3, 4).collect();
123+
assert!(array.all_close(&arr1(&[-1e0, -1e1, -1e2, -1e3]), 1e-5));
124+
}
125+
126+
#[test]
127+
fn iter_forward() {
128+
let mut iter = geomspace(1.0f64, 1e3, 4);
129+
130+
assert!(iter.size_hint() == (4, Some(4)));
131+
132+
assert!((iter.next().unwrap() - 1e0).abs() < 1e-5);
133+
assert!((iter.next().unwrap() - 1e1).abs() < 1e-5);
134+
assert!((iter.next().unwrap() - 1e2).abs() < 1e-5);
135+
assert!((iter.next().unwrap() - 1e3).abs() < 1e-5);
136+
assert!(iter.next().is_none());
137+
138+
assert!(iter.size_hint() == (0, Some(0)));
139+
}
140+
141+
#[test]
142+
fn iter_backward() {
143+
let mut iter = geomspace(1.0f64, 1e3, 4);
144+
145+
assert!(iter.size_hint() == (4, Some(4)));
146+
147+
assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5);
148+
assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5);
149+
assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5);
150+
assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5);
151+
assert!(iter.next_back().is_none());
152+
153+
assert!(iter.size_hint() == (0, Some(0)));
154+
}
155+
156+
#[test]
157+
#[should_panic]
158+
fn zero_lower() {
159+
geomspace(0.0, 1.0, 4);
160+
}
161+
162+
#[test]
163+
#[should_panic]
164+
fn zero_upper() {
165+
geomspace(1.0, 0.0, 4);
166+
}
167+
168+
#[test]
169+
#[should_panic]
170+
fn zero_included() {
171+
geomspace(-1.0, 1.0, 4);
172+
}
173+
}

src/impl_constructors.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@
1010
//!
1111
//!
1212
13-
use num_traits::{Zero, One, Float};
13+
use num_traits::{Float, One, Zero};
1414
use std::isize;
1515
use std::mem;
1616

17-
use crate::imp_prelude::*;
18-
use crate::StrideShape;
1917
use crate::dimension;
20-
use crate::linspace;
2118
use crate::error::{self, ShapeError};
22-
use crate::indices;
19+
use crate::imp_prelude::*;
2320
use crate::indexes;
21+
use crate::indices;
2422
use crate::iterators::{to_vec, to_vec_mapped};
23+
use crate::StrideShape;
24+
use crate::{linspace, geomspace, logspace};
2525

2626
/// # Constructor Methods for Owned Arrays
2727
///
@@ -101,6 +101,55 @@ impl<S, A> ArrayBase<S, Ix1>
101101
{
102102
Self::from_vec(to_vec(linspace::range(start, end, step)))
103103
}
104+
105+
/// Create a one-dimensional array with `n` elements logarithmically spaced,
106+
/// with the starting value being `base.powf(start)` and the final one being
107+
/// `base.powf(end)`. `A` must be a floating point type.
108+
///
109+
/// If `base` is negative, all values will be negative.
110+
///
111+
/// **Panics** if the length is greater than `isize::MAX`.
112+
///
113+
/// ```rust
114+
/// use ndarray::{Array, arr1};
115+
///
116+
/// let array = Array::logspace(10.0, 0.0, 3.0, 4);
117+
/// assert!(array.all_close(&arr1(&[1e0, 1e1, 1e2, 1e3]), 1e-5));
118+
///
119+
/// let array = Array::logspace(-10.0, 3.0, 0.0, 4);
120+
/// assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));
121+
/// ```
122+
pub fn logspace(base: A, start: A, end: A, n: usize) -> Self
123+
where
124+
A: Float,
125+
{
126+
Self::from_vec(to_vec(logspace::logspace(base, start, end, n)))
127+
}
128+
129+
/// Create a one-dimensional array from the inclusive interval `[start,
130+
/// end]` with `n` elements geometrically spaced. `A` must be a floating
131+
/// point type.
132+
///
133+
/// The interval can be either all positive or all negative; however, it
134+
/// cannot contain 0 (including the end points).
135+
///
136+
/// **Panics** if `n` is greater than `isize::MAX`.
137+
///
138+
/// ```rust
139+
/// use ndarray::{Array, arr1};
140+
///
141+
/// let array = Array::geomspace(1e0, 1e3, 4);
142+
/// assert!(array.all_close(&arr1(&[1e0, 1e1, 1e2, 1e3]), 1e-5));
143+
///
144+
/// let array = Array::geomspace(-1e3, -1e0, 4);
145+
/// assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));
146+
/// ```
147+
pub fn geomspace(start: A, end: A, n: usize) -> Self
148+
where
149+
A: Float,
150+
{
151+
Self::from_vec(to_vec(geomspace::geomspace(start, end, n)))
152+
}
104153
}
105154

106155
/// ## Constructor methods for two-dimensional arrays.

src/iterators/mod.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,25 +1311,25 @@ send_sync_read_write!(ElementsBaseMut);
13111311

13121312
/// (Trait used internally) An iterator that we trust
13131313
/// to deliver exactly as many items as it said it would.
1314-
pub unsafe trait TrustedIterator { }
1314+
pub unsafe trait TrustedIterator {}
13151315

1316-
use std;
1317-
use crate::linspace::Linspace;
1318-
use crate::iter::IndicesIter;
13191316
use crate::indexes::IndicesIterF;
1317+
use crate::iter::IndicesIter;
1318+
use crate::{geomspace::Geomspace, linspace::Linspace, logspace::Logspace};
1319+
use std;
13201320

1321-
unsafe impl<F> TrustedIterator for Linspace<F> { }
1322-
unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> { }
1323-
unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> { }
1324-
unsafe impl<I, F> TrustedIterator for std::iter::Map<I, F>
1325-
where I: TrustedIterator { }
1326-
unsafe impl<'a, A> TrustedIterator for slice::Iter<'a, A> { }
1327-
unsafe impl<'a, A> TrustedIterator for slice::IterMut<'a, A> { }
1328-
unsafe impl TrustedIterator for ::std::ops::Range<usize> { }
1321+
unsafe impl<F> TrustedIterator for Geomspace<F> {}
1322+
unsafe impl<F> TrustedIterator for Linspace<F> {}
1323+
unsafe impl<F> TrustedIterator for Logspace<F> {}
1324+
unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> {}
1325+
unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> {}
1326+
unsafe impl<I, F> TrustedIterator for std::iter::Map<I, F> where I: TrustedIterator {}
1327+
unsafe impl<'a, A> TrustedIterator for slice::Iter<'a, A> {}
1328+
unsafe impl<'a, A> TrustedIterator for slice::IterMut<'a, A> {}
1329+
unsafe impl TrustedIterator for ::std::ops::Range<usize> {}
13291330
// FIXME: These indices iter are dubious -- size needs to be checked up front.
1330-
unsafe impl<D> TrustedIterator for IndicesIter<D> where D: Dimension { }
1331-
unsafe impl<D> TrustedIterator for IndicesIterF<D> where D: Dimension { }
1332-
1331+
unsafe impl<D> TrustedIterator for IndicesIter<D> where D: Dimension {}
1332+
unsafe impl<D> TrustedIterator for IndicesIterF<D> where D: Dimension {}
13331333

13341334
/// Like Iterator::collect, but only for trusted length iterators
13351335
pub fn to_vec<I>(iter: I) -> Vec<I::Item>

src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,18 @@ mod free_functions;
167167
pub use crate::free_functions::*;
168168
pub use crate::iterators::iter;
169169

170-
#[macro_use] mod slice;
171-
mod layout;
170+
mod error;
171+
mod geomspace;
172172
mod indexes;
173173
mod iterators;
174+
mod layout;
174175
mod linalg_traits;
175176
mod linspace;
177+
mod logspace;
176178
mod numeric_util;
177-
mod error;
178179
mod shape_builder;
180+
#[macro_use]
181+
mod slice;
179182
mod stacking;
180183
#[macro_use]
181184
mod zip;

0 commit comments

Comments
 (0)