// 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 GpuSelfAdjointEigenSolver: GPU symmetric/Hermitian eigenvalue // decomposition via cuSOLVER. #define EIGEN_USE_GPU #include "main.h" #include #include using namespace Eigen; // ---- Reconstruction: V * diag(W) * V^H ≈ A --------------------------------- template void test_eigen_reconstruction(Index n) { using Mat = Matrix; using RealScalar = typename NumTraits::Real; // Build a symmetric/Hermitian matrix. Mat R = Mat::Random(n, n); Mat A = R + R.adjoint(); GpuSelfAdjointEigenSolver es(A); VERIFY_IS_EQUAL(es.info(), Success); auto W = es.eigenvalues(); Mat V = es.eigenvectors(); VERIFY_IS_EQUAL(W.size(), n); VERIFY_IS_EQUAL(V.rows(), n); VERIFY_IS_EQUAL(V.cols(), n); // Reconstruct: A_hat = V * diag(W) * V^H. Mat A_hat = V * W.asDiagonal() * V.adjoint(); RealScalar tol = RealScalar(5) * std::sqrt(static_cast(n)) * NumTraits::epsilon() * A.norm(); VERIFY((A_hat - A).norm() < tol); // Orthogonality: V^H * V ≈ I. Mat VhV = V.adjoint() * V; Mat eye = Mat::Identity(n, n); VERIFY((VhV - eye).norm() < tol); } // ---- Eigenvalues match CPU SelfAdjointEigenSolver --------------------------- template void test_eigen_values(Index n) { using Mat = Matrix; using RealScalar = typename NumTraits::Real; Mat R = Mat::Random(n, n); Mat A = R + R.adjoint(); GpuSelfAdjointEigenSolver gpu_es(A); VERIFY_IS_EQUAL(gpu_es.info(), Success); auto W_gpu = gpu_es.eigenvalues(); SelfAdjointEigenSolver cpu_es(A); auto W_cpu = cpu_es.eigenvalues(); RealScalar tol = RealScalar(5) * std::sqrt(static_cast(n)) * NumTraits::epsilon() * W_cpu.cwiseAbs().maxCoeff(); VERIFY((W_gpu - W_cpu).norm() < tol); } // ---- Eigenvalues-only mode -------------------------------------------------- template void test_eigen_values_only(Index n) { using Mat = Matrix; using RealScalar = typename NumTraits::Real; Mat R = Mat::Random(n, n); Mat A = R + R.adjoint(); GpuSelfAdjointEigenSolver gpu_es(A, GpuSelfAdjointEigenSolver::EigenvaluesOnly); VERIFY_IS_EQUAL(gpu_es.info(), Success); auto W_gpu = gpu_es.eigenvalues(); SelfAdjointEigenSolver cpu_es(A, EigenvaluesOnly); auto W_cpu = cpu_es.eigenvalues(); RealScalar tol = RealScalar(5) * std::sqrt(static_cast(n)) * NumTraits::epsilon() * W_cpu.cwiseAbs().maxCoeff(); VERIFY((W_gpu - W_cpu).norm() < tol); } // ---- DeviceMatrix input path ------------------------------------------------ template void test_eigen_device_matrix(Index n) { using Mat = Matrix; using RealScalar = typename NumTraits::Real; Mat R = Mat::Random(n, n); Mat A = R + R.adjoint(); auto d_A = DeviceMatrix::fromHost(A); GpuSelfAdjointEigenSolver es; es.compute(d_A); VERIFY_IS_EQUAL(es.info(), Success); auto W_gpu = es.eigenvalues(); Mat V = es.eigenvectors(); // Verify reconstruction. Mat A_hat = V * W_gpu.asDiagonal() * V.adjoint(); RealScalar tol = RealScalar(5) * std::sqrt(static_cast(n)) * NumTraits::epsilon() * A.norm(); VERIFY((A_hat - A).norm() < tol); } // ---- Recompute (reuse solver object) ---------------------------------------- template void test_eigen_recompute(Index n) { using Mat = Matrix; using RealScalar = typename NumTraits::Real; GpuSelfAdjointEigenSolver es; for (int trial = 0; trial < 3; ++trial) { Mat R = Mat::Random(n, n); Mat A = R + R.adjoint(); es.compute(A); VERIFY_IS_EQUAL(es.info(), Success); auto W = es.eigenvalues(); Mat V = es.eigenvectors(); Mat A_hat = V * W.asDiagonal() * V.adjoint(); RealScalar tol = RealScalar(5) * std::sqrt(static_cast(n)) * NumTraits::epsilon() * A.norm(); VERIFY((A_hat - A).norm() < tol); } } // ---- Empty matrix ----------------------------------------------------------- void test_eigen_empty() { GpuSelfAdjointEigenSolver es(MatrixXd(0, 0)); VERIFY_IS_EQUAL(es.info(), Success); VERIFY_IS_EQUAL(es.rows(), 0); VERIFY_IS_EQUAL(es.cols(), 0); } // ---- Per-scalar driver ------------------------------------------------------ template void test_scalar() { // Reconstruction + orthogonality. CALL_SUBTEST(test_eigen_reconstruction(64)); CALL_SUBTEST(test_eigen_reconstruction(128)); // Eigenvalues match CPU. CALL_SUBTEST(test_eigen_values(64)); CALL_SUBTEST(test_eigen_values(128)); // Values-only mode. CALL_SUBTEST(test_eigen_values_only(64)); // DeviceMatrix input. CALL_SUBTEST(test_eigen_device_matrix(64)); // Recompute. CALL_SUBTEST(test_eigen_recompute(32)); } EIGEN_DECLARE_TEST(gpu_cusolver_eigen) { CALL_SUBTEST(test_scalar()); CALL_SUBTEST(test_scalar()); CALL_SUBTEST(test_scalar>()); CALL_SUBTEST(test_scalar>()); CALL_SUBTEST(test_eigen_empty()); }