1

I am new to python and I'm trying a python implementation of a FFT. I have been getting the above mentioned error for a while. What do i do?

import numpy
import cmath

def twiddle(r,s):
    x = (cmath.exp((2*cmath.pi*1j*s)/r))
    return x


def fft(signal):
    n = len(signal)
    if n==1:
        return signal
    else:
        Feven=fft([signal[k] for k in range(0,n,2)])
        Fodd=fft([signal[k] for k in range(1,n,2)])


    for l in range (n/2):
        F1 = Feven[l] + twiddle(n, -l) * Fodd[l]
        F2 = Feven[l] - twiddle(n, -l) * Fodd[l]
        return F1+F2

When I add print statements for Feven and Fodd, and feed in this:

print (fft([4.5, 3.4, 4.7, 3.8, 6.7, 8.0, 4.6, 7.8]))

I get:

Traceback (most recent call last):
  File "FFT.py", line 41, in <module>
    print (fft([4.5, 3.4, 4.7, 3.8, 6.7, 8.0, 4.6, 7.8]))
  File "FFT.py", line 29, in fft
    Feven=fft([signal[k] for k in range(0,n,2)])
  File "FFT.py", line 34, in fft
   F1 = Feven[l] + twiddler(n, -l) * Fodd[l]
 TypeError: 'complex' object has no attribute '__getitem__'

1 Answers1

5

Fixing the bug

The problem is your line

return F1+F2

this causes fft to return immediately (with the complex number F1 + F2), when in fact it should be returning a list. What you meant, I presume, was something like this:

def fft(signal):
    n = len(signal)
    if n==1:
        return signal
    else:
        Feven=fft([signal[k] for k in range(0,n,2)])
        Fodd=fft([signal[k] for k in range(1,n,2)])
    F1 = [Feven[l] + twiddle(n, -l) * Fodd[l] for l in range (n/2)]
    F2 = [Feven[l] - twiddle(n, -l) * Fodd[l] for l in range (n/2)]
    return F1+F2

Other comments on your code

  1. Instead of [signal[k] for k in range(0,n,2)] you can write signal[0:n:2] using Python's slice notation. And similarly, instead of [signal[k] for k in range(1,n,2)] you can write signal[1:n:2].

  2. In fact, since n is the length of the list signal, you can omit it since that's the default behaviour for slices. So you can actually write:

    Feven = fft(signal[::2])
    Fodd = fft(signal[1::2])
    
  3. It doesn't make sense to have some of the "else" code inside the else: clause and some outside. Put it all in one or the other.

  4. Since the Fodd numbers are always multiplied by the twiddle factor, why not do that once instead of twice? Perhaps like this:

    def fft(signal):
        n = len(signal)
        if n == 1:
            return signal
        feven = fft(signal[::2])
        fodd = [twiddle(n, -k) * o for k, o in enumerate(fft(signal[1::2]))]
        f1 = [e + o for e, o in zip(feven, fodd)]
        f2 = [e - o for e, o in zip(feven, fodd)]
        return f1 + f2
    
Community
  • 1
  • 1
Gareth Rees
  • 64,967
  • 9
  • 133
  • 163