concision_core/ops/fft/
utils.rs

1/*
2   Appellation: utils <mod>
3   Contrib: FL03 <jo3mccain@icloud.com>
4*/
5use super::FftPlan;
6use crate::AsComplex;
7use num::complex::{Complex, ComplexFloat};
8use num::traits::{Float, FloatConst, NumAssignOps, NumCast, NumOps};
9
10pub(crate) fn fft_angle<T>(n: usize) -> T
11where
12    T: FloatConst + NumCast + NumOps,
13{
14    T::TAU() / T::from(n).unwrap()
15}
16
17/// Computes the Fast Fourier Transform of a one-dimensional, complex-valued signal.
18pub fn fft<S, T>(input: impl AsRef<[S]>, permute: &FftPlan) -> Vec<Complex<S::Real>>
19where
20    S: ComplexFloat<Real = T>,
21    S::Real: Float + FloatConst,
22    Complex<S::Real>: ComplexFloat<Real = S::Real> + NumOps<S> + NumOps<S::Real>,
23{
24    //
25    let input = input.as_ref();
26    //
27    let n = input.len();
28    // initialize the result vector
29    let mut result = Vec::with_capacity(n);
30    // store the input values in the result vector according to the permutation
31    for position in permute.clone().into_iter() {
32        let arg = input[position];
33        result.push(Complex::new(arg.re(), arg.im()));
34    }
35    let mut segment: usize = 1;
36    while segment < n {
37        segment <<= 1;
38        // compute the angle of the complex number
39        let angle = fft_angle::<T>(segment);
40        // compute the radius of the complex number (length)
41        let radius = Complex::new(angle.cos(), angle.sin());
42        // iterate over the signal in segments of length `segment`
43        for start in (0..n).step_by(segment) {
44            let mut w = Complex::new(T::one(), T::zero());
45            for position in start..(start + segment / 2) {
46                let a = result[position];
47                let b = result[position + segment / 2] * w;
48                result[position] = a + b;
49                result[position + segment / 2] = a - b;
50                w = w * radius;
51            }
52        }
53    }
54    result
55}
56
57/// Computes the Fast Fourier Transform of an one-dimensional, real-valued signal.
58/// TODO: Optimize the function to avoid unnecessary computation.
59pub fn rfft<T>(input: impl AsRef<[T]>, input_permutation: impl AsRef<[usize]>) -> Vec<Complex<T>>
60where
61    T: Float + FloatConst,
62    Complex<T>: ComplexFloat<Real = T> + NumAssignOps,
63{
64    // create a reference to the input
65    let input = input.as_ref();
66    // fetch the length of the input
67    let n = input.len();
68    // compute the size of the result vector
69    let size = (n - (n % 2)) / 2 + 1;
70    // initialize the output vector
71    let mut store = Vec::with_capacity(size);
72    // store the input values in the result vector according to the permutation
73    for position in input_permutation.as_ref() {
74        store.push(input[*position].as_re());
75    }
76    let mut segment: usize = 1;
77    while segment < n {
78        segment <<= 1;
79        // compute the angle of the complex number
80        let angle = fft_angle::<T>(segment);
81        // compute the radius of the complex number (length)
82        let radius = Complex::new(angle.cos(), angle.sin());
83        for start in (0..n).step_by(segment) {
84            let mut w = Complex::new(T::one(), T::zero());
85            for position in start..(start + segment / 2) {
86                let a = store[position];
87                let b = store[position + segment / 2] * w;
88                store[position] = a + b;
89                store[position + segment / 2] = a - b;
90                w *= radius;
91            }
92        }
93    }
94    store
95        .iter()
96        .cloned()
97        .filter(|x| x.im() >= T::zero())
98        .collect()
99}
100/// Computes the Inverse Fast Fourier Transform of an one-dimensional, complex-valued signal.
101pub fn ifft<S, T>(input: &[S], input_permutation: &FftPlan) -> Vec<Complex<T>>
102where
103    S: ComplexFloat<Real = T>,
104    T: Float + FloatConst,
105    Complex<T>: ComplexFloat<Real = T> + NumOps<S> + NumOps<T>,
106{
107    let n = input.len();
108    let mut result = Vec::with_capacity(n);
109    for position in input_permutation.clone().into_iter() {
110        let arg = input[position];
111        result.push(Complex::new(arg.re(), arg.im()));
112    }
113    let mut length: usize = 1;
114    while length < n {
115        length <<= 1;
116        let angle = fft_angle::<T>(length).neg();
117        let radius = Complex::new(T::cos(angle), T::sin(angle)); // w_len
118        for start in (0..n).step_by(length) {
119            let mut w = Complex::new(T::one(), T::zero());
120            for position in start..(start + length / 2) {
121                let a = result[position];
122                let b = result[position + length / 2] * w;
123                result[position] = a + b;
124                result[position + length / 2] = a - b;
125                w = w * radius;
126            }
127        }
128    }
129    let scale = T::from(n).unwrap().recip();
130    result.iter().map(|x| *x * scale).collect()
131}
132/// Computes the Inverse Fast Fourier Transform of an one-dimensional, real-valued signal.
133/// TODO: Fix the function; currently fails to compute the correct result
134pub fn irfft<T>(input: &[Complex<T>], plan: &FftPlan) -> Vec<T>
135where
136    T: Float + FloatConst,
137    Complex<T>: ComplexFloat<Real = T> + NumAssignOps,
138{
139    let n = input.len();
140    let mut result = vec![Complex::new(T::zero(), T::zero()); n];
141
142    for position in plan.clone().into_iter() {
143        result.push(input[position]);
144    }
145    // for res in result.clone() {
146    //     if res.im() > T::zero() {
147    //         result.push(res.conj());
148    //     }
149    // }
150    // segment length
151    let mut segment: usize = 1;
152    while segment < n {
153        segment <<= 1;
154        // compute the angle of the complex number
155        let angle = fft_angle::<T>(segment).neg();
156        // compute the radius of the complex number (length)
157        let radius = Complex::new(T::cos(angle), T::sin(angle));
158        for start in (0..n).step_by(segment) {
159            let mut w = Complex::new(T::one(), T::zero());
160            for position in start..(start + segment / 2) {
161                let a = result[position];
162                let b = result[position + segment / 2] * w;
163                result[position] = a + b;
164                result[position + segment / 2] = a - b;
165                w *= radius;
166            }
167        }
168    }
169    let scale = T::from(n).unwrap().recip();
170    result.iter().map(|x| x.re() * scale).collect()
171}
172
173#[doc(hidden)]
174/// Generates a permutation for the Fast Fourier Transform.
175pub fn fft_permutation(length: usize) -> Vec<usize> {
176    let mut result = Vec::new();
177    result.reserve_exact(length);
178    for i in 0..length {
179        result.push(i);
180    }
181    let mut reverse = 0_usize;
182    let mut position = 1_usize;
183    while position < length {
184        let mut bit = length >> 1;
185        while bit & reverse != 0 {
186            reverse ^= bit;
187            bit >>= 1;
188        }
189        reverse ^= bit;
190        // This is equivalent to adding 1 to a reversed number
191        if position < reverse {
192            // Only swap each element once
193            result.swap(position, reverse);
194        }
195        position += 1;
196    }
197    result
198}