I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Discrete Fourier transform

Discrete Fourier transform is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

The discrete Fourier transform is a linear, invertible transformation which transforms an arbitrary sequence of complex numbers to another sequence of complex numbers of the same length. The Fast Fourier transform (FFT) is an efficient implementation of this mechanism, but one which only works for sequences which have a length which is a power of 2.

The discrete Fourier transform is a useful testing mechanism to verify the correctness of code bases which use or implement the FFT.

1. Implement the discrete fourier transform
2. Implement the inverse fourier transform
3. (optional) implement a cleaning mechanism to remove small errors introduced by floating point representation.
4. Verify the correctness of your implementation using a small sequence of integers, such as 2 3 5 7 11

The fourier transform of a sequence Failed to parse (http://mathoid.testme.wmflabs.org server response is invalid JSON.): x_n

of length ${\displaystyle N}$ is given by:


$k=0}^{N-1} x_k\cdot e^{-i \frac{2 \pi}{N} k n$

The inverse transform is given by: $1}{N} \sum_{k=0}^{N-1} X_k\cdot e^{i \frac{2 \pi}{N} k n$

## J

Implementation:

fourier=: ] +/@:* ^@(0j_2p1 * */[email protected]@# % #)ifourier=: # %~ ] +/@:* ^@(0j2p1 * */[email protected]@# % #) require'general/misc/numeric'clean=: 1e_9&round&.+.

Example use:

   clean fourier 2 3 5 7 115.6 _0.676393j1.7568 _1.12361j0.560034 _1.12361j_0.560034 _0.676393j_1.7568   clean ifourier fourier 2 3 5 7 112 3 5 7 11   clean ifourier 2 * fourier 2 3 5 7 114 6 10 14 22   clean ifourier 2 + fourier 2 3 5 7 114 3 5 7 11

## Julia

The cispi function was added in Julia 1.6. The cispi of x is e to the power of (pi times i times x). Other syntax, such as {T,N} in the function signature and real() and ntuple() in the loop, is designed to support arbitrary dimensional arrays and to cue the compiler so as to have mostly constants in the loop at runtime, speeding run time with large arrays.

function dft(A::AbstractArray{T,N}) where {T,N}    F = zeros(complex(float(T)), size(A)...)    for k in CartesianIndices(F), n in CartesianIndices(A)        F[k] += cispi(-2 * sum(d -> (k[d] - 1) * (n[d] - 1) /            real(eltype(F))(size(A, d)), ntuple(identity, Val{N}()))) * A[n]    end    return Fend function idft(A::AbstractArray{T,N}) where {T,N}    F = zeros(complex(float(T)), size(A)...)    for k in CartesianIndices(F), n in CartesianIndices(A)        F[k] += cispi(2 * sum(d -> (k[d] - 1) * (n[d] - 1) /            real(eltype(F))(size(A, d)), ntuple(identity, Val{N}()))) * A[n]    end    return F ./ length(A)end seq = [2, 3, 5, 7, 11]fseq = dft(seq)newseq = idft(fseq)println("$seq =>\n$fseq =>\n$newseq =>\n", Int.(round.(newseq))) seq2 = [2 7; 3 11; 5 13]fseq = dft(seq2)newseq = idft(fseq)println("$seq2 =>\n$fseq =>\n$newseq =>\n", Int.(round.(newseq)))
Output:
[2, 3, 5, 7, 11] =>
ComplexF64[28.0 + 0.0im, -3.3819660112501033 + 8.784022634946172im, -5.618033988749888 + 2.800168985749483im, -5.618033988749888 - 2.800168985749483im, -3.381966011250112 - 8.78402263494618im] =>
ComplexF64[2.0000000000000013 - 1.4210854715202005e-15im, 2.999999999999996 + 7.993605777301127e-16im, 5.000000000000002 + 2.1316282072803005e-15im, 6.999999999999998 - 8.881784197001252e-16im, 11.0 + 0.0im] =>
[2, 3, 5, 7, 11]
[2 7; 3 11; 5 13] =>
ComplexF64[41.0 + 0.0im -21.0 + 0.0im; -7.000000000000002 + 3.46410161513775im 3.0000000000000053 + 7.105427357601002e-15im; -6.9999999999999964 - 3.4641016151377606im 3.0000000000000053 + 7.105427357601002e-15im] =>
ComplexF64[2.000000000000002 + 5.921189464667501e-16im 6.999999999999997 - 4.144832625267251e-15im; 2.9999999999999996 - 8.881784197001252e-16im 11.000000000000002 + 1.0362081563168128e-15im; 4.999999999999999 + 0.0im 13.000000000000002 + 1.924386576016938e-15im] =>
[2 7; 3 11; 5 13]


## Phix

Translation of: Wren
include complex.e

function dft(sequence x)
integer N = length(x)
sequence y = repeat(0,N)
for k=1 to N do
complex yk = complex_new(0,0)
for n=1 to N do
complex t = complex_new(0,-2*PI*(k-1)*(n-1)/N)
end for
y[k] = yk
end for
return y
end function

function idft(sequence y)
integer N = length(y)
sequence x = repeat(0,N)
for n=1 to N do
object xn = complex_new(0,0)
for k=1 to N do
complex t = complex_new(0,2*PI*(k-1)*(n-1)/N)
end for
xn = complex_div(xn,N)
// clean xn to remove very small imaginary values, and round reals to 14dp
if abs(complex_imag(xn))<1e-14 then xn = round(complex_real(xn),1e14) end if
x[n] = xn
end for
return x
end function

sequence x = {2, 3, 5, 7, 11},
y = dft(x),
z = idft(y)
printf(1,"Original sequence: %v\n",{x})
printf(1,"Discrete Fourier Transform: %v\n",{apply(y,complex_sprint)})
printf(1,"Inverse Discrete Fourier Transform: %v\n",{z})

Output:
Original sequence: {2,3,5,7,11}
Discrete Fourier Transform: {"28","-3.38197+8.78402i","-5.61803+2.80017i","-5.61803-2.80017i","-3.38197-8.78402i"}
Inverse Discrete Fourier Transform: {2,3,5,7,11}


## Raku

• This task could be done with a loop of maps like
@X[k] = sum @x.kv.map: -> \n, \v { v * exp( -i * tau / N * k * n ) }
, but it is a better fit for Raku's concurrent Hyper-operators.
• In the DFT formula, N becomes [email protected], the element count. We would usually omit the plus sign and get the same result from numeric context, but that gets confusing to read when mixed with hyper-ops like »*« .
• The exponents of DFT and IDFT only differ from each other in the sign of i, and the calculation of any given elements only differ by the k index of the element being output, so the inner loop is cleanly shareable between DFT and IDFT.
• Euler's constant, imaginary unit, pi, and 2pi are built-in, with ASCII and Unicode spellings: e 𝑒 i pi π tau τ .
• $one_thing «op« @many_things vectorizes the op operation: 5 «+« (1,2,3) is (6,7,8) . • @many_things »op« @many_more_things distributes the op operation pairwise: (5,6,7) »+« (1,2,3) is (6,8,10) . • (list)».method applies the method to each element of the list. • @array.keys, when @array has e.g. 4 elements, is 0,1,2,3 . •$n.round($r) returns$n rounded to the nearest $r. (13/16).round(1/4) is 3/4 . • .narrow changes a Numeric's type to a "narrower" type, when no precision would be lost. 8/2 is 4, but is type Rat (rational). (8/2).narrow is also 4, but type Int. sub ft_inner ( @x,$k, $pos_neg_i where * == i|-i ) { my @exp := ($pos_neg_i * tau / [email protected]x * $k ) «*« @x.keys; return sum @x »*« 𝑒 «**« @exp;}sub dft ( @x ) { return @x.keys.map: { ft_inner( @x,$_, -i )       } }sub idft  ( @x ) { return @x.keys.map: { ft_inner( @x, \$_,  i ) / [email protected]x } }sub clean ( @x ) { @x».round(1e-12)».narrow } my @tests = ( 1, 2-i, -i, -1+2i     ),            ( 2,   3,  5,     7, 11 ),;for @tests -> @x {    my @x_dft  =  dft(@x);    my @x_idft = idft(@x_dft);     say .key.fmt('%6s:'), .value.&clean.fmt('%5s', ', ') for :@x, :@x_dft, :@x_idft;    say '';    warn "Round-trip failed" unless ( clean(@x) Z== clean(@x_idft) ).all;}
Output:
     x:    1,  2-1i,  0-1i, -1+2i
x_dft:    2, -2-2i,  0-2i,  4+4i
x_idft:    1,  2-1i,  0-1i, -1+2i

x:    2,     3,     5,     7,    11
x_dft:   28, -3.38196601125+8.784022634946i, -5.61803398875+2.800168985749i, -5.61803398875-2.800168985749i, -3.38196601125-8.784022634946i
x_idft:    2,     3,     5,     7,    11

## Wren

Library: Wren-complex
import "/complex" for Complex var dft = Fn.new { |x|    var N = x.count    var y = List.filled(N, null)    for (k in 0...N) {        y[k] = Complex.zero        for (n in 0...N) {            var t = Complex.imagMinusOne * Complex.two * Complex.pi * k * n / N            y[k] = y[k] + x[n] * t.exp        }    }    return y} var idft = Fn.new { |y|    var N = y.count    var x = List.filled(N, null)    for (n in 0...N) {        x[n] = Complex.zero        for (k in 0...N) {            var t = Complex.imagOne * Complex.two * Complex.pi * k * n / N            x[n] = x[n] +  y[k] * t.exp        }        x[n] = x[n] / N        // clean x[n] to remove very small imaginary values        if (x[n].imag.abs < 1e-14) x[n] = Complex.new(x[n].real, 0)    }    return x} var x = [2, 3, 5, 7, 11]System.print("Original sequence: %(x)")for (i in 0...x.count) x[i] = Complex.new(x[i])var y = dft.call(x)Complex.showAsReal = true // don't display the imaginary part if it's 0System.print("\nAfter applying the Discrete Fourier Transform:")System.print(y)System.print("\nAfter applying the Inverse Discrete Fourier Transform to the above transform:")System.print(idft.call(y))
Output:
Original sequence: [2, 3, 5, 7, 11]

After applying the Discrete Fourier Transform:
[28, -3.3819660112501 + 8.7840226349462i, -5.6180339887499 + 2.8001689857495i, -5.6180339887499 - 2.8001689857495i, -3.3819660112501 - 8.7840226349462i]

After applying the Inverse Discrete Fourier Transform to the above transform:
[2, 3, 5, 7, 11]