Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/main.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
fft(X::AbstractArray{<:Complex}, region::Union{Int,AbstractVector} = 1:ndims(X)) = plan_fft(X, region) * X
fft(X::AbstractArray, region::Union{Int,AbstractVector} = 1:ndims(X)) = fft(complex(X), region)
fft(X::AbstractArray{<:Complex,N}, region::NTuple{D,Int} where D = ntuple(identity, N)) where N = plan_fft(X, region) * X
fft(X::AbstractArray{<:Any,N}, region::NTuple{D,Int} where D = ntuple(identity, N)) where N = fft(complex(X), region)

bfft(X::AbstractArray{<:Complex}, region::Union{Int,AbstractVector} = 1:ndims(X)) = plan_bfft(X, region) * X
bfft(X::AbstractArray{<:Complex,N}, region::NTuple{D,Int} where D = ntuple(identity, N)) where N = plan_bfft(X, region) * X

ifft(X::AbstractArray{<:Complex}, region::Union{Int,AbstractVector} = 1:ndims(X)) = bfft(X, region) / mapreduce(Base.Fix1(size, X), *, region; init=1)
ifft(X::AbstractArray{<:Complex,N}, region::NTuple{D,Int} where D = ntuple(identity, N)) where N = bfft(X, region) / mapreduce(Base.Fix1(size, X), *, region; init=1)

rfft(X::AbstractArray{<:Real}, region::Union{Int,AbstractVector} = 1:ndims(X)) = plan_rfft(X, region) * X
rfft(X::AbstractArray{<:Real,N}, region::NTuple{D,Int} where D = ntuple(identity, N)) where N = plan_rfft(X, region) * X

brfft(X::AbstractArray{<:Complex}, len::Int, region::Union{Int,AbstractVector} = 1:ndims(X)) = plan_brfft(X, len, region) * X
brfft(X::AbstractArray{<:Complex,N}, len::Int, region::NTuple{D,Int} where D = ntuple(identity, N)) where N = plan_brfft(X, len, region) * X

function irfft(X::AbstractArray{<:Complex}, len::Int, region::Union{Int,AbstractVector} = 1:ndims(X))
function irfft(X::AbstractArray{<:Complex,N}, len::Int, region::NTuple{D,Int} where D = ntuple(identity, N)) where N
Y = brfft(X, len, region)
Y ./= mapreduce(Base.Fix1(size, Y), *, region; init=1)
return Y
Expand Down
118 changes: 57 additions & 61 deletions src/plan.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Plans
abstract type FFTAPlan{T,N} end
abstract type FFTAPlan{T,D} end

struct FFTAInvPlan{T,N} <: FFTAPlan{T,N} end
struct FFTAInvPlan{T,D} <: FFTAPlan{T,D} end

struct FFTAPlan_cx{T,N} <: FFTAPlan{T,N}
callgraph::NTuple{N, CallGraph{T}}
region::Union{Int,AbstractVector{<:Int}}
struct FFTAPlan_cx{T,D} <: FFTAPlan{T,D}
callgraph::NTuple{D, CallGraph{T}}
region::NTuple{D,Int}
dir::Direction
pinv::FFTAInvPlan{T}
end

struct FFTAPlan_re{T,N} <: FFTAPlan{T,N}
callgraph::NTuple{N, CallGraph{T}}
region::Union{Int,AbstractVector{<:Int}}
struct FFTAPlan_re{T,D} <: FFTAPlan{T,D}
callgraph::NTuple{D, CallGraph{T}}
region::NTuple{D, Int}
dir::Direction
pinv::FFTAInvPlan{T}
flen::Int
Expand All @@ -23,69 +23,65 @@ Base.size(p::FFTAPlan{<:Any,N}) where N = ntuple(Base.Fix1(size, p), Val{N}())

Base.complex(p::FFTAPlan_re{T,N}) where {T,N} = FFTAPlan_cx{T,N}(p.callgraph, p.region, p.dir, p.pinv)

function plan_fft(x::AbstractArray{T,N}, region::Union{Int,AbstractVector} = 1:N)::FFTAPlan_cx{T} where {T <: Complex, N}
FFTN = length(region)
if FFTN == 1
g = CallGraph{T}(size(x,region[]))
pinv = FFTAInvPlan{T,FFTN}()
return FFTAPlan_cx{T,FFTN}((g,), region, FFT_FORWARD, pinv)
elseif FFTN == 2
sort!(region)
g1 = CallGraph{T}(size(x,region[1]))
g2 = CallGraph{T}(size(x,region[2]))
pinv = FFTAInvPlan{T,FFTN}()
return FFTAPlan_cx{T,FFTN}((g1,g2), region, FFT_FORWARD, pinv)
function plan_fft(x::AbstractArray{T,N}, region::NTuple{D,Int} = ntuple(identity, N))::FFTAPlan_cx{T} where {T <: Complex, N, D}
if D == 1
g = CallGraph{T}(size(x, region[1]))
pinv = FFTAInvPlan{T,D}()
return FFTAPlan_cx{T,D}((g,), region, FFT_FORWARD, pinv)
elseif D == 2
region = sort(region)
g1 = CallGraph{T}(size(x, region[1]))
g2 = CallGraph{T}(size(x, region[2]))
pinv = FFTAInvPlan{T,D}()
return FFTAPlan_cx{T,D}((g1, g2), region, FFT_FORWARD, pinv)
else
throw(ArgumentError("only supports 1D and 2D FFTs"))
end
end

function plan_bfft(x::AbstractArray{T,N}, region::Union{Int,AbstractVector} = 1:N)::FFTAPlan_cx{T} where {T <: Complex,N}
FFTN = length(region)
if FFTN == 1
g = CallGraph{T}(size(x,region[]))
pinv = FFTAInvPlan{T,FFTN}()
return FFTAPlan_cx{T,FFTN}((g,), region, FFT_BACKWARD, pinv)
elseif FFTN == 2
sort!(region)
function plan_bfft(x::AbstractArray{T,N}, region::NTuple{D,Int} = ntuple(identity, N))::FFTAPlan_cx{T} where {T <: Complex, N, D}
if D == 1
g = CallGraph{T}(size(x, region[1]))
pinv = FFTAInvPlan{T,D}()
return FFTAPlan_cx{T,D}((g,), region, FFT_BACKWARD, pinv)
elseif D == 2
region = sort(region)
g1 = CallGraph{T}(size(x,region[1]))
g2 = CallGraph{T}(size(x,region[2]))
pinv = FFTAInvPlan{T,FFTN}()
return FFTAPlan_cx{T,FFTN}((g1,g2), region, FFT_BACKWARD, pinv)
pinv = FFTAInvPlan{T,D}()
return FFTAPlan_cx{T,D}((g1,g2), region, FFT_BACKWARD, pinv)
else
throw(ArgumentError("only supports 1D and 2D FFTs"))
end
end

function plan_rfft(x::AbstractArray{T,N}, region::Union{Int,AbstractVector} = 1:N)::FFTAPlan_re{Complex{T}} where {T <: Real,N}
FFTN = length(region)
if FFTN == 1
g = CallGraph{Complex{T}}(size(x,region[]))
pinv = FFTAInvPlan{Complex{T},FFTN}()
return FFTAPlan_re{Complex{T},FFTN}(tuple(g), region, FFT_FORWARD, pinv, size(x,region[]))
elseif FFTN == 2
sort!(region)
g1 = CallGraph{Complex{T}}(size(x,region[1]))
g2 = CallGraph{Complex{T}}(size(x,region[2]))
pinv = FFTAInvPlan{Complex{T},FFTN}()
return FFTAPlan_re{Complex{T},FFTN}(tuple(g1,g2), region, FFT_FORWARD, pinv, size(x,region[1]))
function plan_rfft(x::AbstractArray{T,N}, region::NTuple{D,Int} = ntuple(identity, N))::FFTAPlan_re{Complex{T}} where {T <: Real, N, D}
if D == 1
g = CallGraph{Complex{T}}(size(x, region[1]))
pinv = FFTAInvPlan{Complex{T},D}()
return FFTAPlan_re{Complex{T},D}(tuple(g), region, FFT_FORWARD, pinv, size(x, region[1]))
elseif D == 2
region = sort(region)
g1 = CallGraph{Complex{T}}(size(x, region[1]))
g2 = CallGraph{Complex{T}}(size(x, region[2]))
pinv = FFTAInvPlan{Complex{T},D}()
return FFTAPlan_re{Complex{T},D}(tuple(g1,g2), region, FFT_FORWARD, pinv, size(x,region[1]))
else
throw(ArgumentError("only supports 1D and 2D FFTs"))
end
end

function plan_brfft(x::AbstractArray{T,N}, len::Int, region::Union{Int,AbstractVector} = 1:N)::FFTAPlan_re{T} where {T,N}
FFTN = length(region)
if FFTN == 1
function plan_brfft(x::AbstractArray{T,N}, len::Int, region::NTuple{D,Int} = ntuple(identity, N))::FFTAPlan_re{T} where {T, N, D}
if D == 1
g = CallGraph{T}(len)
pinv = FFTAInvPlan{T,FFTN}()
return FFTAPlan_re{T,FFTN}((g,), region, FFT_BACKWARD, pinv, len)
elseif FFTN == 2
sort!(region)
pinv = FFTAInvPlan{T,D}()
return FFTAPlan_re{T,D}((g,), region, FFT_BACKWARD, pinv, len)
elseif D == 2
region = sort(region)
g1 = CallGraph{T}(len)
g2 = CallGraph{T}(size(x,region[2]))
pinv = FFTAInvPlan{T,FFTN}()
return FFTAPlan_re{T,FFTN}((g1,g2), region, FFT_BACKWARD, pinv, len)
g2 = CallGraph{T}(size(x, region[2]))
pinv = FFTAInvPlan{T,D}()
return FFTAPlan_re{T,D}((g1,g2), region, FFT_BACKWARD, pinv, len)
else
throw(ArgumentError("only supports 1D and 2D FFTs"))
end
Expand All @@ -110,11 +106,11 @@ function LinearAlgebra.mul!(y::AbstractArray{U,N}, p::FFTAPlan_cx{T,1}, x::Abstr
if axes(x) != axes(y)
throw(DimensionMismatch("input array has axes $(axes(x)), but output array has axes $(axes(y))"))
end
if size(p, 1) != size(x, p.region[])
throw(DimensionMismatch("plan has size $(size(p, 1)), but input array has size $(size(x, p.region[])) along region $(p.region[])"))
if size(p, 1) != size(x, p.region[1])
throw(DimensionMismatch("plan has size $(size(p, 1)), but input array has size $(size(x, p.region[1])) along region $(p.region[1])"))
end
Rpre = CartesianIndices(size(x)[1:p.region[]-1])
Rpost = CartesianIndices(size(x)[p.region[]+1:end])
Rpre = CartesianIndices(size(x)[1:p.region[1]-1])
Rpost = CartesianIndices(size(x)[p.region[1]+1:end])
for Ipre in Rpre
for Ipost in Rpost
@views fft!(y[Ipre,:,Ipost], x[Ipre,:,Ipost], 1, 1, p.dir, p.callgraph[1][1].type, p.callgraph[1], 1)
Expand All @@ -136,8 +132,8 @@ function LinearAlgebra.mul!(y::AbstractArray{U,N}, p::FFTAPlan_cx{T,2}, x::Abstr
R1 = CartesianIndices(size(x)[1:p.region[1]-1])
R2 = CartesianIndices(size(x)[p.region[1]+1:p.region[2]-1])
R3 = CartesianIndices(size(x)[p.region[2]+1:end])
y_tmp = similar(y, axes(y)[p.region])
rows,cols = size(x)[p.region]
y_tmp = similar(y, (axes(y, p.region[1]), axes(y, p.region[2])))
rows, cols = size(x, p.region[1]), size(x, p.region[2])
# Introduce function barrier here since the variables used in the loop ranges aren't inferred. This
# is partly because the region field of the plan is abstractly typed but even if that wasn't the case,
# it might be a bit tricky to construct the Rxs in an inferred way.
Expand Down Expand Up @@ -232,8 +228,8 @@ end
##### Backward
function Base.:*(p::FFTAPlan_re{T,1}, x::AbstractArray{T,N}) where {T<:Complex, N}
Base.require_one_based_indexing(x)
if p.flen ÷ 2 + 1 != size(x, p.region[])
throw(DimensionMismatch("real 1D plan has size $(p.flen). Dimension of input array along region $(p.region[]) should have size $(size(p, p.region[]) ÷ 2 + 1), but has size $(size(x, p.region[]))"))
if p.flen ÷ 2 + 1 != size(x, p.region[1])
throw(DimensionMismatch("real 1D plan has size $(p.flen). Dimension of input array along region $(p.region[1]) should have size $(size(p, p.region[1]) ÷ 2 + 1), but has size $(size(x, p.region[1]))"))
end
if p.dir === FFT_BACKWARD
# # for the inverse transformation we have to reconstruct the full array
Expand Down Expand Up @@ -288,7 +284,7 @@ function Base.:*(p::FFTAPlan_re{T,2}, x::AbstractArray{T,N}) where {T<:Complex,
# the second half in the first transform dimension is reversed and conjugated
x_half_2 = selectdim(x_full, p.region[1], half_2) # view to the second half of x
start_reverse = size(x, p.region[1]) - iseven(p.flen)

map!(conj, x_half_2, selectdim(x, p.region[1], start_reverse:-1:2))
# for the 2D transform we have to reverse index 2:end of the same block in the second transform dimension as well
reverse!(selectdim(x_half_2, p.region[2], 2:size(x, p.region[2])), dims=p.region[2])
Expand Down
20 changes: 10 additions & 10 deletions test/argument_checking.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ using LinearAlgebra: LinearAlgebra
@testset "Only 1D and 2D FFTs" begin
xr = zeros(2, 2)
xc = complex(xr)
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_fft(xc, 1:3)
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_bfft(xc, 1:3)
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_rfft(xr, 1:3)
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_brfft(xc, 2, 1:3)
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_fft(xc, (1, 2, 3))
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_bfft(xc, (1, 2, 3))
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_rfft(xr, (1, 2, 3))
@test_throws ArgumentError("only supports 1D and 2D FFTs") plan_brfft(xc, 2, (1, 2, 3))
end

@testset "mismatch between plan and array" begin
Expand All @@ -34,18 +34,18 @@ end
xc2p = [[xc2; ones(1, size(xr2, 2))] ones(size(xc2, 1) + 1, 1)]

@testset "1D plan, region=$(region)" for region in 1:2
yr2 = rfft(xr2, region)
yr2 = rfft(xr2, (region,))

yr2p = if region == 1
[yr2; ones(1, size(yr2, 2))]
else
[yr2 ones(size(yr2, 1), 1)]
end

@test_throws DimensionMismatch plan_fft(xc2, region) * xc2p
@test_throws DimensionMismatch plan_bfft(xc2, region) * xc2p
@test_throws DimensionMismatch plan_rfft(xr2, region) * xr2p
@test_throws DimensionMismatch plan_brfft(yr2, size(xr2, region), region) * yr2p
@test_throws DimensionMismatch plan_fft(xc2, (region,)) * xc2p
@test_throws DimensionMismatch plan_bfft(xc2, (region,)) * xc2p
@test_throws DimensionMismatch plan_rfft(xr2, (region,)) * xr2p
@test_throws DimensionMismatch plan_brfft(yr2, size(xr2, region), (region,)) * yr2p
end

@testset "2D plan" begin
Expand Down Expand Up @@ -74,7 +74,7 @@ end
y2 = similar(x2, size(x2, 1) + 1, size(x2, 2) + 1)

@testset "1D plan, region=$(region)" for region in [1, 2]
@test_throws DimensionMismatch LinearAlgebra.mul!(y2, plan_fft(x2, region), x2)
@test_throws DimensionMismatch LinearAlgebra.mul!(y2, plan_fft(x2, (region,)), x2)
end

@testset "2D plan" begin
Expand Down
2 changes: 1 addition & 1 deletion test/onedim/complex_backward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ end
x = complex.(randn(n, n + 1, n + 2), randn(n, n + 1, n + 2))

@testset "against 1D array with mapslices, r=$r" for r in 1:3
@test bfft(x, r) == mapslices(bfft, x; dims = r)
@test bfft(x, (r,)) == mapslices(bfft, x; dims = r)
end
end

Expand Down
6 changes: 3 additions & 3 deletions test/onedim/complex_forward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ using FFTA, Test
y_ref = 0*y
y_ref[1] = N
@test y ≈ y_ref atol=1e-12
@test y == fft(reshape(x,1,1,N),3)[1,1,:]
@test y == fft(reshape(x,N,1), 1)[:,1]
@test y == fft(reshape(x, 1, 1, N), (3,))[1,1,:]
@test y == fft(reshape(x, N, 1), (1,))[:,1]
end

@testset "1D plan, 1D array. Size: $n" for n in 1:64
Expand All @@ -26,7 +26,7 @@ end
x = complex.(randn(n, n + 1, n + 2), randn(n, n + 1, n + 2))

@testset "against 1D array with mapslices, r=$r" for r in 1:3
@test fft(x, r) == mapslices(fft, x; dims = r)
@test fft(x, (r,)) == mapslices(fft, x; dims = r)
end
end

Expand Down
6 changes: 3 additions & 3 deletions test/onedim/real_backward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ end
x = randn(n, n + 1, n + 2)

@testset "round tripping with irfft, r=$r" for r in 1:3
@test irfft(rfft(x, r), size(x,r), r) ≈ x
@test irfft(rfft(x, (r,)), size(x, r), (r,)) ≈ x
end

@testset "against 1D array with mapslices, r=$r" for r in 1:3
y = rfft(x, r)
@test brfft(y, size(x, r), r) == mapslices(t -> brfft(t, size(x, r)), y; dims = r)
y = rfft(x, (r,))
@test brfft(y, size(x, r), (r,)) == mapslices(t -> brfft(t, size(x, r)), y; dims = r)
end
end

Expand Down
6 changes: 3 additions & 3 deletions test/onedim/real_forward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ using FFTA, Test
y_ref = 0*y
y_ref[1] = N
@test y ≈ y_ref atol=1e-12
@test y == rfft(reshape(x,1,1,N),3)[1,1,:]
@test y == rfft(reshape(x,N,1),1)[:,1]
@test y == rfft(reshape(x, 1, 1, N), (3,))[1,1,:]
@test y == rfft(reshape(x, N, 1), (1,))[:,1]
end

@testset "1D plan, 1D array. Size: $n" for n in 1:64
Expand All @@ -33,7 +33,7 @@ end
x = randn(n, n + 1, n + 2)

@testset "against 1D array with mapslices, r=$r" for r in 1:3
@test rfft(x, r) == mapslices(rfft, x; dims = r)
@test rfft(x, (r,)) == mapslices(rfft, x; dims = r)
end
end

Expand Down
4 changes: 2 additions & 2 deletions test/twodim/complex_backward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ end
@testset "2D plan, ND array. Size: $n" for n in 1:64
x = complex.(randn(n, n + 1, n + 2), randn(n, n + 1, n + 2))

@testset "against 1D array with mapslices, r=$r" for r in [[1,2], [1,3], [2,3]]
@test bfft(x, r) == mapslices(bfft, x; dims = r)
@testset "against 1D array with mapslices, r=$r" for r in [(1,2), (1,3), (2,3)]
@test bfft(x, r) == mapslices(bfft, x; dims = [r...])
end
end

Expand Down
10 changes: 5 additions & 5 deletions test/twodim/complex_forward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ using FFTA, Test
y_ref[1] = length(x)
@test y ≈ y_ref
x = randn(N,N)
@test fft(x) ≈ fft(reshape(x,1,N,N), [2,3])[1,:,:]
@test fft(x) ≈ fft(reshape(x,1,N,N,1), [2,3])[1,:,:,1]
@test fft(x) ≈ fft(reshape(x,1,1,N,N,1), [3,4])[1,1,:,:,1]
@test fft(x) ≈ fft(reshape(x, 1, N, N), (2, 3))[1,:,:]
@test fft(x) ≈ fft(reshape(x, 1, N, N, 1), (2, 3))[1,:,:,1]
@test fft(x) ≈ fft(reshape(x, 1, 1, N, N, 1), (3, 4))[1,1,:,:,1]
end

@testset "2D plan, 2D array. Size: $n" for n in 1:64
Expand All @@ -29,8 +29,8 @@ end
@testset "2D plan, ND array. Size: $n" for n in 1:64
x = complex.(randn(n, n + 1, n + 2), randn(n, n + 1, n + 2))

@testset "against 1D array with mapslices, r=$r" for r in [[1,2], [1,3], [2,3]]
@test fft(x, r) == mapslices(fft, x; dims = r)
@testset "against 1D array with mapslices, r=$r" for r in [(1,2), (1,3), (2,3)]
@test fft(x, r) == mapslices(fft, x; dims = [r...])
end
end

Expand Down
8 changes: 4 additions & 4 deletions test/twodim/real_backward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ end
@testset "2D plan, ND array. Size: $n" for n in 1:64
x = randn(n, n + 1, n + 2)

@testset "round trip with irfft, r=$r" for r in [[1,2], [1,3], [2,3]]
@test x ≈ irfft(rfft(x,r), size(x,r[1]), r)
@testset "round trip with irfft, r=$r" for r in [(1,2), (1,3), (2,3)]
@test x ≈ irfft(rfft(x, r), size(x, r[1]), r)
end

@testset "against 2D array with mapslices, r=$r" for r in [[1,2], [1,3], [2,3]]
@testset "against 2D array with mapslices, r=$r" for r in [(1,2), (1,3), (2,3)]
y = rfft(x, r)
@test brfft(y, size(x, r[1]), r) == mapslices(t -> brfft(t, size(x, r[1])), y; dims = r)
@test brfft(y, size(x, r[1]), r) == mapslices(t -> brfft(t, size(x, r[1])), y; dims = [r...])
end
end

Expand Down
10 changes: 5 additions & 5 deletions test/twodim/real_forward.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ using FFTA, Test
y_ref[1] = length(x)
@test y ≈ y_ref
x = randn(N,N)
@test rfft(x) ≈ rfft(reshape(x,1,N,N), [2,3])[1,:,:]
@test rfft(x) ≈ rfft(reshape(x,1,N,N,1), [2,3])[1,:,:,1]
@test rfft(x) ≈ rfft(reshape(x,1,1,N,N,1), [3,4])[1,1,:,:,1]
@test rfft(x) ≈ rfft(reshape(x ,1, N, N), (2, 3))[1,:,:]
@test rfft(x) ≈ rfft(reshape(x, 1, N, N, 1), (2, 3))[1,:,:,1]
@test rfft(x) ≈ rfft(reshape(x, 1, 1, N, N, 1), (3, 4))[1,1,:,:,1]
@test size(rfft(x)) == (N÷2+1, N)
end

Expand All @@ -30,8 +30,8 @@ end
@testset "2D plan, ND array. Size: $n" for n in 1:64
x = randn(n, n + 1, n + 2)

@testset "against 1D array with mapslices, r=$r" for r in [[1,2], [1,3], [2,3]]
@test rfft(x, r) == mapslices(rfft, x; dims = r)
@testset "against 1D array with mapslices, r=$r" for r in [(1,2), (1,3), (2,3)]
@test rfft(x, r) == mapslices(rfft, x; dims = [r...])
end
end

Expand Down
Loading