Skip to content

Commit

Permalink
Merge pull request #4 from JuliaMath/aa/tests
Browse files Browse the repository at this point in the history
Set up testing
  • Loading branch information
ararslan authored Jun 5, 2017
2 parents bc16ae1 + dee2b60 commit bf0be1e
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 16 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ notifications:
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
# - julia -e 'Pkg.clone(pwd()); Pkg.build("AbstractFFTs"); Pkg.test("AbstractFFTs"; coverage=true)'
after_success:
# - julia -e 'Pkg.add("Coverage"); cd(Pkg.dir("AbstractFFTs")); using Coverage; Coveralls.submit(Coveralls.process_folder())'
# - julia -e 'Pkg.add("Documenter"); cd(Pkg.dir("AbstractFFTs")); include(joinpath("docs", "make.jl"))'
- julia -e 'Pkg.add("Coverage"); cd(Pkg.dir("AbstractFFTs")); using Coverage; Coveralls.submit(Coveralls.process_folder())'
- julia -e 'Pkg.add("Documenter"); cd(Pkg.dir("AbstractFFTs")); include(joinpath("docs", "make.jl"))'
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,38 @@

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

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.
[![Travis](https://travis-ci.org/JuliaMath/AbstractFFTs.jl.svg?branch=master)](https://travis-ci.org/JuliaMath/AbstractFFTs.jl)
[![Coveralls](https://coveralls.io/repos/github/JuliaMath/AbstractFFTs.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaMath/AbstractFFTs.jl?branch=master)

Documentation:
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaMath.github.io/AbstractFFTs.jl/stable)
[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://JuliaMath.github.io/AbstractFFTs.jl/latest)

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 allows multiple FFT packages to co-exist with the same underlying `fft(x)` and `plan_fft(x)` interface.

## Developer information

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

* 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.
* 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.

* 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`.
* 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`.

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

* 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`.
* Define a method of `*(p::MyPlan, x)`, which can simply call your `A_mul_B!` 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 to `p`, and `plan_bfft(x, region; kws...)` for an unnormalized inverse ("backwards") transform of `x`.

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

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).
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).
12 changes: 4 additions & 8 deletions src/AbstractFFTs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ using Base.LinAlg: BlasReal
import Base: show, summary, size, ndims, length, eltype,
*, A_mul_B!, inv, \, A_ldiv_B!

if isdefined(Base, :DFT)
import Base.DFT: fft, ifft, bfft, fft!, ifft!, bfft!,
plan_fft, plan_ifft, plan_bfft, plan_fft!, plan_ifft!, plan_bfft!,
rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft
else
if !isdefined(Base, :DFT)
export fft, ifft, bfft, fft!, ifft!, bfft!,
plan_fft, plan_ifft, plan_bfft, plan_fft!, plan_ifft!, plan_bfft!,
rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft
rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft,
fftshift, ifftshift
end


# DFT plan where the inputs are an array of eltype T
abstract type Plan{T} end

Expand Down Expand Up @@ -358,8 +356,6 @@ plan_irfft

##############################################################################

export fftshift, ifftshift

fftshift(x) = circshift(x, div.([size(x)...],2))

"""
Expand Down
79 changes: 78 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,81 @@
# This file contains code that was formerly part of Julia. License is MIT: https://julialang.org/license

using AbstractFFTs
using Base.Test

# TODO
import AbstractFFTs: Plan, plan_fft, plan_inv, plan_bfft
import Base: A_mul_B!, *

mutable struct TestPlan{T} <: Plan{T}
region
pinv::Plan{T}
TestPlan{T}(region) where {T} = new{T}(region)
end

mutable struct InverseTestPlan{T} <: Plan{T}
region
pinv::Plan{T}
InverseTestPlan{T}(region) where {T} = new{T}(region)
end

AbstractFFTs.plan_fft(x::Vector{T}, region; kwargs...) where {T} = TestPlan{T}(region)
AbstractFFTs.plan_bfft(x::Vector{T}, region; kwargs...) where {T} = InverseTestPlan{T}(region)
AbstractFFTs.plan_inv(p::TestPlan{T}) where {T} = InverseTestPlan{T}

# Just a helper function since forward and backward are nearly identical
function dft!(y::Vector, x::Vector, sign::Int)
n = length(x)
length(y) == n || throw(DimensionMismatch())
fill!(y, zero(complex(float(eltype(x)))))
c = sign * 2π / n
@inbounds for j = 0:n-1, k = 0:n-1
y[k+1] += x[j+1] * cis(c*j*k)
end
return y
end

Base.A_mul_B!(y::Vector, p::TestPlan, x::Vector) = dft!(y, x, -1)
Base.A_mul_B!(y::Vector, p::InverseTestPlan, x::Vector) = dft!(y, x, 1)

Base.:*(p::TestPlan, x::Vector) = A_mul_B!(copy(x), p, x)
Base.:*(p::InverseTestPlan, x::Vector) = A_mul_B!(copy(x), p, x)

@testset "Custom Plan" begin
x = AbstractFFTs.fft(collect(1:8))
# Result computed using FFTW
fftw_fft = [36.0 + 0.0im,
-4.0 + 9.65685424949238im,
-4.0 + 4.0im,
-4.0 + 1.6568542494923806im,
-4.0 + 0.0im,
-4.0 - 1.6568542494923806im,
-4.0 - 4.0im,
-4.0 - 9.65685424949238im]
@test x fftw_fft

fftw_bfft = [Complex{Float64}(8i, 0) for i in 1:8]
@test AbstractFFTs.bfft(x) fftw_bfft

fftw_ifft = [Complex{Float64}(i, 0) for i in 1:8]
@test AbstractFFTs.ifft(x) fftw_ifft
end

@testset "Shift functions" begin
@test AbstractFFTs.fftshift([1 2 3]) == [3 1 2]
@test AbstractFFTs.fftshift([1, 2, 3]) == [3, 1, 2]
@test AbstractFFTs.fftshift([1 2 3; 4 5 6]) == [6 4 5; 3 1 2]

@test AbstractFFTs.fftshift([1 2 3; 4 5 6], 1) == [4 5 6; 1 2 3]
@test AbstractFFTs.fftshift([1 2 3; 4 5 6], ()) == [1 2 3; 4 5 6]
@test AbstractFFTs.fftshift([1 2 3; 4 5 6], (1,2)) == [6 4 5; 3 1 2]
@test AbstractFFTs.fftshift([1 2 3; 4 5 6], 1:2) == [6 4 5; 3 1 2]

@test AbstractFFTs.ifftshift([1 2 3]) == [2 3 1]
@test AbstractFFTs.ifftshift([1, 2, 3]) == [2, 3, 1]
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6]) == [5 6 4; 2 3 1]

@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], 1) == [4 5 6; 1 2 3]
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], ()) == [1 2 3; 4 5 6]
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], (1,2)) == [5 6 4; 2 3 1]
@test AbstractFFTs.ifftshift([1 2 3; 4 5 6], 1:2) == [5 6 4; 2 3 1]
end

0 comments on commit bf0be1e

Please sign in to comment.