FFT Implementations
Existing packages
The following packages extend the functionality provided by AbstractFFTs:
- FFTW.jl: Bindings for the FFTW library. This also used to be part of Base Julia.
- FastTransforms.jl: Pure-Julia implementation of FFT, with support for arbitrary AbstractFloat types.
Defining a new implementation
To define a new FFT implementation in your own module, you should
Define a new subtype (e.g.
MyPlan) ofAbstractFFTs.Plan{T}for FFTs and related transforms on arrays ofT. This must have apinv::Planfield, initially undefined when aMyPlanis created, that is used for caching the inverse plan.Define a new method
AbstractFFTs.plan_fft(x, region; kws...)that returns aMyPlanfor at least some types ofxand some set of dimensionsregion. Theregion(or a copy thereof) should be accessible viafftdims(p::MyPlan)(which defaults top.region), and the input sizesize(x)should be accessible viasize(p::MyPlan).Define a method of
LinearAlgebra.mul!(y, p::MyPlan, x)that computes the transformpofxand stores the result iny.Define a method of
*(p::MyPlan, x), which can simply call yourmul!method. This is not defined generically in this package due to subtleties that arise for in-place and real-input FFTs.If the inverse transform is implemented, you should also define
plan_inv(p::MyPlan), which should construct the inverse plan top, andplan_bfft(x, region; kws...)for an unnormalized inverse ("backwards") transform ofx. Implementations only need to provide the unnormalized backwards FFT, similar to FFTW, and we do the scaling generically to get the inverse FFT.You can also define similar methods of
plan_rfftandplan_brfftfor real-input FFTs.To support adjoints in a new plan, define the trait
AbstractFFTs.AdjointStyle.AbstractFFTsimplements the following adjoint styles:AbstractFFTs.FFTAdjointStyle,AbstractFFTs.RFFTAdjointStyle,AbstractFFTs.IRFFTAdjointStyle, andAbstractFFTs.UnitaryAdjointStyle. To define a new adjoint style, define the methodAbstractFFTs.adjoint_mul.
The normalization convention for your FFT should be that it computes $y_k = \sum_j x_j \exp(-2\pi i j k/n)$ for a transform of length $n$, and the "backwards" (unnormalized inverse) transform computes the same thing but with $\exp(+2\pi i jk/n)$.
Testing implementations
AbstractFFTs.jl provides an experimental TestUtils module to help with testing downstream implementations, available as a weak extension of Test. The following functions test that all FFT functionality has been correctly implemented:
AbstractFFTs.TestUtils.test_complex_ffts — FunctionTestUtils.test_complex_ffts(ArrayType=Array; test_inplace=true, test_adjoint=true)Run tests to verify correctness of FFT, BFFT, and IFFT functionality using a particular backend plan implementation. The backend implementation is assumed to be loaded prior to calling this function.
Arguments
ArrayType: determines theAbstractArrayimplementation for which the correctness tests are run. Arrays are constructed viaconvert(ArrayType, ...).test_inplace=true: whether to test in-place plans.test_adjoint=true: whether to test plan adjoints.
AbstractFFTs.TestUtils.test_real_ffts — FunctionTestUtils.test_real_ffts(ArrayType=Array; test_adjoint=true, copy_input=false)Run tests to verify correctness of RFFT, BRFFT, and IRFFT functionality using a particular backend plan implementation. The backend implementation is assumed to be loaded prior to calling this function.
Arguments
ArrayType: determines theAbstractArrayimplementation for which the correctness tests are run. Arrays are constructed viaconvert(ArrayType, ...).test_adjoint=true: whether to test plan adjoints.copy_input=false: whether to copy the input before applying the plan in tests, to accomodate for input-mutating behaviour of real FFTW plans.
TestUtils also exposes lower level functions for generically testing particular plans:
AbstractFFTs.TestUtils.test_plan — FunctionTestUtils.test_plan(P::Plan, x::AbstractArray, x_transformed::AbstractArray;
inplace_plan=false, copy_input=false)Test basic properties of a plan P given an input array x and expected output x_transformed.
Because real FFTW plans may mutate their input in some cases, we allow specifying copy_input=true to allow for this behaviour in tests by copying the input before applying the plan.
AbstractFFTs.TestUtils.test_plan_adjoint — FunctionTestUtils.test_plan_adjoint(P::Plan, x::AbstractArray; real_plan=false, copy_input=false)Test basic properties of the adjoint P' of a particular plan given an input array x, including its accuracy via the dot test.
Real-to-complex and complex-to-real plans require a slightly modified dot test, in which case real_plan=true should be provided. The plan is assumed out-of-place, as adjoints are not yet supported for in-place plans. Because real FFTW plans may mutate their input in some cases, we allow specifying copy_input=true to allow for this behaviour in tests by copying the input before applying the plan.