LCOV - code coverage report
Current view: top level - src/diagonalize - DiagonalizerFeast.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 53 89 59.6 %
Date: 2025-05-02 21:49:55 Functions: 10 14 71.4 %

          Line data    Source code
       1             : // SPDX-FileCopyrightText: 2024 Pairinteraction Developers
       2             : // SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4             : #include "pairinteraction/diagonalize/DiagonalizerFeast.hpp"
       5             : 
       6             : #include "pairinteraction/enums/FloatType.hpp"
       7             : #include "pairinteraction/utils/eigen_assertion.hpp"
       8             : #include "pairinteraction/utils/eigen_compat.hpp"
       9             : 
      10             : #include <Eigen/Dense>
      11             : #include <cmath>
      12             : #include <optional>
      13             : #include <spdlog/spdlog.h>
      14             : 
      15             : #ifdef WITH_MKL
      16             : #include <fmt/core.h>
      17             : #include <mkl.h>
      18             : #endif // WITH_MKL
      19             : 
      20             : namespace pairinteraction {
      21             : #ifdef WITH_MKL
      22           0 : void feast(const char *uplo, const MKL_INT *n, const float *a, const MKL_INT *lda, MKL_INT *fpm,
      23             :            float *epsout, MKL_INT *loop, const float *emin, const float *emax, MKL_INT *m0,
      24             :            float *e, float *x, MKL_INT *m, float *res, MKL_INT *info) {
      25           0 :     sfeast_syev(uplo, n, a, lda, fpm, epsout, loop, emin, emax, m0, e, x, m, res, info);
      26           0 : }
      27             : 
      28           1 : void feast(const char *uplo, const MKL_INT *n, const double *a, const MKL_INT *lda, MKL_INT *fpm,
      29             :            double *epsout, MKL_INT *loop, const double *emin, const double *emax, MKL_INT *m0,
      30             :            double *e, double *x, MKL_INT *m, double *res, MKL_INT *info) {
      31           1 :     dfeast_syev(uplo, n, a, lda, fpm, epsout, loop, emin, emax, m0, e, x, m, res, info);
      32           1 : }
      33             : 
      34           2 : void feast(const char *uplo, const MKL_INT *n, const MKL_Complex8 *a, const MKL_INT *lda,
      35             :            MKL_INT *fpm, float *epsout, MKL_INT *loop, const float *emin, const float *emax,
      36             :            MKL_INT *m0, float *e, MKL_Complex8 *x, MKL_INT *m, float *res, MKL_INT *info) {
      37           2 :     cfeast_heev(uplo, n, a, lda, fpm, epsout, loop, emin, emax, m0, e, x, m, res, info);
      38           2 : }
      39             : 
      40           3 : void feast(const char *uplo, const MKL_INT *n, const MKL_Complex16 *a, const MKL_INT *lda,
      41             :            MKL_INT *fpm, double *epsout, MKL_INT *loop, const double *emin, const double *emax,
      42             :            MKL_INT *m0, double *e, MKL_Complex16 *x, MKL_INT *m, double *res, MKL_INT *info) {
      43           3 :     zfeast_heev(uplo, n, a, lda, fpm, epsout, loop, emin, emax, m0, e, x, m, res, info);
      44           3 : }
      45             : 
      46             : template <typename Scalar>
      47             : template <typename ScalarLim>
      48             : EigenSystemH<Scalar>
      49           6 : DiagonalizerFeast<Scalar>::dispatch_eigh(const Eigen::SparseMatrix<Scalar, Eigen::RowMajor> &matrix,
      50             :                                          real_t min_eigenvalue, real_t max_eigenvalue,
      51             :                                          double rtol) const {
      52             :     using real_lim_t = typename traits::NumTraits<ScalarLim>::real_t;
      53           6 :     int dim = matrix.rows();
      54             : 
      55             :     // Subtract the mean of the diagonal elements from the diagonal
      56           6 :     real_t shift{};
      57           6 :     Eigen::MatrixX<ScalarLim> hamiltonian =
      58             :         this->template subtract_mean<ScalarLim>(matrix, shift, rtol);
      59             : 
      60             :     // Diagonalize the shifted matrix
      61           6 :     int m0 = std::min(dim, this->m0);
      62             : 
      63           6 :     Eigen::VectorX<real_lim_t> evals(dim);
      64           6 :     Eigen::MatrixX<ScalarLim> evecs(dim, m0); // the first m columns will contain the eigenvectors
      65             : 
      66           6 :     double targeted_trace_relative_error = rtol;
      67           6 :     int precision_feast = static_cast<int>(std::ceil(-std::log10(targeted_trace_relative_error)));
      68             : 
      69           6 :     std::vector<MKL_INT> fpm(128);
      70           6 :     feastinit(fpm.data());
      71           6 :     fpm[0] = 0;                      // disable terminal output
      72           6 :     fpm[1] = 8;                      // number of contour points
      73           6 :     fpm[4] = 0;                      // do not use initial subspace
      74           6 :     fpm[26] = 0;                     // disables matrix checker
      75           6 :     fpm[2] = precision_feast;        // single precision stopping criteria
      76           6 :     fpm[6] = precision_feast;        // double precision stopping criteria
      77           6 :     MKL_INT m{};                     // will contain the number of eigenvalues
      78           6 :     std::vector<real_lim_t> e(m0);   // will contain the first m eigenvalues
      79           6 :     char uplo = 'F';                 // full matrix is stored
      80           6 :     MKL_INT info{};                  // will contain return codes
      81           6 :     real_lim_t epsout{};             // will contain relative error
      82           6 :     MKL_INT loop{};                  // will contain number of used refinement
      83           6 :     std::vector<real_lim_t> res(m0); // will contain the residual errors
      84           6 :     real_lim_t min_eigenvalue_lim = min_eigenvalue - shift;
      85           6 :     real_lim_t max_eigenvalue_lim = max_eigenvalue - shift;
      86             : 
      87           6 :     feast(&uplo, &dim, hamiltonian.data(), &dim, fpm.data(), &epsout, &loop, &min_eigenvalue_lim,
      88             :           &max_eigenvalue_lim, &m0, evals.data(), evecs.data(), &m, res.data(), &info);
      89             : 
      90             :     // https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2023-1/extended-eigensolver-output-details.html
      91           6 :     if (info != 0) {
      92           0 :         if (info == 202) {
      93           0 :             throw std::invalid_argument(
      94             :                 "Diagonalization error: Problem with size of the system n (n≤0).");
      95             :         }
      96           0 :         if (info == 201) {
      97           0 :             throw std::invalid_argument(
      98             :                 "Diagonalization error: Problem with size of initial subspace m0 (m0≤0 or m0>n).");
      99             :         }
     100           0 :         if (info == 200) {
     101           0 :             throw std::invalid_argument(
     102             :                 "Diagonalization error: Problem with emin,emax (emin≥emax).");
     103             :         }
     104           0 :         if (info == 3) {
     105           0 :             throw std::invalid_argument(
     106             :                 "Diagonalization error: Size of the subspace m0 is too small (m0<m).");
     107             :         }
     108           0 :         if (info == 2) {
     109           0 :             throw std::runtime_error(
     110             :                 "Diagonalization error: No convergence (number of iteration loops >fpm[3]).");
     111             :         }
     112           0 :         if (info == 1) {
     113           0 :             throw std::runtime_error(
     114             :                 "Diagonalization error: No eigenvalue found in the search interval.");
     115             :         }
     116           0 :         if (info == -1) {
     117           0 :             throw std::runtime_error(
     118             :                 "Diagonalization error: Internal error for allocation memory.");
     119             :         }
     120           0 :         if (info == -2) {
     121           0 :             throw std::runtime_error(
     122             :                 "Diagonalization error: Internal error of the inner system solver. Possible "
     123             :                 "reasons: not enough memory for inner linear system solver or inconsistent input.");
     124             :         }
     125           0 :         if (info == -3) {
     126           0 :             throw std::runtime_error(
     127             :                 "Diagonalization error: Internal error of the reduced eigenvalue solver. Possible "
     128             :                 "cause: matrix may not be positive definite.");
     129             :         }
     130           0 :         if (info == -4) {
     131           0 :             throw std::invalid_argument("Diagonalization error: Matrix is not positive definite.");
     132             :         }
     133           0 :         if (info <= 100) {
     134           0 :             throw std::invalid_argument(
     135             :                 fmt::format("Diagonalization error: Argument {} to the FEAST *interface* "
     136             :                             "had an illegal value (the counting of the arguments starts with one). "
     137             :                             "For a documentation of the feast interface, see "
     138             :                             "https://www.intel.com/content/www/us/en/docs/onemkl/"
     139             :                             "developer-reference-c/2025-1/feast-syev-feast-heev.html.",
     140           0 :                             -info - 100));
     141             :         }
     142           0 :         if (info >= 100) {
     143           0 :             throw std::invalid_argument(fmt::format(
     144             :                 "Diagonalization error: Argument {} to the FEAST "
     145             :                 "*initialization* had an illegal value (the counting of the arguments starts with "
     146             :                 "one). For a documentation of the feast initialization, see "
     147             :                 "https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2025-1/"
     148             :                 "extended-eigensolver-input-parameters.html.",
     149           0 :                 info - 100));
     150             :         }
     151           0 :         throw std::runtime_error(fmt::format(
     152             :             "Diagonalization error: The FEAST routine failed with error code {}.", info));
     153             :     }
     154             : 
     155             :     // Restrict to the first m eigenvectors and eigenvalues because the rest is not calculated
     156           6 :     evecs.conservativeResize(dim, m);
     157           6 :     evals.conservativeResize(m);
     158             : 
     159           6 :     return {evecs.sparseView(1, 0.5 * rtol / std::sqrt(dim)).template cast<Scalar>(),
     160           6 :             this->add_mean(evals, shift)};
     161          12 : }
     162             : 
     163             : template <typename Scalar>
     164           3 : DiagonalizerFeast<Scalar>::DiagonalizerFeast(int m0, FloatType float_type)
     165           3 :     : DiagonalizerInterface<Scalar>(float_type), m0(m0) {
     166           3 :     if (m0 <= 0) {
     167           0 :         throw std::invalid_argument("The size of the initial subspace m0 (i.e., the number of "
     168             :                                     "maximally obtainable eigenvalues) must be positive.");
     169             :     }
     170           3 : }
     171             : 
     172             : template <typename Scalar>
     173             : EigenSystemH<Scalar>
     174           0 : DiagonalizerFeast<Scalar>::eigh(const Eigen::SparseMatrix<Scalar, Eigen::RowMajor> & /*matrix*/,
     175             :                                 double /*rtol*/) const {
     176           0 :     throw std::invalid_argument("The FEAST routine requires a search interval.");
     177             : }
     178             : 
     179             : template <typename Scalar>
     180             : EigenSystemH<Scalar>
     181           6 : DiagonalizerFeast<Scalar>::eigh(const Eigen::SparseMatrix<Scalar, Eigen::RowMajor> &matrix,
     182             :                                 std::optional<real_t> min_eigenvalue,
     183             :                                 std::optional<real_t> max_eigenvalue, double rtol) const {
     184           6 :     if (!min_eigenvalue.has_value() || !max_eigenvalue.has_value()) {
     185           0 :         throw std::invalid_argument("The FEAST routine requires a search interval.");
     186             :     }
     187           6 :     switch (this->float_type) {
     188           2 :     case FloatType::FLOAT32:
     189             :         return dispatch_eigh<traits::restricted_t<Scalar, FloatType::FLOAT32>>(
     190           2 :             matrix, min_eigenvalue.value(), max_eigenvalue.value(), rtol);
     191           4 :     case FloatType::FLOAT64:
     192             :         return dispatch_eigh<traits::restricted_t<Scalar, FloatType::FLOAT64>>(
     193           4 :             matrix, min_eigenvalue.value(), max_eigenvalue.value(), rtol);
     194           0 :     default:
     195           0 :         throw std::invalid_argument("Unsupported floating point precision.");
     196             :     }
     197             : }
     198             : 
     199             : #else
     200             : 
     201             : template <typename Scalar>
     202             : DiagonalizerFeast<Scalar>::DiagonalizerFeast(int m0, FloatType float_type)
     203             :     : DiagonalizerInterface<Scalar>(float_type), m0(m0) {
     204             :     throw std::runtime_error(
     205             :         "The FEAST routine is not available in this build. Please use a different diagonalizer.");
     206             : }
     207             : 
     208             : template <typename Scalar>
     209             : EigenSystemH<Scalar>
     210             : DiagonalizerFeast<Scalar>::eigh(const Eigen::SparseMatrix<Scalar, Eigen::RowMajor> & /*matrix*/,
     211             :                                 double /*rtol*/) const {
     212             :     std::abort(); // can't happen because the constructor throws
     213             : }
     214             : 
     215             : template <typename Scalar>
     216             : EigenSystemH<Scalar>
     217             : DiagonalizerFeast<Scalar>::eigh(const Eigen::SparseMatrix<Scalar, Eigen::RowMajor> & /*matrix*/,
     218             :                                 std::optional<real_t> /*min_eigenvalue*/,
     219             :                                 std::optional<real_t> /*max_eigenvalue*/, double /*rtol*/) const {
     220             :     std::abort(); // can't happen because the constructor throws
     221             : }
     222             : 
     223             : #endif // WITH_MKL
     224             : 
     225             : // Explicit instantiations
     226             : template class DiagonalizerFeast<double>;
     227             : template class DiagonalizerFeast<std::complex<double>>;
     228             : } // namespace pairinteraction

Generated by: LCOV version 1.16