Skip to content

Commit

Permalink
Document test utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
gaurav-arya committed Jul 9, 2023
1 parent fe2793e commit 98fdcde
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 35 deletions.
10 changes: 10 additions & 0 deletions docs/src/implementations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
39 changes: 5 additions & 34 deletions ext/AbstractFFTsTestUtilsExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down
39 changes: 39 additions & 0 deletions src/TestUtils.jl
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/definitions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand Down

0 comments on commit 98fdcde

Please sign in to comment.