|
1 | | -const AbstractFloats = Union{AbstractFloat,Complex{T} where T<:AbstractFloat} |
2 | | - |
3 | | -# We use these type definitions for clarity |
4 | | -const RealFloats = T where T<:AbstractFloat |
5 | | -const ComplexFloats = Complex{T} where T<:AbstractFloat |
6 | | - |
7 | | - |
8 | | -# The following implements Bluestein's algorithm, following http://www.dsprelated.com/dspbooks/mdft/Bluestein_s_FFT_Algorithm.html |
9 | | -# To add more types, add them in the union of the function's signature. |
10 | | - |
11 | | -function generic_fft(x::StridedVector{T}, region::Integer) where T<:AbstractFloats |
12 | | - region == 1 && (ret = generic_fft(x)) |
13 | | - ret |
14 | | -end |
15 | | - |
16 | | -function generic_fft!(x::StridedVector{T}, region::Integer) where T<:AbstractFloats |
17 | | - region == 1 && (x[:] .= generic_fft(x)) |
18 | | - x |
19 | | -end |
20 | | - |
21 | | -function generic_fft(x::StridedVector{T}, region::UnitRange{I}) where {T<:AbstractFloats, I<:Integer} |
22 | | - region == 1:1 && (ret = generic_fft(x)) |
23 | | - ret |
24 | | -end |
25 | | - |
26 | | -function generic_fft!(x::StridedVector{T}, region::UnitRange{I}) where {T<:AbstractFloats, I<:Integer} |
27 | | - region == 1:1 && (x[:] .= generic_fft(x)) |
28 | | - x |
29 | | -end |
30 | | - |
31 | | -function generic_fft(x::StridedMatrix{T}, region::Integer) where T<:AbstractFloats |
32 | | - if region == 1 |
33 | | - ret = hcat([generic_fft(x[:, j]) for j in 1:size(x, 2)]...) |
34 | | - end |
35 | | - ret |
36 | | -end |
37 | | - |
38 | | -function generic_fft!(x::StridedMatrix{T}, region::Integer) where T<:AbstractFloats |
39 | | - if region == 1 |
40 | | - for j in 1:size(x, 2) |
41 | | - x[:, j] .= generic_fft(x[:, j]) |
42 | | - end |
43 | | - end |
44 | | - x |
45 | | -end |
46 | | - |
47 | | -function generic_fft(x::Vector{T}) where T<:AbstractFloats |
48 | | - T <: FFTW.fftwNumber && (@warn("Using generic fft for FFTW number type.")) |
49 | | - n = length(x) |
50 | | - ispow2(n) && return generic_fft_pow2(x) |
51 | | - ks = range(zero(real(T)),stop=n-one(real(T)),length=n) |
52 | | - Wks = exp.((-im).*convert(T,π).*ks.^2 ./ n) |
53 | | - xq, wq = x.*Wks, conj([exp(-im*convert(T,π)*n);reverse(Wks);Wks[2:end]]) |
54 | | - return Wks.*_conv!(xq,wq)[n+1:2n] |
55 | | -end |
56 | | - |
57 | | -generic_bfft(x::StridedArray{T, N}, region) where {T <: AbstractFloats, N} = conj!(generic_fft(conj(x), region)) |
58 | | -generic_bfft!(x::StridedArray{T, N}, region) where {T <: AbstractFloats, N} = conj!(generic_fft!(conj!(x), region)) |
59 | | -generic_ifft(x::StridedArray{T, N}, region) where {T<:AbstractFloats, N} = ldiv!(length(x), conj!(generic_fft(conj(x), region))) |
60 | | -generic_ifft!(x::StridedArray{T, N}, region) where {T<:AbstractFloats, N} = ldiv!(length(x), conj!(generic_fft!(conj!(x), region))) |
61 | | - |
62 | | -generic_rfft(v::Vector{T}, region) where T<:AbstractFloats = generic_fft(v, region)[1:div(length(v),2)+1] |
63 | | -function generic_irfft(v::Vector{T}, n::Integer, region) where T<:ComplexFloats |
64 | | - @assert n==2length(v)-1 |
65 | | - r = Vector{T}(undef, n) |
66 | | - r[1:length(v)]=v |
67 | | - r[length(v)+1:end]=reverse(conj(v[2:end])) |
68 | | - real(generic_ifft(r, region)) |
69 | | -end |
70 | | -generic_brfft(v::StridedArray, n::Integer, region) = generic_irfft(v, n, region)*n |
71 | | - |
72 | | -function _conv!(u::StridedVector{T}, v::StridedVector{T}) where T<:AbstractFloats |
73 | | - nu = length(u) |
74 | | - nv = length(v) |
75 | | - n = nu + nv - 1 |
76 | | - np2 = nextpow(2, n) |
77 | | - append!(u, zeros(T, np2-nu)) |
78 | | - append!(v, zeros(T, np2-nv)) |
79 | | - y = generic_ifft_pow2(generic_fft_pow2(u).*generic_fft_pow2(v)) |
80 | | - #TODO This would not handle Dual/ComplexDual numbers correctly |
81 | | - y = T<:Real ? real(y[1:n]) : y[1:n] |
82 | | -end |
83 | | - |
84 | | -conv(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T<:AbstractFloat, N} = _conv!(deepcopy(u), deepcopy(v)) |
85 | | -conv(u::AbstractArray{T, N}, v::AbstractArray{Complex{T}, N}) where {T<:AbstractFloat, N} = _conv!(complex(deepcopy(u)), deepcopy(v)) |
86 | | -conv(u::AbstractArray{Complex{T}, N}, v::AbstractArray{T, N}) where {T<:AbstractFloat, N} = _conv!(deepcopy(u), complex(deepcopy(v))) |
87 | | -conv(u::AbstractArray{Complex{T}, N}, v::AbstractArray{Complex{T}, N}) where {T<:AbstractFloat, N} = _conv!(deepcopy(u), deepcopy(v)) |
88 | | - |
89 | | -# This is a Cooley-Tukey FFT algorithm inspired by many widely available algorithms including: |
90 | | -# c_radix2.c in the GNU Scientific Library and four1 in the Numerical Recipes in C. |
91 | | -# However, the trigonometric recurrence is improved for greater efficiency. |
92 | | -# The algorithm starts with bit-reversal, then divides and conquers in-place. |
93 | | -function generic_fft_pow2!(x::Vector{T}) where T<:AbstractFloat |
94 | | - n,big2=length(x),2one(T) |
95 | | - nn,j=n÷2,1 |
96 | | - for i=1:2:n-1 |
97 | | - if j>i |
98 | | - x[j], x[i] = x[i], x[j] |
99 | | - x[j+1], x[i+1] = x[i+1], x[j+1] |
100 | | - end |
101 | | - m = nn |
102 | | - while m ≥ 2 && j > m |
103 | | - j -= m |
104 | | - m = m÷2 |
105 | | - end |
106 | | - j += m |
107 | | - end |
108 | | - logn = 2 |
109 | | - while logn < n |
110 | | - θ=-big2/logn |
111 | | - wtemp = sinpi(θ/2) |
112 | | - wpr, wpi = -2wtemp^2, sinpi(θ) |
113 | | - wr, wi = one(T), zero(T) |
114 | | - for m=1:2:logn-1 |
115 | | - for i=m:2logn:n |
116 | | - j=i+logn |
117 | | - mixr, mixi = wr*x[j]-wi*x[j+1], wr*x[j+1]+wi*x[j] |
118 | | - x[j], x[j+1] = x[i]-mixr, x[i+1]-mixi |
119 | | - x[i], x[i+1] = x[i]+mixr, x[i+1]+mixi |
120 | | - end |
121 | | - wr = (wtemp=wr)*wpr-wi*wpi+wr |
122 | | - wi = wi*wpr+wtemp*wpi+wi |
123 | | - end |
124 | | - logn = logn << 1 |
125 | | - end |
126 | | - return x |
127 | | -end |
128 | | - |
129 | | -function generic_fft_pow2(x::Vector{Complex{T}}) where T<:AbstractFloat |
130 | | - y = interlace(real(x), imag(x)) |
131 | | - generic_fft_pow2!(y) |
132 | | - return complex.(y[1:2:end], y[2:2:end]) |
133 | | -end |
134 | | -generic_fft_pow2(x::Vector{T}) where T<:AbstractFloat = generic_fft_pow2(complex(x)) |
135 | | - |
136 | | -function generic_ifft_pow2(x::Vector{Complex{T}}) where T<:AbstractFloat |
137 | | - y = interlace(real(x), -imag(x)) |
138 | | - generic_fft_pow2!(y) |
139 | | - return ldiv!(length(x), conj!(complex.(y[1:2:end], y[2:2:end]))) |
140 | | -end |
141 | | - |
142 | | -function generic_dct(x::StridedVector{T}, region::Integer) where T<:AbstractFloats |
143 | | - region == 1 && (ret = generic_dct(x)) |
144 | | - ret |
145 | | -end |
146 | | - |
147 | | -function generic_dct!(x::StridedVector{T}, region::Integer) where T<:AbstractFloats |
148 | | - region == 1 && (x[:] .= generic_dct(x)) |
149 | | - x |
150 | | -end |
151 | | - |
152 | | -function generic_idct(x::StridedVector{T}, region::Integer) where T<:AbstractFloats |
153 | | - region == 1 && (ret = generic_idct(x)) |
154 | | - ret |
155 | | -end |
156 | | - |
157 | | -function generic_idct!(x::StridedVector{T}, region::Integer) where T<:AbstractFloats |
158 | | - region == 1 && (x[:] .= generic_idct(x)) |
159 | | - x |
160 | | -end |
161 | | - |
162 | | -function generic_dct(x::StridedVector{T}, region::UnitRange{I}) where {T<:AbstractFloats, I<:Integer} |
163 | | - region == 1:1 && (ret = generic_dct(x)) |
164 | | - ret |
165 | | -end |
166 | | - |
167 | | -function generic_dct!(x::StridedVector{T}, region::UnitRange{I}) where {T<:AbstractFloats, I<:Integer} |
168 | | - region == 1:1 && (x[:] .= generic_dct(x)) |
169 | | - x |
170 | | -end |
171 | | - |
172 | | -function generic_idct(x::StridedVector{T}, region::UnitRange{I}) where {T<:AbstractFloats, I<:Integer} |
173 | | - region == 1:1 && (ret = generic_idct(x)) |
174 | | - ret |
175 | | -end |
176 | | - |
177 | | -function generic_idct!(x::StridedVector{T}, region::UnitRange{I}) where {T<:AbstractFloats, I<:Integer} |
178 | | - region == 1:1 && (x[:] .= generic_idct(x)) |
179 | | - x |
180 | | -end |
181 | | - |
182 | | -function generic_dct(a::AbstractVector{Complex{T}}) where {T <: AbstractFloat} |
183 | | - T <: FFTW.fftwNumber && (@warn("Using generic dct for FFTW number type.")) |
184 | | - N = length(a) |
185 | | - twoN = convert(T,2) * N |
186 | | - c = generic_fft([a; reverse(a, dims=1)]) # c = generic_fft([a; flipdim(a,1)]) |
187 | | - d = c[1:N] |
188 | | - d .*= exp.((-im*convert(T, pi)).*(0:N-1)./twoN) |
189 | | - d[1] = d[1] / sqrt(convert(T, 2)) |
190 | | - lmul!(inv(sqrt(twoN)), d) |
191 | | -end |
192 | | - |
193 | | -generic_dct(a::AbstractArray{T}) where {T <: AbstractFloat} = real(generic_dct(complex(a))) |
194 | | - |
195 | | -function generic_idct(a::AbstractVector{Complex{T}}) where {T <: AbstractFloat} |
196 | | - T <: FFTW.fftwNumber && (@warn("Using generic idct for FFTW number type.")) |
197 | | - N = length(a) |
198 | | - twoN = convert(T,2)*N |
199 | | - b = a * sqrt(twoN) |
200 | | - b[1] = b[1] * sqrt(convert(T,2)) |
201 | | - shift = exp.(-im * 2 * convert(T, pi) * (N - convert(T,1)/2) * (0:(2N-1)) / twoN) |
202 | | - b = [b; 0; -reverse(b[2:end], dims=1)] .* shift # b = [b; 0; -flipdim(b[2:end],1)] .* shift |
203 | | - c = ifft(b) |
204 | | - reverse(c[1:N]; dims=1)#flipdim(c[1:N],1) |
205 | | -end |
206 | | - |
207 | | -generic_idct(a::AbstractArray{T}) where {T <: AbstractFloat} = real(generic_idct(complex(a))) |
208 | | - |
209 | | - |
210 | | -# These lines mimick the corresponding ones in FFTW/src/dct.jl, but with |
211 | | -# AbstractFloat rather than fftwNumber. |
212 | | -for f in (:dct, :dct!, :idct, :idct!) |
213 | | - pf = Symbol("plan_", f) |
214 | | - @eval begin |
215 | | - $f(x::AbstractArray{<:AbstractFloats}) = $pf(x) * x |
216 | | - $f(x::AbstractArray{<:AbstractFloats}, region) = $pf(x, region) * x |
217 | | - end |
218 | | -end |
219 | | - |
220 | | -# dummy plans |
221 | | -abstract type DummyPlan{T} <: Plan{T} end |
222 | | -for P in (:DummyFFTPlan, :DummyiFFTPlan, :DummybFFTPlan, :DummyDCTPlan, :DummyiDCTPlan) |
223 | | - # All plans need an initially undefined pinv field |
224 | | - @eval begin |
225 | | - mutable struct $P{T,inplace,G} <: DummyPlan{T} |
226 | | - region::G # region (iterable) of dims that are transformed |
227 | | - pinv::DummyPlan{T} |
228 | | - $P{T,inplace,G}(region::G) where {T<:AbstractFloats, inplace, G} = new(region) |
229 | | - end |
230 | | - end |
231 | | -end |
232 | | -for P in (:DummyrFFTPlan, :DummyirFFTPlan, :DummybrFFTPlan) |
233 | | - @eval begin |
234 | | - mutable struct $P{T,inplace,G} <: DummyPlan{T} |
235 | | - n::Integer |
236 | | - region::G # region (iterable) of dims that are transformed |
237 | | - pinv::DummyPlan{T} |
238 | | - $P{T,inplace,G}(n::Integer, region::G) where {T<:AbstractFloats, inplace, G} = new(n, region) |
239 | | - end |
240 | | - end |
241 | | -end |
242 | | - |
243 | | -for (Plan,iPlan) in ((:DummyFFTPlan,:DummyiFFTPlan), |
244 | | - (:DummyDCTPlan,:DummyiDCTPlan)) |
245 | | - @eval begin |
246 | | - plan_inv(p::$Plan{T,inplace,G}) where {T,inplace,G} = $iPlan{T,inplace,G}(p.region) |
247 | | - plan_inv(p::$iPlan{T,inplace,G}) where {T,inplace,G} = $Plan{T,inplace,G}(p.region) |
248 | | - end |
249 | | -end |
250 | | - |
251 | | -# Specific for rfft, irfft and brfft: |
252 | | -plan_inv(p::DummyirFFTPlan{T,inplace,G}) where {T,inplace,G} = DummyrFFTPlan{T,Inplace,G}(p.n, p.region) |
253 | | -plan_inv(p::DummyrFFTPlan{T,inplace,G}) where {T,inplace,G} = DummyirFFTPlan{T,Inplace,G}(p.n, p.region) |
254 | | - |
255 | | - |
256 | | - |
257 | | -for (Plan,ff,ff!) in ((:DummyFFTPlan,:generic_fft,:generic_fft!), |
258 | | - (:DummybFFTPlan,:generic_bfft,:generic_bfft!), |
259 | | - (:DummyiFFTPlan,:generic_ifft,:generic_ifft!), |
260 | | - (:DummyrFFTPlan,:generic_rfft,:generic_rfft!), |
261 | | - (:DummyDCTPlan,:generic_dct,:generic_dct!), |
262 | | - (:DummyiDCTPlan,:generic_idct,:generic_idct!)) |
263 | | - @eval begin |
264 | | - *(p::$Plan{T,true}, x::StridedArray{T,N}) where {T<:AbstractFloats,N} = $ff!(x, p.region) |
265 | | - *(p::$Plan{T,false}, x::StridedArray{T,N}) where {T<:AbstractFloats,N} = $ff(x, p.region) |
266 | | - function mul!(C::StridedVector, p::$Plan, x::StridedVector) |
267 | | - C[:] = $ff(x, p.region) |
268 | | - C |
269 | | - end |
270 | | - end |
271 | | -end |
272 | | - |
273 | | -# Specific for irfft and brfft: |
274 | | -*(p::DummyirFFTPlan{T,true}, x::StridedArray{T,N}) where {T<:AbstractFloats,N} = generic_irfft!(x, p.n, p.region) |
275 | | -*(p::DummyirFFTPlan{T,false}, x::StridedArray{T,N}) where {T<:AbstractFloats,N} = generic_irfft(x, p.n, p.region) |
276 | | -function mul!(C::StridedVector, p::DummyirFFTPlan, x::StridedVector) |
277 | | - C[:] = generic_irfft(x, p.n, p.region) |
278 | | - C |
279 | | -end |
280 | | -*(p::DummybrFFTPlan{T,true}, x::StridedArray{T,N}) where {T<:AbstractFloats,N} = generic_brfft!(x, p.n, p.region) |
281 | | -*(p::DummybrFFTPlan{T,false}, x::StridedArray{T,N}) where {T<:AbstractFloats,N} = generic_brfft(x, p.n, p.region) |
282 | | -function mul!(C::StridedVector, p::DummybrFFTPlan, x::StridedVector) |
283 | | - C[:] = generic_brfft(x, p.n, p.region) |
284 | | - C |
285 | | -end |
286 | | - |
287 | | - |
288 | | -# We override these for AbstractFloat, so that conversion from reals to |
289 | | -# complex numbers works for any AbstractFloat (instead of only BlasFloat's) |
290 | | -AbstractFFTs.complexfloat(x::StridedArray{Complex{<:AbstractFloat}}) = x |
291 | | -AbstractFFTs.realfloat(x::StridedArray{<:Real}) = x |
292 | | -# We override this one in order to avoid throwing an error that the type is |
293 | | -# unsupported (as defined in AbstractFFTs) |
294 | | -AbstractFFTs._fftfloat(::Type{T}) where {T <: AbstractFloat} = T |
295 | | - |
296 | | - |
297 | | -# We intercept the calls to plan_X(x, region) below. |
298 | | -# In order not to capture any calls that should go to FFTW, we have to be |
299 | | -# careful about the typing, so that the calls to FFTW remain more specific. |
300 | | -# This is the reason for using StridedArray below. We also have to carefully |
301 | | -# distinguish between real and complex arguments. |
302 | | - |
303 | | -plan_fft(x::StridedArray{T}, region) where {T <: ComplexFloats} = DummyFFTPlan{Complex{real(T)},false,typeof(region)}(region) |
304 | | -plan_fft!(x::StridedArray{T}, region) where {T <: ComplexFloats} = DummyFFTPlan{Complex{real(T)},true,typeof(region)}(region) |
305 | | - |
306 | | -plan_bfft(x::StridedArray{T}, region) where {T <: ComplexFloats} = DummybFFTPlan{Complex{real(T)},false,typeof(region)}(region) |
307 | | -plan_bfft!(x::StridedArray{T}, region) where {T <: ComplexFloats} = DummybFFTPlan{Complex{real(T)},true,typeof(region)}(region) |
308 | | - |
309 | | -# The ifft plans are automatically provided in terms of the bfft plans above. |
310 | | -# plan_ifft(x::StridedArray{T}, region) where {T <: ComplexFloats} = DummyiFFTPlan{Complex{real(T)},false,typeof(region)}(region) |
311 | | -# plan_ifft!(x::StridedArray{T}, region) where {T <: ComplexFloats} = DummyiFFTPlan{Complex{real(T)},true,typeof(region)}(region) |
312 | | - |
313 | | -plan_dct(x::StridedArray{T}, region) where {T <: AbstractFloats} = DummyDCTPlan{T,false,typeof(region)}(region) |
314 | | -plan_dct!(x::StridedArray{T}, region) where {T <: AbstractFloats} = DummyDCTPlan{T,true,typeof(region)}(region) |
315 | | - |
316 | | -plan_idct(x::StridedArray{T}, region) where {T <: AbstractFloats} = DummyiDCTPlan{T,false,typeof(region)}(region) |
317 | | -plan_idct!(x::StridedArray{T}, region) where {T <: AbstractFloats} = DummyiDCTPlan{T,true,typeof(region)}(region) |
318 | | - |
319 | | -plan_rfft(x::StridedArray{T}, region) where {T <: RealFloats} = DummyrFFTPlan{Complex{real(T)},false,typeof(region)}(length(x), region) |
320 | | -plan_brfft(x::StridedArray{T}, n::Integer, region) where {T <: ComplexFloats} = DummybrFFTPlan{Complex{real(T)},false,typeof(region)}(n, region) |
321 | | - |
322 | | -# A plan for irfft is created in terms of a plan for brfft. |
323 | | -# plan_irfft(x::StridedArray{T}, n::Integer, region) where {T <: ComplexFloats} = DummyirFFTPlan{Complex{real(T)},false,typeof(region)}(n, region) |
324 | | - |
325 | | -# These don't exist for now: |
326 | | -# plan_rfft!(x::StridedArray{T}) where {T <: RealFloats} = DummyrFFTPlan{Complex{real(T)},true}() |
327 | | -# plan_irfft!(x::StridedArray{T},n::Integer) where {T <: RealFloats} = DummyirFFTPlan{Complex{real(T)},true}() |
328 | | - |
329 | | -function interlace(a::Vector{S},b::Vector{V}) where {S<:Number,V<:Number} |
330 | | - na=length(a);nb=length(b) |
331 | | - T=promote_type(S,V) |
332 | | - if nb≥na |
333 | | - ret=zeros(T,2nb) |
334 | | - ret[1:2:1+2*(na-1)]=a |
335 | | - ret[2:2:end]=b |
336 | | - ret |
337 | | - else |
338 | | - ret=zeros(T,2na-1) |
339 | | - ret[1:2:end]=a |
340 | | - if !isempty(b) |
341 | | - ret[2:2:2+2*(nb-1)]=b |
342 | | - end |
343 | | - ret |
344 | | - end |
345 | | -end |
| 1 | +conv(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T<:AbstractFloat, N} = GenericFFT._conv!(deepcopy(u), deepcopy(v)) |
| 2 | +conv(u::AbstractArray{T, N}, v::AbstractArray{Complex{T}, N}) where {T<:AbstractFloat, N} = GenericFFT._conv!(complex(deepcopy(u)), deepcopy(v)) |
| 3 | +conv(u::AbstractArray{Complex{T}, N}, v::AbstractArray{T, N}) where {T<:AbstractFloat, N} = GenericFFT._conv!(deepcopy(u), complex(deepcopy(v))) |
| 4 | +conv(u::AbstractArray{Complex{T}, N}, v::AbstractArray{Complex{T}, N}) where {T<:AbstractFloat, N} = GenericFFT._conv!(deepcopy(u), deepcopy(v)) |
0 commit comments