// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2014 Jianwei Cui // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #include "main.h" #include using Eigen::Tensor; template static void test_fft_2D_golden() { Tensor input(2, 3); input(0, 0) = 1; input(0, 1) = 2; input(0, 2) = 3; input(1, 0) = 4; input(1, 1) = 5; input(1, 2) = 6; array fft; fft[0] = 0; fft[1] = 1; Tensor, 2, DataLayout> output = input.template fft(fft); std::complex output_golden[6]; // in ColMajor order output_golden[0] = std::complex(21, 0); output_golden[1] = std::complex(-9, 0); output_golden[2] = std::complex(-3, 1.73205); output_golden[3] = std::complex( 0, 0); output_golden[4] = std::complex(-3, -1.73205); output_golden[5] = std::complex(0 ,0); std::complex c_offset = std::complex(1.0, 1.0); if (DataLayout == ColMajor) { VERIFY_IS_APPROX(output(0) + c_offset, output_golden[0] + c_offset); VERIFY_IS_APPROX(output(1) + c_offset, output_golden[1] + c_offset); VERIFY_IS_APPROX(output(2) + c_offset, output_golden[2] + c_offset); VERIFY_IS_APPROX(output(3) + c_offset, output_golden[3] + c_offset); VERIFY_IS_APPROX(output(4) + c_offset, output_golden[4] + c_offset); VERIFY_IS_APPROX(output(5) + c_offset, output_golden[5] + c_offset); } else { VERIFY_IS_APPROX(output(0)+ c_offset, output_golden[0]+ c_offset); VERIFY_IS_APPROX(output(1)+ c_offset, output_golden[2]+ c_offset); VERIFY_IS_APPROX(output(2)+ c_offset, output_golden[4]+ c_offset); VERIFY_IS_APPROX(output(3)+ c_offset, output_golden[1]+ c_offset); VERIFY_IS_APPROX(output(4)+ c_offset, output_golden[3]+ c_offset); VERIFY_IS_APPROX(output(5)+ c_offset, output_golden[5]+ c_offset); } } static void test_fft_complex_input_golden() { Tensor, 1, ColMajor> input(5); input(0) = std::complex(1, 1); input(1) = std::complex(2, 2); input(2) = std::complex(3, 3); input(3) = std::complex(4, 4); input(4) = std::complex(5, 5); array fft; fft[0] = 0; Tensor, 1, ColMajor> forward_output_both_parts = input.fft(fft); Tensor, 1, ColMajor> reverse_output_both_parts = input.fft(fft); Tensor forward_output_real_part = input.fft(fft); Tensor reverse_output_real_part = input.fft(fft); Tensor forward_output_imag_part = input.fft(fft); Tensor reverse_output_imag_part = input.fft(fft); VERIFY_IS_EQUAL(forward_output_both_parts.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(reverse_output_both_parts.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(forward_output_real_part.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(reverse_output_real_part.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(forward_output_imag_part.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(reverse_output_imag_part.dimension(0), input.dimension(0)); std::complex forward_golden_result[5]; std::complex reverse_golden_result[5]; forward_golden_result[0] = std::complex(15.000000000000000,+15.000000000000000); forward_golden_result[1] = std::complex(-5.940954801177935, +0.940954801177934); forward_golden_result[2] = std::complex(-3.312299240582266, -1.687700759417735); forward_golden_result[3] = std::complex(-1.687700759417735, -3.312299240582266); forward_golden_result[4] = std::complex( 0.940954801177934, -5.940954801177935); reverse_golden_result[0] = std::complex( 3.000000000000000, + 3.000000000000000); reverse_golden_result[1] = std::complex( 0.188190960235587, - 1.188190960235587); reverse_golden_result[2] = std::complex(-0.337540151883547, - 0.662459848116453); reverse_golden_result[3] = std::complex(-0.662459848116453, - 0.337540151883547); reverse_golden_result[4] = std::complex(-1.188190960235587, + 0.188190960235587); for(int i = 0; i < 5; ++i) { VERIFY_IS_APPROX(forward_output_both_parts(i), forward_golden_result[i]); VERIFY_IS_APPROX(forward_output_real_part(i), forward_golden_result[i].real()); VERIFY_IS_APPROX(forward_output_imag_part(i), forward_golden_result[i].imag()); } for(int i = 0; i < 5; ++i) { VERIFY_IS_APPROX(reverse_output_both_parts(i), reverse_golden_result[i]); VERIFY_IS_APPROX(reverse_output_real_part(i), reverse_golden_result[i].real()); VERIFY_IS_APPROX(reverse_output_imag_part(i), reverse_golden_result[i].imag()); } } static void test_fft_real_input_golden() { Tensor input(5); input(0) = 1.0; input(1) = 2.0; input(2) = 3.0; input(3) = 4.0; input(4) = 5.0; array fft; fft[0] = 0; Tensor, 1, ColMajor> forward_output_both_parts = input.fft(fft); Tensor, 1, ColMajor> reverse_output_both_parts = input.fft(fft); Tensor forward_output_real_part = input.fft(fft); Tensor reverse_output_real_part = input.fft(fft); Tensor forward_output_imag_part = input.fft(fft); Tensor reverse_output_imag_part = input.fft(fft); VERIFY_IS_EQUAL(forward_output_both_parts.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(reverse_output_both_parts.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(forward_output_real_part.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(reverse_output_real_part.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(forward_output_imag_part.dimension(0), input.dimension(0)); VERIFY_IS_EQUAL(reverse_output_imag_part.dimension(0), input.dimension(0)); std::complex forward_golden_result[5]; std::complex reverse_golden_result[5]; forward_golden_result[0] = std::complex( 15, 0); forward_golden_result[1] = std::complex(-2.5, +3.44095480117793); forward_golden_result[2] = std::complex(-2.5, +0.81229924058227); forward_golden_result[3] = std::complex(-2.5, -0.81229924058227); forward_golden_result[4] = std::complex(-2.5, -3.44095480117793); reverse_golden_result[0] = std::complex( 3.0, 0); reverse_golden_result[1] = std::complex(-0.5, -0.688190960235587); reverse_golden_result[2] = std::complex(-0.5, -0.162459848116453); reverse_golden_result[3] = std::complex(-0.5, +0.162459848116453); reverse_golden_result[4] = std::complex(-0.5, +0.688190960235587); std::complex c_offset(1.0, 1.0); float r_offset = 1.0; for(int i = 0; i < 5; ++i) { VERIFY_IS_APPROX(forward_output_both_parts(i) + c_offset, forward_golden_result[i] + c_offset); VERIFY_IS_APPROX(forward_output_real_part(i) + r_offset, forward_golden_result[i].real() + r_offset); VERIFY_IS_APPROX(forward_output_imag_part(i) + r_offset, forward_golden_result[i].imag() + r_offset); } for(int i = 0; i < 5; ++i) { VERIFY_IS_APPROX(reverse_output_both_parts(i) + c_offset, reverse_golden_result[i] + c_offset); VERIFY_IS_APPROX(reverse_output_real_part(i) + r_offset, reverse_golden_result[i].real() + r_offset); VERIFY_IS_APPROX(reverse_output_imag_part(i) + r_offset, reverse_golden_result[i].imag() + r_offset); } } template static void test_fft_real_input_energy() { Eigen::DSizes dimensions; ptrdiff_t total_size = 1; for (int i = 0; i < TensorRank; ++i) { dimensions[i] = rand() % 20 + 1; total_size *= dimensions[i]; } const DSizes arr = dimensions; typedef typename internal::conditional, RealScalar>::type InputScalar; Tensor input; input.resize(arr); input.setRandom(); array fft; for (int i = 0; i < TensorRank; ++i) { fft[i] = i; } typedef typename internal::conditional, RealScalar>::type OutputScalar; Tensor output; output = input.template fft(fft); for (int i = 0; i < TensorRank; ++i) { VERIFY_IS_EQUAL(output.dimension(i), input.dimension(i)); } RealScalar energy_original = 0.0; RealScalar energy_after_fft = 0.0; for (int i = 0; i < total_size; ++i) { energy_original += numext::abs2(input(i)); } for (int i = 0; i < total_size; ++i) { energy_after_fft += numext::abs2(output(i)); } if(FFTDirection == FFT_FORWARD) { VERIFY_IS_APPROX(energy_original, energy_after_fft / total_size); } else { VERIFY_IS_APPROX(energy_original, energy_after_fft * total_size); } } template static void test_fft_non_power_of_2_round_trip(int exponent) { int n = (1 << exponent) + 1; Eigen::DSizes dimensions; dimensions[0] = n; const DSizes arr = dimensions; Tensor input; input.resize(arr); input.setRandom(); array fft; fft[0] = 0; Tensor, 1, ColMajor> forward = input.template fft(fft); Tensor output = forward.template fft(fft); for (int i = 0; i < n; ++i) { RealScalar tol = test_precision() * (std::abs(input[i]) + std::abs(output[i]) + 1); VERIFY_IS_APPROX_OR_LESS_THAN(std::abs(input[i] - output[i]), tol); } } EIGEN_DECLARE_TEST(cxx11_tensor_fft) { test_fft_complex_input_golden(); test_fft_real_input_golden(); test_fft_2D_golden(); test_fft_2D_golden(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_real_input_energy(); test_fft_non_power_of_2_round_trip(7); test_fft_non_power_of_2_round_trip(7); }