Search code examples
pythonswiftnumpyfftcoreml

Accelerate and NumPy produce different results for FFT


I am working on a feature engineering pipeline for CoreML and I need to perform the FFT on my data. The problem is that the results of the Accelerate framework and the results of the NumPy FFT are different.

Swift:

public func fft(_ input: [Double]) -> [Double] {
    var real = [Double](input)
    var imaginary = [Double](repeating: 0.0, count: input.count)
    var splitComplex = DSPDoubleSplitComplex(realp: &real, imagp: &imaginary)

    let length = vDSP_Length(floor(log2(Float(input.count))))
    let radix = FFTRadix(kFFTRadix2)
    let weights = vDSP_create_fftsetupD(length, radix)
    vDSP_fft_zipD(weights!, &splitComplex, 1, length, FFTDirection(FFT_FORWARD))

    var magnitudes = [Double](repeating: 0.0, count: input.count)
    vDSP_zvmagsD(&splitComplex, 1, &magnitudes, 1, vDSP_Length(input.count))

    var normalizedMagnitudes = [Double](repeating: 0.0, count: input.count)
    vDSP_vsmulD(sqrt(magnitudes), 1, [2.0 / Double(input.count)], &normalizedMagnitudes, 1, vDSP_Length(input.count))

    vDSP_destroy_fftsetupD(weights)

    return normalizedMagnitudes
}

Python:

def fft(series: pd.Series):
    f = np.fft.fft(series)
    fa = np.abs(f)
    return pd.Series(fa)

I use the same 100 values for every method.

I guess it has something to do with the normalizing part, but i'm not even sure if both arrays contain the same things like:

  • index 0: zero frequency term
  • index 1-50: positive magnitudes
  • index 50-99: negative magnitudes

I'm only interested in the positive magnitudes.

Edit:

Here is the NumPy plot:

NumPy

And here is the Accelerate plot:

Accelerate

I hope someone can help :)


Solution

  • There are two issues:

    • As pointed out by E.Coms, the implementation using Accelerate framework's FFT includes a normalization step which takes the square root of the magnitude and multiplies by the scalar 2/N. The implementation using NumPy doesn't.

    • NumPy's FFT supports arbitrary length inputs, and the resulting frequency bins are as you are expecting (zero frequency at index 0, positive frequencies at index 1-50 and negative frequencies at index 51-99). On the other hand, the FFT in Accelerate framework needs to have a length that is a power of 2. Correspondingly, that code sample computes the FFT of your first 64 input values. This puts the zero frequency at index 0, positive frequencies at index 1-32 and negative frequencies at index 33-63. The remaining index (64-99) are just leftover untouched inputs.