// ---------------------------------------------------------------------------- // Numerical diagonalization of 3x3 matrcies // Copyright (C) 2006 Joachim Kopp // ---------------------------------------------------------------------------- // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // ---------------------------------------------------------------------------- #include #include #include #include "zhetrd3.h" #include "zheevq3.h" // Macros #define SQR(x) ((x)*(x)) // x^2 #define SQR_ABS(x) (SQR(real(x)) + SQR(imag(x))) // |x|^2 // ---------------------------------------------------------------------------- int zheevq3(std::complex A[3][3], std::complex Q[3][3], double w[3]) // ---------------------------------------------------------------------------- // Calculates the eigenvalues and normalized eigenvectors of a hermitian 3x3 // matrix A using the QL algorithm with implicit shifts, preceded by a // Householder reduction to real tridiagonal form. // The function accesses only the diagonal and upper triangular parts of A. // The access is read-only. // ---------------------------------------------------------------------------- // Parameters: // A: The hermitian input matrix // Q: Storage buffer for eigenvectors // w: Storage buffer for eigenvalues // ---------------------------------------------------------------------------- // Return value: // 0: Success // -1: Error (no convergence) // ---------------------------------------------------------------------------- // Dependencies: // zhetrd3() // ---------------------------------------------------------------------------- { const int n = 3; double e[3]; // The third element is used only as temporary workspace double g, r, p, f, b, s, c; // Intermediate storage std::complex t; int nIter; int m; // Transform A to real tridiagonal form by the Householder method zhetrd3(A, Q, w, e); // Calculate eigensystem of the remaining real symmetric tridiagonal matrix // with the QL method // // Loop over all off-diagonal elements for (int l=0; l < n-1; l++) { nIter = 0; while (1) { // Check for convergence and exit iteration loop if off-diagonal // element e(l) is zero for (m=l; m <= n-2; m++) { g = fabs(w[m])+fabs(w[m+1]); if (fabs(e[m]) + g == g) break; } if (m == l) break; if (nIter++ >= 30) return -1; // Calculate g = d_m - k g = (w[l+1] - w[l]) / (e[l] + e[l]); r = sqrt(SQR(g) + 1.0); if (g > 0) g = w[m] - w[l] + e[l]/(g + r); else g = w[m] - w[l] + e[l]/(g - r); s = c = 1.0; p = 0.0; for (int i=m-1; i >= l; i--) { f = s * e[i]; b = c * e[i]; if (fabs(f) > fabs(g)) { c = g / f; r = sqrt(SQR(c) + 1.0); e[i+1] = f * r; c *= (s = 1.0/r); } else { s = f / g; r = sqrt(SQR(s) + 1.0); e[i+1] = g * r; s *= (c = 1.0/r); } g = w[i+1] - p; r = (w[i] - g)*s + 2.0*c*b; p = s * r; w[i+1] = g + p; g = c*r - b; // Form eigenvectors #ifndef EVALS_ONLY for (int k=0; k < n; k++) { t = Q[k][i+1]; Q[k][i+1] = s*Q[k][i] + c*t; Q[k][i] = c*Q[k][i] - s*t; } #endif } w[l] -= p; e[l] = g; e[m] = 0.0; } } return 0; }