Fast Fourier Transform
This time I want to talk about Fast Fourier Transform functions and how to use these in C# properly.
Intel MKL team
work mainly with C++ and Fortran. They didn’t dedicate a lot of effort toward
C# so sometimes if you work in C# and you want to use MKL you face some
difficulties.
As I said
in the previous article about MKL, the first difficulty is how to declare the
functions in C# code. For this I suggest a document from Intel: 33774-Using_Intel_MKL_IPP_NET_Framework.pdf.
You can find it on line. It is not only a problem of declaration, but also a
question of how to interpret the error messages, sometimes you don’t know
what’s going on.
For example
if you wrong something, you receive messages like these:
- "Inconsistent configuration parameter"
- "Functionality is not implemented".
Where is a
tutorial that explain what I have wronged? You have to contact the Intel
Support and be patient.
With a
little help you can understand that the first error is because you are
assigning a wrong value to a variable (DftiSetValue). The second one is because
you had configured a “Descriptor” with the wrong parameters (DftiCreateDescriptor).
Then after
completed a declarative part you can test something, for example I used this
web site (http://www.sccon.ca/sccon/fft/fft3.htm) with the FFT input and output
certified, as reference.
For some
example of FFT usage this is the page: https://software.intel.com/en-us/node/471390.
The problem
is that, those example are written in Fortran or C++ and contains some little
imprecisions that can confuse you.
Let
consider the paragraph: “One-dimensional Out-of-place FFT”. Where they perform
a real to complex conjugate-even transform.
They use a
real vector for the input and a real vector for the output. But the output is a
complex vector! Also the vector size was wrong. These things and other little
imprecisions make me lost a lot of time. But not desperate because I will
provide you what you need to use FFT in C#.
There are
some possible versions of DftiComputeForward() function that compute the FF
Transform so at a beginning I adopted this one:
status =
DftiComputeForward ( desc_handle, xre_in, xim_in , yre_out, yim_out )
It allows 2
real vectors in input and two real vectors for the output. These vectors represents
real and imaginary part.
By the way,
although I didn’t receive any error from the method execution the result was always
zero! Again problems!
But as I
always say: “it ain't over til it's over” I have tried another version of the
function, this time with a input a complex vector, and output a complex vector.
And like a magic this time worked.
status =
DftiComputeForward( desc_handle, x_in, y_out )
So for your
joy here the code:
using System; using System.Numerics; using System.Runtime.InteropServices; using System.Security; namespace mkl { [SuppressUnmanagedCodeSecurity] public unsafe class MKLWrapper { //================================================== #region FFT CONSTANTS enum DFTI_CONFIG_PARAM { /* Domain for forward transform. No default value */ DFTI_FORWARD_DOMAIN = 0, /* Dimensionality, or rank. No default value */ DFTI_DIMENSION = 1, /* Length(s) of transform. No default value */ DFTI_LENGTHS = 2, /* Floating point precision. No default value */ DFTI_PRECISION = 3, /* Scale factor for forward transform [1.0] */ DFTI_FORWARD_SCALE = 4, /* Scale factor for backward transform [1.0] */ DFTI_BACKWARD_SCALE = 5, /* Exponent sign for forward transform [DFTI_NEGATIVE] */ /* DFTI_FORWARD_SIGN = 6, ## NOT IMPLEMENTED */ /* Number of data sets to be transformed [1] */ DFTI_NUMBER_OF_TRANSFORMS = 7, /* Storage of finite complex-valued sequences in complex domain [DFTI_COMPLEX_COMPLEX] */ DFTI_COMPLEX_STORAGE = 8, /* Storage of finite real-valued sequences in real domain [DFTI_REAL_REAL] */ DFTI_REAL_STORAGE = 9, /* Storage of finite complex-valued sequences in conjugate-even domain [DFTI_COMPLEX_REAL] */ DFTI_CONJUGATE_EVEN_STORAGE = 10, /* Placement of result [DFTI_INPLACE] */ DFTI_PLACEMENT = 11, /* Generalized strides for input data layout [tigth, row-major for C] */ DFTI_INPUT_STRIDES = 12, /* Generalized strides for output data layout [tight, row-major for C] */ DFTI_OUTPUT_STRIDES = 13, /* Distance between first input elements for multiple transforms [0] */ DFTI_INPUT_DISTANCE = 14, /* Distance between first output elements for multiple transforms [0] */ DFTI_OUTPUT_DISTANCE = 15, /* Effort spent in initialization [DFTI_MEDIUM] */ /* DFTI_INITIALIZATION_EFFORT = 16, ## NOT IMPLEMENTED */ /* Use of workspace during computation [DFTI_ALLOW] */ DFTI_WORKSPACE = 17, /* Ordering of the result [DFTI_ORDERED] */ DFTI_ORDERING = 18, /* Possible transposition of result [DFTI_NONE] */ DFTI_TRANSPOSE = 19, /* User-settable descriptor name [""] */ DFTI_DESCRIPTOR_NAME = 20, /* DEPRECATED */ /* Packing format for DFTI_COMPLEX_REAL storage of finite conjugate-even sequences [DFTI_CCS_FORMAT] */ DFTI_PACKED_FORMAT = 21, /* Commit status of the descriptor - R/O parameter */ DFTI_COMMIT_STATUS = 22, /* Version string for this DFTI implementation - R/O parameter */ DFTI_VERSION = 23, /* Ordering of the forward transform - R/O parameter */ /* DFTI_FORWARD_ORDERING = 24, ## NOT IMPLEMENTED */ /* Ordering of the backward transform - R/O parameter */ /* DFTI_BACKWARD_ORDERING = 25, ## NOT IMPLEMENTED */ /* Number of user threads that share the descriptor [1] */ DFTI_NUMBER_OF_USER_THREADS = 26, /* Limit the number of threads used by this descriptor [0 = don't care] */ DFTI_THREAD_LIMIT = 27, /* Possible input data destruction [DFTI_AVOID = prevent input data]*/ DFTI_DESTROY_INPUT = 28 }; enum DFTI_CONFIG_VALUE { /* DFTI_COMMIT_STATUS */ DFTI_COMMITTED = 30, DFTI_UNCOMMITTED = 31, /* DFTI_FORWARD_DOMAIN */ DFTI_COMPLEX = 32, DFTI_REAL = 33, /* DFTI_CONJUGATE_EVEN = 34, ## NOT IMPLEMENTED */ /* DFTI_PRECISION */ DFTI_SINGLE = 35, DFTI_DOUBLE = 36, /* DFTI_FORWARD_SIGN */ /* DFTI_NEGATIVE = 37, ## NOT IMPLEMENTED */ /* DFTI_POSITIVE = 38, ## NOT IMPLEMENTED */ /* DFTI_COMPLEX_STORAGE and DFTI_CONJUGATE_EVEN_STORAGE */ DFTI_COMPLEX_COMPLEX = 39, DFTI_COMPLEX_REAL = 40, /* DFTI_REAL_STORAGE */ DFTI_REAL_COMPLEX = 41, DFTI_REAL_REAL = 42, /* DFTI_PLACEMENT */ DFTI_INPLACE = 43, /* Result overwrites input */ DFTI_NOT_INPLACE = 44, /* Have another place for result */ /* DFTI_INITIALIZATION_EFFORT */ /* DFTI_LOW = 45, ## NOT IMPLEMENTED */ /* DFTI_MEDIUM = 46, ## NOT IMPLEMENTED */ /* DFTI_HIGH = 47, ## NOT IMPLEMENTED */ /* DFTI_ORDERING */ DFTI_ORDERED = 48, DFTI_BACKWARD_SCRAMBLED = 49, /* DFTI_FORWARD_SCRAMBLED = 50, ## NOT IMPLEMENTED */ /* Allow/avoid certain usages */ DFTI_ALLOW = 51, /* Allow transposition or workspace */ DFTI_AVOID = 52, DFTI_NONE = 53, /* DFTI_PACKED_FORMAT (for storing congugate-even finite sequence in real array) */ DFTI_CCS_FORMAT = 54, /* Complex conjugate-symmetric */ DFTI_PACK_FORMAT = 55, /* Pack format for real DFT */ DFTI_PERM_FORMAT = 56, /* Perm format for real DFT */ DFTI_CCE_FORMAT = 57 /* Complex conjugate-even */ }; #endregion private MKLWrapper() { } //================================================== #region FFT (DECLARATIONS) //DFTI_EXTERN char* DftiErrorMessage(MKL_LONG); [DllImport("mkl_rt.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr DftiErrorMessage( long error_code ); //DFTI_EXTERN MKL_LONG DftiCreateDescriptor(DFTI_DESCRIPTOR_HANDLE*, // enum DFTI_CONFIG_VALUE, /* precision */ // enum DFTI_CONFIG_VALUE, /* domain */ // MKL_LONG, ...); [DllImport("mkl_rt.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int DftiCreateDescriptor( ref IntPtr handle_descriptor, int precision, int domain, int dime, int size ); //DFTI_EXTERN MKL_LONG DftiCommitDescriptor(DFTI_DESCRIPTOR_HANDLE); [DllImport("mkl_rt.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int DftiCommitDescriptor( IntPtr handle_descriptor ); //DFTI_EXTERN MKL_LONG DftiSetValue(DFTI_DESCRIPTOR_HANDLE, enum DFTI_CONFIG_PARAM, ...); [DllImport("mkl_rt.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int DftiSetValue(IntPtr handle_descriptor, int config_param, __arglist); //DFTI_EXTERN MKL_LONG DftiComputeBackward(DFTI_DESCRIPTOR_HANDLE, void*, ...); [DllImport("mkl_rt.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int DftiComputeBackward( IntPtr handle_descriptor, [In] Complex[] x_in, [Out] Complex[] x_out ); [DllImport("mkl_rt.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int DftiComputeForward( IntPtr handle_descriptor, [In] Complex[] x_in, [Out] Complex[] x_out ); //DFTI_EXTERN MKL_LONG DftiFreeDescriptor(DFTI_DESCRIPTOR_HANDLE*); [DllImport("mkl_rt.dll", ExactSpelling = true, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] internal static extern int DftiFreeDescriptor( ref IntPtr handle_descriptor ); #endregion //================================================== #region FFT (METHODS) public static string MKL_FFT_Error(int error, string sText = "") { if (error == 0) return ""; IntPtr s = DftiErrorMessage(error); string sMsg = Marshal.PtrToStringAnsi(s); throw new Exception(sText + sMsg); } public static Complex[] MKL_FFT(Complex[] in_matrix, double fScalingFactor = 1) { int status; IntPtr des_handle = IntPtr.Zero; Complex[] out_matrix = new Complex[in_matrix.Length]; try { status = DftiCreateDescriptor(ref des_handle, (int)DFTI_CONFIG_VALUE.DFTI_DOUBLE, (int)DFTI_CONFIG_VALUE.DFTI_COMPLEX, 1, in_matrix.Length); MKL_FFT_Error(status, "DftiCreateDescriptor() "); status = DftiSetValue(des_handle, (int)DFTI_CONFIG_PARAM.DFTI_FORWARD_SCALE, __arglist(fScalingFactor)); MKL_FFT_Error(status, "DftiSetValue() "); status = DftiSetValue(des_handle, (int)DFTI_CONFIG_PARAM.DFTI_PLACEMENT, __arglist((int)DFTI_CONFIG_VALUE.DFTI_NOT_INPLACE)); MKL_FFT_Error(status, "DftiSetValue() "); status = DftiCommitDescriptor(des_handle); //Finalize the descriptor MKL_FFT_Error(status, "DftiCommitDescriptor() "); status = DftiComputeForward(des_handle, in_matrix, out_matrix); //Compute the Backward FFT MKL_FFT_Error(status, "DftiComputeForward() "); status = DftiFreeDescriptor(ref des_handle); //Free the descriptor MKL_FFT_Error(status, "DftiFreeDescriptor() "); return out_matrix; } catch (Exception ex) { if (des_handle != IntPtr.Zero) DftiFreeDescriptor(ref des_handle); //Free the descriptor throw ex; } } public static Complex[] MKL_FFT_Inv(Complex[] in_matrix, double fScalingFactor = 1) { int status; IntPtr des_handle = IntPtr.Zero; Complex[] out_matrix = new Complex[in_matrix.Length]; try { status = DftiCreateDescriptor(ref des_handle, (int)DFTI_CONFIG_VALUE.DFTI_DOUBLE, (int)DFTI_CONFIG_VALUE.DFTI_COMPLEX, 1, in_matrix.Length); MKL_FFT_Error(status, "DftiCreateDescriptor() "); status = DftiSetValue(des_handle, (int)DFTI_CONFIG_PARAM.DFTI_BACKWARD_SCALE, __arglist(fScalingFactor)); MKL_FFT_Error(status, "DftiSetValue() "); status = DftiSetValue(des_handle, (int)DFTI_CONFIG_PARAM.DFTI_PLACEMENT, __arglist((int)DFTI_CONFIG_VALUE.DFTI_NOT_INPLACE)); MKL_FFT_Error(status, "DftiSetValue() "); status = DftiCommitDescriptor(des_handle); //Finalize the descriptor MKL_FFT_Error(status, "DftiCommitDescriptor() "); status = DftiComputeBackward(des_handle, in_matrix, out_matrix); //Compute the Backward FFT MKL_FFT_Error(status, "DftiComputeBackward() "); status = DftiFreeDescriptor(ref des_handle); //Free the descriptor return out_matrix; } catch (Exception ex) { if (des_handle != IntPtr.Zero) DftiFreeDescriptor(ref des_handle); //Free the descriptor throw ex; } } #endregion } }Here the example:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using mkl; using System.Numerics; namespace FFTTest { class Program { #region TEST INPUT MATRIX //http://www.sccon.ca/sccon/fft/fft3.htm //INPUT //REAL[0] = 1.000, IMAG[0] = 0.000 //REAL[1] = 0.000, IMAG[1] = 0.000 //REAL[2] = 0.000, IMAG[2] = 0.000 //REAL[3] = 0.000, IMAG[3] = 0.000 //REAL[4] = 0.000, IMAG[4] = 0.000 //REAL[5] = 0.000, IMAG[5] = 0.000 //REAL[6] = 0.000, IMAG[6] = 0.000 //REAL[7] = 0.000, IMAG[7] = 0.000 //RESULTS //REAL[0] = 0.125, IMAG[0] = 0.000 //REAL[1] = 0.125, IMAG[1] = 0.000 //REAL[2] = 0.125, IMAG[2] = 0.000 //REAL[3] = 0.125, IMAG[3] = 0.000 //REAL[4] = 0.125, IMAG[4] = 0.000 //REAL[5] = 0.125, IMAG[5] = 0.000 //REAL[6] = 0.125, IMAG[6] = 0.000 //REAL[7] = 0.125, IMAG[7] = 0.000 #endregion static void Main(string[] args) { Complex[] A = new Complex[8]; Complex[] B = null; Complex[] C = null; A[0] = new Complex(0, 0); A[1] = new Complex(1, 0); A[2] = new Complex(0, 0); A[3] = new Complex(0, 0); A[4] = new Complex(0, 0); A[5] = new Complex(0, 0); A[6] = new Complex(0, 0); A[7] = new Complex(0, 0); try { DebugMatrix(A, "A:"); B = MKLWrapper.MKL_FFT(A, 1.0f/ A.Length); DebugMatrix(B, "FFT() B:"); C = MKLWrapper.MKL_FFT_Inv(B); //FFT Backward DebugMatrix(C, "iFFT() C:"); //C = MKLWrapper.MKL_FFT(B); //DebugMatrix(lstResults, C, "A:"); //MessageBox.Show("Operation completed succesfully!"); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } Console.ReadLine(); } public static void DebugMatrix(Complex[] a, string sCaption, int iRowLimit = 10) { if (a == null) return; string sLine = ""; Console.WriteLine(""); Console.WriteLine(sCaption); int iDime1 = a.GetUpperBound(0); int iStart1 = a.GetLowerBound(0); Console.WriteLine("Size: " + a.GetLength(0).ToString()); for (int i = iStart1; i <= iDime1; i++) { sLine = i.ToString() + "> "; sLine += a[i].ToString("0.###E+000") + "\t"; //sLine += a[i, j].ToString("") + "\t"; Console.WriteLine(sLine); if (i > iRowLimit) break; } Console.WriteLine(""); Console.WriteLine(""); } } }
Part 1 | Part 2 | Part 3 | Part 4
No comments :
Post a Comment