// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2026 Rasmus Munk Larsen // // 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/. // Tests for GpuFFT: GPU FFT via cuFFT. #define EIGEN_USE_GPU #include "main.h" #include using namespace Eigen; // ---- 1D C2C roundtrip: inv(fwd(x)) ≈ x ------------------------------------- template void test_c2c_roundtrip(Index n) { using Complex = std::complex; using Vec = Matrix; using RealScalar = Scalar; Vec x = Vec::Random(n); GpuFFT fft; Vec X = fft.fwd(x); VERIFY_IS_EQUAL(X.size(), n); Vec y = fft.inv(X); VERIFY_IS_EQUAL(y.size(), n); RealScalar tol = RealScalar(10) * RealScalar(n) * NumTraits::epsilon(); VERIFY((y - x).norm() / x.norm() < tol); } // ---- 1D C2C known signal: FFT of constant = delta -------------------------- template void test_c2c_constant() { using Complex = std::complex; using Vec = Matrix; using RealScalar = Scalar; const int n = 64; Vec x = Vec::Constant(n, Complex(3.0, 0.0)); GpuFFT fft; Vec X = fft.fwd(x); // FFT of constant c: X[0] = c*n, X[k] = 0 for k > 0. RealScalar tol = RealScalar(10) * NumTraits::epsilon() * RealScalar(n); VERIFY(std::abs(X(0) - Complex(3.0 * n, 0.0)) < tol); for (int k = 1; k < n; ++k) { VERIFY(std::abs(X(k)) < tol); } } // ---- 1D R2C/C2R roundtrip: invReal(fwd(r), n) ≈ r -------------------------- template void test_r2c_roundtrip(Index n) { using Complex = std::complex; using CVec = Matrix; using RVec = Matrix; using RealScalar = Scalar; RVec r = RVec::Random(n); GpuFFT fft; CVec R = fft.fwd(r); // R2C returns n/2+1 complex values. VERIFY_IS_EQUAL(R.size(), n / 2 + 1); RVec s = fft.invReal(R, n); VERIFY_IS_EQUAL(s.size(), n); RealScalar tol = RealScalar(10) * RealScalar(n) * NumTraits::epsilon(); VERIFY((s - r).norm() / r.norm() < tol); } // ---- 2D C2C roundtrip: inv2d(fwd2d(A)) ≈ A --------------------------------- template void test_2d_roundtrip(Index rows, Index cols) { using Complex = std::complex; using Mat = Matrix; using RealScalar = Scalar; Mat A = Mat::Random(rows, cols); GpuFFT fft; Mat B = fft.fwd2d(A); VERIFY_IS_EQUAL(B.rows(), rows); VERIFY_IS_EQUAL(B.cols(), cols); Mat C = fft.inv2d(B); VERIFY_IS_EQUAL(C.rows(), rows); VERIFY_IS_EQUAL(C.cols(), cols); RealScalar tol = RealScalar(10) * RealScalar(rows * cols) * NumTraits::epsilon(); VERIFY((C - A).norm() / A.norm() < tol); } // ---- 2D C2C known signal: constant matrix ----------------------------------- template void test_2d_constant() { using Complex = std::complex; using Mat = Matrix; using RealScalar = Scalar; const int rows = 16, cols = 32; Mat A = Mat::Constant(rows, cols, Complex(2.0, 0.0)); GpuFFT fft; Mat B = fft.fwd2d(A); // 2D FFT of constant c: B(0,0) = c*rows*cols, all others = 0. RealScalar tol = RealScalar(10) * NumTraits::epsilon() * RealScalar(rows * cols); VERIFY(std::abs(B(0, 0) - Complex(2.0 * rows * cols, 0.0)) < tol); for (int j = 0; j < cols; ++j) { for (int i = 0; i < rows; ++i) { if (i == 0 && j == 0) continue; VERIFY(std::abs(B(i, j)) < tol); } } } // ---- Plan reuse: repeated calls should work --------------------------------- template void test_plan_reuse() { using Complex = std::complex; using Vec = Matrix; using RealScalar = Scalar; GpuFFT fft; for (int trial = 0; trial < 5; ++trial) { Vec x = Vec::Random(128); Vec X = fft.fwd(x); Vec y = fft.inv(X); RealScalar tol = RealScalar(10) * RealScalar(128) * NumTraits::epsilon(); VERIFY((y - x).norm() / x.norm() < tol); } } // ---- Empty ------------------------------------------------------------------ template void test_empty() { using Complex = std::complex; using Vec = Matrix; GpuFFT fft; Vec x(0); Vec X = fft.fwd(x); VERIFY_IS_EQUAL(X.size(), 0); Vec y = fft.inv(X); VERIFY_IS_EQUAL(y.size(), 0); } // ---- Per-scalar driver ------------------------------------------------------ template void test_scalar() { CALL_SUBTEST(test_c2c_roundtrip(64)); CALL_SUBTEST(test_c2c_roundtrip(256)); CALL_SUBTEST(test_c2c_roundtrip(1000)); // non-power-of-2 CALL_SUBTEST(test_c2c_constant()); CALL_SUBTEST(test_r2c_roundtrip(64)); CALL_SUBTEST(test_r2c_roundtrip(256)); CALL_SUBTEST(test_2d_roundtrip(32, 32)); CALL_SUBTEST(test_2d_roundtrip(16, 64)); // non-square CALL_SUBTEST(test_2d_constant()); CALL_SUBTEST(test_plan_reuse()); CALL_SUBTEST(test_empty()); } EIGEN_DECLARE_TEST(gpu_cufft) { CALL_SUBTEST(test_scalar()); CALL_SUBTEST(test_scalar()); }