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 2 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
34 changes: 29 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, logspace};

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

/// Create a one-dimensional array from the inclusive interval `[start,
/// end]` with `n` elements logarithmically spaced. `A` must be a floating
/// point type.
///
/// **Panics** if `n` is greater than `isize::MAX`.
///
/// ```rust
/// use ndarray::{Array, arr1};
///
/// let array = Array::logspace(1e0, 1e3, 4);
/// println!("{}", array);
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
/// assert!(array.all_close(&arr1(&[1e0, 1e1, 1e2, 1e3]), 1e-5));
///
/// let array = Array::logspace(-1e3, -1e0, 4);
/// println!("{}", array);
/// assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));
/// ```
pub fn logspace(start: A, end: A, n: usize) -> Self
where
A: Float,
{
Self::from_vec(to_vec(logspace::logspace(start, end, n)))
}
}

/// ## Constructor methods for two-dimensional arrays.
Expand Down
29 changes: 14 additions & 15 deletions src/iterators/mod.rs
Expand Up @@ -1214,25 +1214,24 @@ 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::{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 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
8 changes: 5 additions & 3 deletions src/lib.rs
Expand Up @@ -167,14 +167,16 @@ mod free_functions;
pub use crate::free_functions::*;
pub use crate::iterators::iter;

#[macro_use] mod slice;
mod layout;
#[macro_use]
mod slice;
mod error;
mod indexes;
mod iterators;
mod layout;
mod linalg_traits;
mod linspace;
mod logspace;
mod numeric_util;
mod error;
mod shape_builder;
mod stacking;
#[macro_use]
Expand Down
168 changes: 168 additions & 0 deletions src/logspace.rs
@@ -0,0 +1,168 @@
// 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 logarithmically evenly spaced floats.
///
/// Iterator element type is `F`.
pub struct Logspace<F> {
current: F,
last: F,
step: F,
index: usize,
len: usize,
}

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

#[inline]
fn next(&mut self) -> Option<F> {
if self.index >= self.len {
None
} else {
self.index += 1;

let v = self.current;
self.current = self.current * self.step;
Some(v)
}
}

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

impl<F> DoubleEndedIterator for Logspace<F>
where
F: Float,
{
#[inline]
fn next_back(&mut self) -> Option<F> {
if self.index >= self.len {
None
} else {
self.len -= 1;

let v = self.last;
self.last = self.last / self.step;
Some(v)
}
}
}

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

/// Return an iterator of logarithmically evenly spaced floats.
///
/// The `Logspace` has `n` elements, where the first element is `a` and the last
/// element is `b`.
///
/// The sign of `a` and `b` must be the same so that the interval does not
/// include 0.
///
/// Iterator element type is `F`, where `F` must be either `f32` or `f64`.
#[inline]
pub fn logspace<F>(a: F, b: F, n: usize) -> Logspace<F>
where
F: Float,
{
assert!(
a != F::zero() && b != F::zero(),
"Start and/or end of logspace 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::from(n).unwrap();
((log_b - log_a) / (nf - F::one())).exp()
} else {
F::one()
};
Logspace {
current: a,
last: b,
step: step,
index: 0,
len: n,
}
}

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

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

let array: Array1<_> = logspace(-1e3, -1e0, 4).collect();
assert!(array.all_close(&arr1(&[-1e3, -1e2, -1e1, -1e0]), 1e-5));
LukeMathWalker marked this conversation as resolved.
Show resolved Hide resolved
}

#[test]
fn iter_forward() {
let mut iter = logspace(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 = logspace(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() {
logspace(0.0, 1.0, 4);
}

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

#[test]
#[should_panic]
fn zero_included() {
logspace(-1.0, 1.0, 4);
}
}