Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logspace constructor #617

Merged
merged 5 commits into from May 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
173 changes: 173 additions & 0 deletions src/geomspace.rs
@@ -0,0 +1,173 @@
// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use num_traits::Float;

/// An iterator of a sequence of geometrically spaced floats.
///
/// Iterator element type is `F`.
pub struct Geomspace<F> {
sign: F,
start: F,
step: F,
index: usize,
len: usize,
}

impl<F> Iterator for Geomspace<F>
where
F: Float,
{
type Item = F;

#[inline]
fn next(&mut self) -> Option<F> {
if self.index >= self.len {
None
} else {
// Calculate the value just like numpy.linspace does
let i = self.index;
self.index += 1;
let exponent = self.start + self.step * F::from(i).unwrap();
Some(self.sign * exponent.exp())
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.len - self.index;
(n, Some(n))
}
}

impl<F> DoubleEndedIterator for Geomspace<F>
where
F: Float,
{
#[inline]
fn next_back(&mut self) -> Option<F> {
if self.index >= self.len {
None
} else {
// Calculate the value just like numpy.linspace does
self.len -= 1;
let i = self.len;
let exponent = self.start + self.step * F::from(i).unwrap();
Some(self.sign * exponent.exp())
}
}
}

impl<F> ExactSizeIterator for Geomspace<F> where Geomspace<F>: Iterator {}

/// An iterator of a sequence of geometrically spaced values.
///
/// The `Geomspace` has `n` elements, where the first element is `a` and the
/// last element is `b`.
///
/// Iterator element type is `F`, where `F` must be either `f32` or `f64`.
///
/// **Panics** if the interval `[a, b]` contains zero (including the end points).
#[inline]
pub fn geomspace<F>(a: F, b: F, n: usize) -> Geomspace<F>
where
F: Float,
{
assert!(
a != F::zero() && b != F::zero(),
"Start and/or end of geomspace cannot be zero.",
);
assert!(
a.is_sign_negative() == b.is_sign_negative(),
"Logarithmic interval cannot cross 0."
);

let log_a = a.abs().ln();
let log_b = b.abs().ln();
let step = if n > 1 {
let nf: F = F::from(n).unwrap();
(log_b - log_a) / (nf - F::one())
} else {
F::zero()
};
Geomspace {
sign: a.signum(),
start: log_a,
step: step,
index: 0,
len: n,
}
}

#[cfg(test)]
mod tests {
use super::geomspace;
use crate::{arr1, Array1};

#[test]
fn valid() {
let array: Array1<_> = geomspace(1e0, 1e3, 4).collect();
assert!(array.all_close(&arr1(&[1e0, 1e1, 1e2, 1e3]), 1e-5));

let array: Array1<_> = geomspace(1e3, 1e0, 4).collect();
assert!(array.all_close(&arr1(&[1e3, 1e2, 1e1, 1e0]), 1e-5));

let array: Array1<_> = geomspace(-1e3, -1e0, 4).collect();
assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));

let array: Array1<_> = geomspace(-1e0, -1e3, 4).collect();
assert!(array.all_close(&arr1(&[-1e0, -1e1, -1e2, -1e3]), 1e-5));
}

#[test]
fn iter_forward() {
let mut iter = geomspace(1.0f64, 1e3, 4);

assert!(iter.size_hint() == (4, Some(4)));

assert!((iter.next().unwrap() - 1e0).abs() < 1e-5);
assert!((iter.next().unwrap() - 1e1).abs() < 1e-5);
assert!((iter.next().unwrap() - 1e2).abs() < 1e-5);
assert!((iter.next().unwrap() - 1e3).abs() < 1e-5);
assert!(iter.next().is_none());

assert!(iter.size_hint() == (0, Some(0)));
}

#[test]
fn iter_backward() {
let mut iter = geomspace(1.0f64, 1e3, 4);

assert!(iter.size_hint() == (4, Some(4)));

assert!((iter.next_back().unwrap() - 1e3).abs() < 1e-5);
assert!((iter.next_back().unwrap() - 1e2).abs() < 1e-5);
assert!((iter.next_back().unwrap() - 1e1).abs() < 1e-5);
assert!((iter.next_back().unwrap() - 1e0).abs() < 1e-5);
assert!(iter.next_back().is_none());

assert!(iter.size_hint() == (0, Some(0)));
}

#[test]
#[should_panic]
fn zero_lower() {
geomspace(0.0, 1.0, 4);
}

#[test]
#[should_panic]
fn zero_upper() {
geomspace(1.0, 0.0, 4);
}

#[test]
#[should_panic]
fn zero_included() {
geomspace(-1.0, 1.0, 4);
}
}
59 changes: 54 additions & 5 deletions src/impl_constructors.rs
Expand Up @@ -10,18 +10,18 @@
//!
//!

use num_traits::{Zero, One, Float};
use num_traits::{Float, One, Zero};
use std::isize;
use std::mem;

use crate::imp_prelude::*;
use crate::StrideShape;
use crate::dimension;
use crate::linspace;
use crate::error::{self, ShapeError};
use crate::indices;
use crate::imp_prelude::*;
use crate::indexes;
use crate::indices;
use crate::iterators::{to_vec, to_vec_mapped};
use crate::StrideShape;
use crate::{linspace, geomspace, logspace};

/// # Constructor Methods for Owned Arrays
///
Expand Down Expand Up @@ -101,6 +101,55 @@ impl<S, A> ArrayBase<S, Ix1>
{
Self::from_vec(to_vec(linspace::range(start, end, step)))
}

/// Create a one-dimensional array with `n` elements logarithmically spaced,
/// with the starting value being `base.powf(start)` and the final one being
/// `base.powf(end)`. `A` must be a floating point type.
///
/// If `base` is negative, all values will be negative.
///
/// **Panics** if the length is greater than `isize::MAX`.
///
/// ```rust
/// use ndarray::{Array, arr1};
///
/// let array = Array::logspace(10.0, 0.0, 3.0, 4);
/// assert!(array.all_close(&arr1(&[1e0, 1e1, 1e2, 1e3]), 1e-5));
///
/// let array = Array::logspace(-10.0, 3.0, 0.0, 4);
/// assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));
/// ```
pub fn logspace(base: A, start: A, end: A, n: usize) -> Self
where
A: Float,
{
Self::from_vec(to_vec(logspace::logspace(base, start, end, n)))
}

/// Create a one-dimensional array from the inclusive interval `[start,
/// end]` with `n` elements geometrically spaced. `A` must be a floating
/// point type.
///
/// The interval can be either all positive or all negative; however, it
/// cannot contain 0 (including the end points).
///
/// **Panics** if `n` is greater than `isize::MAX`.
///
/// ```rust
/// use ndarray::{Array, arr1};
///
/// let array = Array::geomspace(1e0, 1e3, 4);
/// assert!(array.all_close(&arr1(&[1e0, 1e1, 1e2, 1e3]), 1e-5));
///
/// let array = Array::geomspace(-1e3, -1e0, 4);
/// assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));
/// ```
pub fn geomspace(start: A, end: A, n: usize) -> Self
where
A: Float,
{
Self::from_vec(to_vec(geomspace::geomspace(start, end, n)))
}
}

/// ## Constructor methods for two-dimensional arrays.
Expand Down
30 changes: 15 additions & 15 deletions src/iterators/mod.rs
Expand Up @@ -1214,25 +1214,25 @@ send_sync_read_write!(ElementsBaseMut);

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

use std;
use crate::linspace::Linspace;
use crate::iter::IndicesIter;
use crate::indexes::IndicesIterF;
use crate::iter::IndicesIter;
use crate::{geomspace::Geomspace, linspace::Linspace, logspace::Logspace};
use std;

unsafe impl<F> TrustedIterator for Linspace<F> { }
unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> { }
unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> { }
unsafe impl<I, F> TrustedIterator for std::iter::Map<I, F>
where I: TrustedIterator { }
unsafe impl<'a, A> TrustedIterator for slice::Iter<'a, A> { }
unsafe impl<'a, A> TrustedIterator for slice::IterMut<'a, A> { }
unsafe impl TrustedIterator for ::std::ops::Range<usize> { }
unsafe impl<F> TrustedIterator for Geomspace<F> {}
unsafe impl<F> TrustedIterator for Linspace<F> {}
unsafe impl<F> TrustedIterator for Logspace<F> {}
unsafe impl<'a, A, D> TrustedIterator for Iter<'a, A, D> {}
unsafe impl<'a, A, D> TrustedIterator for IterMut<'a, A, D> {}
unsafe impl<I, F> TrustedIterator for std::iter::Map<I, F> where I: TrustedIterator {}
unsafe impl<'a, A> TrustedIterator for slice::Iter<'a, A> {}
unsafe impl<'a, A> TrustedIterator for slice::IterMut<'a, A> {}
unsafe impl TrustedIterator for ::std::ops::Range<usize> {}
// FIXME: These indices iter are dubious -- size needs to be checked up front.
unsafe impl<D> TrustedIterator for IndicesIter<D> where D: Dimension { }
unsafe impl<D> TrustedIterator for IndicesIterF<D> where D: Dimension { }

unsafe impl<D> TrustedIterator for IndicesIter<D> where D: Dimension {}
unsafe impl<D> TrustedIterator for IndicesIterF<D> where D: Dimension {}

/// Like Iterator::collect, but only for trusted length iterators
pub fn to_vec<I>(iter: I) -> Vec<I::Item>
Expand Down
9 changes: 6 additions & 3 deletions src/lib.rs
Expand Up @@ -167,15 +167,18 @@ mod free_functions;
pub use crate::free_functions::*;
pub use crate::iterators::iter;

#[macro_use] mod slice;
mod layout;
mod error;
mod geomspace;
mod indexes;
mod iterators;
mod layout;
mod linalg_traits;
mod linspace;
mod logspace;
mod numeric_util;
mod error;
mod shape_builder;
#[macro_use]
mod slice;
mod stacking;
#[macro_use]
mod zip;
Expand Down