From 4f9a3cf1e64f9bc7f4067309a6a178e6a3c5715f Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 6 Feb 2019 21:15:33 -0600 Subject: [PATCH 01/12] basic algorithm defined --- examples/convolution.rs | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 examples/convolution.rs diff --git a/examples/convolution.rs b/examples/convolution.rs new file mode 100644 index 00000000..f2ab42cc --- /dev/null +++ b/examples/convolution.rs @@ -0,0 +1,62 @@ +extern crate nalgebra as na; +#[allow(unused_imports)] +use na::{Vector,Dim,Real,Vector4,Vector3,Vector2,U1,Matrix,DVector,Dynamic,VecStorage}; +use na::storage::{Storage}; +use std::cmp; + + + +// evum CovvolveMode{ +// Full, +// Valid, +// Same +// } + + + +#[allow(non_snake_case)] +fn Convolve1D, Q: Storage>( + Vector : Vector, + Kernel : Vector + ) -> Matrix> + { + // + // Vector is the vector, Kervel is the kervel + // C is the returv vector + // + if Kernel.len() > Vector.len(){ + return Convolve1D(Kernel, Vector); + } + + let V = Vector.len(); + let K = Kernel.len(); + let L = V + K - 1; + let v = V as i8; + let k = K as i8; + let l = L as i8; + let mut C = DVector::::zeros(L); + + for i in 0..l{ + let u_i = cmp::max(0, i - k); + let u_f = cmp::min(i, v - 1); + if u_i == u_f{ + C[i as usize] += Vector[u_i as usize] * Kernel[(i - u_i) as usize]; + } + else{ + for u in u_i..(u_f+1){ + if i - u < k{ + C[i as usize] += Vector[u as usize] * Kernel[(i - u ) as usize]; + } + } + } + } + C + } + + +fn main() { + let v1 = Vector2::new(3.0,3.0); + let v2 = Vector4::new(1.0,2.0,5.0,9.0); + let x = Convolve1D(v1,v2); + println!("{:?}",x) +} \ No newline at end of file From fd0c497c90d946085aa53388f8d4f16ba0552119 Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 7 Feb 2019 19:58:09 -0600 Subject: [PATCH 02/12] Added valid method --- examples/convolution.rs | 123 ++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 30 deletions(-) diff --git a/examples/convolution.rs b/examples/convolution.rs index f2ab42cc..4619d8db 100644 --- a/examples/convolution.rs +++ b/examples/convolution.rs @@ -6,57 +6,120 @@ use std::cmp; -// evum CovvolveMode{ -// Full, -// Valid, -// Same -// } +enum ConvolveMode{ + Full, + Valid, + Same +} - - -#[allow(non_snake_case)] -fn Convolve1D, Q: Storage>( - Vector : Vector, - Kernel : Vector +fn convolve_full, Q: Storage>( + vector : Vector, + kernel : Vector ) -> Matrix> { - // - // Vector is the vector, Kervel is the kervel - // C is the returv vector - // - if Kernel.len() > Vector.len(){ - return Convolve1D(Kernel, Vector); - } - let V = Vector.len(); - let K = Kernel.len(); - let L = V + K - 1; - let v = V as i8; - let k = K as i8; - let l = L as i8; - let mut C = DVector::::zeros(L); + let vec = vector.len(); + let ker = kernel.len(); + let len = vec + ker - 1; + let v = vec as i8; + let k = ker as i8; + let l = len as i8; + let mut conv = DVector::::zeros(len); for i in 0..l{ let u_i = cmp::max(0, i - k); let u_f = cmp::min(i, v - 1); + if u_i == u_f{ - C[i as usize] += Vector[u_i as usize] * Kernel[(i - u_i) as usize]; + conv[i as usize] += vector[u_i as usize] * kernel[(i - u_i) as usize]; } else{ for u in u_i..(u_f+1){ if i - u < k{ - C[i as usize] += Vector[u as usize] * Kernel[(i - u ) as usize]; + conv[i as usize] += vector[u as usize] * kernel[(i - u ) as usize]; } } } } - C + conv + } + +fn convolve_valid, Q: Storage>( + vector : Vector, + kernel : Vector + ) -> Matrix> + { + let vec = vector.len(); + let ker = kernel.len(); + let len = vec - ker + 1; + let mut conv = DVector::::zeros(len); + + for i in 0..len { + for j in 0..ker { + conv[i] += vector[i + j] * kernel[ker - j - 1]; + } + } + + conv + } + +fn convolve_same, Q: Storage>( + vector : Vector, + kernel : Vector + ) -> Matrix> + { + + let vec = vector.len(); + let ker = kernel.len(); + let len = vec + ker - 1; + let v = vec as i8; + let k = ker as i8; + let l = len as i8; + let mut conv = DVector::::zeros(len); + + for i in 0..l { + let u_i = cmp::max(0, i - k); + let u_f = cmp::min(i, v - 1); + + if u_i == u_f { + conv[i as usize] += vector[u_i as usize] * kernel[(i - u_i) as usize]; + } + else{ + for u in u_i..(u_f+1){ + if i - u < k{ + conv[i as usize] += vector[u as usize] * kernel[(i - u ) as usize]; + } + } + } + } + conv + } + +fn convolve, Q: Storage>( + vector : Vector, + kernel : Vector, + mode : Option + ) -> Matrix> + { + // + // vector is the vector, Kervel is the kervel + // C is the returv vector + // + if kernel.len() > vector.len(){ + return convolve(kernel, vector, mode); + } + + match mode.unwrap_or(ConvolveMode::Full) { + ConvolveMode::Full => return convolve_full(vector,kernel), + ConvolveMode::Valid => return convolve_valid(vector,kernel), + ConvolveMode::Same => return convolve_same(vector,kernel) + } } fn main() { - let v1 = Vector2::new(3.0,3.0); + let v1 = Vector2::new(3.0,1.0); let v2 = Vector4::new(1.0,2.0,5.0,9.0); - let x = Convolve1D(v1,v2); + let x = convolve(v1,v2,Some(ConvolveMode::Valid)); println!("{:?}",x) } \ No newline at end of file From 3d83860fad91fdd21ad79aad08fd1bfd863d1324 Mon Sep 17 00:00:00 2001 From: Nathan Date: Sat, 9 Feb 2019 20:19:42 -0600 Subject: [PATCH 03/12] Cleaned up some labels and steps --- examples/convolution.rs | 183 +++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 88 deletions(-) diff --git a/examples/convolution.rs b/examples/convolution.rs index 4619d8db..c3c61eca 100644 --- a/examples/convolution.rs +++ b/examples/convolution.rs @@ -1,125 +1,132 @@ extern crate nalgebra as na; +use na::storage::Storage; #[allow(unused_imports)] -use na::{Vector,Dim,Real,Vector4,Vector3,Vector2,U1,Matrix,DVector,Dynamic,VecStorage}; -use na::storage::{Storage}; +use na::{ + DMatrix, DVector, Dim, Dynamic, Matrix, Matrix2x3, Real, VecStorage, Vector, Vector2, Vector3, + Vector4, U1, +}; use std::cmp; - - -enum ConvolveMode{ +enum ConvolveMode { Full, Valid, - Same + Same, } fn convolve_full, Q: Storage>( - vector : Vector, - kernel : Vector - ) -> Matrix> - { + vector: Vector, + kernel: Vector, +) -> Matrix> { + let vec = vector.len(); + let ker = kernel.len(); + let newlen = vec + ker - 1; - let vec = vector.len(); - let ker = kernel.len(); - let len = vec + ker - 1; - let v = vec as i8; - let k = ker as i8; - let l = len as i8; - let mut conv = DVector::::zeros(len); + let mut conv = DVector::::zeros(newlen); - for i in 0..l{ - let u_i = cmp::max(0, i - k); - let u_f = cmp::min(i, v - 1); + for i in 0..newlen { + let u_i = if i > ker {i - ker} else {0}; + let u_f = cmp::min(i, vec - 1); - if u_i == u_f{ - conv[i as usize] += vector[u_i as usize] * kernel[(i - u_i) as usize]; - } - else{ - for u in u_i..(u_f+1){ - if i - u < k{ - conv[i as usize] += vector[u as usize] * kernel[(i - u ) as usize]; - } + if u_i == u_f { + conv[i] += vector[u_i] * kernel[(i - u_i)]; + } else { + for u in u_i..(u_f + 1) { + if i - u < ker { + conv[i] += vector[u] * kernel[(i - u)]; } } } - conv } + conv +} fn convolve_valid, Q: Storage>( - vector : Vector, - kernel : Vector - ) -> Matrix> - { - let vec = vector.len(); - let ker = kernel.len(); - let len = vec - ker + 1; - let mut conv = DVector::::zeros(len); + vector: Vector, + kernel: Vector, +) -> Matrix> { + let vec = vector.len(); + let ker = kernel.len(); + let newlen = vec - ker + 1; + let mut conv = DVector::::zeros(newlen); - for i in 0..len { - for j in 0..ker { - conv[i] += vector[i + j] * kernel[ker - j - 1]; - } + for i in 0..newlen { + for j in 0..ker { + conv[i] += vector[i + j] * kernel[ker - j - 1]; } - - conv } + conv +} fn convolve_same, Q: Storage>( - vector : Vector, - kernel : Vector - ) -> Matrix> - { + vector: Vector, + kernel: Vector, +) -> Matrix> { + let vec = vector.len(); + let ker = kernel.len(); + let newlen = vec + ker - 1; - let vec = vector.len(); - let ker = kernel.len(); - let len = vec + ker - 1; - let v = vec as i8; - let k = ker as i8; - let l = len as i8; - let mut conv = DVector::::zeros(len); + let mut conv = DVector::::zeros(newlen); - for i in 0..l { - let u_i = cmp::max(0, i - k); - let u_f = cmp::min(i, v - 1); + for i in 0..newlen { + // let u_i = cmp::max(0, i - k); + // let u_f = cmp::min(i, v - 1); - if u_i == u_f { - conv[i as usize] += vector[u_i as usize] * kernel[(i - u_i) as usize]; - } - else{ - for u in u_i..(u_f+1){ - if i - u < k{ - conv[i as usize] += vector[u as usize] * kernel[(i - u ) as usize]; - } - } - } - } - conv + // if u_i == u_f { + // conv[i as usize] += vector[u_i as usize] * kernel[(i - u_i) as usize]; + // } else { + // for u in u_i..(u_f + 1) { + // if i - u < k { + // conv[i as usize] += vector[u as usize] * kernel[(i - u) as usize]; + // } + // } + // } } + conv +} fn convolve, Q: Storage>( - vector : Vector, - kernel : Vector, - mode : Option - ) -> Matrix> - { - // - // vector is the vector, Kervel is the kervel - // C is the returv vector - // - if kernel.len() > vector.len(){ - return convolve(kernel, vector, mode); - } - - match mode.unwrap_or(ConvolveMode::Full) { - ConvolveMode::Full => return convolve_full(vector,kernel), - ConvolveMode::Valid => return convolve_valid(vector,kernel), - ConvolveMode::Same => return convolve_same(vector,kernel) - } + vector: Vector, + kernel: Vector, + mode: Option, +) -> Matrix> { + // + // vector is the vector, Kervel is the kervel + // C is the returv vector + // + if kernel.len() > vector.len() { + return convolve(kernel, vector, mode); } + match mode.unwrap_or(ConvolveMode::Full) { + ConvolveMode::Full => return convolve_full(vector, kernel), + ConvolveMode::Valid => return convolve_valid(vector, kernel), + ConvolveMode::Same => return convolve_same(vector, kernel), + } +} fn main() { let v1 = Vector2::new(3.0,1.0); let v2 = Vector4::new(1.0,2.0,5.0,9.0); let x = convolve(v1,v2,Some(ConvolveMode::Valid)); println!("{:?}",x) -} \ No newline at end of file + + // let m = Matrix2x3::from_anti_diagonal_element(5.0); + // The two additional arguments represent the matrix dimensions. + // let dm = DMatrix::from_anti_diagonal_element(2, 3, 5.0); + let mut m = Matrix2x3::new(1.1, 1.2, 1.3, + 2.1, 2.2, 2.3); + + // assert!(m.m11 == 0.0 && m.m12 == 0.0 && m.m13 == 5.0 && + // m.m21 == 0.0 && m.m22 == 5.0 && m.m23 == 0.0); + // assert!(dm[(0, 0)] == 0.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 5.0 && + // dm[(1, 0)] == 0.0 && dm[(1, 1)] == 5.0 && dm[(1, 2)] == 0.0); + println!("m={:?}",m); + for i in 0..std::cmp::min(m.nrows(),m.ncols()) { + // for j in 0..3 { + println!("m({:?},{:?})={:?}",i,3-i-1,m[(i,3-i-1)]); +unsafe { println!("m({:?},{:?})={:?}",i,3-i-1,*m.get_unchecked_mut((i, 3-i-1))) } + // } + } + + +} From d038ec7b732293557de67424d33a8952f954507d Mon Sep 17 00:00:00 2001 From: Nathan Date: Sat, 9 Feb 2019 21:51:20 -0600 Subject: [PATCH 04/12] All 3 modes of direct done --- examples/convolution.rs | 65 ++++++++++++----------------------------- 1 file changed, 18 insertions(+), 47 deletions(-) diff --git a/examples/convolution.rs b/examples/convolution.rs index c3c61eca..7a3e8780 100644 --- a/examples/convolution.rs +++ b/examples/convolution.rs @@ -1,9 +1,8 @@ extern crate nalgebra as na; use na::storage::Storage; -#[allow(unused_imports)] use na::{ - DMatrix, DVector, Dim, Dynamic, Matrix, Matrix2x3, Real, VecStorage, Vector, Vector2, Vector3, - Vector4, U1, + convert, zero, DMatrix, DVector, Dim, Dynamic, Matrix, Matrix2x3, Real, VecStorage, Vector, + Vector2, Vector3, Vector4, Vector5, U1, }; use std::cmp; @@ -24,7 +23,7 @@ fn convolve_full, Q: Storage>( let mut conv = DVector::::zeros(newlen); for i in 0..newlen { - let u_i = if i > ker {i - ker} else {0}; + let u_i = if i > ker { i - ker } else { 0 }; let u_f = cmp::min(i, vec - 1); if u_i == u_f { @@ -47,6 +46,7 @@ fn convolve_valid, Q: Storage>( let vec = vector.len(); let ker = kernel.len(); let newlen = vec - ker + 1; + let mut conv = DVector::::zeros(newlen); for i in 0..newlen { @@ -63,23 +63,18 @@ fn convolve_same, Q: Storage>( ) -> Matrix> { let vec = vector.len(); let ker = kernel.len(); - let newlen = vec + ker - 1; - let mut conv = DVector::::zeros(newlen); + let mut conv = DVector::::zeros(vec); - for i in 0..newlen { - // let u_i = cmp::max(0, i - k); - // let u_f = cmp::min(i, v - 1); - - // if u_i == u_f { - // conv[i as usize] += vector[u_i as usize] * kernel[(i - u_i) as usize]; - // } else { - // for u in u_i..(u_f + 1) { - // if i - u < k { - // conv[i as usize] += vector[u as usize] * kernel[(i - u) as usize]; - // } - // } - // } + for i in 0..vec { + for j in 0..ker { + let val = if i + j < 1 || i + j >= vec + 1 { + zero::() + } else { + vector[i + j - 1] + }; + conv[i] += val * kernel[ker - j - 1]; + } } conv } @@ -89,10 +84,6 @@ fn convolve, Q: Storage>( kernel: Vector, mode: Option, ) -> Matrix> { - // - // vector is the vector, Kervel is the kervel - // C is the returv vector - // if kernel.len() > vector.len() { return convolve(kernel, vector, mode); } @@ -105,28 +96,8 @@ fn convolve, Q: Storage>( } fn main() { - let v1 = Vector2::new(3.0,1.0); - let v2 = Vector4::new(1.0,2.0,5.0,9.0); - let x = convolve(v1,v2,Some(ConvolveMode::Valid)); - println!("{:?}",x) - - // let m = Matrix2x3::from_anti_diagonal_element(5.0); - // The two additional arguments represent the matrix dimensions. - // let dm = DMatrix::from_anti_diagonal_element(2, 3, 5.0); - let mut m = Matrix2x3::new(1.1, 1.2, 1.3, - 2.1, 2.2, 2.3); - - // assert!(m.m11 == 0.0 && m.m12 == 0.0 && m.m13 == 5.0 && - // m.m21 == 0.0 && m.m22 == 5.0 && m.m23 == 0.0); - // assert!(dm[(0, 0)] == 0.0 && dm[(0, 1)] == 0.0 && dm[(0, 2)] == 5.0 && - // dm[(1, 0)] == 0.0 && dm[(1, 1)] == 5.0 && dm[(1, 2)] == 0.0); - println!("m={:?}",m); - for i in 0..std::cmp::min(m.nrows(),m.ncols()) { - // for j in 0..3 { - println!("m({:?},{:?})={:?}",i,3-i-1,m[(i,3-i-1)]); -unsafe { println!("m({:?},{:?})={:?}",i,3-i-1,*m.get_unchecked_mut((i, 3-i-1))) } - // } - } - - + let v1 = Vector4::new(1.0, 2.0, 1.0, 0.0); + let v2 = Vector4::new(1.0, 2.0, 5.0, 9.0); + let x = convolve(v1, v2, Some(ConvolveMode::Same)); + println!("{:?}", x); } From 645ca8ad52623f926651277a6391cc75ca207f87 Mon Sep 17 00:00:00 2001 From: Nathan Date: Sat, 9 Feb 2019 21:53:22 -0600 Subject: [PATCH 05/12] All 3 modes of direct done --- examples/convolution.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/examples/convolution.rs b/examples/convolution.rs index 7a3e8780..07b1f974 100644 --- a/examples/convolution.rs +++ b/examples/convolution.rs @@ -1,9 +1,6 @@ extern crate nalgebra as na; use na::storage::Storage; -use na::{ - convert, zero, DMatrix, DVector, Dim, Dynamic, Matrix, Matrix2x3, Real, VecStorage, Vector, - Vector2, Vector3, Vector4, Vector5, U1, -}; +use na::{zero, DVector, Dim, Dynamic, Matrix, Real, VecStorage, Vector, U1}; use std::cmp; enum ConvolveMode { @@ -96,8 +93,5 @@ fn convolve, Q: Storage>( } fn main() { - let v1 = Vector4::new(1.0, 2.0, 1.0, 0.0); - let v2 = Vector4::new(1.0, 2.0, 5.0, 9.0); - let x = convolve(v1, v2, Some(ConvolveMode::Same)); - println!("{:?}", x); + } From b3c6492530cf3e93e197d222465da01d5af3f2a7 Mon Sep 17 00:00:00 2001 From: Nathan Date: Sun, 10 Feb 2019 13:40:32 -0600 Subject: [PATCH 06/12] Moved test file to lingal folder, wrote tests based on github ticket request (scipy reference) --- {examples => src/linalg}/convolution.rs | 74 ++++++++++++++----------- src/linalg/mod.rs | 2 + tests/linalg/convolution.rs | 49 ++++++++++++++++ tests/linalg/mod.rs | 1 + 4 files changed, 93 insertions(+), 33 deletions(-) rename {examples => src/linalg}/convolution.rs (58%) create mode 100644 tests/linalg/convolution.rs diff --git a/examples/convolution.rs b/src/linalg/convolution.rs similarity index 58% rename from examples/convolution.rs rename to src/linalg/convolution.rs index 07b1f974..dba6c97d 100644 --- a/examples/convolution.rs +++ b/src/linalg/convolution.rs @@ -1,20 +1,25 @@ -extern crate nalgebra as na; -use na::storage::Storage; -use na::{zero, DVector, Dim, Dynamic, Matrix, Real, VecStorage, Vector, U1}; +use storage::Storage; +use {zero, DVector, Dim, Dynamic, Matrix, Real, VecStorage, Vector, U1}; use std::cmp; -enum ConvolveMode { - Full, - Valid, - Same, -} - -fn convolve_full, Q: Storage>( +/// +/// The output is the full discrete linear convolution of the inputs +/// +pub fn convolve_full, Q: Storage>( vector: Vector, kernel: Vector, ) -> Matrix> { 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); + } + let newlen = vec + ker - 1; let mut conv = DVector::::zeros(newlen); @@ -36,12 +41,24 @@ fn convolve_full, Q: Storage>( conv } -fn convolve_valid, Q: Storage>( +/// +/// The output consists only of those elements that do not rely on the zero-padding. +/// +pub fn convolve_valid, Q: Storage>( vector: Vector, kernel: Vector, ) -> Matrix> { 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_valid(kernel, vector); + } + let newlen = vec - ker + 1; let mut conv = DVector::::zeros(newlen); @@ -54,13 +71,24 @@ fn convolve_valid, Q: Storage>( conv } -fn convolve_same, Q: Storage>( +/// +/// The output is the same size as in1, centered with respect to the ‘full’ output. +/// +pub fn convolve_same, Q: Storage>( vector: Vector, kernel: Vector, ) -> Matrix> { 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); + } + let mut conv = DVector::::zeros(vec); for i in 0..vec { @@ -74,24 +102,4 @@ fn convolve_same, Q: Storage>( } } conv -} - -fn convolve, Q: Storage>( - vector: Vector, - kernel: Vector, - mode: Option, -) -> Matrix> { - if kernel.len() > vector.len() { - return convolve(kernel, vector, mode); - } - - match mode.unwrap_or(ConvolveMode::Full) { - ConvolveMode::Full => return convolve_full(vector, kernel), - ConvolveMode::Valid => return convolve_valid(vector, kernel), - ConvolveMode::Same => return convolve_same(vector, kernel), - } -} - -fn main() { - -} +} \ No newline at end of file diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs index 4418b283..b6a9e8d8 100644 --- a/src/linalg/mod.rs +++ b/src/linalg/mod.rs @@ -17,6 +17,7 @@ mod solve; mod svd; mod symmetric_eigen; mod symmetric_tridiagonal; +mod convolution; //// FIXME: Not complete enough for publishing. //// This handles only cases where each eigenvalue has multiplicity one. @@ -33,3 +34,4 @@ pub use self::schur::*; pub use self::svd::*; pub use self::symmetric_eigen::*; pub use self::symmetric_tridiagonal::*; +pub use self::convolution::*; diff --git a/tests/linalg/convolution.rs b/tests/linalg/convolution.rs new file mode 100644 index 00000000..ef3a02db --- /dev/null +++ b/tests/linalg/convolution.rs @@ -0,0 +1,49 @@ +use na::linalg::{convolve_full,convolve_valid,convolve_same}; +use na::{Vector2,Vector4,DVector}; + +// +// Should mimic calculations in Python's scipy library +// >>>from scipy.signal import convolve +// + +// >>> convolve([1,2,3,4],[1,2],"same") +// array([ 1, 4, 7, 10]) +#[test] +fn convolve_same_check(){ + let vec = Vector4::new(1.0,2.0,3.0,4.0); + let ker = Vector2::new(1.0,2.0); + + let actual = DVector::from_vec(4, vec![1.0,4.0,7.0,10.0]); + + let expected = convolve_same(vec,ker); + + assert!(relative_eq!(actual, expected, epsilon = 1.0e-7)); +} + +// >>> convolve([1,2,3,4],[1,2],"valid") +// array([ 1, 4, 7, 10, 8]) +#[test] +fn convolve_full_check(){ + let vec = Vector4::new(1.0,2.0,3.0,4.0); + let ker = Vector2::new(1.0,2.0); + + let actual = DVector::from_vec(5, vec![1.0,4.0,7.0,10.0,8.0]); + + let expected = convolve_full(vec,ker); + + assert!(relative_eq!(actual, expected, epsilon = 1.0e-7)); +} + +// >>> 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); + + let actual = DVector::from_vec(3, vec![4.0,7.0,10.0]); + + let expected = convolve_valid(vec,ker); + + assert!(relative_eq!(actual, expected, epsilon = 1.0e-7)); +} \ No newline at end of file diff --git a/tests/linalg/mod.rs b/tests/linalg/mod.rs index 74a5e03c..4e0bf2eb 100644 --- a/tests/linalg/mod.rs +++ b/tests/linalg/mod.rs @@ -11,3 +11,4 @@ mod real_schur; mod solve; mod svd; mod tridiagonal; +mod convolution; \ No newline at end of file From bca385ea6b132d8300c476e3637452dbb9f12411 Mon Sep 17 00:00:00 2001 From: Nathan Date: Sun, 10 Feb 2019 13:46:37 -0600 Subject: [PATCH 07/12] Quick fix to documentation --- src/linalg/convolution.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/linalg/convolution.rs b/src/linalg/convolution.rs index dba6c97d..e7e20c21 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -42,7 +42,7 @@ pub fn convolve_full, Q: Storage } /// -/// The output consists only of those elements that do not rely on the zero-padding. +/// The output convolution consists only of those elements that do not rely on the zero-padding. /// pub fn convolve_valid, Q: Storage>( vector: Vector, @@ -72,7 +72,7 @@ pub fn convolve_valid, Q: Storage, Q: Storage>( vector: Vector, From b08c2ad70d00fb4322f9ed860bb57a51840b2fac Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 14 Feb 2019 20:54:26 -0600 Subject: [PATCH 08/12] Feedback updates round 1 --- examples/convolution.rs | 5 +++ src/linalg/convolution.rs | 77 ++++++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 examples/convolution.rs diff --git a/examples/convolution.rs b/examples/convolution.rs new file mode 100644 index 00000000..5290440c --- /dev/null +++ b/examples/convolution.rs @@ -0,0 +1,5 @@ +fn main(){ + let (x,y) = (1,2); + + println!("{}", x); +} \ No newline at end of file diff --git a/src/linalg/convolution.rs b/src/linalg/convolution.rs index e7e20c21..ac0ba71e 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -1,45 +1,63 @@ use storage::Storage; -use {zero, DVector, Dim, Dynamic, Matrix, Real, VecStorage, Vector, U1}; +use {zero, DVector, Dim, Dynamic, Matrix, Real, VecStorage, Vector, U1, Add}; use std::cmp; -/// -/// The output is the full discrete linear convolution of the inputs -/// -pub fn convolve_full, Q: Storage>( - vector: Vector, - kernel: Vector, -) -> Matrix> { - let vec = vector.len(); - let ker = kernel.len(); +impl> Vector{ - if vec == 0 || ker == 0 { - panic!("Convolve's inputs must not be 0-sized. "); - } + /// Returns the convolution of the vector and a kernel + /// + /// # Arguments + /// + /// * `self` - A DVector with size D > 0 + /// * `kernel` - A DVector with size D > 0 + /// + /// # Note: + /// This function is commutative. If D_kernel > D_vector, + /// they will swap their roles as in + /// (self, kernel) = (kernel,self) + /// + /// # Example + /// + /// ``` + /// + /// ``` + pub fn convolve_full>(&self, kernel: Vector) -> Vector,Add> + { + let vec = self.len(); + let ker = kernel.len(); - if ker > vec { - return convolve_full(kernel, vector); - } + // if vec == 0 || ker == 0 { + // panic!("Convolve's inputs must not be 0-sized. "); + // } - let newlen = vec + ker - 1; + // if ker > vec { + // return kernel::convolve_full(vector); + // } - let mut conv = DVector::::zeros(newlen); + let newlen = vec + ker - 1; + let mut conv = DVector::::zeros(newlen); - for i in 0..newlen { - let u_i = if i > ker { i - ker } else { 0 }; - let u_f = cmp::min(i, vec - 1); + for i in 0..newlen { + let u_i = if i > ker { i - ker } else { 0 }; + let u_f = cmp::min(i, vec - 1); - if u_i == u_f { - conv[i] += vector[u_i] * kernel[(i - u_i)]; - } else { - for u in u_i..(u_f + 1) { - if i - u < ker { - conv[i] += vector[u] * kernel[(i - u)]; + if u_i == u_f { + conv[i] += self[u_i] * kernel[(i - u_i)]; + } else { + for u in u_i..(u_f + 1) { + if i - u < ker { + conv[i] += self[u] * kernel[(i - u)]; + } } } } + // conv } - conv } +/// +/// The output is the full discrete linear convolution of the inputs +/// + /// /// The output convolution consists only of those elements that do not rely on the zero-padding. @@ -102,4 +120,5 @@ pub fn convolve_same, Q: Storage } } conv -} \ No newline at end of file +} + From 9f5201938594056a80a9b20423fa8d4993f7289a Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 18 Feb 2019 19:01:18 -0600 Subject: [PATCH 09/12] Fixing type traits based on feedback, `convolve_full` still broken --- src/linalg/convolution.rs | 196 ++++++++++++++++++++++++------------ tests/linalg/convolution.rs | 72 +++++++++---- 2 files changed, 181 insertions(+), 87 deletions(-) diff --git a/src/linalg/convolution.rs b/src/linalg/convolution.rs index ac0ba71e..c587f8f2 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -1,71 +1,110 @@ -use storage::Storage; -use {zero, DVector, Dim, Dynamic, Matrix, Real, VecStorage, Vector, U1, Add}; +use base::allocator::Allocator; +use base::default_allocator::DefaultAllocator; +use base::dimension::{DimAdd, DimDiff, DimMax, DimMaximum, DimName, DimSub, DimSum,Dim}; use std::cmp; +use storage::Storage; +use {zero, Real, Vector, VectorN, U1}; -impl> Vector{ +/// Returns the convolution of the vector and a kernel +/// +/// # Arguments +/// +/// * `vector` - A Vector with size > 0 +/// * `kernel` - A Vector with size > 0 +/// +/// # Note: +/// This function is commutative. If kernel > vector, +/// they will swap their roles as in +/// (self, kernel) = (kernel,self) +/// +/// # Example +/// +/// ``` +/// let vec = Vector3::new(1.0,2.0,3.0); +/// let ker = Vector2::new(0.4,0.6); +/// let convolve = convolve_full(vec,ker); +/// ``` +pub fn convolve_full( + vector: Vector, + kernel: Vector, +) -> VectorN, U1>> +where + N: Real, + D1: DimAdd, + D2: DimAdd>, + DimSum: DimSub, + S1: Storage, + S2: Storage, + DimSum: Dim, + DefaultAllocator: Allocator, U1>>, +{ + let vec = vector.len(); + let ker = kernel.len(); - /// Returns the convolution of the vector and a kernel - /// - /// # Arguments - /// - /// * `self` - A DVector with size D > 0 - /// * `kernel` - A DVector with size D > 0 - /// - /// # Note: - /// This function is commutative. If D_kernel > D_vector, - /// they will swap their roles as in - /// (self, kernel) = (kernel,self) - /// - /// # Example - /// - /// ``` - /// - /// ``` - pub fn convolve_full>(&self, kernel: Vector) -> Vector,Add> - { - let vec = self.len(); - let ker = kernel.len(); + if vec == 0 || ker == 0 { + panic!("Convolve's inputs must not be 0-sized. "); + } - // if vec == 0 || ker == 0 { - // panic!("Convolve's inputs must not be 0-sized. "); - // } + if ker > vec { + return convolve_full(kernel, vector); + } - // if ker > vec { - // return kernel::convolve_full(vector); - // } + let result_len = vector.data.shape().0.add(kernel.data.shape().0).sub(U1); + let mut conv = VectorN::zeros_generic(result_len, U1); - let newlen = vec + ker - 1; - let mut conv = DVector::::zeros(newlen); + for i in 0..(vec + ker - 1) { + let u_i = if i > vec { i - ker } else { 0 }; + let u_f = cmp::min(i, vec - 1); - for i in 0..newlen { - let u_i = if i > ker { i - ker } else { 0 }; - let u_f = cmp::min(i, vec - 1); - - if u_i == u_f { - conv[i] += self[u_i] * kernel[(i - u_i)]; - } else { - for u in u_i..(u_f + 1) { - if i - u < ker { - conv[i] += self[u] * kernel[(i - u)]; - } + if u_i == u_f { + conv[i] += vector[u_i] * kernel[(i - u_i)]; + } else { + for u in u_i..(u_f + 1) { + if i - u < ker { + conv[i] += vector[u] * kernel[(i - u)]; } } } - // conv } + conv } -/// -/// The output is the full discrete linear convolution of the inputs -/// + +/// 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 /// -/// The output convolution consists only of those elements that do not rely on the zero-padding. -/// -pub fn convolve_valid, Q: Storage>( - vector: Vector, - kernel: Vector, -) -> Matrix> { +/// * `vector` - A Vector with size > 0 +/// * `kernel` - A Vector with size > 0 +/// +/// # Note: +/// This function is commutative. If kernel > vector, +/// they will swap their roles as in +/// (self, kernel) = (kernel,self) +/// +/// # Example +/// +/// ``` +/// let vec = Vector3::new(1.0,2.0,3.0); +/// let ker = Vector2::new(0.4,0.6); +/// let convolve = convolve_valid(vec,ker); +/// ``` +pub fn convolve_valid( + vector: Vector, + kernel: Vector, +) -> VectorN, U1>> +where + N: Real, + D1: DimSub, + D2: DimSub>, + DimDiff: DimAdd, + S1: Storage, + S2: Storage, + DimDiff: DimName, + DefaultAllocator: Allocator, U1>> +{ + let vec = vector.len(); let ker = kernel.len(); @@ -76,12 +115,10 @@ pub fn convolve_valid, Q: Storage vec { return convolve_valid(kernel, vector); } + let result_len = vector.data.shape().0.sub(kernel.data.shape().0).add(U1); + let mut conv = VectorN::zeros_generic(result_len, U1); - let newlen = vec - ker + 1; - - let mut conv = DVector::::zeros(newlen); - - for i in 0..newlen { + for i in 0..(vec - ker + 1) { for j in 0..ker { conv[i] += vector[i + j] * kernel[ker - j - 1]; } @@ -89,13 +126,38 @@ pub fn convolve_valid, Q: Storage, Q: Storage>( - vector: Vector, - kernel: Vector, -) -> Matrix> { +/// # Arguments +/// +/// * `vector` - A Vector with size > 0 +/// * `kernel` - A Vector with size > 0 +/// +/// # Note: +/// This function is commutative. If kernel > vector, +/// they will swap their roles as in +/// (self, kernel) = (kernel,self) +/// +/// # Example +/// +/// ``` +/// let vec = Vector3::new(1.0,2.0,3.0); +/// let ker = Vector2::new(0.4,0.6); +/// let convolve = convolve_same(vec,ker); +/// ``` +pub fn convolve_same( + vector: Vector, + kernel: Vector, +) -> VectorN> +where + N: Real, + D1: DimMax, + D2: DimMax>, + S1: Storage, + S2: Storage, + DimMaximum: Dim, + DefaultAllocator: Allocator>, +{ let vec = vector.len(); let ker = kernel.len(); @@ -107,12 +169,13 @@ pub fn convolve_same, Q: Storage return convolve_same(kernel, vector); } - let mut conv = DVector::::zeros(vec); + let result_len = vector.data.shape().0.max(kernel.data.shape().0); + let mut conv = VectorN::zeros_generic(result_len, U1); for i in 0..vec { for j in 0..ker { let val = if i + j < 1 || i + j >= vec + 1 { - zero::() + zero::() } else { vector[i + j - 1] }; @@ -121,4 +184,3 @@ pub fn convolve_same, Q: Storage } conv } - diff --git a/tests/linalg/convolution.rs b/tests/linalg/convolution.rs index ef3a02db..454c29c6 100644 --- a/tests/linalg/convolution.rs +++ b/tests/linalg/convolution.rs @@ -1,5 +1,7 @@ +#[allow(unused_imports)] // remove after fixing unit test use na::linalg::{convolve_full,convolve_valid,convolve_same}; -use na::{Vector2,Vector4,DVector}; +#[allow(unused_imports)] +use na::{Vector2,Vector3,Vector4,Vector5,DVector}; // // Should mimic calculations in Python's scipy library @@ -10,40 +12,70 @@ use na::{Vector2,Vector4,DVector}; // array([ 1, 4, 7, 10]) #[test] fn convolve_same_check(){ - let vec = Vector4::new(1.0,2.0,3.0,4.0); - let ker = Vector2::new(1.0,2.0); + let vec_s = Vector4::new(1.0,2.0,3.0,4.0); + let ker_s = Vector2::new(1.0,2.0); - let actual = DVector::from_vec(4, vec![1.0,4.0,7.0,10.0]); + let actual_s = Vector4::from_vec(vec![1.0,4.0,7.0,10.0]); - let expected = convolve_same(vec,ker); + let expected_s = convolve_same(vec_s,ker_s); + let expected_s_r = convolve_same(ker_s,vec_s); - assert!(relative_eq!(actual, expected, epsilon = 1.0e-7)); + 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); + + assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); + assert!(relative_eq!(actual_d, expected_d_r, epsilon = 1.0e-7)); } -// >>> convolve([1,2,3,4],[1,2],"valid") +// >>> convolve([1,2,3,4],[1,2],"full") // array([ 1, 4, 7, 10, 8]) #[test] fn convolve_full_check(){ - let vec = Vector4::new(1.0,2.0,3.0,4.0); - let ker = Vector2::new(1.0,2.0); + let vec_s = Vector4::new(1.0,2.0,3.0,4.0); + let ker_s = Vector2::new(1.0,2.0); - let actual = DVector::from_vec(5, vec![1.0,4.0,7.0,10.0,8.0]); + let actual_s = Vector5::new(1.0,4.0,7.0,10.0,8.0); - let expected = convolve_full(vec,ker); + let expected_s = convolve_full(vec_s,ker_s); + let expected_s_r = convolve_full(ker_s,vec_s); - assert!(relative_eq!(actual, expected, epsilon = 1.0e-7)); + 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); + + assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); + assert!(relative_eq!(actual_d, expected_d_r, epsilon = 1.0e-7)); } // >>> 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(){ +// let vec = Vector4::new(1.0,2.0,3.0,4.0); +// let ker = Vector2::new(1.0,2.0); - let actual = DVector::from_vec(3, vec![4.0,7.0,10.0]); +// let actual = Vector3::from_vec(vec![4.0,7.0,10.0]); - let expected = convolve_valid(vec,ker); +// let expected1 = convolve_valid(vec, ker); +// let expected2 = convolve_valid(ker, vec); - assert!(relative_eq!(actual, expected, epsilon = 1.0e-7)); -} \ No newline at end of file + +// assert!(relative_eq!(actual, expected1, epsilon = 1.0e-7)); +// assert!(relative_eq!(actual, expected2, epsilon = 1.0e-7)); + +// } \ No newline at end of file From 2a2debf58be6d30c043cfce9285238cb69605ee1 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 20 Feb 2019 20:32:09 -0600 Subject: [PATCH 10/12] Fixing documentation --- examples/convolution.rs | 18 +++++++++++++--- src/linalg/convolution.rs | 44 +++++++++------------------------------ 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/examples/convolution.rs b/examples/convolution.rs index 5290440c..d23ed389 100644 --- a/examples/convolution.rs +++ b/examples/convolution.rs @@ -1,5 +1,17 @@ -fn main(){ - let (x,y) = (1,2); +extern crate nalgebra; +use nalgebra::{Vector2,Vector3,Vector4,Vector5,convolve_full,convolve_same,convolve_valid}; - println!("{}", x); +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 c587f8f2..69ae3f5e 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -12,18 +12,10 @@ use {zero, Real, Vector, VectorN, U1}; /// * `vector` - A Vector with size > 0 /// * `kernel` - A Vector with size > 0 /// -/// # Note: -/// This function is commutative. If kernel > vector, -/// they will swap their roles as in -/// (self, kernel) = (kernel,self) +/// This function is commutative. If kernel > vector, +/// they will swap their roles as in +/// (self, kernel) = (kernel,self) /// -/// # Example -/// -/// ``` -/// let vec = Vector3::new(1.0,2.0,3.0); -/// let ker = Vector2::new(0.4,0.6); -/// let convolve = convolve_full(vec,ker); -/// ``` pub fn convolve_full( vector: Vector, kernel: Vector, @@ -77,19 +69,11 @@ 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) /// -/// # Note: -/// This function is commutative. If kernel > vector, -/// they will swap their roles as in -/// (self, kernel) = (kernel,self) -/// -/// # Example -/// -/// ``` -/// let vec = Vector3::new(1.0,2.0,3.0); -/// let ker = Vector2::new(0.4,0.6); -/// let convolve = convolve_valid(vec,ker); -/// ``` pub fn convolve_valid( vector: Vector, kernel: Vector, @@ -133,18 +117,10 @@ where /// * `vector` - A Vector with size > 0 /// * `kernel` - A Vector with size > 0 /// -/// # Note: -/// This function is commutative. If kernel > vector, -/// they will swap their roles as in -/// (self, kernel) = (kernel,self) +/// This function is commutative. If kernel > vector, +/// they will swap their roles as in +/// (self, kernel) = (kernel,self) /// -/// # Example -/// -/// ``` -/// let vec = Vector3::new(1.0,2.0,3.0); -/// let ker = Vector2::new(0.4,0.6); -/// let convolve = convolve_same(vec,ker); -/// ``` pub fn convolve_same( vector: Vector, kernel: Vector, From 28525bfc203740fb7f8c159c7c712770a73b4123 Mon Sep 17 00:00:00 2001 From: Nathan Date: Sun, 24 Feb 2019 19:53:09 -0600 Subject: [PATCH 11/12] Restructured usage of convolves, added unit testing. --- examples/convolution.rs | 17 ----- src/linalg/convolution.rs | 64 +++++++------------ tests/linalg/convolution.rs | 123 ++++++++++++++++++++++++------------ 3 files changed, 103 insertions(+), 101 deletions(-) delete mode 100644 examples/convolution.rs 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 From 36feddb8c2277f3a0ffa83c262761755393f704a Mon Sep 17 00:00:00 2001 From: Nathan Date: Sat, 2 Mar 2019 15:00:40 -0600 Subject: [PATCH 12/12] Moving functions into impl for Vector --- src/linalg/convolution.rs | 227 +++++++++++++++++------------------- tests/linalg/convolution.rs | 31 +++-- 2 files changed, 122 insertions(+), 136 deletions(-) diff --git a/src/linalg/convolution.rs b/src/linalg/convolution.rs index 49c8b154..b121b34a 100644 --- a/src/linalg/convolution.rs +++ b/src/linalg/convolution.rs @@ -5,138 +5,125 @@ use std::cmp; use storage::Storage; use {zero, Real, Vector, VectorN, U1}; -/// Returns the convolution of the target vector and a kernel -/// -/// # Arguments -/// -/// * `vector` - A Vector with size > 0 -/// * `kernel` - A Vector with size > 0 -/// -/// # Errors -/// Inputs must statisfy `vector.len() >= kernel.len() > 0`. -/// -pub fn convolve_full( - vector: Vector, - kernel: Vector, -) -> VectorN, U1>> -where - N: Real, - D1: DimAdd, - D2: DimAdd>, - DimSum: DimSub, - S1: Storage, - S2: Storage, - DefaultAllocator: Allocator, U1>>, -{ - let vec = vector.len(); - let ker = kernel.len(); +impl> Vector { + /// Returns the convolution of the target vector and a kernel + /// + /// # Arguments + /// + /// * `kernel` - A Vector with size > 0 + /// + /// # Errors + /// Inputs must statisfy `vector.len() >= kernel.len() > 0`. + /// + pub fn convolve_full( + &self, + kernel: Vector, + ) -> VectorN, U1>> + where + D1: DimAdd, + D2: DimAdd>, + DimSum: DimSub, + S2: Storage, + DefaultAllocator: Allocator, U1>>, + { + let vec = self.len(); + let ker = kernel.len(); - if ker == 0 || ker > vec { - panic!("convolve_full expects `vector.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); - } + if ker == 0 || ker > vec { + panic!("convolve_full expects `self.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); + } - let result_len = vector.data.shape().0.add(kernel.data.shape().0).sub(U1); - let mut conv = VectorN::zeros_generic(result_len, U1); + let result_len = self.data.shape().0.add(kernel.data.shape().0).sub(U1); + let mut conv = VectorN::zeros_generic(result_len, U1); - for i in 0..(vec + ker - 1) { - let u_i = if i > vec { i - ker } else { 0 }; - let u_f = cmp::min(i, vec - 1); + for i in 0..(vec + ker - 1) { + let u_i = if i > vec { i - ker } else { 0 }; + let u_f = cmp::min(i, vec - 1); - if u_i == u_f { - conv[i] += vector[u_i] * kernel[(i - u_i)]; - } else { - for u in u_i..(u_f + 1) { - if i - u < ker { - conv[i] += vector[u] * kernel[(i - u)]; + if u_i == u_f { + conv[i] += self[u_i] * kernel[(i - u_i)]; + } else { + for u in u_i..(u_f + 1) { + if i - u < ker { + conv[i] += self[u] * kernel[(i - u)]; + } } } } + conv } - conv -} + /// Returns the convolution of the target vector and a kernel + /// The output convolution consists only of those elements that do not rely on the zero-padding. + /// # Arguments + /// + /// * `kernel` - A Vector with size > 0 + /// + /// + /// # Errors + /// Inputs must statisfy `self.len() >= kernel.len() > 0`. + /// + pub fn convolve_valid(&self, kernel: Vector, + ) -> VectorN, D2>> + where + D1: DimAdd, + D2: Dim, + DimSum: DimSub, + S2: Storage, + DefaultAllocator: Allocator, D2>>, + { + let vec = self.len(); + let ker = kernel.len(); -/// 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 -/// -/// -/// # Errors -/// Inputs must statisfy `vector.len() >= kernel.len() > 0`. -/// -pub fn convolve_valid( - vector: Vector, - kernel: Vector, -) -> VectorN, D2>> -where - N: Real, - D1: DimAdd, - D2: Dim, - DimSum: DimSub, - S1: Storage, - S2: Storage, - DefaultAllocator: Allocator, D2>>, -{ - let vec = vector.len(); - let ker = kernel.len(); - - if ker == 0 || ker > vec { - panic!("convolve_valid expects `vector.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); - } - - 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) { - for j in 0..ker { - conv[i] += vector[i + j] * kernel[ker - j - 1]; + if ker == 0 || ker > vec { + panic!("convolve_valid expects `self.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); } - } - conv -} -/// Returns the convolution of the vector and a kernel -/// The output convolution is the same size as vector, centered with respect to the ‘full’ output. -/// # Arguments -/// -/// * `vector` - A Vector with size > 0 -/// * `kernel` - A Vector with size > 0 -/// -/// # Errors -/// Inputs must statisfy `vector.len() >= kernel.len() > 0`. -pub fn convolve_same( - vector: Vector, - kernel: Vector, -) -> VectorN> -where - N: Real, - D1: DimMax, - D2: DimMax>, - S1: Storage, - S2: Storage, - DefaultAllocator: Allocator>, -{ - let vec = vector.len(); - let ker = kernel.len(); + let result_len = self.data.shape().0.add(U1).sub(kernel.data.shape().0); + let mut conv = VectorN::zeros_generic(result_len, U1); - 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); - let mut conv = VectorN::zeros_generic(result_len, U1); - - for i in 0..vec { - for j in 0..ker { - let val = if i + j < 1 || i + j >= vec + 1 { - zero::() - } else { - vector[i + j - 1] - }; - conv[i] += val * kernel[ker - j - 1]; + for i in 0..(vec - ker + 1) { + for j in 0..ker { + conv[i] += self[i + j] * kernel[ker - j - 1]; + } } + conv + } + + /// Returns the convolution of the targetvector and a kernel + /// The output convolution is the same size as vector, centered with respect to the ‘full’ output. + /// # Arguments + /// + /// * `kernel` - A Vector with size > 0 + /// + /// # Errors + /// Inputs must statisfy `self.len() >= kernel.len() > 0`. + pub fn convolve_same(&self, kernel: Vector) -> VectorN> + where + D1: DimMax, + D2: DimMax>, + S2: Storage, + DefaultAllocator: Allocator>, + { + let vec = self.len(); + let ker = kernel.len(); + + if ker == 0 || ker > vec { + panic!("convolve_same expects `self.len() >= kernel.len() > 0`, received {} and {} respectively.",vec,ker); + } + + let result_len = self.data.shape().0.max(kernel.data.shape().0); + let mut conv = VectorN::zeros_generic(result_len, U1); + + for i in 0..vec { + for j in 0..ker { + let val = if i + j < 1 || i + j >= vec + 1 { + zero::() + } else { + self[i + j - 1] + }; + conv[i] += val * kernel[ker - j - 1]; + } + } + conv } - conv } diff --git a/tests/linalg/convolution.rs b/tests/linalg/convolution.rs index ddcfe9f6..b0d57f72 100644 --- a/tests/linalg/convolution.rs +++ b/tests/linalg/convolution.rs @@ -1,4 +1,3 @@ -use na::linalg::{convolve_full,convolve_valid,convolve_same}; use na::{Vector2,Vector3,Vector4,Vector5,DVector}; use std::panic; @@ -13,13 +12,13 @@ use std::panic; fn convolve_same_check(){ // Static Tests let actual_s = Vector4::from_vec(vec![1.0,4.0,7.0,10.0]); - let expected_s = convolve_same(Vector4::new(1.0,2.0,3.0,4.0), Vector2::new(1.0,2.0)); + let expected_s = Vector4::new(1.0,2.0,3.0,4.0).convolve_same(Vector2::new(1.0,2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); // 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])); + let expected_d = DVector::from_vec(vec![1.0,2.0,3.0,4.0]).convolve_same(DVector::from_vec(vec![1.0,2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); @@ -27,19 +26,19 @@ fn convolve_same_check(){ // 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])); + DVector::from_vec(vec![1.0,2.0]).convolve_same(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])); + DVector::::from_vec(vec![]).convolve_same(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![])); + DVector::from_vec(vec![1.0,2.0,3.0,4.0]).convolve_same(DVector::::from_vec(vec![])); }).is_err() ); } @@ -50,13 +49,13 @@ fn convolve_same_check(){ fn convolve_full_check(){ // Static Tests let actual_s = Vector5::new(1.0,4.0,7.0,10.0,8.0); - let expected_s = convolve_full(Vector4::new(1.0,2.0,3.0,4.0), Vector2::new(1.0,2.0)); + let expected_s = Vector4::new(1.0,2.0,3.0,4.0).convolve_full(Vector2::new(1.0,2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); // 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])); + let expected_d = DVector::from_vec(vec![1.0,2.0,3.0,4.0]).convolve_full(DVector::from_vec(vec![1.0,2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); @@ -64,19 +63,19 @@ fn convolve_full_check(){ // 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])); + DVector::from_vec(vec![1.0,2.0]).convolve_full(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])); + DVector::::from_vec(vec![]).convolve_full(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![])); + DVector::from_vec(vec![1.0,2.0,3.0,4.0]).convolve_full(DVector::::from_vec(vec![])); }).is_err() ); } @@ -87,13 +86,13 @@ fn convolve_full_check(){ 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 expected_s = Vector4::new(1.0,2.0,3.0,4.0).convolve_valid( Vector2::new(1.0,2.0)); assert!(relative_eq!(actual_s, expected_s, epsilon = 1.0e-7)); // 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])); + let expected_d = DVector::from_vec(vec![1.0,2.0,3.0,4.0]).convolve_valid(DVector::from_vec(vec![1.0,2.0])); assert!(relative_eq!(actual_d, expected_d, epsilon = 1.0e-7)); @@ -101,19 +100,19 @@ fn convolve_valid_check(){ // 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])); + DVector::from_vec(vec![1.0,2.0]).convolve_valid(DVector::from_vec(vec![1.0,2.0,3.0,4.0])); }).is_err() ); assert!( panic::catch_unwind(|| { - convolve_valid(DVector::::from_vec(vec![]), DVector::from_vec(vec![1.0,2.0,3.0,4.0])); + DVector::::from_vec(vec![]).convolve_valid(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![])); + DVector::from_vec(vec![1.0,2.0,3.0,4.0]).convolve_valid(DVector::::from_vec(vec![])); }).is_err() );