/******************************************************************************* * * TRIQS: a Toolbox for Research in Interacting Quantum Systems * * Copyright (C) 2011-2014 by O. Parcollet * * TRIQS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * TRIQS. If not, see . * ******************************************************************************/ #pragma once #include #include "../array.hpp" #include "../matrix.hpp" #include "../vector.hpp" #include namespace triqs { namespace arrays { namespace linalg { extern "C" { void TRIQS_FORTRAN_MANGLING(dsyev)(char *, char *, // JOBZ and UPLO int &, // Matrix Size double[], // matrix int &, // LDA of the matrix double[], // Eigenvalues array double[], // WORK int &, // LWORK int & // INFO ); void TRIQS_FORTRAN_MANGLING(zheev)(char *, char *, // JOBZ and UPLO int &, // Matrix Size std::complex [], // matrix int &, // LDA of the matrix double[], // Eigenvalues array std::complex [], // WORK int &, // LWORK double[], // WORK2 int & // INFO ); } /** * A worker to call lapack routine with the matrices. Handles both real and complex case. */ template class eigenelements_worker { public: eigenelements_worker() = default; /// The eigenvalues template array eigenvalues(M &mat) const { _prepare(mat); _invoke(is_complex(), 'N', mat); return ev; } /// The eigensystems template std::pair, matrix> eigenelements(M &mat) const { _prepare(mat); _invoke(is_complex(), 'V', mat); return {ev, _conj(mat, is_complex())}; } private: mutable array ev, work2; // work2 only used for T complex mutable array work; mutable int dim, lwork, info; // dispatch the implementation of invoke for T = double or complex void _invoke(std::false_type, char compz, matrix_view mat) const { // the case double (is_complex = false) char uplo = 'U'; TRIQS_FORTRAN_MANGLING(dsyev)(&compz, &uplo, dim, mat.data_start(), dim, ev.data_start(), work.data_start(), lwork, info); if (info) TRIQS_RUNTIME_ERROR << "eigenelements_worker :error code dsyev : " << info << " for matrix " << mat; } void _invoke(std::true_type, char compz, matrix_view> mat) const { // the case complex (is_complex = true) char uplo = 'U'; TRIQS_FORTRAN_MANGLING(zheev)(&compz, &uplo, dim, mat.data_start(), dim, ev.data_start(), work.data_start(), lwork, work2.data_start(), info); if (info) TRIQS_RUNTIME_ERROR << "eigenelements_worker :error code zheev : " << info << " for matrix " << mat; } template void _prepare(M const &mat) const { if (mat.is_empty()) TRIQS_RUNTIME_ERROR << "eigenelements_worker : the matrix is empty : matrix = " << mat << " "; if (!mat.is_square()) TRIQS_RUNTIME_ERROR << "eigenelements_worker : the matrix " << mat << " is not square "; if (!mat.indexmap().is_contiguous()) TRIQS_RUNTIME_ERROR << "eigenelements_worker : the matrix " << mat << " is not contiguous in memory"; dim = first_dim(mat); ev.resize(dim); lwork = 64 * dim; work.resize(lwork); if (is_complex::value) work2.resize(lwork); } template matrix _conj(M const &m, std::false_type) const { return m; } // impl : since we call fortran lapack, if the order is C (!), the matrix is transposed, or conjugated, so we obtain // the conjugate of the eigenvectors... Fix #119. // Do nothing if the order is fortran already... template matrix> _conj(M const &m, std::true_type) const { if (m.memory_layout_is_c()) return conj(m); else return m.transpose(); // the matrix mat is understood as a fortran matrix. After the lapack, in memory, it contains the // correct answer. // but since it is a fortran matrix, the C will see its transpose. We need to compensate this transpose (!). } }; //-------------------------------- /** * Simple diagonalization call, return all eigenelements. * Handles both real and complex case. * @param M : the matrix or view. MUST be contiguous. It is modified by the call. * If you wish not to modify it, call eigenelements(make_clone(A)) */ template std::pair, matrix::value_type>> eigenelements(M &&m) { return eigenelements_worker::value_type>().eigenelements(m); } //-------------------------------- /** * Simple diagonalization call, returning only the eigenvalues. * Handles both real and complex case. * @param M : the matrix VIEW : it MUST be contiguous * @param take_copy : makes a copy of the matrix before calling lapack, so that the original is preserved. * if false : no copy is made and the content of the matrix M is destroyed. * if true : a copy is made, M is preserved, but of course it is slower... */ template array eigenvalues(M &&m) { return eigenelements_worker::value_type>().eigenvalues(m); } } } } // namespace triqs::arrays::linalg