diff --git a/docs/src/implementations.md b/docs/src/implementations.md index 7367fd4..5c8303c 100644 --- a/docs/src/implementations.md +++ b/docs/src/implementations.md @@ -39,3 +39,13 @@ To define a new FFT implementation in your own module, you should 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 a `TestUtils` module to help with testing downstream implementations. + +```@docs +AbstractFFTs.TestUtils.test_complex_fft +AbstractFFTs.TestUtils.test_real_fft +AbstractFFTs.TestUtils.test_plan_adjoint +``` diff --git a/ext/AbstractFFTsTestUtilsExt.jl b/ext/AbstractFFTsTestUtilsExt.jl index ea49da1..1d6efc8 100644 --- a/ext/AbstractFFTsTestUtilsExt.jl +++ b/ext/AbstractFFTsTestUtilsExt.jl @@ -51,8 +51,7 @@ const TEST_CASES = ( dims=3)), ) -# Perform generic adjoint plan tests -function _adjoint_test(P, x; real_plan=false) +function TestUtils.test_plan_adjoint(P::AbstractFFTs.Plan, x::AbstractArray; real_plan=false) y = rand(eltype(P * x), size(P * x)) # test basic properties @test_broken eltype(P') === typeof(y) # (AbstractFFTs.jl#110) @@ -71,20 +70,6 @@ function _adjoint_test(P, x; real_plan=false) @test_throws MethodError mul!(x, P', y) end -""" - TestUtils.test_complex_fft(ArrayType=Array; test_real=true, test_inplace=true) - -Run tests to verify correctness of FFT/BFFT/IFFT functionality using a particular backend plan implementation. -The backend implementation is assumed to be loaded prior to calling this function. - -# Arguments - -- `ArrayType`: determines the `AbstractArray` implementation for - which the correctness tests are run. Arrays are constructed via - `convert(ArrayType, ...)`. -- `test_inplace=true`: whether to test in-place plans. -- `test_adjoint=true`: whether to test adjoints of plans. -""" function TestUtils.test_complex_fft(ArrayType=Array; test_inplace=true, test_adjoint=true) @testset "correctness of fft, bfft, ifft" begin for test_case in TEST_CASES @@ -111,7 +96,7 @@ function TestUtils.test_complex_fft(ArrayType=Array; test_inplace=true, test_adj @test mul!(_x_out, P, x_complexf) ≈ x_fft @test _x_out ≈ x_fft if test_adjoint - _adjoint_test(P, x_complexf) + TestUtils.test_plan_adjoint(P, x_complexf) end end if test_inplace @@ -145,7 +130,7 @@ function TestUtils.test_complex_fft(ArrayType=Array; test_inplace=true, test_adj @test mul!(_x_complexf, P, x_fft) ≈ x_scaled @test _x_complexf ≈ x_scaled if test_adjoint - _adjoint_test(P, x_complexf) + TestUtils.test_plan_adjoint(P, x_complexf) end end # test IIP plans @@ -176,7 +161,7 @@ function TestUtils.test_complex_fft(ArrayType=Array; test_inplace=true, test_adj @test mul!(_x_complexf, P, x_fft) ≈ x @test _x_complexf ≈ x if test_adjoint - _adjoint_test(P, x_complexf) + TestUtils.test_plan_adjoint(P, x_complexf) end end # test IIP plans @@ -195,20 +180,6 @@ function TestUtils.test_complex_fft(ArrayType=Array; test_inplace=true, test_adj end end -""" - TestUtils.test_real_fft(ArrayType=Array; test_real=true, test_inplace=true) - -Run tests to verify correctness of RFFT/BRFFT/IRFFT functionality using a particular backend plan implementation. -The backend implementation is assumed to be loaded prior to calling this function. - -# Arguments - -- `ArrayType`: determines the `AbstractArray` implementation for - which the correctness tests are run. Arrays are constructed via - `convert(ArrayType, ...)`. -- `test_inplace=true`: whether to test in-place plans. -- `test_adjoint=true`: whether to test adjoints of plans. -""" function TestUtils.test_real_fft(ArrayType=Array; test_inplace=true, test_adjoint=true) @testset "correctness of rfft, brfft, irfft" begin for test_case in TEST_CASES @@ -234,7 +205,7 @@ function TestUtils.test_real_fft(ArrayType=Array; test_inplace=true, test_adjoin @test mul!(_x_rfft, P, copy(x_real)) ≈ x_rfft @test _x_rfft ≈ x_rfft if test_adjoint - _adjoint_test(P, x_real; real_plan=true) + TestUtils.test_plan_adjoint(P, x_real; real_plan=true) end end diff --git a/src/TestUtils.jl b/src/TestUtils.jl index 50ae346..17ac76a 100644 --- a/src/TestUtils.jl +++ b/src/TestUtils.jl @@ -1,8 +1,47 @@ module TestUtils +""" + TestUtils.test_complex_fft(ArrayType=Array; test_real=true, test_inplace=true) + +Run tests to verify correctness of FFT/BFFT/IFFT functionality using a particular backend plan implementation. +The backend implementation is assumed to be loaded prior to calling this function. + +# Arguments + +- `ArrayType`: determines the `AbstractArray` implementation for + which the correctness tests are run. Arrays are constructed via + `convert(ArrayType, ...)`. +- `test_inplace=true`: whether to test in-place plans. +- `test_adjoint=true`: whether to test [plan adjoints](api.md#Base.adjoint). +""" function test_complex_fft end + +""" + TestUtils.test_real_fft(ArrayType=Array; test_real=true, test_inplace=true) + +Run tests to verify correctness of RFFT/BRFFT/IRFFT functionality using a particular backend plan implementation. +The backend implementation is assumed to be loaded prior to calling this function. + +# Arguments + +- `ArrayType`: determines the `AbstractArray` implementation for + which the correctness tests are run. Arrays are constructed via + `convert(ArrayType, ...)`. +- `test_inplace=true`: whether to test in-place plans. +- `test_adjoint=true`: whether to test [plan adjoints](api.md#Base.adjoint). +""" function test_real_fft end +""" + TestUtils.test_plan_adjoint(P::Plan, x::AbstractArray; real_plan=false) + +Test basic properties of the adjoint `P'` of a particular plan given an input array to the plan `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. + +""" +function test_plan_adjoint end + function __init__() if isdefined(Base, :Experimental) # Better error message if users forget to load Test diff --git a/src/definitions.jl b/src/definitions.jl index 23f4b16..f349349 100644 --- a/src/definitions.jl +++ b/src/definitions.jl @@ -608,7 +608,7 @@ Form the adjoint operator of an FFT plan. Returns a plan that performs the adjoi the original plan. Note that this differs from the corresponding backwards plan in the case of real FFTs due to the halving of one of the dimensions of the FFT output, as described in [`rfft`](@ref). -!!! note +!!! warning Adjoint plans do not currently support `LinearAlgebra.mul!`. Further, as a new addition to `AbstractFFTs`, coverage of `Base.adjoint` in downstream implementations may be limited. """