Mixed radix, begining rader
This commit is contained in:
39
src/fft/dft.rs
Normal file
39
src/fft/dft.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::complex::Complex32;
|
||||
use crate::fft::DFT;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct NaiveDFT {
|
||||
output_buffer: Box<[Complex32]>,
|
||||
input_buffer: Box<[Complex32]>,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl DFT for NaiveDFT {
|
||||
fn create(size: usize) -> Self
|
||||
where Self: Sized
|
||||
{
|
||||
NaiveDFT
|
||||
{
|
||||
output_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
input_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(&mut self)
|
||||
{
|
||||
self.output_buffer.iter_mut().enumerate().for_each(|(freq, out)|
|
||||
*out = self.input_buffer.iter().enumerate().fold(Complex32::zero(), |acc, (i, s)|
|
||||
acc + (*s * Complex32::cexp(- 2. * PI * (freq as f32 * i as f32 / self.size as f32)))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn get_input(&mut self) -> &mut [Complex32] {
|
||||
&mut self.input_buffer
|
||||
}
|
||||
|
||||
fn get_output(&self) -> &[Complex32] {
|
||||
&self.output_buffer
|
||||
}
|
||||
}
|
||||
@ -1,32 +1,106 @@
|
||||
// To perform a mixed radix cooley tuckey fft
|
||||
|
||||
use crate::{complex::Complex32, fft::DFT};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct MixedRadixFFT<'a> {
|
||||
input_buffer: &'a [Complex32],
|
||||
output_buffer: &'a mut [Complex32],
|
||||
use crate::{complex::Complex32, fft::{dft::NaiveDFT, DFT}};
|
||||
|
||||
pub struct MixedRadixFFT {
|
||||
input_buffer: Box<[Complex32]>,
|
||||
output_buffer: Box<[Complex32]>,
|
||||
size: usize,
|
||||
|
||||
p: usize,
|
||||
q: usize,
|
||||
twiddle_factors: Box<[Complex32]>,
|
||||
|
||||
qfft: Box<dyn DFT>,
|
||||
pfft: Box<dyn DFT>,
|
||||
|
||||
staging_buffer: Box<[Complex32]>
|
||||
}
|
||||
|
||||
impl<'a> DFT<'a> for MixedRadixFFT<'a> {
|
||||
fn create(size: usize, input: &'a [Complex32], output: &'a mut [Complex32]) -> Self {
|
||||
impl DFT for MixedRadixFFT {
|
||||
fn create(size: usize) -> Self {
|
||||
let q = decide_radix_factor(size);
|
||||
let p = size / q;
|
||||
let qfft = NaiveDFT::create(q);
|
||||
let pfft = NaiveDFT::create(p);
|
||||
|
||||
MixedRadixFFT {
|
||||
input_buffer: input,
|
||||
output_buffer: output,
|
||||
input_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
output_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
size,
|
||||
twiddle_factors: compute_twiddle_factors(q, p),
|
||||
qfft: Box::new(qfft),
|
||||
pfft: Box::new(pfft),
|
||||
|
||||
staging_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
p,
|
||||
q,
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(&mut self) {}
|
||||
fn execute(&mut self)
|
||||
{
|
||||
// Perform p ffts of size q
|
||||
for k0 in 0..self.p
|
||||
{
|
||||
// Copy samples into input buffer
|
||||
for k1 in 0..self.q
|
||||
{
|
||||
self.qfft.get_input()[k1] = self.input_buffer[k1 * self.p + k0];
|
||||
}
|
||||
|
||||
self.qfft.execute();
|
||||
|
||||
for j0 in 0..self.q
|
||||
{
|
||||
// "Store j0'th of k0'th fft into staging buffer"
|
||||
self.staging_buffer[k0 * self.q + j0] = self.qfft.get_output()[j0] * self.twiddle_factors[k0 * self.q + j0];
|
||||
}
|
||||
}
|
||||
|
||||
// Perform q ffts of size p
|
||||
for j0 in 0..self.q
|
||||
{
|
||||
// Copy input
|
||||
for k0 in 0..self.p
|
||||
{
|
||||
self.pfft.get_input()[k0] = self.staging_buffer[k0 * self.q + j0];
|
||||
}
|
||||
|
||||
self.pfft.execute();
|
||||
|
||||
for j1 in 0..self.p
|
||||
{
|
||||
self.output_buffer[j1 * self.q + j0] = self.pfft.get_output()[j1];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn get_input(&mut self) -> &mut [Complex32] {
|
||||
&mut self.input_buffer
|
||||
}
|
||||
|
||||
fn get_output(&self) -> &[Complex32] {
|
||||
&self.output_buffer
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_twiddle_factors(q: usize, p: usize) -> Box<[Complex32]>
|
||||
{
|
||||
let mut factors = vec![Complex32::zero(); q * p].into_boxed_slice();
|
||||
let n = p * q;
|
||||
|
||||
for i in 0..q
|
||||
{
|
||||
for j in 0..p
|
||||
{
|
||||
factors[i * p + j] = Complex32::cexp(2. * PI / (n as f32));
|
||||
}
|
||||
}
|
||||
factors
|
||||
}
|
||||
|
||||
// This will decide on a good factor to use for the mixed radix fft
|
||||
@ -68,6 +142,6 @@ fn prime_factors(n: usize) -> Vec<usize> {
|
||||
factors
|
||||
}
|
||||
|
||||
fn is_prime(n: usize) -> bool {
|
||||
pub fn is_prime(n: usize) -> bool {
|
||||
prime_factors(n).len() == 1
|
||||
}
|
||||
|
||||
160
src/fft/rader.rs
Normal file
160
src/fft/rader.rs
Normal file
@ -0,0 +1,160 @@
|
||||
// Implementation of raders's fft for prime sized ffts
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use crate::{complex::Complex32, fft::{dft::NaiveDFT, mixed_radix::is_prime, DFT}};
|
||||
use super::mixed_radix;
|
||||
|
||||
pub struct RaderFFT
|
||||
{
|
||||
input_buffer: Box<[Complex32]>,
|
||||
output_buffer: Box<[Complex32]>,
|
||||
|
||||
size: usize,
|
||||
g: usize,
|
||||
|
||||
// Fourrier transform of the exponential terms
|
||||
convolution_operand: Box<[Complex32]>,
|
||||
convolution_fft: NaiveDFT, // TODO: Use fft
|
||||
}
|
||||
|
||||
impl DFT for RaderFFT
|
||||
{
|
||||
fn create(size: usize) -> Self
|
||||
where Self: Sized {
|
||||
let g = compute_prime_primitive_root(size);
|
||||
RaderFFT
|
||||
{
|
||||
input_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
output_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
|
||||
size,
|
||||
g,
|
||||
|
||||
convolution_operand: compute_convolution_operand(size, g),
|
||||
convolution_fft: NaiveDFT::create(size - 1),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(&mut self) {
|
||||
|
||||
// Copy to convolution fft with permutation
|
||||
for i in 0..(self.size - 1)
|
||||
{
|
||||
self.convolution_fft.get_input()[i] = self.input_buffer[exp_mod(self.g, self.size - 1 - i - 1, self.size)];
|
||||
}
|
||||
|
||||
self.convolution_fft.execute();
|
||||
|
||||
// Convolve (use output buffer as staging buffer)
|
||||
self.output_buffer
|
||||
.iter_mut()
|
||||
.skip(1)
|
||||
.zip(self.convolution_operand.iter())
|
||||
.zip(self.convolution_fft.get_output().iter())
|
||||
.for_each(|((dest, a), b)| *dest = *a * *b);
|
||||
|
||||
// Add first sample as DC-offset
|
||||
//self.output_buffer[1] = self.output_buffer[1] + self.input_buffer[0];
|
||||
|
||||
// Copy to input
|
||||
self
|
||||
.convolution_fft
|
||||
.get_input()
|
||||
.iter_mut()
|
||||
.zip(self.output_buffer.iter().skip(1))
|
||||
.for_each(|(dest, x)| *dest = - *x);
|
||||
|
||||
self.convolution_fft.execute(); // Inverse fft
|
||||
|
||||
// Copy to output buffer : n - 1 terms to copy
|
||||
for i in 1..(self.size)
|
||||
{
|
||||
self.output_buffer[i] =
|
||||
self.convolution_fft.get_output()[exp_mod(self.g, i, self.size) - 1] / (self.size as f32 - 1.) + self.input_buffer[0];
|
||||
}
|
||||
self.output_buffer[0] = self.input_buffer.iter().copied().sum();
|
||||
}
|
||||
|
||||
fn get_input(&mut self) -> &mut [Complex32] {
|
||||
&mut self.input_buffer
|
||||
}
|
||||
|
||||
fn get_output(&self) -> &[Complex32] {
|
||||
&self.output_buffer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn compute_convolution_operand(n: usize, g: usize) -> Box<[Complex32]>
|
||||
{
|
||||
println!("TODO: Change to better fft");
|
||||
let mut fft = NaiveDFT::create(n - 1); //TODO: Use fft
|
||||
|
||||
fft.get_input().iter_mut().enumerate()
|
||||
.for_each(|(i, x)| *x = Complex32::cexp(- 2. * PI * (exp_mod(g, i + 1, n) as f32) / (n as f32)));
|
||||
fft.execute();
|
||||
fft.get_output().iter().map(|x| *x).collect::<Vec<_>>().into_boxed_slice()
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn compute_prime_primitive_root(n: usize) -> usize
|
||||
{
|
||||
assert!(is_prime(n));
|
||||
|
||||
let phi = n - 1; // Euler's totient for n prime
|
||||
|
||||
// Test all candidates
|
||||
for i in 1..(n + 1)
|
||||
{
|
||||
// Find multiplicative order of i
|
||||
let mut val = i;
|
||||
let mut order = 1;
|
||||
for j in 0..n
|
||||
{
|
||||
if val == 1
|
||||
{
|
||||
break;
|
||||
}
|
||||
val = (val * i) % n;
|
||||
order += 1;
|
||||
}
|
||||
|
||||
if order == phi
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!("Prime must have primitive root");
|
||||
}
|
||||
|
||||
pub fn exp_mod(n: usize, exp: usize, m: usize) -> usize
|
||||
{
|
||||
let mut num = n % m;
|
||||
let mut acc = 1;
|
||||
let mut exp = exp;
|
||||
if exp == 0
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
while exp != 1
|
||||
{
|
||||
if exp % 2 == 0
|
||||
{
|
||||
num = (num * num) % m;
|
||||
exp /= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
acc = (acc * n) % m;
|
||||
exp -= 1;
|
||||
num = num * num % m;
|
||||
exp /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
(num * acc) % m
|
||||
}
|
||||
@ -4,23 +4,23 @@ use crate::complex::Complex32;
|
||||
use crate::fft::DFT;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct Radix2FFT<'a> {
|
||||
output_buffer: &'a mut [Complex32],
|
||||
input_buffer: &'a [Complex32],
|
||||
pub struct Radix2FFT {
|
||||
output_buffer: Box<[Complex32]>,
|
||||
input_buffer: Box<[Complex32]>,
|
||||
size: usize,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl<'a> DFT<'a> for Radix2FFT<'a> {
|
||||
impl DFT for Radix2FFT {
|
||||
// Size as power of two
|
||||
fn create(size: usize, input: &'a [Complex32], output: &'a mut [Complex32]) -> Self {
|
||||
fn create(size: usize) -> Self {
|
||||
if !is_power_of_two(size) {
|
||||
panic!("Tried to create a Radix2 FFT with a non power of two size.");
|
||||
}
|
||||
|
||||
Radix2FFT {
|
||||
output_buffer: output,
|
||||
input_buffer: input,
|
||||
output_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
input_buffer: vec![Complex32::zero(); size].into_boxed_slice(),
|
||||
size: size.ilog2() as usize,
|
||||
length: size,
|
||||
}
|
||||
@ -32,7 +32,7 @@ impl<'a> DFT<'a> for Radix2FFT<'a> {
|
||||
*x = self.input_buffer[reverse_bits(i, self.size as u32)];
|
||||
}
|
||||
|
||||
for step in 1..self.size {
|
||||
for step in 1..(self.size + 1) {
|
||||
let pol_length = 2usize.pow(step as u32);
|
||||
let mid_point = pol_length / 2;
|
||||
for s in (0..(self.length / pol_length)).map(|i| i * pol_length) {
|
||||
@ -40,7 +40,7 @@ impl<'a> DFT<'a> for Radix2FFT<'a> {
|
||||
// Compute current polynomial at each unit root
|
||||
let a = self.output_buffer[s + i];
|
||||
let b = self.output_buffer[s + i + mid_point];
|
||||
let angle = 2. * PI * (i as f32) / (pol_length as f32);
|
||||
let angle = - 2. * PI * (i as f32) / (pol_length as f32);
|
||||
let phasor = Complex32::cexp(angle);
|
||||
self.output_buffer[i + s] = a + phasor * b;
|
||||
self.output_buffer[i + s + mid_point] = a - phasor * b;
|
||||
@ -48,6 +48,14 @@ impl<'a> DFT<'a> for Radix2FFT<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_input(&mut self) -> &mut [Complex32] {
|
||||
&mut self.input_buffer
|
||||
}
|
||||
|
||||
fn get_output(&self) -> &[Complex32] {
|
||||
&self.output_buffer
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities
|
||||
@ -56,7 +64,7 @@ pub fn reverse_bits(n: usize, bit_count: u32) -> usize {
|
||||
let mut output = 0usize;
|
||||
for _ in 0..bit_count {
|
||||
output <<= 1;
|
||||
output |= if (num & 1) == 1 { 0 } else { 1 };
|
||||
output |= num & 1;
|
||||
num >>= 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user