diff --git a/t.cc b/t.cc index 7bac655..27e2ed3 100644 --- a/t.cc +++ b/t.cc @@ -2295,7 +2295,7 @@ cout <<"non-zero = "<< ((n&1)?p.odd_powers():p.even_powers())< v({1,2,3,4}); cout < pp({1,2,3,4,5}); cout< tmp(3,3); +tmp.randomize(2.); +Mat3 mm(tmp); +mm.symmetrize(); +NRMat m(&mm[0][0],3,3); +cout < w(3); +diagonalize(m,w); +cout << w< ww; +Mat3 vv; +mm.eivec_sym(ww,vv); +cout <<"3\n"< www(&ww[0],3); +NRMat vvv(&vv[0][0],3,3); +cout<<"eival error = "<<(w-www).norm()< A(*this); //scratch copy + + wmax = fabs(w[0]); + if ((t=fabs(w[1])) > wmax) + wmax = t; + if ((t=fabs(w[2])) > wmax) + wmax = t; + thresh = SQR(8.0 * DBL_EPSILON * wmax); + + // Prepare calculation of eigenvectors + n0tmp = SQR(A[0][1]) + SQR(A[0][2]); + n1tmp = SQR(A[0][1]) + SQR(A[1][2]); + v[0][1] = A[0][1]*A[1][2] - A[0][2]*A[1][1]; + v[1][1] = A[0][2]*A[0][1] - A[1][2]*A[0][0]; + v[2][1] = SQR(A[0][1]); + + // Calculate first eigenvector by the formula + // v[0] = (A - w[0]).e1 x (A - w[0]).e2 + A[0][0] -= w[0]; + A[1][1] -= w[0]; + v[0][0] = v[0][1] + A[0][2]*w[0]; + v[1][0] = v[1][1] + A[1][2]*w[0]; + v[2][0] = A[0][0]*A[1][1] - v[2][1]; + norm = SQR(v[0][0]) + SQR(v[1][0]) + SQR(v[2][0]); + n0 = n0tmp + SQR(A[0][0]); + n1 = n1tmp + SQR(A[1][1]); + error = n0 * n1; + + if (n0 <= thresh) // If the first column is zero, then (1,0,0) is an eigenvector + { + v[0][0] = 1.0; + v[1][0] = 0.0; + v[2][0] = 0.0; + } + else if (n1 <= thresh) // If the second column is zero, then (0,1,0) is an eigenvector + { + v[0][0] = 0.0; + v[1][0] = 1.0; + v[2][0] = 0.0; + } + else if (norm < SQR(64.0 * DBL_EPSILON) * error) + { // If angle between A[0] and A[1] is too small, don't use + t = SQR(A[0][1]); // cross product, but calculate v ~ (1, -A0/A1, 0) + f = -A[0][0] / A[0][1]; + if (SQR(A[1][1]) > t) + { + t = SQR(A[1][1]); + f = -A[0][1] / A[1][1]; + } + if (SQR(A[1][2]) > t) + f = -A[0][2] / A[1][2]; + norm = 1.0/sqrt(1 + SQR(f)); + v[0][0] = norm; + v[1][0] = f * norm; + v[2][0] = 0.0; + } + else // This is the standard branch + { + norm = sqrt(1.0 / norm); + for (j=0; j < 3; j++) + v[j][0] = v[j][0] * norm; + } + + + // Prepare calculation of second eigenvector + t = w[0] - w[1]; + if (fabs(t) > 8.0 * DBL_EPSILON * wmax) + { + // For non-degenerate eigenvalue, calculate second eigenvector by the formula + // v[1] = (A - w[1]).e1 x (A - w[1]).e2 + A[0][0] += t; + A[1][1] += t; + v[0][1] = v[0][1] + A[0][2]*w[1]; + v[1][1] = v[1][1] + A[1][2]*w[1]; + v[2][1] = A[0][0]*A[1][1] - v[2][1]; + norm = SQR(v[0][1]) + SQR(v[1][1]) + SQR(v[2][1]); + n0 = n0tmp + SQR(A[0][0]); + n1 = n1tmp + SQR(A[1][1]); + error = n0 * n1; + + if (n0 <= thresh) // If the first column is zero, then (1,0,0) is an eigenvector + { + v[0][1] = 1.0; + v[1][1] = 0.0; + v[2][1] = 0.0; + } + else if (n1 <= thresh) // If the second column is zero, then (0,1,0) is an eigenvector + { + v[0][1] = 0.0; + v[1][1] = 1.0; + v[2][1] = 0.0; + } + else if (norm < SQR(64.0 * DBL_EPSILON) * error) + { // If angle between A[0] and A[1] is too small, don't use + t = SQR(A[0][1]); // cross product, but calculate v ~ (1, -A0/A1, 0) + f = -A[0][0] / A[0][1]; + if (SQR(A[1][1]) > t) + { + t = SQR(A[1][1]); + f = -A[0][1] / A[1][1]; + } + if (SQR(A[1][2]) > t) + f = -A[0][2] / A[1][2]; + norm = 1.0/sqrt(1 + SQR(f)); + v[0][1] = norm; + v[1][1] = f * norm; + v[2][1] = 0.0; + } + else + { + norm = sqrt(1.0 / norm); + for (j=0; j < 3; j++) + v[j][1] = v[j][1] * norm; + } + } + else + { + // For degenerate eigenvalue, calculate second eigenvector according to + // v[1] = v[0] x (A - w[1]).e[i] + // + // This would really get to complicated if we could not assume all of A to + // contain meaningful values. + A[1][0] = A[0][1]; + A[2][0] = A[0][2]; + A[2][1] = A[1][2]; + A[0][0] += w[0]; + A[1][1] += w[0]; + for (i=0; i < 3; i++) + { + A[i][i] -= w[1]; + n0 = SQR(A[0][i]) + SQR(A[1][i]) + SQR(A[2][i]); + if (n0 > thresh) + { + v[0][1] = v[1][0]*A[2][i] - v[2][0]*A[1][i]; + v[1][1] = v[2][0]*A[0][i] - v[0][0]*A[2][i]; + v[2][1] = v[0][0]*A[1][i] - v[1][0]*A[0][i]; + norm = SQR(v[0][1]) + SQR(v[1][1]) + SQR(v[2][1]); + if (norm > SQR(256.0 * DBL_EPSILON) * n0) // Accept cross product only if the angle between + { // the two vectors was not too small + norm = sqrt(1.0 / norm); + for (j=0; j < 3; j++) + v[j][1] = v[j][1] * norm; + break; + } + } + } + + if (i == 3) // This means that any vector orthogonal to v[0] is an EV. + { + for (j=0; j < 3; j++) + if (v[j][0] != 0.0) // Find nonzero element of v[0] ... + { // ... and swap it with the next one + norm = 1.0 / sqrt(SQR(v[j][0]) + SQR(v[(j+1)%3][0])); + v[j][1] = v[(j+1)%3][0] * norm; + v[(j+1)%3][1] = -v[j][0] * norm; + v[(j+2)%3][1] = 0.0; + break; + } + } + } + + + // Calculate third eigenvector according to + // v[2] = v[0] x v[1] + v[0][2] = v[1][0]*v[2][1] - v[2][0]*v[1][1]; + v[1][2] = v[2][0]*v[0][1] - v[0][0]*v[2][1]; + v[2][2] = v[0][0]*v[1][1] - v[1][0]*v[0][1]; + +} + +#undef SQR +//end eigensolver for 3x3 matrix +///////////////////////////////////////////////////////////////////////////////////////////// //force instantization #define INSTANTIZE(T) \ diff --git a/vecmat3.h b/vecmat3.h index b18ebc5..9d7f7e1 100644 --- a/vecmat3.h +++ b/vecmat3.h @@ -145,7 +145,9 @@ public: //C-style IO int fprintf(FILE *f, const char *format) const {int n= ::fprintf(f,format,q[0][0],q[0][1],q[0][2]); n+=::fprintf(f,format,q[1][0],q[1][1],q[1][2]); n+=::fprintf(f,format,q[2][0],q[2][1],q[2][2]); return n;}; int fscanf(FILE *f, const char *format) const {return ::fscanf(f,format,q[0][0],q[0][1],q[0][2]) + ::fscanf(f,format,q[1][0],q[1][1],q[1][2]) + ::fscanf(f,format,q[2][0],q[2][1],q[2][2]);}; - + void symmetrize(); //average offdiagonal elements + void eival_sym(Vec3 &w) const; //only for real symmetric matrix, symmetry is not checked + void eivec_sym(Vec3 &w, Mat3 &v) const; //only for real symmetric matrix, symmetry is not checked };