#![warn(missing_docs)]
#![warn(missing_doc_code_examples)]
extern crate ndarray;
extern crate rustfft;
use ndarray::{Array1, ArrayBase, Dimension, RemoveAxis, Zip};
use ndarray::{Data, DataMut};
pub use rustfft::num_complex::Complex;
pub use rustfft::num_traits::Zero;
pub use rustfft::FftNum;
use rustfft::FftPlanner;
use std::sync::Arc;
macro_rules! create_transform {
(
$(#[$meta:meta])* $i: ident, $a: ty, $b: ty, $h: ty, $p: ident
) => {
$(#[$meta])*
pub fn $i<R, S, T, D>(
input: &mut ArrayBase<R, D>,
output: &mut ArrayBase<S, D>,
handler: &mut $h,
axis: usize,
) where
T: FftNum,
R: Data<Elem = $a>,
S: Data<Elem = $b> + DataMut,
D: Dimension + RemoveAxis,
{
let outer_axis = input.ndim() - 1;
if axis == outer_axis {
Zip::from(input.rows())
.and(output.rows_mut())
.for_each(|x, mut y| {
handler.$p(x.as_slice().unwrap(), y.as_slice_mut().unwrap());
});
} else {
let mut outvec = Array1::zeros(output.shape()[axis]);
input.swap_axes(outer_axis, axis);
output.swap_axes(outer_axis, axis);
Zip::from(input.rows())
.and(output.rows_mut())
.for_each(|x, mut y| {
handler.$p(&x.to_vec(), outvec.as_slice_mut().unwrap());
y.assign(&outvec);
});
input.swap_axes(outer_axis, axis);
output.swap_axes(outer_axis, axis);
}
}
};
}
macro_rules! create_transform_par {
($(#[$meta:meta])* $i: ident, $a: ty, $b: ty, $h: ty, $p: ident) => {
$(#[$meta])*
pub fn $i<R, S, T, D>(
input: &mut ArrayBase<R, D>,
output: &mut ArrayBase<S, D>,
handler: &mut $h,
axis: usize,
) where
T: FftNum,
R: Data<Elem = $a>,
S: Data<Elem = $b> + DataMut,
D: Dimension + RemoveAxis,
{
let outer_axis = input.ndim() - 1;
if axis == outer_axis {
Zip::from(input.rows())
.and(output.rows_mut())
.par_for_each(|x, mut y| {
handler.$p(x.as_slice().unwrap(), y.as_slice_mut().unwrap());
});
} else {
let n = output.shape()[axis];
input.swap_axes(outer_axis, axis);
output.swap_axes(outer_axis, axis);
Zip::from(input.rows())
.and(output.rows_mut())
.par_for_each(|x, mut y| {
let mut outvec = Array1::zeros(n);
handler.$p(&x.to_vec(), outvec.as_slice_mut().unwrap());
y.assign(&outvec);
});
input.swap_axes(outer_axis, axis);
output.swap_axes(outer_axis, axis);
}
}
};
}
#[derive(Clone)]
pub struct FftHandler<T> {
n: usize,
m: usize,
plan_fwd: Arc<dyn rustfft::Fft<T>>,
plan_bwd: Arc<dyn rustfft::Fft<T>>,
buffer: Vec<Complex<T>>,
}
impl<T: FftNum> FftHandler<T> {
#[allow(clippy::similar_names)]
#[must_use]
pub fn new(n: usize) -> Self {
let mut planner = FftPlanner::<T>::new();
let fwd = planner.plan_fft_forward(n);
let bwd = planner.plan_fft_inverse(n);
let buffer = vec![Complex::zero(); n];
FftHandler::<T> {
n,
m: n / 2 + 1,
plan_fwd: Arc::clone(&fwd),
plan_bwd: Arc::clone(&bwd),
buffer,
}
}
fn fft_lane(&self, data: &[Complex<T>], out: &mut [Complex<T>]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.n, out.len());
for (b, d) in out.iter_mut().zip(data.iter()) {
*b = *d;
}
self.plan_fwd.process(out);
}
#[allow(clippy::cast_precision_loss)]
fn ifft_lane(&self, data: &[Complex<T>], out: &mut [Complex<T>]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.n, out.len());
for (b, d) in out.iter_mut().zip(data.iter()) {
*b = *d;
}
self.plan_bwd.process(out);
let n64 = T::from_f64(1. / self.n as f64).unwrap();
for b in out.iter_mut() {
*b = *b * n64;
}
}
fn fft_r2c_lane(&mut self, data: &[T], out: &mut [Complex<T>]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.m, out.len());
for (b, d) in self.buffer.iter_mut().zip(data.iter()) {
*b = Complex::new(*d, T::zero());
}
self.plan_fwd.process(&mut self.buffer);
for (b, d) in self.buffer[0..=self.n / 2].iter().zip(out.iter_mut()) {
*d = *b;
}
}
fn fft_r2c_lane_par(&self, data: &[T], out: &mut [Complex<T>]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.m, out.len());
let mut buffer = vec![Complex::zero(); self.n];
for (b, d) in buffer.iter_mut().zip(data.iter()) {
*b = Complex::new(*d, T::zero());
}
self.plan_fwd.process(&mut buffer);
for (b, d) in buffer[0..=self.n / 2].iter().zip(out.iter_mut()) {
*d = *b;
}
}
#[allow(clippy::cast_precision_loss)]
fn ifft_r2c_lane(&mut self, data: &[Complex<T>], out: &mut [T]) {
Self::assert_size(self.m, data.len());
Self::assert_size(self.n, out.len());
let m = data.len();
for (b, d) in self.buffer[..m].iter_mut().zip(data.iter()) {
*b = *d;
}
for (b, d) in self.buffer[m..].iter_mut().zip(data[1..].iter()) {
b.re = d.re;
b.im = -d.im;
}
self.plan_bwd.process(&mut self.buffer);
let n64 = T::from_f64(1. / self.n as f64).unwrap();
for (b, d) in self.buffer.iter().zip(out.iter_mut()) {
*d = b.re * n64;
}
}
#[allow(clippy::cast_precision_loss)]
fn ifft_r2c_lane_par(&self, data: &[Complex<T>], out: &mut [T]) {
Self::assert_size(self.m, data.len());
let m = data.len();
let mut buffer = vec![Complex::zero(); self.n];
for (b, d) in buffer[..m].iter_mut().zip(data.iter()) {
*b = *d;
}
for (b, d) in buffer[m..].iter_mut().zip(data[1..].iter()) {
b.re = d.re;
b.im = -d.im;
}
self.plan_bwd.process(&mut buffer);
let n64 = T::from_f64(1. / self.n as f64).unwrap();
for (b, d) in buffer.iter().zip(out.iter_mut()) {
*d = b.re * n64;
}
}
#[allow(clippy::cast_precision_loss)]
fn fft_r2hc_lane(&mut self, data: &[T], out: &mut [T]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.n, out.len());
for (b, d) in self.buffer.iter_mut().zip(data.iter()) {
*b = Complex::new(*d, T::zero());
}
self.plan_fwd.process(&mut self.buffer);
out[0] = self.buffer[0].re;
out[self.n / 2] = self.buffer[self.n / 2].re;
let (left, right) = out.split_at_mut(self.n / 2);
for (b, (d1, d2)) in self.buffer[1..self.n / 2]
.iter()
.zip(left[1..].iter_mut().zip(right[1..].iter_mut().rev()))
{
*d1 = b.re;
*d2 = b.im;
}
}
#[allow(clippy::cast_precision_loss)]
fn fft_r2hc_lane_par(&self, data: &[T], out: &mut [T]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.n, out.len());
let mut buffer = vec![Complex::zero(); self.n];
for (b, d) in buffer.iter_mut().zip(data.iter()) {
*b = Complex::new(*d, T::zero());
}
self.plan_fwd.process(&mut buffer);
out[0] = buffer[0].re;
out[self.n / 2] = buffer[self.n / 2].re;
let (left, right) = out.split_at_mut(self.n / 2);
for (b, (d1, d2)) in buffer[1..self.n / 2]
.iter()
.zip(left[1..].iter_mut().zip(right[1..].iter_mut().rev()))
{
*d1 = b.re;
*d2 = b.im;
}
}
#[allow(clippy::cast_precision_loss, clippy::shadow_unrelated)]
fn ifft_r2hc_lane(&mut self, data: &[T], out: &mut [T]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.n, out.len());
self.buffer[0].re = data[0];
self.buffer[0].im = T::zero();
self.buffer[self.n / 2].re = data[self.n / 2];
self.buffer[self.n / 2].im = T::zero();
let (left, right) = data.split_at(self.n / 2);
for (b, (d1, d2)) in self.buffer[1..self.n / 2]
.iter_mut()
.zip(left[1..].iter().zip(right[1..].iter().rev()))
{
b.re = *d1;
b.im = *d2;
}
let (left, right) = self.buffer.split_at_mut(self.n / 2);
for (r, l) in right[1..].iter_mut().zip(left[1..].iter()) {
r.re = l.re;
r.im = -l.im;
}
self.plan_bwd.process(&mut self.buffer);
let n64 = T::from_f64(1. / self.n as f64).unwrap();
for (b, d) in self.buffer.iter().zip(out.iter_mut()) {
*d = b.re * n64;
}
}
#[allow(clippy::cast_precision_loss, clippy::shadow_unrelated)]
fn ifft_r2hc_lane_par(&self, data: &[T], out: &mut [T]) {
Self::assert_size(self.n, data.len());
Self::assert_size(self.n, out.len());
let mut buffer = vec![Complex::zero(); self.n];
buffer[0].re = data[0];
buffer[self.n / 2].re = data[self.n / 2];
let (left, right) = data.split_at(self.n / 2);
for (b, (d1, d2)) in buffer[1..self.n / 2]
.iter_mut()
.zip(left[1..].iter().zip(right[1..].iter().rev()))
{
b.re = *d1;
b.im = *d2;
}
let (left, right) = buffer.split_at_mut(self.n / 2);
for (r, l) in right[1..].iter_mut().zip(left[1..].iter()) {
r.re = l.re;
r.im = -l.im;
}
self.plan_bwd.process(&mut buffer);
let n64 = T::from_f64(1. / self.n as f64).unwrap();
for (b, d) in buffer.iter().zip(out.iter_mut()) {
*d = b.re * n64;
}
}
fn assert_size(n: usize, size: usize) {
assert!(
n == size,
"Size mismatch in fft, got {} expected {}",
size,
n
);
}
}
create_transform!(
ndfft,
Complex<T>,
Complex<T>,
FftHandler<T>,
fft_lane
);
create_transform!(
ndifft,
Complex<T>,
Complex<T>,
FftHandler<T>,
ifft_lane
);
create_transform!(
ndfft_r2c,
T,
Complex<T>,
FftHandler<T>,
fft_r2c_lane
);
create_transform!(
ndifft_r2c,
Complex<T>,
T,
FftHandler<T>,
ifft_r2c_lane
);
create_transform!(
ndfft_r2hc,
T,
T,
FftHandler<T>,
fft_r2hc_lane
);
create_transform!(
ndifft_r2hc,
T,
T,
FftHandler<T>,
ifft_r2hc_lane
);
create_transform_par!(
ndfft_par,
Complex<T>,
Complex<T>,
FftHandler<T>,
fft_lane
);
create_transform_par!(
ndifft_par,
Complex<T>,
Complex<T>,
FftHandler<T>,
ifft_lane
);
create_transform_par!(
ndfft_r2c_par,
T,
Complex<T>,
FftHandler<T>,
fft_r2c_lane_par
);
create_transform_par!(
ndifft_r2c_par,
Complex<T>,
T,
FftHandler<T>,
ifft_r2c_lane_par
);
create_transform_par!(
ndfft_r2hc_par,
T,
T,
FftHandler<T>,
fft_r2hc_lane_par
);
create_transform_par!(
ndifft_r2hc_par,
T,
T,
FftHandler<T>,
ifft_r2hc_lane_par
);
#[derive(Clone)]
pub struct DctHandler<T> {
n: usize,
plan: Arc<dyn rustfft::Fft<T>>,
buffer: Vec<Complex<T>>,
}
impl<T: FftNum> DctHandler<T> {
#[must_use]
pub fn new(n: usize) -> Self {
let m = 2 * (n - 1);
let mut planner = FftPlanner::<T>::new();
let fft = planner.plan_fft_forward(m);
let buffer = vec![Complex::zero(); m];
DctHandler::<T> {
n,
plan: Arc::clone(&fft),
buffer,
}
}
fn dct1_lane(&mut self, data: &[T], out: &mut [T]) {
self.assert_size(data.len());
let m = self.buffer.len();
for b in &mut self.buffer.iter_mut() {
b.re = T::zero();
b.im = T::zero();
}
self.buffer[0] = Complex::new(data[0], T::zero());
for (i, d) in data[1..].iter().enumerate() {
self.buffer[i + 1] = Complex::new(*d, T::zero());
self.buffer[m - i - 1] = Complex::new(*d, T::zero());
}
self.plan.process(&mut self.buffer);
out[0] = self.buffer[0].re;
for (i, d) in out[1..].iter_mut().enumerate() {
*d = self.buffer[i + 1].re;
}
}
fn dct1_lane_par(&self, data: &[T], out: &mut [T]) {
self.assert_size(data.len());
let m = 2 * (self.n - 1);
let mut buffer = vec![Complex::zero(); m];
buffer[0] = Complex::new(data[0], T::zero());
for (i, d) in data[1..].iter().enumerate() {
buffer[i + 1] = Complex::new(*d, T::zero());
buffer[m - i - 1] = Complex::new(*d, T::zero());
}
self.plan.process(&mut buffer);
out[0] = buffer[0].re;
for (i, d) in out[1..].iter_mut().enumerate() {
*d = buffer[i + 1].re;
}
}
fn assert_size(&self, size: usize) {
assert!(
self.n == size,
"Size mismatch in dct, got {} expected {}",
size,
self.n
);
}
}
create_transform!(
nddct1,
T,
T,
DctHandler<T>,
dct1_lane
);
create_transform_par!(
nddct1_par,
T,
T,
DctHandler<T>,
dct1_lane_par
);
#[cfg(test)]
mod test {
use super::*;
use ndarray::Array2;
#[test]
fn test_fft() {
let (nx, ny) = (6, 4);
let mut data = Array2::<Complex<f64>>::zeros((nx, ny));
let mut vhat = Array2::<Complex<f64>>::zeros((nx, ny));
for (i, v) in data.iter_mut().enumerate() {
v.re = i as f64;
v.im = -1.0 * i as f64;
}
let mut handler: FftHandler<f64> = FftHandler::new(ny);
let expected = data.clone();
ndfft(&mut data, &mut vhat, &mut handler, 1);
ndifft(&mut vhat, &mut data, &mut handler, 1);
let dif = 1e-6;
for (a, b) in expected.iter().zip(data.iter()) {
if (a.re - b.re).abs() > dif {
panic!("Large difference of values, got {} expected {}.", b, a)
}
if (a.im - b.im).abs() > dif {
panic!("Large difference of values, got {} expected {}.", b, a)
}
}
}
#[test]
fn test_fft_r2c() {
let (nx, ny) = (6, 4);
let mut data = Array2::<f64>::zeros((nx, ny));
let mut vhat = Array2::<Complex<f64>>::zeros((nx, ny / 2 + 1));
for (i, v) in data.iter_mut().enumerate() {
*v = i as f64 + i.pow(2) as f64;
}
let mut handler: FftHandler<f64> = FftHandler::new(ny);
let expected = data.clone();
ndfft_r2c(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 1);
ndifft_r2c(&mut vhat.view_mut(), &mut data.view_mut(), &mut handler, 1);
let dif = 1e-6;
for (a, b) in expected.iter().zip(data.iter()) {
if (a - b).abs() > dif {
panic!("Large difference of values, got {} expected {}.", b, a)
}
}
}
#[test]
fn test_fft_r2hc() {
let (nx, ny) = (6, 4);
let mut data = Array2::<f64>::zeros((nx, ny));
let mut vhat = Array2::<f64>::zeros((nx, ny));
for (i, v) in data.iter_mut().enumerate() {
*v = i as f64 + i.pow(2) as f64;
}
let mut handler: FftHandler<f64> = FftHandler::new(ny);
let expected = data.clone();
ndfft_r2hc(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 1);
ndifft_r2hc(&mut vhat.view_mut(), &mut data.view_mut(), &mut handler, 1);
let dif = 1e-6;
for (a, b) in expected.iter().zip(data.iter()) {
if (a - b).abs() > dif {
panic!("Large difference of values, got {} expected {}.", b, a)
}
}
}
#[test]
fn test_fft_r2c_vs_r2hc() {
use ndarray::s;
let nx = 6;
let mut data = Array1::<f64>::zeros(nx);
let mut vhat_r2c = Array1::<Complex<f64>>::zeros(nx / 2 + 1);
for (i, v) in data.iter_mut().enumerate() {
*v = i as f64;
}
let mut handler: FftHandler<f64> = FftHandler::new(nx);
ndfft_r2c(
&mut data.view_mut(),
&mut vhat_r2c.view_mut(),
&mut handler,
0,
);
let mut vhat_r2hc = Array1::<f64>::zeros(nx);
ndfft_r2hc(
&mut data.view_mut(),
&mut vhat_r2hc.view_mut(),
&mut handler,
0,
);
assert!(vhat_r2hc[0] == vhat_r2c[0].re);
assert!(vhat_r2hc[nx / 2] == vhat_r2c[nx / 2].re);
for (b, d) in vhat_r2c
.slice(s![1..nx / 2])
.iter()
.zip(vhat_r2hc.slice(s![1..nx / 2]).iter())
{
assert!(*d == b.re);
}
for (b, d) in vhat_r2c
.slice(s![1..nx / 2])
.iter()
.zip(vhat_r2hc.slice(s![nx / 2 + 1..]).iter().rev())
{
assert!(*d == b.im);
}
}
#[test]
fn test_fft_r2c_serial_vs_parallel() {
let (nx, ny) = (6, 4);
let mut data = Array2::<f64>::zeros((nx, ny));
let mut vhat = Array2::<Complex<f64>>::zeros((nx, ny / 2 + 1));
let mut vhat_par = Array2::<Complex<f64>>::zeros((nx, ny / 2 + 1));
for (i, v) in data.iter_mut().enumerate() {
*v = i as f64;
}
let mut handler: FftHandler<f64> = FftHandler::new(ny);
ndfft_r2c(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 1);
ndfft_r2c_par(
&mut data.view_mut(),
&mut vhat_par.view_mut(),
&mut handler,
1,
);
let dif = 1e-6;
for (a, b) in vhat.iter().zip(vhat_par.iter()) {
if (a.re - b.re).abs() > dif {
panic!("Large difference of values, got {} expected {}.", b, a)
}
if (a.im - b.im).abs() > dif {
panic!("Large difference of values, got {} expected {}.", b, a)
}
}
}
#[test]
fn test_dct_serial_vs_parallel() {
let (nx, ny) = (6, 4);
let mut data = Array2::<f64>::zeros((nx, ny));
let mut vhat = Array2::<f64>::zeros((nx, ny));
let mut vhat_par = Array2::<f64>::zeros((nx, ny));
for (i, v) in data.iter_mut().enumerate() {
*v = i as f64;
}
let mut handler: DctHandler<f64> = DctHandler::new(nx);
nddct1(&mut data, &mut vhat, &mut handler, 0);
nddct1_par(&mut data, &mut vhat_par, &mut handler, 0);
let dif = 1e-6;
for (a, b) in vhat.iter().zip(vhat_par.iter()) {
if (a - b).abs() > dif {
panic!("Large difference of values, got {} expected {}.", b, a)
}
}
}
}