// ---------------------------------------------------------------------------- // 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 #include "zheevc3.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 zheevh3(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 Cardano's method for the eigenvalues and an analytical // method based on vector cross products for the eigenvectors. However, // if conditions are such that a large error in the results is to be // expected, the routine falls back to using the slower, but more // accurate QL algorithm. Only the diagonal and upper triangular parts of A need // to contain meaningful values. Access to A 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 // ---------------------------------------------------------------------------- // Dependencies: // zheevc3(), zhetrd3(), zheevq3() // ---------------------------------------------------------------------------- // Version history: // v1.1: Simplified fallback condition --> speed-up // v1.0: First released version // ---------------------------------------------------------------------------- { #ifndef EVALS_ONLY double norm; // Squared norm or inverse norm of current eigenvector // double n0, n1; // Norm of first and second columns of A double error; // Estimated maximum roundoff error double t, u; // Intermediate storage int j; // Loop counter #endif // Calculate eigenvalues zheevc3(A, w); #ifndef EVALS_ONLY // n0 = SQR(real(A[0][0])) + SQR_ABS(A[0][1]) + SQR_ABS(A[0][2]); // n1 = SQR_ABS(A[0][1]) + SQR(real(A[1][1])) + SQR_ABS(A[1][2]); t = fabs(w[0]); if ((u=fabs(w[1])) > t) t = u; if ((u=fabs(w[2])) > t) t = u; if (t < 1.0) u = t; else u = SQR(t); error = 256.0 * DBL_EPSILON * SQR(u); // error = 256.0 * DBL_EPSILON * (n0 + u) * (n1 + u); Q[0][1] = A[0][1]*A[1][2] - A[0][2]*real(A[1][1]); Q[1][1] = A[0][2]*conj(A[0][1]) - A[1][2]*real(A[0][0]); Q[2][1] = SQR_ABS(A[0][1]); // Calculate first eigenvector by the formula // v[0] = conj( (A - w[0]).e1 x (A - w[0]).e2 ) Q[0][0] = Q[0][1] + A[0][2]*w[0]; Q[1][0] = Q[1][1] + A[1][2]*w[0]; Q[2][0] = (real(A[0][0]) - w[0]) * (real(A[1][1]) - w[0]) - Q[2][1]; norm = SQR_ABS(Q[0][0]) + SQR_ABS(Q[1][0]) + SQR(real(Q[2][0])); // If vectors are nearly linearly dependent, or if there might have // been large cancellations in the calculation of A(I,I) - W(1), fall // back to QL algorithm // Note that this simultaneously ensures that multiple eigenvalues do // not cause problems: If W(1) = W(2), then A - W(1) * I has rank 1, // i.e. all columns of A - W(1) * I are linearly dependent. if (norm <= error) return zheevq3(A, Q, w); else // This is the standard branch { norm = sqrt(1.0 / norm); for (j=0; j < 3; j++) Q[j][0] = Q[j][0] * norm; } // Calculate second eigenvector by the formula // v[1] = conj( (A - w[1]).e1 x (A - w[1]).e2 ) Q[0][1] = Q[0][1] + A[0][2]*w[1]; Q[1][1] = Q[1][1] + A[1][2]*w[1]; Q[2][1] = (real(A[0][0]) - w[1]) * (real(A[1][1]) - w[1]) - real(Q[2][1]); norm = SQR_ABS(Q[0][1]) + SQR_ABS(Q[1][1]) + SQR(real(Q[2][1])); if (norm <= error) return zheevq3(A, Q, w); else { norm = sqrt(1.0 / norm); for (j=0; j < 3; j++) Q[j][1] = Q[j][1] * norm; } // Calculate third eigenvector according to // v[2] = conj(v[0] x v[1]) Q[0][2] = conj(Q[1][0]*Q[2][1] - Q[2][0]*Q[1][1]); Q[1][2] = conj(Q[2][0]*Q[0][1] - Q[0][0]*Q[2][1]); Q[2][2] = conj(Q[0][0]*Q[1][1] - Q[1][0]*Q[0][1]); #endif return 0; }