HOPS
HOPS class reference
MHO_FastFourierTransformUtilities.hh
Go to the documentation of this file.
1 #ifndef MHO_FastFourierTransformUtilities_HH__
2 #define MHO_FastFourierTransformUtilities_HH__
3 
4 #include <cmath>
5 #include <complex>
6 #include <cstddef>
7 
9 #include "MHO_Message.hh"
10 
11 namespace hops
12 {
13 
22 template< typename XFloatType > class MHO_FastFourierTransformUtilities
23 {
24  public:
27 
35  static void Conjugate(unsigned int N, std::complex< XFloatType >* array)
36  {
37  for(unsigned int i = 0; i < N; i++)
38  {
39  array[i] = std::conj(array[i]);
40  }
41  }
42 
51  static void Conjugate(unsigned int N, std::complex< XFloatType >* array, unsigned int stride)
52  {
53  for(unsigned int i = 0; i < N; i++)
54  {
55  array[i * stride] = std::conj(array[i * stride]);
56  }
57  }
58 
67  static void ComputeTwiddleFactors(unsigned int N, std::complex< XFloatType >* twiddle);
68 
76  static void ComputeConjugateTwiddleFactors(unsigned int N, std::complex< XFloatType >* conj_twiddle)
77  {
78 
79  //to compute the twiddle factors
80  ComputeTwiddleFactors(N, conj_twiddle);
81  Conjugate(N, conj_twiddle);
82  }
83 
85 
94  static void ComputeTwiddleFactorBasis(unsigned int log2N, std::complex< XFloatType >* twiddle);
95 
103  static void ComputeConjugateTwiddleFactorBasis(unsigned int log2N, std::complex< XFloatType >* conj_twiddle)
104  {
105  ComputeTwiddleFactors(log2N, conj_twiddle);
106  Conjugate(log2N, conj_twiddle);
107  }
108 
119  static void FFTRadixTwo_DIT(unsigned int N, XFloatType* data, XFloatType* twiddle, unsigned int stride = 1)
120  {
121  //decimation in time, N is assumed to be a power of 2
122  unsigned int logN = MHO_BitReversalPermutation::LogBaseTwo(N);
123  unsigned int butterfly_width;
124  unsigned int n_butterfly_groups;
125  unsigned int group_start;
126  unsigned int butterfly_index;
127  unsigned int access_stride = 2 * stride;
128  XFloatType* H0;
129  XFloatType* H1;
130  XFloatType* W;
131 
132  for(unsigned int stage = 0; stage < logN; stage++)
133  {
134  //compute the width of each butterfly
135  butterfly_width = MHO_BitReversalPermutation::TwoToThePowerOf(stage);
136  //compute the number of butterfly groups
137  n_butterfly_groups = N / (2 * butterfly_width);
138 
139  for(unsigned int n = 0; n < n_butterfly_groups; n++)
140  {
141  //compute the starting index of this butterfly group
142  group_start = 2 * n * butterfly_width;
143  for(unsigned int k = 0; k < butterfly_width; k++)
144  {
145  butterfly_index = group_start + k; //index
146  H0 = data + (access_stride * butterfly_index);
147  H1 = H0 + access_stride * butterfly_width;
148  W = twiddle + 2 * n_butterfly_groups * k;
150  }
151  }
152  }
153  }
154 
163  static inline void ButterflyRadixTwo_CooleyTukey(XFloatType* H0, XFloatType* H1, XFloatType* W)
164  {
165  //H0 is the element from the even indexed array
166  //H1 is the element from the odd index array
167  //W is the twiddle factor
168 
169  //fetch the data
170  XFloatType H00, H01, H10, H11, W0, W1, alpha_i, alpha_r;
171  H00 = H0[0];
172  H01 = H0[1];
173  H10 = H1[0];
174  H11 = H1[1];
175  W0 = W[0];
176  W1 = W[1];
177 
178  //apply the butterfly
179  alpha_r = W0 * H10 - W1 * H11;
180  alpha_i = W1 * H10 + W0 * H11;
181  H10 = H00 - alpha_r;
182  H11 = H01 - alpha_i;
183  H00 = H00 + alpha_r;
184  H01 = H01 + alpha_i;
185 
186  //write out
187  H0[0] = H00;
188  H0[1] = H01;
189  H1[0] = H10;
190  H1[1] = H11;
191  }
192 
193  //RADIX-2 DIF
204  static void FFTRadixTwo_DIF(unsigned int N, XFloatType* data, XFloatType* twiddle, unsigned int stride = 1)
205  {
206  //decimation in frequency, N is assumed to be a power of 2
207  unsigned int logN = MHO_BitReversalPermutation::LogBaseTwo(N);
208  unsigned int butterfly_width;
209  unsigned int n_butterfly_groups;
210  unsigned int group_start;
211  unsigned int butterfly_index;
212  unsigned int access_stride = 2 * stride;
213  XFloatType* H0;
214  XFloatType* H1;
215  XFloatType* W;
216 
217  for(unsigned int stage = 0; stage < logN; stage++)
218  {
219  //compute the number of butterfly groups
220  n_butterfly_groups = MHO_BitReversalPermutation::TwoToThePowerOf(stage);
221 
222  //compute the width of each butterfly
223  butterfly_width = N / (2 * n_butterfly_groups);
224  for(unsigned int n = 0; n < n_butterfly_groups; n++)
225  {
226  //compute the starting index of this butterfly group
227  group_start = 2 * n * butterfly_width;
228  for(unsigned int k = 0; k < butterfly_width; k++)
229  {
230  butterfly_index = group_start + k; //index
231 
232  H0 = data + (access_stride * butterfly_index);
233  H1 = H0 + access_stride * butterfly_width;
234  W = twiddle + 2 * n_butterfly_groups * k;
236  }
237  }
238  }
239  }
240 
249  static inline void ButterflyRadixTwo_GentlemanSande(XFloatType* H0, XFloatType* H1, XFloatType* W)
250  {
251  //H0 is the element from the even indexed array
252  //H1 is the element from the odd index array
253  //W is the twiddle factor
254 
255  //fetch the data
256  XFloatType H00, H01, H10, H11, W0, W1, alpha_i, alpha_r, h1_r, h1_i;
257  H00 = H0[0];
258  H01 = H0[1];
259  H10 = H1[0];
260  H11 = H1[1];
261  W0 = W[0];
262  W1 = W[1];
263 
264  //cache H1
265  h1_r = H10;
266  h1_i = H11;
267 
268  //compute new h1
269  H10 = H00 - h1_r;
270  H11 = H01 - h1_i;
271 
272  //compute new H0
273  H00 += h1_r;
274  H01 += h1_i;
275 
276  //multiply new h1 by twiddle factor
277  alpha_r = W0 * H10 - W1 * H11;
278  alpha_i = W1 * H10 + W0 * H11;
279 
280  H10 = alpha_r;
281  H11 = alpha_i;
282 
283  //write out
284  H0[0] = H00;
285  H0[1] = H01;
286  H1[0] = H10;
287  H1[1] = H11;
288  }
289 
299  static void FFTRadixTwo_DIT(unsigned int N, std::complex< XFloatType >* data, std::complex< XFloatType >* twiddle,
300  unsigned int stride = 1)
301  {
302  FFTRadixTwo_DIT(N, (XFloatType*)&(data[0]), (XFloatType*)&(twiddle[0]), stride);
303  }
304 
314  static void FFTRadixTwo_DIF(unsigned int N, std::complex< XFloatType >* data, std::complex< XFloatType >* twiddle,
315  unsigned int stride = 1)
316  {
317  FFTRadixTwo_DIF(N, (XFloatType*)&(data[0]), (XFloatType*)&(twiddle[0]), stride);
318  }
319 
321  //Bluestein/Chirp-Z Algorithm for arbitrary N
322  //"Inside the FFT Black Box", E. Chu and A. George, Ch. 13, CRC Press, 2000
323 
331  static unsigned int ComputeBluesteinArraySize(unsigned int N) //returns smallest M = 2^p >= (2N - 2);
332  {
333  unsigned int M = 2 * (N - 1);
335  {
336  return M;
337  }
338  else
339  {
341  }
342  }
343 
351  static void ComputeBluesteinScaleFactors(unsigned int N, std::complex< XFloatType >* scale);
352 
363  static void ComputeBluesteinCirculantVector(unsigned int N, unsigned int M, std::complex< XFloatType >* twiddle,
364  std::complex< XFloatType >* scale, std::complex< XFloatType >* circulant)
365  {
366  //STEP B
367  unsigned int mid = M - N + 1;
368  for(unsigned int i = 0; i < N; i++)
369  {
370  //we take the conjugate here because the way we have computed the scale factors
371  circulant[i] = std::conj(scale[i]);
372  }
373 
374  for(unsigned int i = mid; i < M; i++)
375  {
376  //we take the conjugate here because the way we have computed the scale factors
377  circulant[i] = std::conj(scale[M - i]);
378  }
379 
380  for(unsigned int i = N; i < mid; i++)
381  {
382  circulant[i] = 0.0;
383  }
384 
385  //STEP C
386  //now we perform an in-place DFT on the circulant vector
387 
388  //expects normal order input, produces bit-address permutated output
390  }
391 
406  static void FFTBluestein(unsigned int N, unsigned int M, std::complex< XFloatType >* data,
407  std::complex< XFloatType >* twiddle, std::complex< XFloatType >* conj_twiddle,
408  std::complex< XFloatType >* scale, std::complex< XFloatType >* circulant,
409  std::complex< XFloatType >* workspace, unsigned int stride = 1)
410  {
411 
412  //STEP D
413  //copy the data into the workspace and scale by the scale factor
414  for(unsigned int i = 0; i < N; i++)
415  {
416  workspace[i] = data[i * stride] * scale[i];
417  }
418 
419  //fill out the rest of the extended vector with zeros
420  for(unsigned int i = N; i < M; i++)
421  {
422  workspace[i] = std::complex< XFloatType >(0.0, 0.0);
423  }
424 
425  //STEP E
426  //perform the DFT on the workspace
427  //do radix-2 FFT w/ decimation in frequency (normal order input, bit-address permutated output)
428  FFTRadixTwo_DIF(M, (XFloatType*)(&(workspace[0])), (XFloatType*)(&(twiddle[0])));
429 
430  //STEP F
431  //now we scale the workspace with the circulant vector
432  for(unsigned int i = 0; i < M; i++)
433  {
434  workspace[i] *= circulant[i];
435  }
436 
437  //STEP G
438  //now perform the inverse DFT on the workspace
439  //do radix-2 FFT w/ decimation in time (bit-address permutated input, normal order output)
440  FFTRadixTwo_DIT(M, (XFloatType*)(&(workspace[0])), (XFloatType*)(&(conj_twiddle[0])));
441 
442  //STEP H
443  //renormalize to complete IDFT, extract and scale at the same time
444  XFloatType norm = 1.0 / ((XFloatType)M);
445  for(unsigned int i = 0; i < N; i++)
446  {
447  data[i * stride] = norm * workspace[i] * scale[i];
448  }
449  }
450 
451 }; //end of class
452 
454 //template specializations for float, double, and long double
455 
463 template<>
464 inline void MHO_FastFourierTransformUtilities< float >::ComputeTwiddleFactors(unsigned int N, std::complex< float >* twiddle)
465 {
466  //using std::cos and std::sin is more accurate than the recursive method
467  //to compute the twiddle factors
468  float di, dN;
469  dN = N;
470  for(unsigned int i = 0; i < N; i++)
471  {
472  di = i;
473  twiddle[i] = std::complex< float >(std::cos((2.0 * M_PI * di) / dN), std::sin((2.0 * M_PI * di) / dN));
474  }
475 }
476 
484 template<>
485 inline void MHO_FastFourierTransformUtilities< double >::ComputeTwiddleFactors(unsigned int N, std::complex< double >* twiddle)
486 {
487  //using std::cos and std::sin is more accurate than the recursive method
488  //to compute the twiddle factors
489  double di, dN;
490  dN = N;
491  for(unsigned int i = 0; i < N; i++)
492  {
493  di = i;
494  twiddle[i] = std::complex< double >(std::cos((2.0 * M_PI * di) / dN), std::sin((2.0 * M_PI * di) / dN));
495  }
496 }
497 
505 template<>
507  std::complex< long double >* twiddle)
508 {
509  //using std::cos and std::sin is more accurate than the recursive method
510  //to compute the twiddle factors
511  long double di, dN;
512  dN = N;
513  for(unsigned int i = 0; i < N; i++)
514  {
515  di = i;
516  twiddle[i] = std::complex< long double >(cosl((2.0 * M_PIl * di) / dN), sinl((2.0 * M_PIl * di) / dN));
517  }
518 }
519 
521 
522 //template specializations for float, double, and long double
523 
531 template<>
533  std::complex< float >* twiddle)
534 {
535  float dN, di;
537  di = 1;
538  for(std::size_t i = 0; i < log2N; i++)
539  {
540  twiddle[i] = std::complex< float >(std::cos((2.0 * M_PI * di) / dN), std::sin((2.0 * M_PI * di) / dN));
541  di *= 2;
542  }
543 }
544 
552 template<>
554  std::complex< double >* twiddle)
555 {
556  double dN, di;
558  di = 1;
559  for(std::size_t i = 0; i < log2N; i++)
560  {
561  twiddle[i] = std::complex< double >(std::cos((2.0 * M_PI * di) / dN), std::sin((2.0 * M_PI * di) / dN));
562  di *= 2;
563  }
564 }
565 
573 template<>
575  std::complex< long double >* twiddle)
576 {
577  long double dN, di;
579  di = 1;
580  for(std::size_t i = 0; i < log2N; i++)
581  {
582  twiddle[i] = std::complex< long double >(std::cos((2.0 * M_PI * di) / dN), std::sin((2.0 * M_PI * di) / dN));
583  di *= 2;
584  }
585 }
586 
588 
603 template<>
605  std::complex< float >* scale)
606 {
607  //STEP A
608  float theta = M_PI / ((float)N);
609  unsigned int i2;
610  float x;
611 
612  for(unsigned int i = 0; i < N; i++)
613  {
614  i2 = i * i % (2 * N); //taking the modulus here results in a more accurate DFT/IDFT
615  x = theta * i2;
616  scale[i] = std::complex< float >(std::cos(x), std::sin(x));
617  }
618 }
619 
634 template<>
636  std::complex< double >* scale)
637 {
638  //STEP A
639  double theta = M_PI / ((double)N);
640  unsigned int i2;
641  double x;
642 
643  for(unsigned int i = 0; i < N; i++)
644  {
645  i2 = i * i % (2 * N); //taking the modulus here results in a more accurate DFT/IDFT
646  x = theta * i2;
647  scale[i] = std::complex< double >(cos(x), sin(x));
648  }
649 }
650 
665 template<>
667  std::complex< long double >* scale)
668 {
669  //STEP A
670  long double theta = M_PIl / ((long double)N);
671  unsigned int i2;
672  long double x;
673 
674  for(unsigned int i = 0; i < N; i++)
675  {
676  i2 = i * i % (2 * N); //taking the modulus here results in a more accurate DFT/IDFT
677  x = theta * i2;
678  scale[i] = std::complex< long double >(cosl(x), sinl(x));
679  }
680 }
681 
682 } // namespace hops
683 
684 #endif
static unsigned int NextLowestPowerOfTwo(unsigned int N)
Calculates the next lowest power of two for a given unsigned integer.
Definition: MHO_BitReversalPermutation.cc:35
static unsigned int TwoToThePowerOf(unsigned int N)
Calculates 2 raised to the power of N using bit shifting.
Definition: MHO_BitReversalPermutation.cc:29
static bool IsPowerOfTwo(unsigned int N)
Checks if an unsigned integer is a power of two.
Definition: MHO_BitReversalPermutation.cc:10
static unsigned int LogBaseTwo(unsigned int N)
Calculates the logarithm base two of an unsigned integer N using bitwise operations.
Definition: MHO_BitReversalPermutation.cc:17
basic utility functions for native FFTs
Definition: MHO_FastFourierTransformUtilities.hh:23
static void FFTRadixTwo_DIT(unsigned int N, std::complex< XFloatType > *data, std::complex< XFloatType > *twiddle, unsigned int stride=1)
Radix-2 DIT FFT wrapper for a std::complex array.
Definition: MHO_FastFourierTransformUtilities.hh:299
static void ButterflyRadixTwo_CooleyTukey(XFloatType *H0, XFloatType *H1, XFloatType *W)
Function ButterflyRadixTwo_CooleyTukey See page 23 of "Inside the FFT Black Box", E....
Definition: MHO_FastFourierTransformUtilities.hh:163
static void FFTRadixTwo_DIT(unsigned int N, XFloatType *data, XFloatType *twiddle, unsigned int stride=1)
Computes a Radix-2 decimation in time (DIT) FFT. input: data array in bit-address permutated order ou...
Definition: MHO_FastFourierTransformUtilities.hh:119
static void ComputeBluesteinScaleFactors(unsigned int N, std::complex< XFloatType > *scale)
Function ComputeBluesteinScaleFactors.
static void ButterflyRadixTwo_GentlemanSande(XFloatType *H0, XFloatType *H1, XFloatType *W)
Function ButterflyRadixTwo_GentlemanSande See page 25 of "Inside the FFT Black Box",...
Definition: MHO_FastFourierTransformUtilities.hh:249
static unsigned int ComputeBluesteinArraySize(unsigned int N)
Function ComputeBluesteinArraySize Computes the array size needed to perform a Bluestein/Chirp-Z FFT ...
Definition: MHO_FastFourierTransformUtilities.hh:331
static void ComputeConjugateTwiddleFactorBasis(unsigned int log2N, std::complex< XFloatType > *conj_twiddle)
Computes conjugate twiddle factor basis for given log2N and stores result in conj_twiddle.
Definition: MHO_FastFourierTransformUtilities.hh:103
static void FFTRadixTwo_DIF(unsigned int N, std::complex< XFloatType > *data, std::complex< XFloatType > *twiddle, unsigned int stride=1)
Radix-2 DIF FFT wrapper for a std::complex array.
Definition: MHO_FastFourierTransformUtilities.hh:314
static void ComputeTwiddleFactorBasis(unsigned int log2N, std::complex< XFloatType > *twiddle)
Computes twiddle factors for Fast Fourier Transform up to log2N. computes only the twiddle factors we...
MHO_FastFourierTransformUtilities()
Definition: MHO_FastFourierTransformUtilities.hh:25
static void Conjugate(unsigned int N, std::complex< XFloatType > *array, unsigned int stride)
Conjugates each element in a complex (strided) array.
Definition: MHO_FastFourierTransformUtilities.hh:51
static void FFTBluestein(unsigned int N, unsigned int M, std::complex< XFloatType > *data, std::complex< XFloatType > *twiddle, std::complex< XFloatType > *conj_twiddle, std::complex< XFloatType > *scale, std::complex< XFloatType > *circulant, std::complex< XFloatType > *workspace, unsigned int stride=1)
Function Bluestein algorithm for arbitrary length, N is length of the data, (supports strided data ac...
Definition: MHO_FastFourierTransformUtilities.hh:406
virtual ~MHO_FastFourierTransformUtilities()
Definition: MHO_FastFourierTransformUtilities.hh:26
static void Conjugate(unsigned int N, std::complex< XFloatType > *array)
Conjugates each element in a complex array.
Definition: MHO_FastFourierTransformUtilities.hh:35
static void FFTRadixTwo_DIF(unsigned int N, XFloatType *data, XFloatType *twiddle, unsigned int stride=1)
Performs Radix-2 Decimation In Frequency (DIF) FFT using conjugate array and twiddle factors....
Definition: MHO_FastFourierTransformUtilities.hh:204
static void ComputeConjugateTwiddleFactors(unsigned int N, std::complex< XFloatType > *conj_twiddle)
Computes the conjugate twiddle factors for given size N and stores them in provided array.
Definition: MHO_FastFourierTransformUtilities.hh:76
static void ComputeTwiddleFactors(unsigned int N, std::complex< XFloatType > *twiddle)
Compute twiddle factors for a Fast Fourier Transform. computes all the twiddle factors e^{i*2*pi/N} f...
static void ComputeBluesteinCirculantVector(unsigned int N, unsigned int M, std::complex< XFloatType > *twiddle, std::complex< XFloatType > *scale, std::complex< XFloatType > *circulant)
Function ComputeBluesteinCirculantVector twiddle and circulant array must be length M = 2^p >= (2N - ...
Definition: MHO_FastFourierTransformUtilities.hh:363
Definition: MHO_ChannelLabeler.hh:17