Skip to content

Commit bf0be1e

Browse files
authored
Merge pull request #4 from JuliaMath/aa/tests
Set up testing
2 parents bc16ae1 + dee2b60 commit bf0be1e

File tree

4 files changed

+107
-16
lines changed

4 files changed

+107
-16
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ notifications:
1212
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
1313
# - julia -e 'Pkg.clone(pwd()); Pkg.build("AbstractFFTs"); Pkg.test("AbstractFFTs"; coverage=true)'
1414
after_success:
15-
# - julia -e 'Pkg.add("Coverage"); cd(Pkg.dir("AbstractFFTs")); using Coverage; Coveralls.submit(Coveralls.process_folder())'
16-
# - julia -e 'Pkg.add("Documenter"); cd(Pkg.dir("AbstractFFTs")); include(joinpath("docs", "make.jl"))'
15+
- julia -e 'Pkg.add("Coverage"); cd(Pkg.dir("AbstractFFTs")); using Coverage; Coveralls.submit(Coveralls.process_folder())'
16+
- julia -e 'Pkg.add("Documenter"); cd(Pkg.dir("AbstractFFTs")); include(joinpath("docs", "make.jl"))'

README.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,38 @@
22

33
A general framework for fast Fourier transforms (FFTs) in Julia.
44

5-
This package is mainly not intended to be used directly. Instead, developers of packages that implement FFTs (such as [FFTW.jl](https://github.com/JuliaMath/FFTW.jl)) extend the types/functions defined in `AbstractFFTs`. This multiple FFT packages to co-exist with the same underlying `fft(x)` and `plan_fft(x)` interface.
5+
[![Travis](https://travis-ci.org/JuliaMath/AbstractFFTs.jl.svg?branch=master)](https://travis-ci.org/JuliaMath/AbstractFFTs.jl)
6+
[![Coveralls](https://coveralls.io/repos/github/JuliaMath/AbstractFFTs.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaMath/AbstractFFTs.jl?branch=master)
7+
8+
Documentation:
9+
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaMath.github.io/AbstractFFTs.jl/stable)
10+
[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://JuliaMath.github.io/AbstractFFTs.jl/latest)
11+
12+
This package is mainly not intended to be used directly.
13+
Instead, developers of packages that implement FFTs (such as [FFTW.jl](https://github.com/JuliaMath/FFTW.jl))
14+
extend the types/functions defined in `AbstractFFTs`.
15+
This allows multiple FFT packages to co-exist with the same underlying `fft(x)` and `plan_fft(x)` interface.
616

717
## Developer information
818

919
To define a new FFT implementation in your own module, you should
1020

11-
* Define a new subtype (e.g. `MyPlan`) of `AbstractFFTs.Plan{T}` for FFTs and related transforms on arrays of `T`. This must have a `pinv::Plan` field, initially undefined when a `MyPlan` is created, that is used for caching the inverse plan.
21+
* Define a new subtype (e.g. `MyPlan`) of `AbstractFFTs.Plan{T}` for FFTs and related transforms on arrays of `T`.
22+
This must have a `pinv::Plan` field, initially undefined when a `MyPlan` is created, that is used for caching the
23+
inverse plan.
1224

13-
* Define a new method `AbstractFFTs.plan_fft(x, region; kws...)` that returns a `MyPlan` for at least some types of `x` and some set of dimensions `region`.
25+
* Define a new method `AbstractFFTs.plan_fft(x, region; kws...)` that returns a `MyPlan` for at least some types of
26+
`x` and some set of dimensions `region`.
1427

1528
* Define a method of `A_mul_B!(y, p::MyPlan, x)` that computes the transform `p` of `x` and stores the result in `y`.
1629

17-
* If the inverse transform is implemented, you should also define `plan_inv(p::MyPlan)`, which should construct the inverse plan to `p`, and `plan_bfft(x, region; kws...)` for an unnormalized inverse ("backwards") transform of `x`.
30+
* Define a method of `*(p::MyPlan, x)`, which can simply call your `A_mul_B!` method.
31+
This is not defined generically in this package due to subtleties that arise for in-place and real-input FFTs.
32+
33+
* If the inverse transform is implemented, you should also define `plan_inv(p::MyPlan)`, which should construct the
34+
inverse plan to `p`, and `plan_bfft(x, region; kws...)` for an unnormalized inverse ("backwards") transform of `x`.
1835

1936
* You can also define similar methods of `plan_rfft` and `plan_brfft` for real-input FFTs.
2037

21-
The normalization convention for your FFT should be that it computes yₖ = ∑ⱼ xⱼ exp(-2πi jk/n) for a transform of length n, and the "backwards" (unnormalized inverse) transform computes the same thing but with exp(+2πi jk/n).
38+
The normalization convention for your FFT should be that it computes yₖ = ∑ⱼ xⱼ exp(-2πi jk/n) for a transform of
39+
length n, and the "backwards" (unnormalized inverse) transform computes the same thing but with exp(+2πi jk/n).

src/AbstractFFTs.jl

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@ using Base.LinAlg: BlasReal
77
import Base: show, summary, size, ndims, length, eltype,
88
*, A_mul_B!, inv, \, A_ldiv_B!
99

10-
if isdefined(Base, :DFT)
11-
import Base.DFT: fft, ifft, bfft, fft!, ifft!, bfft!,
12-
plan_fft, plan_ifft, plan_bfft, plan_fft!, plan_ifft!, plan_bfft!,
13-
rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft
14-
else
10+
if !isdefined(Base, :DFT)
1511
export fft, ifft, bfft, fft!, ifft!, bfft!,
1612
plan_fft, plan_ifft, plan_bfft, plan_fft!, plan_ifft!, plan_bfft!,
17-
rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft
13+
rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft,
14+
fftshift, ifftshift
1815
end
1916

17+
2018
# DFT plan where the inputs are an array of eltype T
2119
abstract type Plan{T} end
2220

@@ -358,8 +356,6 @@ plan_irfft
358356

359357
##############################################################################
360358

361-
export fftshift, ifftshift
362-
363359
fftshift(x) = circshift(x, div.([size(x)...],2))
364360

365361
"""

test/runtests.jl

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,81 @@
1+
# This file contains code that was formerly part of Julia. License is MIT: https://julialang.org/license
2+
13
using AbstractFFTs
24
using Base.Test
35

4-
# TODO
6+
import AbstractFFTs: Plan, plan_fft, plan_inv, plan_bfft
7+
import Base: A_mul_B!, *
8+
9+
mutable struct TestPlan{T} <: Plan{T}
10+
region
11+
pinv::Plan{T}
12+
TestPlan{T}(region) where {T} = new{T}(region)
13+
end
14+
15+
mutable struct InverseTestPlan{T} <: Plan{T}
16+
region
17+
pinv::Plan{T}
18+
InverseTestPlan{T}(region) where {T} = new{T}(region)
19+
end
20+
21+
AbstractFFTs.plan_fft(x::Vector{T}, region; kwargs...) where {T} = TestPlan{T}(region)
22+
AbstractFFTs.plan_bfft(x::Vector{T}, region; kwargs...) where {T} = InverseTestPlan{T}(region)
23+
AbstractFFTs.plan_inv(p::TestPlan{T}) where {T} = InverseTestPlan{T}
24+
25+
# Just a helper function since forward and backward are nearly identical
26+
function dft!(y::Vector, x::Vector, sign::Int)
27+
n = length(x)
28+
length(y) == n || throw(DimensionMismatch())
29+
fill!(y, zero(complex(float(eltype(x)))))
30+
c = sign * 2π / n
31+
@inbounds for j = 0:n-1, k = 0:n-1
32+
y[k+1] += x[j+1] * cis(c*j*k)
33+
end
34+
return y
35+
end
36+
37+
Base.A_mul_B!(y::Vector, p::TestPlan, x::Vector) = dft!(y, x, -1)
38+
Base.A_mul_B!(y::Vector, p::InverseTestPlan, x::Vector) = dft!(y, x, 1)
39+
40+
Base.:*(p::TestPlan, x::Vector) = A_mul_B!(copy(x), p, x)
41+
Base.:*(p::InverseTestPlan, x::Vector) = A_mul_B!(copy(x), p, x)
42+
43+
@testset "Custom Plan" begin
44+
x = AbstractFFTs.fft(collect(1:8))
45+
# Result computed using FFTW
46+
fftw_fft = [36.0 + 0.0im,
47+
-4.0 + 9.65685424949238im,
48+
-4.0 + 4.0im,
49+
-4.0 + 1.6568542494923806im,
50+
-4.0 + 0.0im,
51+
-4.0 - 1.6568542494923806im,
52+
-4.0 - 4.0im,
53+
-4.0 - 9.65685424949238im]
54+
@test x fftw_fft
55+
56+
fftw_bfft = [Complex{Float64}(8i, 0) for i in 1:8]
57+
@test AbstractFFTs.bfft(x) fftw_bfft
58+
59+
fftw_ifft = [Complex{Float64}(i, 0) for i in 1:8]
60+
@test AbstractFFTs.ifft(x) fftw_ifft
61+
end
62+
63+
@testset "Shift functions" begin
64+
@test AbstractFFTs.fftshift([1 2 3]) == [3 1 2]
65+
@test AbstractFFTs.fftshift([1, 2, 3]) == [3, 1, 2]
66+
@test AbstractFFTs.fftshift([1 2 3; 4 5 6]) == [6 4 5; 3 1 2]
67+
68+
@test AbstractFFTs.fftshift([1 2 3; 4 5 6], 1) == [4 5 6; 1 2 3]
69+
@test AbstractFFTs.fftshift([1 2 3; 4 5 6], ()) == [1 2 3; 4 5 6]
70+
@test AbstractFFTs.fftshift([1 2 3; 4 5 6], (1,2)) == [6 4 5; 3 1 2]
71+
@test AbstractFFTs.fftshift([1 2 3; 4 5 6], 1:2) == [6 4 5; 3 1 2]
72+
73+
@test AbstractFFTs.ifftshift([1 2 3]) == [2 3 1]
74+
@test AbstractFFTs.ifftshift([1, 2, 3]) == [2, 3, 1]
75+
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6]) == [5 6 4; 2 3 1]
76+
77+
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], 1) == [4 5 6; 1 2 3]
78+
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], ()) == [1 2 3; 4 5 6]
79+
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], (1,2)) == [5 6 4; 2 3 1]
80+
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], 1:2) == [5 6 4; 2 3 1]
81+
end

0 commit comments

Comments
 (0)