When doing a complex-to-real FFT (using FFTW.jl), the real input and complex output arrays take roughly the same amount of memory. To quote from the rfft()
docstring,
If
A
has size(n_1, …, n_d)
, the result has size(n_1 ÷ 2 + 1, …, n_d)
.
where A
is the real input array. To save memory, it’s useful to convert between the two interpretations, i. e. a real array of size (n_1, …, n_d)
(leaving some extra unused memory) and a complex array of size (n_1 ÷ 2 + 1, …, n_d)
. In C, this is simple: A
is usually a memory block from malloc()
, and the interpretation just depends on how this is indexed.
My question is how to do the same thing in Julia, e. g. take the complex output array from rfft()
and re-use it as a real array for some other computation. As an example, let’s take a real array A
and a complex array C
from rfft()
:
julia> A = collect(reshape(1.0:5 * 3 * 4, (5, 3, 4)))
5×3×4 Array{Float64, 3}:
[:, :, 1] =
1.0 6.0 11.0
2.0 7.0 12.0
3.0 8.0 13.0
4.0 9.0 14.0
5.0 10.0 15.0
[:, :, 2] =
16.0 21.0 26.0
17.0 22.0 27.0
18.0 23.0 28.0
19.0 24.0 29.0
20.0 25.0 30.0
[:, :, 3] =
31.0 36.0 41.0
32.0 37.0 42.0
33.0 38.0 43.0
34.0 39.0 44.0
35.0 40.0 45.0
[:, :, 4] =
46.0 51.0 56.0
47.0 52.0 57.0
48.0 53.0 58.0
49.0 54.0 59.0
50.0 55.0 60.0
julia> C = rfft(A)
# set elements to consecutive integers
reshape(C, :) .= collect(1.0:2:2 * length(C)) .+ 1im .* collect(2.0:2:2 * length(C))
C
3×3×4 Array{ComplexF64, 3}:
[:, :, 1] =
1.0+2.0im 7.0+8.0im 13.0+14.0im
3.0+4.0im 9.0+10.0im 15.0+16.0im
5.0+6.0im 11.0+12.0im 17.0+18.0im
[:, :, 2] =
19.0+20.0im 25.0+26.0im 31.0+32.0im
21.0+22.0im 27.0+28.0im 33.0+34.0im
23.0+24.0im 29.0+30.0im 35.0+36.0im
[:, :, 3] =
37.0+38.0im 43.0+44.0im 49.0+50.0im
39.0+40.0im 45.0+46.0im 51.0+52.0im
41.0+42.0im 47.0+48.0im 53.0+54.0im
[:, :, 4] =
55.0+56.0im 61.0+62.0im 67.0+68.0im
57.0+58.0im 63.0+64.0im 69.0+70.0im
59.0+60.0im 65.0+66.0im 71.0+72.0im
How do I turn C
into a real array with the same shape as A
? This is what I came up with:
C_as_real = reshape(@view(reshape(reinterpret(eltype(A), C), :)[1:length(A)]), size(A))
5×3×4 reshape(view(reshape(reinterpret(Float64, ::Array{ComplexF64, 3}), 72), 1:60), 5, 3, 4) with eltype Float64:
[:, :, 1] =
1.0 6.0 11.0
2.0 7.0 12.0
3.0 8.0 13.0
4.0 9.0 14.0
5.0 10.0 15.0
[:, :, 2] =
16.0 21.0 26.0
17.0 22.0 27.0
18.0 23.0 28.0
19.0 24.0 29.0
20.0 25.0 30.0
[:, :, 3] =
31.0 36.0 41.0
32.0 37.0 42.0
33.0 38.0 43.0
34.0 39.0 44.0
35.0 40.0 45.0
[:, :, 4] =
46.0 51.0 56.0
47.0 52.0 57.0
48.0 53.0 58.0
49.0 54.0 59.0
50.0 55.0 60.0
julia> A == C_as_real
true
Explanation:
reinterpret(eltype(A), C)
: Reinterpret the complex array as realreshape(..., :)
: Reshape the array as 1D (to be able to get rid of the extra elements)@view(...[1:length(A)])
: Discard extra memoryreshape(..., size(A))
: Reshape back to the original shape
Am I overcomplicating things, or is this how it needs to be done?