@@ -19,7 +19,25 @@ struct FFTAPlan_re{T,N} <: FFTAPlan{T,N}
1919 flen:: Int
2020end
2121
22- Base. size (p:: FFTAPlan , i:: Int ) = i <= length (p. callgraph) ? first (p. callgraph[i]. nodes). sz : 1
22+ Base. size (p:: FFTAPlan_cx , i:: Int ) = i <= length (p. callgraph) ? first (p. callgraph[i]. nodes). sz : 1
23+ function Base. size (p:: FFTAPlan_re{<:Any,1} , i:: Int )
24+ if i == p. region[]
25+ p. flen
26+ elseif i <= length (p. callgraph)
27+ first (p. callgraph[i]. nodes). sz
28+ else
29+ 1
30+ end
31+ end
32+ function Base. size (p:: FFTAPlan_re{<:Any,2} , i:: Int )
33+ if i == 1
34+ return p. flen
35+ elseif i == 2
36+ first (p. callgraph[2 ]. nodes). sz
37+ else
38+ 1
39+ end
40+ end
2341Base. size (p:: FFTAPlan{<:Any,N} ) where N = ntuple (Base. Fix1 (size, p), Val {N} ())
2442
2543Base. complex (p:: FFTAPlan_re{T,N} ) where {T,N} = FFTAPlan_cx {T,N} (p. callgraph, p. region, p. dir, p. pinv)
6179function AbstractFFTs. plan_rfft (x:: AbstractArray{T,N} , region; kwargs... ):: FFTAPlan_re{Complex{T}} where {T <: Real ,N}
6280 FFTN = length (region)
6381 if FFTN == 1
64- g = CallGraph {Complex{T}} (size (x,region[]))
82+ n = size (x, region[])
83+ # For even length problems, we solve the real problem with
84+ # two n/2 complex FFTs followed by a butterfly. For odd size
85+ # problems, we just solve the problem as a single complex
86+ nn = iseven (n) ? n >> 1 : n
87+ g = CallGraph {Complex{T}} (nn)
6588 pinv = FFTAInvPlan {Complex{T},FFTN} ()
66- return FFTAPlan_re {Complex{T},FFTN} (tuple (g), region, FFT_FORWARD, pinv, size (x,region[]) )
89+ return FFTAPlan_re {Complex{T},FFTN} (tuple (g), region, FFT_FORWARD, pinv, n )
6790 elseif FFTN == 2
6891 sort! (region)
6992 g1 = CallGraph {Complex{T}} (size (x,region[1 ]))
78101function AbstractFFTs. plan_brfft (x:: AbstractArray{T,N} , len, region; kwargs... ):: FFTAPlan_re{T} where {T,N}
79102 FFTN = length (region)
80103 if FFTN == 1
81- g = CallGraph {T} (len)
104+ # For even length problems, we solve the real problem with
105+ # two n/2 complex FFTs followed by a butterfly. For odd size
106+ # problems, we just solve the problem as a single complex
107+ nn = iseven (len) ? len >> 1 : len
108+ g = CallGraph {T} (nn)
82109 pinv = FFTAInvPlan {T,FFTN} ()
83110 return FFTAPlan_re {T,FFTN} ((g,), region, FFT_BACKWARD, pinv, len)
84111 elseif FFTN == 2
96123# Multiplication
97124# # mul!
98125# ## Complex
126+ # ### 1D plan 1D array
99127function LinearAlgebra. mul! (y:: AbstractVector{U} , p:: FFTAPlan_cx{T,1} , x:: AbstractVector{T} ) where {T,U}
100128 if axes (x) != axes (y)
101129 throw (DimensionMismatch (" input array has axes $(axes (x)) , but output array has axes $(axes (y)) " ))
@@ -106,6 +134,7 @@ function LinearAlgebra.mul!(y::AbstractVector{U}, p::FFTAPlan_cx{T,1}, x::Abstra
106134 fft! (y, x, 1 , 1 , p. dir, p. callgraph[1 ][1 ]. type, p. callgraph[1 ], 1 )
107135end
108136
137+ # ### 1D plan ND array
109138function LinearAlgebra. mul! (y:: AbstractArray{U,N} , p:: FFTAPlan_cx{T,1} , x:: AbstractArray{T,N} ) where {T,U,N}
110139 Base. require_one_based_indexing (x)
111140 if axes (x) != axes (y)
@@ -123,6 +152,7 @@ function LinearAlgebra.mul!(y::AbstractArray{U,N}, p::FFTAPlan_cx{T,1}, x::Abstr
123152 end
124153end
125154
155+ # ### 2D plan ND array
126156function LinearAlgebra. mul! (y:: AbstractArray{U,N} , p:: FFTAPlan_cx{T,2} , x:: AbstractArray{T,N} ) where {T,U,N}
127157 Base. require_one_based_indexing (x)
128158 if axes (x) != axes (y)
@@ -192,11 +222,70 @@ end
192222function Base.:* (p:: FFTAPlan_re{Complex{T},1} , x:: AbstractVector{T} ) where {T<: Real }
193223 Base. require_one_based_indexing (x)
194224 if p. dir === FFT_FORWARD
195- x_c = similar (x, Complex{T})
196- copy! (x_c, x)
197- y = similar (x_c)
198- LinearAlgebra. mul! (y, complex (p), x_c)
199- return y[1 : end ÷ 2 + 1 ]
225+ n = p. flen
226+ if iseven (n)
227+ # For problems of even size, we solve the rfft problem by splitting the
228+ # problem into the even and odd part and solving the simultanously as
229+ # a single (complex) fft of half the size, see equations (6)-(8) of
230+ # Sorensen, H. V., D. Jones, Michael Heideman, and C. Burrus.
231+ # "Real-valued fast Fourier transform algorithms."
232+ # IEEE Transactions on acoustics, speech, and signal processing 35, no. 6 (2003): 849-863.
233+ if x isa Vector && isbitstype (T)
234+ # For a vector of bits, we can just reintepret the bits to get the
235+ # approciate representation of even (zero based) elements as the real
236+ # part and the odd as the complex part
237+ x_c = reinterpret (Complex{T}, x)
238+ else
239+ # for non-bits, we'd have to copy to a new array
240+ x_c = complex .(view (x, 1 : 2 : n), view (x, 2 : 2 : n))
241+ end
242+
243+ m = n >> 1
244+ # Allocate complex result vector of half the input size plus one
245+ y = similar (x_c, m + 1 )
246+ # Solve the complex fft of half the size
247+ LinearAlgebra. mul! (view (y, 1 : m), complex (p), x_c)
248+
249+ # The w stored in the plan is for m, not n, so probably cheapest to
250+ # just recompute it instead of taking a square root
251+ wj = w = cispi (- T (2 ) / n)
252+
253+ # Construct the result by first constructing the elements of the
254+ # real and imaginary part, followed by the usual radix-2 assembly,
255+ # see eq (9)
256+ @inbounds begin
257+ y1 = y[1 ]
258+ y[1 ] = real (y1) + imag (y1)
259+ y[end ] = real (y1) - imag (y1)
260+ for j in 2 : ((m >> 1 ) + 1 )
261+ yj = y[j]
262+ yjr, yji = real (yj), imag (yj)
263+ ymj = y[m - j + 2 ]
264+ ymjr, ymji = real (ymj), imag (ymj)
265+ XX = complex (
266+ (yjr + ymjr) * T (0.5 ),
267+ (yji - ymji) * T (0.5 ),
268+ )
269+ XY = complex (
270+ (ymji + yji) * T (0.5 ),
271+ (ymjr - yjr) * T (0.5 ),
272+ )
273+ y[j] = XX + wj* XY
274+ y[m - j + 2 ] = conj (XX - wj* XY)
275+ wj *= w
276+ end
277+ end
278+ return y
279+ else
280+ # when the problem cannot be split in two equal size chunks we
281+ # convert the problem to a complex fft and truncate the redundant
282+ # part of the result vector
283+ x_c = similar (x, Complex{T})
284+ copy! (x_c, x)
285+ y = similar (x_c)
286+ LinearAlgebra. mul! (y, complex (p), x_c)
287+ return y[1 : end ÷ 2 + 1 ]
288+ end
200289 end
201290 throw (ArgumentError (" only FFT_FORWARD supported for real vectors" ))
202291end
@@ -205,12 +294,43 @@ end
205294function Base.:* (p:: FFTAPlan_re{T,1} , x:: AbstractVector{T} ) where {T<: Complex }
206295 Base. require_one_based_indexing (x)
207296 if p. dir === FFT_BACKWARD
208- x_tmp = similar (x, p. flen)
209- x_tmp[1 : end ÷ 2 + 1 ] .= x
210- x_tmp[end ÷ 2 + 2 : end ] .= iseven (p. flen) ? conj .(x[end - 1 : - 1 : 2 ]) : conj .(x[end : - 1 : 2 ])
211- y = similar (x_tmp)
212- LinearAlgebra. mul! (y, complex (p), x_tmp)
213- return real (y)
297+ n = p. flen
298+ # See explantion of this approach in the method for the FORWARD transform
299+ if iseven (n)
300+ m = n >> 1
301+ wj = w = cispi (T (2 ) / n)
302+ x_tmp = similar (x, length (x) - 1 )
303+ x_tmp[1 ] = complex (
304+ (real (x[1 ]) + real (x[end ])),
305+ (real (x[1 ]) - real (x[end ]))
306+ )
307+ for j in 2 : ((m >> 1 ) + 1 )
308+ XX = x[j] + conj (x[m - j + 2 ])
309+ XY = wj* (x[j] - conj (x[m - j + 2 ]))
310+ x_tmp[j] = complex (
311+ real (XX) - imag (XY),
312+ real (XY) + imag (XX)
313+ )
314+ x_tmp[m - j + 2 ] = complex (
315+ real (XX) + imag (XY),
316+ real (XY) - imag (XX)
317+ )
318+ wj *= w
319+ end
320+ y_c = complex (p) * x_tmp
321+ if isbitstype (T)
322+ return copy (reinterpret (real (T), y_c))
323+ else
324+ return mapreduce (t -> [real (t); imag (t)], vcat, y_c)
325+ end
326+ else
327+ x_tmp = similar (x, n)
328+ x_tmp[1 : end ÷ 2 + 1 ] .= x
329+ x_tmp[end ÷ 2 + 2 : end ] .= iseven (n) ? conj .(x[end - 1 : - 1 : 2 ]) : conj .(x[end : - 1 : 2 ])
330+ y = similar (x_tmp)
331+ LinearAlgebra. mul! (y, complex (p), x_tmp)
332+ return real (y)
333+ end
214334 end
215335 throw (ArgumentError (" only FFT_BACKWARD supported for complex vectors" ))
216336end
220340function Base.:* (p:: FFTAPlan_re{Complex{T},1} , x:: AbstractArray{T,N} ) where {T<: Real , N}
221341 Base. require_one_based_indexing (x)
222342 if p. dir === FFT_FORWARD
223- half_1 = 1 : (p. flen ÷ 2 + 1 )
224- x_c = similar (x, Complex{T})
225- copy! (x_c, x)
226- y = similar (x_c)
227- LinearAlgebra. mul! (y, complex (p), x_c)
228- return copy (selectdim (y, p. region[1 ], half_1))
343+ return mapslices (Base. Fix1 (* , p), x; dims = p. region[1 ])
229344 end
230345 throw (ArgumentError (" only FFT_FORWARD supported for real arrays" ))
231346end
@@ -237,21 +352,7 @@ function Base.:*(p::FFTAPlan_re{T,1}, x::AbstractArray{T,N}) where {T<:Complex,
237352 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[])) " ))
238353 end
239354 if p. dir === FFT_BACKWARD
240- # # for the inverse transformation we have to reconstruct the full array
241- half_1 = 1 : (p. flen ÷ 2 + 1 )
242- half_2 = half_1[end ]+ 1 : p. flen
243- res_size = ntuple (i-> ifelse (i== p. region[1 ], p. flen, size (x,i)), ndims (x))
244- # for the inverse transformation we have to reconstruct the full array
245- x_full = similar (x, res_size)
246- # use first half as is
247- copy! (selectdim (x_full, p. region[1 ], half_1), x)
248- start_reverse = size (x, p. region[1 ]) - iseven (p. flen)
249- half_reverse = (start_reverse: - 1 : 2 )
250- # the second half is reversed and conjugated
251- map! (conj, selectdim (x_full, p. region[1 ], half_2), selectdim (x, p. region[1 ], half_reverse))
252- y = similar (x_full)
253- LinearAlgebra. mul! (y, complex (p), x_full)
254- return real (y)
355+ return mapslices (Base. Fix1 (* , p), x; dims = p. region[1 ])
255356 end
256357 throw (ArgumentError (" only FFT_BACKWARD supported for complex arrays" ))
257358end
@@ -289,7 +390,7 @@ function Base.:*(p::FFTAPlan_re{T,2}, x::AbstractArray{T,N}) where {T<:Complex,
289390 # the second half in the first transform dimension is reversed and conjugated
290391 x_half_2 = selectdim (x_full, p. region[1 ], half_2) # view to the second half of x
291392 start_reverse = size (x, p. region[1 ]) - iseven (p. flen)
292-
393+
293394 map! (conj, x_half_2, selectdim (x, p. region[1 ], start_reverse: - 1 : 2 ))
294395 # for the 2D transform we have to reverse index 2:end of the same block in the second transform dimension as well
295396 reverse! (selectdim (x_half_2, p. region[2 ], 2 : size (x, p. region[2 ])), dims= p. region[2 ])
0 commit comments