From 9aba098f00d7cb44ce831ed6cc8d3a2c42c257cb Mon Sep 17 00:00:00 2001 From: sebasv Date: Wed, 24 Oct 2018 14:50:32 +0200 Subject: [PATCH 1/3] implement partialord min/max --- src/numeric/impl_numeric.rs | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 14485fc79..1eb25a23f 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -46,6 +46,84 @@ impl ArrayBase sum } + /// Return a reference to a maximum of all values. + /// Return None if a comparison fails or if self is empty. + /// + /// # Example + /// ``` + /// use ndarray::arr2; + /// use std::f64; + /// + /// let a = arr2(&[[1., 2.], [3., 4.]]); + /// + /// assert_eq!(a.scalar_max(), Some(&4.)); + /// + /// let b = arr2(&[[1., f64::NAN], [3., 4.]]); + /// + /// assert_eq!(b.scalar_max(), None); + /// ``` + pub fn scalar_max(&self) -> Option<&A> + where A: PartialOrd + { + if let Some(slc) = self.as_slice_memory_order() { + let mut max = self.first(); + for item in slc.iter().skip(1) { + match max.partial_cmp(&Some(item)) { + None => return None, + Some(::std::cmp::Ordering::Less) => max = Some(item), + _ => {}, + } + } + max + } else { + Zip::from(self).fold_while(self.first(), |acc, x| + match acc.partial_cmp(&Some(x)) { + None => FoldWhile::Done(None), + Some(::std::cmp::Ordering::Less) => FoldWhile::Continue(Some(x)), + _ => FoldWhile::Continue(acc), + }).into_inner() + } + } + + /// Return a reference to a minimum of all values. + /// Return None if a comparison fails or if self is empty. + /// + /// # Example + /// ``` + /// use ndarray::arr2; + /// use std::f64; + /// + /// let a = arr2(&[[1., 2.], [3., 4.]]); + /// + /// assert_eq!(a.scalar_min(), Some(&1.)); + /// + /// let b = arr2(&[[1., f64::NAN], [3., 4.]]); + /// + /// assert_eq!(b.scalar_min(), None); + /// ``` + pub fn scalar_min(&self) -> Option<&A> + where A: PartialOrd + { + if let Some(slc) = self.as_slice_memory_order() { + let mut min = self.first(); + for item in slc.iter().skip(1) { + match min.partial_cmp(&Some(item)) { + None => return None, + Some(::std::cmp::Ordering::Greater) => min = Some(item), + _ => {}, + } + } + min + } else { + Zip::from(self).fold_while(self.first(), |acc, x| + match acc.partial_cmp(&Some(x)) { + None => FoldWhile::Done(None), + Some(::std::cmp::Ordering::Greater) => FoldWhile::Continue(Some(x)), + _ => FoldWhile::Continue(acc), + }).into_inner() + } + } + /// Return sum along `axis`. /// /// ``` From e869453020e67b5f4c6ff58b2ff31e9cf6c75100 Mon Sep 17 00:00:00 2001 From: sebasv Date: Sun, 28 Oct 2018 07:27:40 +0100 Subject: [PATCH 2/3] started on nanmax --- src/numeric/impl_numeric.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 1eb25a23f..a6c88cc9b 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -85,6 +85,34 @@ impl ArrayBase } } + pub fn nanmax(&self) -> Option<&A> + where A: PartialOrd, + { + if self.len() == 0 { + None + } + else if let Some(slc) = self.as_slice_memory_order() { + let f = |acc, x| if acc == acc && x < acc {acc} else {x}; + // numeric_util::unrolled_fold(slc, ) + let mut max = self.first(); + for item in slc.iter().skip(1) { + match max.partial_cmp(&Some(item)) { + None => return None, + Some(::std::cmp::Ordering::Less) => max = Some(item), + _ => {}, + } + } + max + } else { + Zip::from(self).fold_while(self.first(), |acc, x| + match acc.partial_cmp(&Some(x)) { + None => FoldWhile::Done(None), + Some(::std::cmp::Ordering::Less) => FoldWhile::Continue(Some(x)), + _ => FoldWhile::Continue(acc), + }).into_inner() + } + } + /// Return a reference to a minimum of all values. /// Return None if a comparison fails or if self is empty. /// From f975643d600e6fff8b773d7cfe916a1125cd28c5 Mon Sep 17 00:00:00 2001 From: sebasv Date: Sun, 28 Oct 2018 10:11:32 +0100 Subject: [PATCH 3/3] implement nan(min/max) and let loops auto unroll --- benches/numeric.rs | 50 ++++++++++++ src/numeric/impl_numeric.rs | 157 ++++++++++++++++++++++-------------- 2 files changed, 147 insertions(+), 60 deletions(-) diff --git a/benches/numeric.rs b/benches/numeric.rs index db0252c8a..493e79958 100644 --- a/benches/numeric.rs +++ b/benches/numeric.rs @@ -6,6 +6,7 @@ use test::Bencher; extern crate ndarray; use ndarray::prelude::*; +use ndarray::{Zip, FoldWhile}; const N: usize = 1024; const X: usize = 64; @@ -25,3 +26,52 @@ fn clip(bench: &mut Bencher) }) }); } + +#[bench] +fn max_early_return(bench: &mut Bencher) +{ + fn max(arr: &Array2) -> Option<&f64> + { + if let Some(mut max) = arr.first() { + if let Some(slc) = arr.as_slice_memory_order() { + for item in slc.iter().skip(1) { + match max.partial_cmp(item) { + None => return None, + Some(::std::cmp::Ordering::Less) => max = item, + _ => {}, + } + } + Some(max) + } else { + Zip::from(arr).fold_while(Some(max), |acc, x| + match acc.partial_cmp(&Some(x)) { + None => FoldWhile::Done(None), + Some(::std::cmp::Ordering::Less) => FoldWhile::Continue(Some(x)), + _ => FoldWhile::Continue(acc), + }).into_inner() + } + } else { + None + } + } + let mut a = Array::linspace(0., 127., N * 2).into_shape([X, Y * 2]).unwrap(); + bench.iter(|| max(&a)); +} + +#[bench] +fn max_short(bench: &mut Bencher) { + fn max(arr: &Array2) -> Option<&f64> { + if let Some(first) = arr.first() { + let max = arr.fold(first, |acc, x| if x>acc {x} else {acc}); + if max == max { + Some(max) + } else { + None + } + } else { + None + } + } + let mut a = Array::linspace(0., 127., N * 2).into_shape([X, Y * 2]).unwrap(); + bench.iter(|| max(&a)); +} \ No newline at end of file diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 47a6ee62d..43ee6010c 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -77,65 +77,70 @@ impl ArrayBase /// /// # Example /// ``` - /// use ndarray::arr2; + /// use ndarray::{arr2, Array2}; /// use std::f64; /// /// let a = arr2(&[[1., 2.], [3., 4.]]); - /// - /// assert_eq!(a.scalar_max(), Some(&4.)); + /// assert_eq!(a.max(), Some(&4.)); /// /// let b = arr2(&[[1., f64::NAN], [3., 4.]]); + /// assert_eq!(b.max(), None); /// - /// assert_eq!(b.scalar_max(), None); + /// let c = arr2(&[[f64::NAN]]); + /// assert_eq!(c.max(), None); + /// + /// let d: Array2 = arr2(&[[]]); + /// assert_eq!(d.max(), None); /// ``` - pub fn scalar_max(&self) -> Option<&A> + pub fn max(&self) -> Option<&A> where A: PartialOrd { - if let Some(slc) = self.as_slice_memory_order() { - let mut max = self.first(); - for item in slc.iter().skip(1) { - match max.partial_cmp(&Some(item)) { - None => return None, - Some(::std::cmp::Ordering::Less) => max = Some(item), - _ => {}, - } + if let Some(first) = self.first() { + let max = self.fold(first, |acc, x| if acc == acc && !(x < acc) {x} else {acc}); + if max == max { + Some(max) + } else { + None } - max } else { - Zip::from(self).fold_while(self.first(), |acc, x| - match acc.partial_cmp(&Some(x)) { - None => FoldWhile::Done(None), - Some(::std::cmp::Ordering::Less) => FoldWhile::Continue(Some(x)), - _ => FoldWhile::Continue(acc), - }).into_inner() + None } + } + /// Return a reference to a maximum of all values, ignoring + /// incomparable elements. Returns None if `self` is empty, + /// or contains only incomparable elements. + /// + /// # Example + /// ``` + /// use ndarray::{arr2, Array2}; + /// use std::f64; + /// + /// let a = arr2(&[[1., 2.], [3., 4.]]); + /// assert_eq!(a.nanmax(), Some(&4.)); + /// + /// let b = arr2(&[[1., f64::NAN], [3., f64::NAN]]); + /// assert_eq!(b.nanmax(), Some(&3.)); + /// + /// let c: Array2 = arr2(&[[]]); + /// assert_eq!(c.nanmax(), None); + /// + /// let d = arr2(&[[f64::NAN, f64::NAN],[f64::NAN, f64::NAN]]); + /// assert_eq!(d.nanmax(), None); + /// ``` pub fn nanmax(&self) -> Option<&A> where A: PartialOrd, { - if self.len() == 0 { - None - } - else if let Some(slc) = self.as_slice_memory_order() { - let f = |acc, x| if acc == acc && x < acc {acc} else {x}; - // numeric_util::unrolled_fold(slc, ) - let mut max = self.first(); - for item in slc.iter().skip(1) { - match max.partial_cmp(&Some(item)) { - None => return None, - Some(::std::cmp::Ordering::Less) => max = Some(item), - _ => {}, - } + if let Some(first) = self.first() { + let max = self.fold(first, |acc, x| if acc == acc && !(x > acc) {acc} else {x}); + if max == max { + Some(max) + } else { + None } - max } else { - Zip::from(self).fold_while(self.first(), |acc, x| - match acc.partial_cmp(&Some(x)) { - None => FoldWhile::Done(None), - Some(::std::cmp::Ordering::Less) => FoldWhile::Continue(Some(x)), - _ => FoldWhile::Continue(acc), - }).into_inner() + None } } @@ -144,40 +149,72 @@ impl ArrayBase /// /// # Example /// ``` - /// use ndarray::arr2; + /// use ndarray::{arr2, Array2}; /// use std::f64; /// /// let a = arr2(&[[1., 2.], [3., 4.]]); - /// - /// assert_eq!(a.scalar_min(), Some(&1.)); + /// assert_eq!(a.min(), Some(&1.)); /// /// let b = arr2(&[[1., f64::NAN], [3., 4.]]); + /// assert_eq!(b.min(), None); /// - /// assert_eq!(b.scalar_min(), None); + /// let c = arr2(&[[f64::NAN]]); + /// assert_eq!(c.min(), None); + /// + /// let d: Array2 = arr2(&[[]]); + /// assert_eq!(d.min(), None); /// ``` - pub fn scalar_min(&self) -> Option<&A> + pub fn min(&self) -> Option<&A> where A: PartialOrd { - if let Some(slc) = self.as_slice_memory_order() { - let mut min = self.first(); - for item in slc.iter().skip(1) { - match min.partial_cmp(&Some(item)) { - None => return None, - Some(::std::cmp::Ordering::Greater) => min = Some(item), - _ => {}, - } + if let Some(first) = self.first() { + let min = self.fold(first, |acc, x| if acc == acc && !(acc < x) {x} else {acc}); + if min == min { + Some(min) + } else { + None } - min } else { - Zip::from(self).fold_while(self.first(), |acc, x| - match acc.partial_cmp(&Some(x)) { - None => FoldWhile::Done(None), - Some(::std::cmp::Ordering::Greater) => FoldWhile::Continue(Some(x)), - _ => FoldWhile::Continue(acc), - }).into_inner() + None } } + /// Return a reference to a minimum of all values, ignoring + /// incomparable elements. Returns None if `self` is empty, + /// or contains only incomparable elements. + /// + /// # Example + /// ``` + /// use ndarray::{arr2, Array2}; + /// use std::f64; + /// + /// let a = arr2(&[[1., 2.], [3., 4.]]); + /// assert_eq!(a.nanmin(), Some(&1.)); + /// + /// let b = arr2(&[[f64::NAN, 2.], [3., f64::NAN]]); + /// assert_eq!(b.nanmin(), Some(&2.)); + /// + /// let c: Array2 = arr2(&[[]]); + /// assert_eq!(c.nanmin(), None); + /// + /// let d = arr2(&[[f64::NAN, f64::NAN],[f64::NAN, f64::NAN]]); + /// assert_eq!(d.nanmin(), None); + /// ``` + pub fn nanmin(&self) -> Option<&A> + where A: PartialOrd, + { + if let Some(first) = self.first() { + let min = self.fold(first, |acc, x| if acc == acc && !(x < acc) {acc} else {x}); + if min == min { + Some(min) + } else { + None + } + } else { + None + } + } + /// Return sum along `axis`. /// /// ```