diff --git a/examples/convolution.rs b/examples/convolution.rs deleted file mode 100644 index d23ed389..00000000 --- a/examples/convolution.rs +++ /dev/null @@ -1,17 +0,0 @@ -extern crate nalgebra; -use nalgebra::{Vector2,Vector3,Vector4,Vector5,convolve_full,convolve_same,convolve_valid}; - -fn main(){ - let vec = Vector4::new(1.0,2.0,3.0,4.0); - let ker = Vector3::new(1.0,2.0,2.1); - - let actual = Vector5::from_vec(vec![1.0,4.0,7.0,10.0,8.0]); - - let expected = convolve_full(vec,ker); - let expected2 = convolve_same(vec,ker); - // let expected3 = convolve_valid(vec,ker); - println!("{}", actual); - println!("{}", expected); - println!("{}", expected2); - // println!("{}", expected3); -} \ No newline at end of file diff --git a/src/linalg/convolution.rs b/src/linalg/convolution.rs index 69ae3f5e..49c8b154 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -1,20 +1,19 @@ use base::allocator::Allocator; use base::default_allocator::DefaultAllocator; -use base::dimension::{DimAdd, DimDiff, DimMax, DimMaximum, DimName, DimSub, DimSum,Dim}; +use base::dimension::{Dim, DimAdd, DimDiff, DimMax, DimMaximum, DimSub, DimSum}; use std::cmp; use storage::Storage; use {zero, Real, Vector, VectorN, U1}; -/// Returns the convolution of the vector and a kernel +/// Returns the convolution of the target vector and a kernel /// /// # Arguments /// /// * `vector` - A Vector with size > 0 /// * `kernel` - A Vector with size > 0 /// -/// This function is commutative. If kernel > vector, -/// they will swap their roles as in -/// (self, kernel) = (kernel,self) +/// # Errors +/// Inputs must statisfy `vector.len() >= kernel.len() > 0`. /// pub fn convolve_full( vector: Vector, @@ -27,18 +26,13 @@ where DimSum: DimSub, S1: Storage, S2: Storage, - DimSum: Dim, DefaultAllocator: Allocator, U1>>, { let vec = vector.len(); let ker = kernel.len(); - if vec == 0 || ker == 0 { - panic!("Convolve's inputs must not be 0-sized. "); - } - - if ker > vec { - return convolve_full(kernel, vector); + if ker == 0 || ker > vec { + panic!("convolve_full expects `vector.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); } let result_len = vector.data.shape().0.add(kernel.data.shape().0).sub(U1); @@ -61,45 +55,38 @@ where conv } - - /// Returns the convolution of the vector and a kernel /// The output convolution consists only of those elements that do not rely on the zero-padding. /// # Arguments /// /// * `vector` - A Vector with size > 0 /// * `kernel` - A Vector with size > 0 -/// -/// This function is commutative. If kernel > vector, -/// they will swap their roles as in -/// (self, kernel) = (kernel,self) +/// +/// +/// # Errors +/// Inputs must statisfy `vector.len() >= kernel.len() > 0`. /// pub fn convolve_valid( vector: Vector, kernel: Vector, -) -> VectorN, U1>> +) -> VectorN, D2>> where N: Real, - D1: DimSub, - D2: DimSub>, - DimDiff: DimAdd, + D1: DimAdd, + D2: Dim, + DimSum: DimSub, S1: Storage, S2: Storage, - DimDiff: DimName, - DefaultAllocator: Allocator, U1>> + DefaultAllocator: Allocator, D2>>, { - let vec = vector.len(); let ker = kernel.len(); - if vec == 0 || ker == 0 { - panic!("Convolve's inputs must not be 0-sized. "); + if ker == 0 || ker > vec { + panic!("convolve_valid expects `vector.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); } - if ker > vec { - return convolve_valid(kernel, vector); - } - let result_len = vector.data.shape().0.sub(kernel.data.shape().0).add(U1); + let result_len = vector.data.shape().0.add(U1).sub(kernel.data.shape().0); let mut conv = VectorN::zeros_generic(result_len, U1); for i in 0..(vec - ker + 1) { @@ -117,10 +104,8 @@ where /// * `vector` - A Vector with size > 0 /// * `kernel` - A Vector with size > 0 /// -/// This function is commutative. If kernel > vector, -/// they will swap their roles as in -/// (self, kernel) = (kernel,self) -/// +/// # Errors +/// Inputs must statisfy `vector.len() >= kernel.len() > 0`. pub fn convolve_same( vector: Vector, kernel: Vector, @@ -131,18 +116,13 @@ where D2: DimMax>, S1: Storage, S2: Storage, - DimMaximum: Dim, DefaultAllocator: Allocator>, { let vec = vector.len(); let ker = kernel.len(); - if vec == 0 || ker == 0 { - panic!("Convolve's inputs must not be 0-sized. "); - } - - if ker > vec { - return convolve_same(kernel, vector); + if ker == 0 || ker > vec { + panic!("convolve_same expects `vector.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); } let result_len = vector.data.shape().0.max(kernel.data.shape().0); diff --git a/tests/linalg/convolution.rs b/tests/linalg/convolution.rs index 454c29c6..ddcfe9f6 100644 --- a/tests/linalg/convolution.rs +++ b/tests/linalg/convolution.rs @@ -1,7 +1,6 @@ -#[allow(unused_imports)] // remove after fixing unit test use na::linalg::{convolve_full,convolve_valid,convolve_same}; -#[allow(unused_imports)] use na::{Vector2,Vector3,Vector4,Vector5,DVector}; +use std::panic; // // Should mimic calculations in Python's scipy library @@ -12,70 +11,110 @@ use na::{Vector2,Vector3,Vector4,Vector5,DVector}; // array([ 1, 4, 7, 10]) #[test] fn convolve_same_check(){ - let vec_s = Vector4::new(1.0,2.0,3.0,4.0); - let ker_s = Vector2::new(1.0,2.0); - + // Static Tests let actual_s = Vector4::from_vec(vec![1.0,4.0,7.0,10.0]); - - let expected_s = convolve_same(vec_s,ker_s); - let expected_s_r = convolve_same(ker_s,vec_s); + let expected_s = convolve_same(Vector4::new(1.0,2.0,3.0,4.0), Vector2::new(1.0,2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); - assert!(relative_eq!(actual_s, expected_s_r, epsilon = 1.0e-7)); - - let vec_d = DVector::from_vec(4,vec![1.0,2.0,3.0,4.0]); - let ker_d = DVector::from_vec(2,vec![1.0,2.0]); - let actual_d = DVector::from_vec(4,vec![1.0,4.0,7.0,10.0]); - - let expected_d = convolve_same(vec_d.clone(),ker_d.clone()); - let expected_d_r = convolve_same(ker_d,vec_d); + // Dynamic Tests + let actual_d = DVector::from_vec(vec![1.0,4.0,7.0,10.0]); + let expected_d = convolve_same(DVector::from_vec(vec![1.0,2.0,3.0,4.0]),DVector::from_vec(vec![1.0,2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); - assert!(relative_eq!(actual_d, expected_d_r, epsilon = 1.0e-7)); + + // Panic Tests + // These really only apply to dynamic sized vectors + assert!( + panic::catch_unwind(|| { + convolve_same(DVector::from_vec(vec![1.0,2.0]), DVector::from_vec(vec![1.0,2.0,3.0,4.0])); + }).is_err() + ); + + assert!( + panic::catch_unwind(|| { + convolve_same(DVector::::from_vec(vec![]), DVector::from_vec(vec![1.0,2.0,3.0,4.0])); + }).is_err() + ); + + assert!( + panic::catch_unwind(|| { + convolve_same(DVector::from_vec(vec![1.0,2.0,3.0,4.0]),DVector::::from_vec(vec![])); + }).is_err() + ); } // >>> convolve([1,2,3,4],[1,2],"full") // array([ 1, 4, 7, 10, 8]) #[test] fn convolve_full_check(){ - let vec_s = Vector4::new(1.0,2.0,3.0,4.0); - let ker_s = Vector2::new(1.0,2.0); - + // Static Tests let actual_s = Vector5::new(1.0,4.0,7.0,10.0,8.0); - - let expected_s = convolve_full(vec_s,ker_s); - let expected_s_r = convolve_full(ker_s,vec_s); + let expected_s = convolve_full(Vector4::new(1.0,2.0,3.0,4.0), Vector2::new(1.0,2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); - assert!(relative_eq!(actual_s, expected_s_r, epsilon = 1.0e-7)); - - let vec_d = DVector::from_vec(4,vec![1.0,2.0,3.0,4.0]); - let ker_d = DVector::from_vec(2,vec![1.0,2.0]); - let actual_d = DVector::from_vec(5,vec![1.0,4.0,7.0,10.0,8.0]); - - let expected_d = convolve_full(vec_d.clone(),ker_d.clone()); - let expected_d_r = convolve_full(ker_d,vec_d); + // Dynamic Tests + let actual_d = DVector::from_vec(vec![1.0,4.0,7.0,10.0,8.0]); + let expected_d = convolve_full(DVector::from_vec(vec![1.0,2.0,3.0,4.0]), DVector::from_vec(vec![1.0,2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); - assert!(relative_eq!(actual_d, expected_d_r, epsilon = 1.0e-7)); + + // Panic Tests + // These really only apply to dynamic sized vectors + assert!( + panic::catch_unwind(|| { + convolve_full(DVector::from_vec(vec![1.0,2.0]), DVector::from_vec(vec![1.0,2.0,3.0,4.0])); + }).is_err() + ); + + assert!( + panic::catch_unwind(|| { + convolve_full(DVector::::from_vec(vec![]), DVector::from_vec(vec![1.0,2.0,3.0,4.0])); + }).is_err() + ); + + assert!( + panic::catch_unwind(|| { + convolve_full(DVector::from_vec(vec![1.0,2.0,3.0,4.0]),DVector::::from_vec(vec![])); + }).is_err() + ); } // >>> convolve([1,2,3,4],[1,2],"valid") // array([ 4, 7, 10]) -// #[test] -// fn convolve_valid_check(){ -// let vec = Vector4::new(1.0,2.0,3.0,4.0); -// let ker = Vector2::new(1.0,2.0); +#[test] +fn convolve_valid_check(){ + // Static Tests + let actual_s = Vector3::from_vec(vec![4.0,7.0,10.0]); + let expected_s = convolve_valid( Vector4::new(1.0,2.0,3.0,4.0), Vector2::new(1.0,2.0)); -// let actual = Vector3::from_vec(vec![4.0,7.0,10.0]); + assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); -// let expected1 = convolve_valid(vec, ker); -// let expected2 = convolve_valid(ker, vec); + // Dynamic Tests + let actual_d = DVector::from_vec(vec![4.0,7.0,10.0]); + let expected_d = convolve_valid(DVector::from_vec(vec![1.0,2.0,3.0,4.0]), DVector::from_vec(vec![1.0,2.0])); + assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); -// assert!(relative_eq!(actual, expected1, epsilon = 1.0e-7)); -// assert!(relative_eq!(actual, expected2, epsilon = 1.0e-7)); + // Panic Tests + // These really only apply to dynamic sized vectors + assert!( + panic::catch_unwind(|| { + convolve_valid(DVector::from_vec(vec![1.0,2.0]), DVector::from_vec(vec![1.0,2.0,3.0,4.0])); + }).is_err() + ); -// } \ No newline at end of file + assert!( + panic::catch_unwind(|| { + convolve_valid(DVector::::from_vec(vec![]), DVector::from_vec(vec![1.0,2.0,3.0,4.0])); + }).is_err() + ); + + assert!( + panic::catch_unwind(|| { + convolve_valid(DVector::from_vec(vec![1.0,2.0,3.0,4.0]),DVector::::from_vec(vec![])); + }).is_err() + ); + +} \ No newline at end of file