Is there any simple C++ example on how to use Intel MKL FFT?
Asked Answered
O

2

14

I need to perform FFT and Inverse-FFT transformations. The input would be vector and matrices of double. Ideally, the output should be an array of std::complex but I can live with double _Complex.

I haven't found any simple example, all Intel examples are doing a lot of things at once without enough comments.

I just want a simple example in C++ taking a vector (or a matrix) of double as input and outputting the FFT-transformed result (ideally with std::complex).

Oren answered 22/4, 2015 at 18:15 Comment(3)
I'd assume you are referring to those examples, perhaps more specifically the "C Interface" ones. If that's the case, make sure to read the accompanying Fourier Transform Functions link at the top.Tanah
@Tanah Yes, I was referring to them. I personally don't find these bloated examples useful, but it is probably enough for others. I'd like to find simpler examples. It seems I'll probably have to do with them.Oren
I continued checking the official examples, but some of them don't even compile... It is not a really good way to start with...Oren
O
13

I ended up testing several things and I finally ended up with this three functions that do what I want and that I considered simple examples.

I tested it against some inputs and I had the good results. I haven't done extensive testing though.

//Note after each operation status should be 0 on success 

std::vector<std::complex<float>> fft_complex(std::vector<std::complex<float>>& in){
    std::vector<std::complex<float>> out(in.size());

    DFTI_DESCRIPTOR_HANDLE descriptor;
    MKL_LONG status;

    status = DftiCreateDescriptor(&descriptor, DFTI_SINGLE, DFTI_COMPLEX, 1, in.size()); //Specify size and precision
    status = DftiSetValue(descriptor, DFTI_PLACEMENT, DFTI_NOT_INPLACE); //Out of place FFT
    status = DftiCommitDescriptor(descriptor); //Finalize the descriptor
    status = DftiComputeForward(descriptor, in.data(), out.data()); //Compute the Forward FFT
    status = DftiFreeDescriptor(&descriptor); //Free the descriptor

    return out;
}

std::vector<std::complex<float>> fft_real(std::vector<float>& in_real){
    std::vector<std::complex<float>> in(in_real.size());

    std::copy(in_real.begin(), in_real.end(), in.begin());

    return fft_complex(in);
}

std::vector<float> ifft(std::vector<std::complex<float>>& in){
    std::vector<std::complex<float>> out(in.size());

    DFTI_DESCRIPTOR_HANDLE descriptor;
    MKL_LONG status;

    status = DftiCreateDescriptor(&descriptor, DFTI_SINGLE, DFTI_COMPLEX, 1, in.size()); //Specify size and precision
    status = DftiSetValue(descriptor, DFTI_PLACEMENT, DFTI_NOT_INPLACE); //Out of place FFT
    status = DftiSetValue(descriptor, DFTI_BACKWARD_SCALE, 1.0f / in.size()); //Scale down the output
    status = DftiCommitDescriptor(descriptor); //Finalize the descriptor
    status = DftiComputeBackward(descriptor, in.data(), out.data()); //Compute the Forward FFT
    status = DftiFreeDescriptor(&descriptor); //Free the descriptor

    std::vector<float> output(out.size());

    for(std::size_t i = 0; i < out.size(); ++i){
        output[i] = out[i].real();
    }

    return output;
}
Oren answered 24/4, 2015 at 20:49 Comment(0)
G
2

While Baptiste's answer works, usually one would like to use a more efficient version when applying fourier transformation to real values.

For the fourier transformation F of real values, the following holds:

F(k) = conj(F(-k))

and thus only about half of the values must be calculated. Using mkl's real fourier transformation leads to the following code:

//helper function for fft and ifft:
DFTI_DESCRIPTOR* create_descriptor(MKL_LONG length) {
    DFTI_DESCRIPTOR* handle = nullptr;
    // using DFTI_DOUBLE for double precision
    // using DFTI_REAL for using the real version
    bool valid = (DFTI_NO_ERROR == DftiCreateDescriptor(&handle, DFTI_DOUBLE, DFTI_REAL, 1, length)) &&
        // the result should not be inplace:
        (DFTI_NO_ERROR == DftiSetValue(handle, DFTI_PLACEMENT, DFTI_NOT_INPLACE)) &&
        // make clear that the result should be a vector of complex:
        (DFTI_NO_ERROR == DftiSetValue(handle, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX));
        // chosen normalization is fft(constant)[0] = constant:
        (DFTI_NO_ERROR == DftiSetValue(handle, DFTI_FORWARD_SCALE, 1. / length)) &&
        (DFTI_NO_ERROR == DftiCommitDescriptor(handle));
    if (!valid) {
        DftiFreeDescriptor(&handle);
        return nullptr; //nullptr means error
    }
    return handle;
}

std::vector<std::complex<double>> real_fft(std::vector<double>& in) {
    size_t out_size = in.size() / 2 + 1; //so many complex numbers needed
    std::vector<std::complex<double>> result(out_size);
    DFTI_DESCRIPTOR* handle = create_descriptor(static_cast<MKL_LONG>(in.size()));
    bool valid = handle &&
        (DFTI_NO_ERROR == DftiComputeForward(handle, in.data(), result.data()));
    if (handle) {
        valid &= (DFTI_NO_ERROR == DftiFreeDescriptor(&handle));
    }
    if (!valid) {
        result.clear(); //empty vector -> error
    }
    return result;
}

For the inverse version we need to know the size of the original vector - this information cannot be restored from the fourier-transform. While we know, that if the original real vector had even number of elements, last element in the fourier transform is real, we cannot follow from last element of the fourier transform being real, that the original real vector had an even number of elements! That is the reason for a slightly strange signature of the inverse function:

std::vector<double> real_fft(std::vector<std::complex<double>> & in, size_t original_size) {
    size_t expected_size = original_size / 2 + 1;
    if (expected_size != in.size()) {
        return {};// empty vector -> error
    }
    std::vector<double> result(original_size);
    DFTI_DESCRIPTOR* handle = create_descriptor(static_cast<MKL_LONG>(original_size));
    bool valid = handle &&
        (DFTI_NO_ERROR == DftiComputeBackward(handle, in.data(), result.data()));
    if (handle) {
        valid &= (DFTI_NO_ERROR == DftiFreeDescriptor(&handle));
    }
    if (!valid) {
        result.clear(); //empty vector -> error
    }
    return result;
}

Note: the same descriptor is used for forward and backward transformations.

Gotha answered 8/7, 2021 at 13:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.