From 6dd00c5585b64cceeb4d4a618f7fbf7f053b89b4 Mon Sep 17 00:00:00 2001 From: fanninpm Date: Wed, 6 May 2020 12:46:32 -0400 Subject: [PATCH 1/7] Implement FFT in Rust This code is incomplete right now. (It hasn't resolved all compiler warnings/errors, and I haven't put it through Clippy yet.) --- contents/cooley_tukey/code/rust/fft.rs | 100 +++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 contents/cooley_tukey/code/rust/fft.rs diff --git a/contents/cooley_tukey/code/rust/fft.rs b/contents/cooley_tukey/code/rust/fft.rs new file mode 100644 index 000000000..715dd09b4 --- /dev/null +++ b/contents/cooley_tukey/code/rust/fft.rs @@ -0,0 +1,100 @@ +extern crate num; +extern crate rand; + +use num::complex::Complex; +use rand::prelude::*; +use std::f64::consts::PI; + +fn dft(x: &Vec>) -> Vec> { + let n = x.len(); + // TODO: replace this with something that uses an iterable + let mut temp = vec![Complex::new(0.0_f64, 0.0_f64); n]; + for i in 0..n { + for k in 0..n { + temp[i] += x[k] + * (Complex::new(0.0_f64, -2.0_f64) * PI * (i as f64) * (k as f64) / (n as f64)) + .exp(); + } + } + temp +} + +fn cooley_tukey(x: &Vec>) -> Vec> { + let n = x.len(); + if n <= 1 { + return x.clone(); + } + let even = cooley_tukey(&x.iter().step_by(2).cloned().collect::>()); + let odd = cooley_tukey(&x.iter().skip(1).step_by(2).cloned().collect::>()); + + let mut temp = vec![Complex::new(0.0_f64, 0.0_f64); n]; + for k in 0..(n / 2) { + temp[k] = even[k] + + (Complex::new(0.0_f64, -2.0_f64) * PI * (k as f64) / (n as f64)).exp() * odd[k]; + temp[k + n / 2] = even[k] + - (Complex::new(0.0_f64, -2.0_f64) * PI * (k as f64) / (n as f64)).exp() * odd[k]; + } + temp +} + +fn bit_reverse(x: &Vec>) -> Vec> { + let n = x.len(); + let mut temp = vec![Complex::new(0.0_f64, 0.0_f64); n]; + for k in 0..n { + let b: usize = (0..((n as f64).log2() as usize)) + .into_iter() + .filter(|i| (k << i & 1) != 0) + .map(|i| 1 << (((n as f64).log2()) as usize) - 1 - i) + .sum(); + temp[k] = x[b]; + temp[b] = x[k]; + } + temp +} + +fn iterative_cooley_tukey(x: &Vec>) -> Vec> { + let n = x.len(); + + let mut new_x = bit_reverse(x); + + for i in 1..=((n as f64).log2() as usize) { + let stride = 2_u128.pow(i as u32); + let w = (Complex::new(0.0_f64, -2.0_f64) * PI / (stride as f64)).exp(); + for j in (0..n).into_iter().step_by(stride as usize) { + let mut v = Complex::new(1.0_f64, 0.0_f64); + for k in 0..((stride / 2) as usize) { + new_x[k + j + ((stride / 2) as usize)] = + new_x[k + j] - v * new_x[k + j + ((stride / 2) as usize)]; + new_x[k + j] -= + new_x[k + j + ((stride / 2) as usize)].clone() - new_x[k + j].clone(); + v *= w; + } + } + } + + new_x +} + +fn main() { + let mut x = Vec::with_capacity(64); + for _i in 0..64 { + let real: f64 = random(); + x.push(Complex::new(real, 0.0_f64)); + } + let y = cooley_tukey(&x); + let z = iterative_cooley_tukey(&x); + let t = dft(&x); + + println!( + "{}", + y.iter() + .zip(z.into_iter()) + .all(|i| (i.0 - i.1).norm() < 1.0) + ); + println!( + "{}", + y.iter() + .zip(t.into_iter()) + .all(|i| (i.0 - i.1).norm() < 1.0) + ); +} From 5a96451469a80955431eae73aaef44e2f090667c Mon Sep 17 00:00:00 2001 From: fanninpm Date: Wed, 6 May 2020 15:05:06 -0400 Subject: [PATCH 2/7] Finished FFT implementation in Rust --- contents/cooley_tukey/code/rust/fft.rs | 62 +++++++++++++------------- contents/cooley_tukey/cooley_tukey.md | 6 +++ 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/contents/cooley_tukey/code/rust/fft.rs b/contents/cooley_tukey/code/rust/fft.rs index 715dd09b4..d4ce454e8 100644 --- a/contents/cooley_tukey/code/rust/fft.rs +++ b/contents/cooley_tukey/code/rust/fft.rs @@ -5,24 +5,27 @@ use num::complex::Complex; use rand::prelude::*; use std::f64::consts::PI; -fn dft(x: &Vec>) -> Vec> { +// This is based on the Python implementation. + +fn dft(x: &[Complex]) -> Vec> { let n = x.len(); - // TODO: replace this with something that uses an iterable - let mut temp = vec![Complex::new(0.0_f64, 0.0_f64); n]; - for i in 0..n { - for k in 0..n { - temp[i] += x[k] - * (Complex::new(0.0_f64, -2.0_f64) * PI * (i as f64) * (k as f64) / (n as f64)) - .exp(); - } - } - temp + (0..n) + .map(|i| { + (0..n) + .map(|k| { + x[k] * (Complex::new(0.0_f64, -2.0_f64) * PI * (i as f64) * (k as f64) + / (n as f64)) + .exp() + }) + .sum() + }) + .collect::>() } -fn cooley_tukey(x: &Vec>) -> Vec> { +fn cooley_tukey(x: &[Complex]) -> Vec> { let n = x.len(); if n <= 1 { - return x.clone(); + return x.to_owned(); } let even = cooley_tukey(&x.iter().step_by(2).cloned().collect::>()); let odd = cooley_tukey(&x.iter().skip(1).step_by(2).cloned().collect::>()); @@ -37,14 +40,13 @@ fn cooley_tukey(x: &Vec>) -> Vec> { temp } -fn bit_reverse(x: &Vec>) -> Vec> { +fn bit_reverse(x: &[Complex]) -> Vec> { let n = x.len(); let mut temp = vec![Complex::new(0.0_f64, 0.0_f64); n]; for k in 0..n { let b: usize = (0..((n as f64).log2() as usize)) - .into_iter() - .filter(|i| (k << i & 1) != 0) - .map(|i| 1 << (((n as f64).log2()) as usize) - 1 - i) + .filter(|i| k >> i & 1 != 0) + .map(|i| 1 << ((((n as f64).log2()) as usize) - 1 - i)) .sum(); temp[k] = x[b]; temp[b] = x[k]; @@ -52,7 +54,7 @@ fn bit_reverse(x: &Vec>) -> Vec> { temp } -fn iterative_cooley_tukey(x: &Vec>) -> Vec> { +fn iterative_cooley_tukey(x: &[Complex]) -> Vec> { let n = x.len(); let mut new_x = bit_reverse(x); @@ -60,13 +62,14 @@ fn iterative_cooley_tukey(x: &Vec>) -> Vec> { for i in 1..=((n as f64).log2() as usize) { let stride = 2_u128.pow(i as u32); let w = (Complex::new(0.0_f64, -2.0_f64) * PI / (stride as f64)).exp(); - for j in (0..n).into_iter().step_by(stride as usize) { + for j in (0..n).step_by(stride as usize) { let mut v = Complex::new(1.0_f64, 0.0_f64); for k in 0..((stride / 2) as usize) { - new_x[k + j + ((stride / 2) as usize)] = - new_x[k + j] - v * new_x[k + j + ((stride / 2) as usize)]; - new_x[k + j] -= - new_x[k + j + ((stride / 2) as usize)].clone() - new_x[k + j].clone(); + let k_plus_j = new_x[k + j]; + let k_plus_j_plus_half_stride = new_x[k + j + ((stride / 2) as usize)]; + new_x[k + j + ((stride / 2) as usize)] = k_plus_j - v * k_plus_j_plus_half_stride; + let k_plus_j_plus_half_stride_2 = new_x[k + j + ((stride / 2) as usize)]; + new_x[k + j] -= k_plus_j_plus_half_stride_2 - k_plus_j; v *= w; } } @@ -77,19 +80,18 @@ fn iterative_cooley_tukey(x: &Vec>) -> Vec> { fn main() { let mut x = Vec::with_capacity(64); + let mut rng = thread_rng(); for _i in 0..64 { - let real: f64 = random(); + let real = rng.gen_range(0.0_f64, 1.0_f64); x.push(Complex::new(real, 0.0_f64)); } - let y = cooley_tukey(&x); - let z = iterative_cooley_tukey(&x); - let t = dft(&x); + let y = cooley_tukey(&x.clone()); + let z = iterative_cooley_tukey(&x.clone()); + let t = dft(&x.clone()); println!( "{}", - y.iter() - .zip(z.into_iter()) - .all(|i| (i.0 - i.1).norm() < 1.0) + y.iter().zip(z.iter()).all(|i| (i.0 - i.1).norm() < 1.0) ); println!( "{}", diff --git a/contents/cooley_tukey/cooley_tukey.md b/contents/cooley_tukey/cooley_tukey.md index b40923ca5..b42b6451b 100644 --- a/contents/cooley_tukey/cooley_tukey.md +++ b/contents/cooley_tukey/cooley_tukey.md @@ -87,6 +87,8 @@ For some reason, though, putting code to this transformation really helped me fi [import:15-74, lang:"asm-x64"](code/asm-x64/fft.s) {% sample lang="js" %} [import:3-15, lang:"javascript"](code/javascript/fft.js) +{% sample lang="rs" %} +[import:10-23, lang:"rust"](code/rust/fft.rs) {% endmethod %} In this function, we define `n` to be a set of integers from $$0 \rightarrow N-1$$ and arrange them to be a column. @@ -138,6 +140,8 @@ In the end, the code looks like: [import:76-165, lang:"asm-x64"](code/asm-x64/fft.s) {% sample lang="js" %} [import:17-39, lang="javascript"](code/javascript/fft.js) +{% sample lang="rs" %} +[import:25-41, lang:"rust"](code/rust/fft.rs) {% endmethod %} As a side note, we are enforcing that the array must be a power of 2 for the operation to work. @@ -249,6 +253,8 @@ Some rather impressive scratch code was submitted by Jie and can be found here: [import, lang:"asm-x64"](code/asm-x64/fft.s) {% sample lang="js" %} [import, lang:"javascript"](code/javascript/fft.js) +{% sample lang="rs" %} +[import, lang:"rust"](code/rust/fft.rs) {% endmethod %}