// Warning: This method only works for symmetric, Hermitian matrices // Find Eigenvectors and return three orthogonal vectors in columns of the matrix // Input is Vector with eigenvalues of this matrix (not needed for 3D matrices) // No error checking (that is symmetric and no error in algorithm) Matrix3 Matrix3::Eigenvectors(Vector &Eigenvals) const { Matrix3 Eigenvecs; if(is2D) { double cs,sn; dsyev2(m[0][0],m[0][1],m[1][1],&(Eigenvals.x),&(Eigenvals.y),&cs,&sn); Eigenvecs.set(cs,-sn,sn,cs,1.); } else { // set to 3D matrix double w[3]; double Q[3][3]; double A[3][3]; A[0][0] = m[0][0]; A[0][1] = m[0][1]; A[0][2] = m[0][2]; A[1][1] = m[1][1]; A[1][2] = m[1][2]; A[2][2] = m[2][2]; dsyevq3(A, Q, w); Eigenvecs.set(Q); Eigenvals.x = w[0]; Eigenvals.y = w[1]; Eigenvals.z = w[2]; } return Eigenvecs; }
// Compute eigenvalues and eigenvectors of a 3x3 matrix A. // At output, the eigenvalues are saved in lambdas in decending order. // The columns of A are replaced by the orthonormal eigenvectors. void FaceOffset_3:: compute_eigenvectors( Vector_3 A[3], Vector_3 &lambdas) { double abuf[3][3] = { {A[0][0], A[0][1], A[0][2]}, {A[1][0], A[1][1], A[1][2]}, {A[2][0], A[2][1], A[2][2]}}; double ebuf[3][3]; int info = dsyevq3( abuf, ebuf, &lambdas[0]); COM_assertion_msg( info==0, "Computation of eigenvectos failed"); std::swap( ebuf[0][1], ebuf[1][0]); std::swap( ebuf[0][2], ebuf[2][0]); std::swap( ebuf[1][2], ebuf[2][1]); const Vector_3 *buf = (const Vector_3*)&ebuf[0][0]; if ( lambdas[0]<lambdas[1]) { if ( lambdas[1]<lambdas[2]) { // l2>l1>l0 std::swap( lambdas[0], lambdas[2]); A[0] = buf[2]; A[1] = buf[1]; A[2] = buf[0]; } else { double t = lambdas[0]; lambdas[0] = lambdas[1]; A[0] = buf[1]; if ( t<lambdas[2]) { // l1>=l2>l0 lambdas[1] = lambdas[2]; lambdas[2] = t; A[1] = buf[2]; A[2] = buf[0]; } else { // l1>l0>=l2 lambdas[1] = t; A[1] = buf[0]; A[2] = buf[2]; } } } else if ( lambdas[0]<lambdas[2]) { // l2>l0>=l1 double t = lambdas[0]; lambdas[0] = lambdas[2]; lambdas[2] = lambdas[1]; lambdas[1] = t; A[0] = buf[2]; A[1] = buf[0]; A[2] = buf[1]; } else { A[0] = buf[0]; if ( lambdas[1]<lambdas[2]) { // l0>=l2>l1 std::swap( lambdas[1], lambdas[2]); A[1] = buf[2]; A[2] = buf[1]; } else {// l0>=l1>=l2 A[1] = buf[1]; A[2] = buf[2]; } } }
// ---------------------------------------------------------------------------- int dsyevh3(double A[3][3], double Q[3][3], double w[3]) // ---------------------------------------------------------------------------- // Calculates the eigenvalues and normalized eigenvectors of a symmetric 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 symmetric input matrix // Q: Storage buffer for eigenvectors // w: Storage buffer for eigenvalues // ---------------------------------------------------------------------------- // Return value: // 0: Success // -1: Error // ---------------------------------------------------------------------------- // Dependencies: // dsyevc3(), dsytrd3(), dsyevq3() // ---------------------------------------------------------------------------- // 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 dsyevc3(A, w); #ifndef EVALS_ONLY // n0 = SQR(A[0][0]) + SQR(A[0][1]) + SQR(A[0][2]); // n1 = SQR(A[0][1]) + SQR(A[1][1]) + SQR(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]*A[1][1]; Q[1][1] = A[0][2]*A[0][1] - A[1][2]*A[0][0]; Q[2][1] = SQR(A[0][1]); // Calculate first eigenvector by the formula // v[0] = (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] = (A[0][0] - w[0]) * (A[1][1] - w[0]) - Q[2][1]; norm = SQR(Q[0][0]) + SQR(Q[1][0]) + SQR(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[0], fall // back to QL algorithm // Note that this simultaneously ensures that multiple eigenvalues do // not cause problems: If w[0] = w[1], then A - w[0] * I has rank 1, // i.e. all columns of A - w[0] * I are linearly dependent. if (norm <= error) return dsyevq3(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] = (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] = (A[0][0] - w[1]) * (A[1][1] - w[1]) - Q[2][1]; norm = SQR(Q[0][1]) + SQR(Q[1][1]) + SQR(Q[2][1]); if (norm <= error) return dsyevq3(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] = v[0] x v[1] Q[0][2] = Q[1][0]*Q[2][1] - Q[2][0]*Q[1][1]; Q[1][2] = Q[2][0]*Q[0][1] - Q[0][0]*Q[2][1]; Q[2][2] = Q[0][0]*Q[1][1] - Q[1][0]*Q[0][1]; #endif return 0; }