cxx11_tensor_fft.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. // This file is part of Eigen, a lightweight C++ template library
  2. // for linear algebra.
  3. //
  4. // Copyright (C) 2014 Jianwei Cui <thucjw@gmail.com>
  5. //
  6. // This Source Code Form is subject to the terms of the Mozilla
  7. // Public License v. 2.0. If a copy of the MPL was not distributed
  8. // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. #include "main.h"
  10. #include <Eigen/CXX11/Tensor>
  11. using Eigen::Tensor;
  12. template <int DataLayout>
  13. static void test_fft_2D_golden() {
  14. Tensor<float, 2, DataLayout> input(2, 3);
  15. input(0, 0) = 1;
  16. input(0, 1) = 2;
  17. input(0, 2) = 3;
  18. input(1, 0) = 4;
  19. input(1, 1) = 5;
  20. input(1, 2) = 6;
  21. array<ptrdiff_t, 2> fft;
  22. fft[0] = 0;
  23. fft[1] = 1;
  24. Tensor<std::complex<float>, 2, DataLayout> output = input.template fft<Eigen::BothParts, Eigen::FFT_FORWARD>(fft);
  25. std::complex<float> output_golden[6]; // in ColMajor order
  26. output_golden[0] = std::complex<float>(21, 0);
  27. output_golden[1] = std::complex<float>(-9, 0);
  28. output_golden[2] = std::complex<float>(-3, 1.73205);
  29. output_golden[3] = std::complex<float>( 0, 0);
  30. output_golden[4] = std::complex<float>(-3, -1.73205);
  31. output_golden[5] = std::complex<float>(0 ,0);
  32. std::complex<float> c_offset = std::complex<float>(1.0, 1.0);
  33. if (DataLayout == ColMajor) {
  34. VERIFY_IS_APPROX(output(0) + c_offset, output_golden[0] + c_offset);
  35. VERIFY_IS_APPROX(output(1) + c_offset, output_golden[1] + c_offset);
  36. VERIFY_IS_APPROX(output(2) + c_offset, output_golden[2] + c_offset);
  37. VERIFY_IS_APPROX(output(3) + c_offset, output_golden[3] + c_offset);
  38. VERIFY_IS_APPROX(output(4) + c_offset, output_golden[4] + c_offset);
  39. VERIFY_IS_APPROX(output(5) + c_offset, output_golden[5] + c_offset);
  40. }
  41. else {
  42. VERIFY_IS_APPROX(output(0)+ c_offset, output_golden[0]+ c_offset);
  43. VERIFY_IS_APPROX(output(1)+ c_offset, output_golden[2]+ c_offset);
  44. VERIFY_IS_APPROX(output(2)+ c_offset, output_golden[4]+ c_offset);
  45. VERIFY_IS_APPROX(output(3)+ c_offset, output_golden[1]+ c_offset);
  46. VERIFY_IS_APPROX(output(4)+ c_offset, output_golden[3]+ c_offset);
  47. VERIFY_IS_APPROX(output(5)+ c_offset, output_golden[5]+ c_offset);
  48. }
  49. }
  50. static void test_fft_complex_input_golden() {
  51. Tensor<std::complex<float>, 1, ColMajor> input(5);
  52. input(0) = std::complex<float>(1, 1);
  53. input(1) = std::complex<float>(2, 2);
  54. input(2) = std::complex<float>(3, 3);
  55. input(3) = std::complex<float>(4, 4);
  56. input(4) = std::complex<float>(5, 5);
  57. array<ptrdiff_t, 1> fft;
  58. fft[0] = 0;
  59. Tensor<std::complex<float>, 1, ColMajor> forward_output_both_parts = input.fft<BothParts, FFT_FORWARD>(fft);
  60. Tensor<std::complex<float>, 1, ColMajor> reverse_output_both_parts = input.fft<BothParts, FFT_REVERSE>(fft);
  61. Tensor<float, 1, ColMajor> forward_output_real_part = input.fft<RealPart, FFT_FORWARD>(fft);
  62. Tensor<float, 1, ColMajor> reverse_output_real_part = input.fft<RealPart, FFT_REVERSE>(fft);
  63. Tensor<float, 1, ColMajor> forward_output_imag_part = input.fft<ImagPart, FFT_FORWARD>(fft);
  64. Tensor<float, 1, ColMajor> reverse_output_imag_part = input.fft<ImagPart, FFT_REVERSE>(fft);
  65. VERIFY_IS_EQUAL(forward_output_both_parts.dimension(0), input.dimension(0));
  66. VERIFY_IS_EQUAL(reverse_output_both_parts.dimension(0), input.dimension(0));
  67. VERIFY_IS_EQUAL(forward_output_real_part.dimension(0), input.dimension(0));
  68. VERIFY_IS_EQUAL(reverse_output_real_part.dimension(0), input.dimension(0));
  69. VERIFY_IS_EQUAL(forward_output_imag_part.dimension(0), input.dimension(0));
  70. VERIFY_IS_EQUAL(reverse_output_imag_part.dimension(0), input.dimension(0));
  71. std::complex<float> forward_golden_result[5];
  72. std::complex<float> reverse_golden_result[5];
  73. forward_golden_result[0] = std::complex<float>(15.000000000000000,+15.000000000000000);
  74. forward_golden_result[1] = std::complex<float>(-5.940954801177935, +0.940954801177934);
  75. forward_golden_result[2] = std::complex<float>(-3.312299240582266, -1.687700759417735);
  76. forward_golden_result[3] = std::complex<float>(-1.687700759417735, -3.312299240582266);
  77. forward_golden_result[4] = std::complex<float>( 0.940954801177934, -5.940954801177935);
  78. reverse_golden_result[0] = std::complex<float>( 3.000000000000000, + 3.000000000000000);
  79. reverse_golden_result[1] = std::complex<float>( 0.188190960235587, - 1.188190960235587);
  80. reverse_golden_result[2] = std::complex<float>(-0.337540151883547, - 0.662459848116453);
  81. reverse_golden_result[3] = std::complex<float>(-0.662459848116453, - 0.337540151883547);
  82. reverse_golden_result[4] = std::complex<float>(-1.188190960235587, + 0.188190960235587);
  83. for(int i = 0; i < 5; ++i) {
  84. VERIFY_IS_APPROX(forward_output_both_parts(i), forward_golden_result[i]);
  85. VERIFY_IS_APPROX(forward_output_real_part(i), forward_golden_result[i].real());
  86. VERIFY_IS_APPROX(forward_output_imag_part(i), forward_golden_result[i].imag());
  87. }
  88. for(int i = 0; i < 5; ++i) {
  89. VERIFY_IS_APPROX(reverse_output_both_parts(i), reverse_golden_result[i]);
  90. VERIFY_IS_APPROX(reverse_output_real_part(i), reverse_golden_result[i].real());
  91. VERIFY_IS_APPROX(reverse_output_imag_part(i), reverse_golden_result[i].imag());
  92. }
  93. }
  94. static void test_fft_real_input_golden() {
  95. Tensor<float, 1, ColMajor> input(5);
  96. input(0) = 1.0;
  97. input(1) = 2.0;
  98. input(2) = 3.0;
  99. input(3) = 4.0;
  100. input(4) = 5.0;
  101. array<ptrdiff_t, 1> fft;
  102. fft[0] = 0;
  103. Tensor<std::complex<float>, 1, ColMajor> forward_output_both_parts = input.fft<BothParts, FFT_FORWARD>(fft);
  104. Tensor<std::complex<float>, 1, ColMajor> reverse_output_both_parts = input.fft<BothParts, FFT_REVERSE>(fft);
  105. Tensor<float, 1, ColMajor> forward_output_real_part = input.fft<RealPart, FFT_FORWARD>(fft);
  106. Tensor<float, 1, ColMajor> reverse_output_real_part = input.fft<RealPart, FFT_REVERSE>(fft);
  107. Tensor<float, 1, ColMajor> forward_output_imag_part = input.fft<ImagPart, FFT_FORWARD>(fft);
  108. Tensor<float, 1, ColMajor> reverse_output_imag_part = input.fft<ImagPart, FFT_REVERSE>(fft);
  109. VERIFY_IS_EQUAL(forward_output_both_parts.dimension(0), input.dimension(0));
  110. VERIFY_IS_EQUAL(reverse_output_both_parts.dimension(0), input.dimension(0));
  111. VERIFY_IS_EQUAL(forward_output_real_part.dimension(0), input.dimension(0));
  112. VERIFY_IS_EQUAL(reverse_output_real_part.dimension(0), input.dimension(0));
  113. VERIFY_IS_EQUAL(forward_output_imag_part.dimension(0), input.dimension(0));
  114. VERIFY_IS_EQUAL(reverse_output_imag_part.dimension(0), input.dimension(0));
  115. std::complex<float> forward_golden_result[5];
  116. std::complex<float> reverse_golden_result[5];
  117. forward_golden_result[0] = std::complex<float>( 15, 0);
  118. forward_golden_result[1] = std::complex<float>(-2.5, +3.44095480117793);
  119. forward_golden_result[2] = std::complex<float>(-2.5, +0.81229924058227);
  120. forward_golden_result[3] = std::complex<float>(-2.5, -0.81229924058227);
  121. forward_golden_result[4] = std::complex<float>(-2.5, -3.44095480117793);
  122. reverse_golden_result[0] = std::complex<float>( 3.0, 0);
  123. reverse_golden_result[1] = std::complex<float>(-0.5, -0.688190960235587);
  124. reverse_golden_result[2] = std::complex<float>(-0.5, -0.162459848116453);
  125. reverse_golden_result[3] = std::complex<float>(-0.5, +0.162459848116453);
  126. reverse_golden_result[4] = std::complex<float>(-0.5, +0.688190960235587);
  127. std::complex<float> c_offset(1.0, 1.0);
  128. float r_offset = 1.0;
  129. for(int i = 0; i < 5; ++i) {
  130. VERIFY_IS_APPROX(forward_output_both_parts(i) + c_offset, forward_golden_result[i] + c_offset);
  131. VERIFY_IS_APPROX(forward_output_real_part(i) + r_offset, forward_golden_result[i].real() + r_offset);
  132. VERIFY_IS_APPROX(forward_output_imag_part(i) + r_offset, forward_golden_result[i].imag() + r_offset);
  133. }
  134. for(int i = 0; i < 5; ++i) {
  135. VERIFY_IS_APPROX(reverse_output_both_parts(i) + c_offset, reverse_golden_result[i] + c_offset);
  136. VERIFY_IS_APPROX(reverse_output_real_part(i) + r_offset, reverse_golden_result[i].real() + r_offset);
  137. VERIFY_IS_APPROX(reverse_output_imag_part(i) + r_offset, reverse_golden_result[i].imag() + r_offset);
  138. }
  139. }
  140. template <int DataLayout, typename RealScalar, bool isComplexInput, int FFTResultType, int FFTDirection, int TensorRank>
  141. static void test_fft_real_input_energy() {
  142. Eigen::DSizes<ptrdiff_t, TensorRank> dimensions;
  143. ptrdiff_t total_size = 1;
  144. for (int i = 0; i < TensorRank; ++i) {
  145. dimensions[i] = rand() % 20 + 1;
  146. total_size *= dimensions[i];
  147. }
  148. const DSizes<ptrdiff_t, TensorRank> arr = dimensions;
  149. typedef typename internal::conditional<isComplexInput == true, std::complex<RealScalar>, RealScalar>::type InputScalar;
  150. Tensor<InputScalar, TensorRank, DataLayout> input;
  151. input.resize(arr);
  152. input.setRandom();
  153. array<ptrdiff_t, TensorRank> fft;
  154. for (int i = 0; i < TensorRank; ++i) {
  155. fft[i] = i;
  156. }
  157. typedef typename internal::conditional<FFTResultType == Eigen::BothParts, std::complex<RealScalar>, RealScalar>::type OutputScalar;
  158. Tensor<OutputScalar, TensorRank, DataLayout> output;
  159. output = input.template fft<FFTResultType, FFTDirection>(fft);
  160. for (int i = 0; i < TensorRank; ++i) {
  161. VERIFY_IS_EQUAL(output.dimension(i), input.dimension(i));
  162. }
  163. RealScalar energy_original = 0.0;
  164. RealScalar energy_after_fft = 0.0;
  165. for (int i = 0; i < total_size; ++i) {
  166. energy_original += numext::abs2(input(i));
  167. }
  168. for (int i = 0; i < total_size; ++i) {
  169. energy_after_fft += numext::abs2(output(i));
  170. }
  171. if(FFTDirection == FFT_FORWARD) {
  172. VERIFY_IS_APPROX(energy_original, energy_after_fft / total_size);
  173. }
  174. else {
  175. VERIFY_IS_APPROX(energy_original, energy_after_fft * total_size);
  176. }
  177. }
  178. template <typename RealScalar>
  179. static void test_fft_non_power_of_2_round_trip(int exponent) {
  180. int n = (1 << exponent) + 1;
  181. Eigen::DSizes<ptrdiff_t, 1> dimensions;
  182. dimensions[0] = n;
  183. const DSizes<ptrdiff_t, 1> arr = dimensions;
  184. Tensor<RealScalar, 1, ColMajor, ptrdiff_t> input;
  185. input.resize(arr);
  186. input.setRandom();
  187. array<int, 1> fft;
  188. fft[0] = 0;
  189. Tensor<std::complex<RealScalar>, 1, ColMajor> forward =
  190. input.template fft<BothParts, FFT_FORWARD>(fft);
  191. Tensor<RealScalar, 1, ColMajor, ptrdiff_t> output =
  192. forward.template fft<RealPart, FFT_REVERSE>(fft);
  193. for (int i = 0; i < n; ++i) {
  194. RealScalar tol = test_precision<RealScalar>() *
  195. (std::abs(input[i]) + std::abs(output[i]) + 1);
  196. VERIFY_IS_APPROX_OR_LESS_THAN(std::abs(input[i] - output[i]), tol);
  197. }
  198. }
  199. EIGEN_DECLARE_TEST(cxx11_tensor_fft) {
  200. test_fft_complex_input_golden();
  201. test_fft_real_input_golden();
  202. test_fft_2D_golden<ColMajor>();
  203. test_fft_2D_golden<RowMajor>();
  204. test_fft_real_input_energy<ColMajor, float, true, Eigen::BothParts, FFT_FORWARD, 1>();
  205. test_fft_real_input_energy<ColMajor, double, true, Eigen::BothParts, FFT_FORWARD, 1>();
  206. test_fft_real_input_energy<ColMajor, float, false, Eigen::BothParts, FFT_FORWARD, 1>();
  207. test_fft_real_input_energy<ColMajor, double, false, Eigen::BothParts, FFT_FORWARD, 1>();
  208. test_fft_real_input_energy<ColMajor, float, true, Eigen::BothParts, FFT_FORWARD, 2>();
  209. test_fft_real_input_energy<ColMajor, double, true, Eigen::BothParts, FFT_FORWARD, 2>();
  210. test_fft_real_input_energy<ColMajor, float, false, Eigen::BothParts, FFT_FORWARD, 2>();
  211. test_fft_real_input_energy<ColMajor, double, false, Eigen::BothParts, FFT_FORWARD, 2>();
  212. test_fft_real_input_energy<ColMajor, float, true, Eigen::BothParts, FFT_FORWARD, 3>();
  213. test_fft_real_input_energy<ColMajor, double, true, Eigen::BothParts, FFT_FORWARD, 3>();
  214. test_fft_real_input_energy<ColMajor, float, false, Eigen::BothParts, FFT_FORWARD, 3>();
  215. test_fft_real_input_energy<ColMajor, double, false, Eigen::BothParts, FFT_FORWARD, 3>();
  216. test_fft_real_input_energy<ColMajor, float, true, Eigen::BothParts, FFT_FORWARD, 4>();
  217. test_fft_real_input_energy<ColMajor, double, true, Eigen::BothParts, FFT_FORWARD, 4>();
  218. test_fft_real_input_energy<ColMajor, float, false, Eigen::BothParts, FFT_FORWARD, 4>();
  219. test_fft_real_input_energy<ColMajor, double, false, Eigen::BothParts, FFT_FORWARD, 4>();
  220. test_fft_real_input_energy<RowMajor, float, true, Eigen::BothParts, FFT_FORWARD, 1>();
  221. test_fft_real_input_energy<RowMajor, double, true, Eigen::BothParts, FFT_FORWARD, 1>();
  222. test_fft_real_input_energy<RowMajor, float, false, Eigen::BothParts, FFT_FORWARD, 1>();
  223. test_fft_real_input_energy<RowMajor, double, false, Eigen::BothParts, FFT_FORWARD, 1>();
  224. test_fft_real_input_energy<RowMajor, float, true, Eigen::BothParts, FFT_FORWARD, 2>();
  225. test_fft_real_input_energy<RowMajor, double, true, Eigen::BothParts, FFT_FORWARD, 2>();
  226. test_fft_real_input_energy<RowMajor, float, false, Eigen::BothParts, FFT_FORWARD, 2>();
  227. test_fft_real_input_energy<RowMajor, double, false, Eigen::BothParts, FFT_FORWARD, 2>();
  228. test_fft_real_input_energy<RowMajor, float, true, Eigen::BothParts, FFT_FORWARD, 3>();
  229. test_fft_real_input_energy<RowMajor, double, true, Eigen::BothParts, FFT_FORWARD, 3>();
  230. test_fft_real_input_energy<RowMajor, float, false, Eigen::BothParts, FFT_FORWARD, 3>();
  231. test_fft_real_input_energy<RowMajor, double, false, Eigen::BothParts, FFT_FORWARD, 3>();
  232. test_fft_real_input_energy<RowMajor, float, true, Eigen::BothParts, FFT_FORWARD, 4>();
  233. test_fft_real_input_energy<RowMajor, double, true, Eigen::BothParts, FFT_FORWARD, 4>();
  234. test_fft_real_input_energy<RowMajor, float, false, Eigen::BothParts, FFT_FORWARD, 4>();
  235. test_fft_real_input_energy<RowMajor, double, false, Eigen::BothParts, FFT_FORWARD, 4>();
  236. test_fft_non_power_of_2_round_trip<float>(7);
  237. test_fft_non_power_of_2_round_trip<double>(7);
  238. }