00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00047
00048
00049
00050 #include <assert.h>
00051 #include <math.h>
00052 #include "EigValComp3x3.h"
00053 #include <iostream>
00054 #include <limits>
00055 using namespace std;
00056
00057 namespace {
00058 const double EPS = numeric_limits<double>::epsilon();
00059 const double PI = 3.1415926535897932384;
00060 const double THIRD = double(1)/3;
00061 const double TW7TH = double(1)/27;
00062
00063 inline void xprod(const double* const u, const double* const v, double* res)
00064 {
00065 res[0] = u[1] * v[2] - u[2] * v[1];
00066 res[1] = u[2] * v[0] - u[0] * v[2];
00067 res[2] = u[0] * v[1] - u[1] * v[0];
00068 }
00069
00070 inline double norm2(const double* v) {return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];}
00071
00072
00073 inline void normalize(double* v)
00074 {
00075 double invnorm = double(1) / sqrt(norm2(v));
00076 for (int i = 0; i < 3; ++i) {
00077 v[i] *= invnorm;
00078 }
00079 }
00080
00081 inline void find_non_collinear(const double* const v, double* res)
00082 {
00083
00084 int min_ix = fabs(v[0]) < fabs(v[1]) ? 0 : 1;
00085 min_ix = fabs(v[min_ix]) < fabs(v[2]) ? min_ix : 2;
00086
00087 for (int i = 0; i < 3; ++i) {
00088 res[i] = (i == min_ix) ? 1 : 0;
00089 }
00090
00091 }
00092
00093
00094 void find_kernel(const double* const col1,
00095 const double* const col2,
00096 const double* const col3,
00097 double* ker);
00098
00099 inline void givens(const double a, const double b, double& c, double& s)
00100 {
00101 if (b == 0) {
00102 c = 1;
00103 s = 0;
00104 } else {
00105 if (fabs(b) > fabs(a)) {
00106 const double tau = -a / b;
00107 s = 1 / sqrt(1 + tau * tau);
00108 c = tau * s;
00109 } else {
00110 const double tau = -b / a;
00111 c = 1 / sqrt(1 + tau * tau);
00112 s = tau * c;
00113 }
00114 }
00115 }
00116
00117 void apply_givens(const double c, const double s,
00118 double& alpha1, double& alpha2, double& alpha3,
00119 double& beta, double& delta, double& corner)
00120 {
00121 const double tmp1 = c*alpha1 - s*beta;
00122 const double tmp2 = c*beta - s*alpha2;
00123 const double tmp3 = s*alpha1 + c*beta;
00124 const double tmp4 = s*beta + c*alpha2;
00125 const double ctemp = corner;
00126
00127 corner = -s * delta + c * ctemp;
00128 delta = c * delta + s * ctemp;
00129
00130 alpha1 = c * tmp1 - s * tmp2;
00131 alpha2 = s * tmp3 + c * tmp4;
00132 beta = s * tmp1 + c * tmp2;
00133 }
00134
00135 void apply_givens(const double c, const double s,
00136 double& alpha1, double& alpha2, double& beta)
00137 {
00138 const double tmp1 = c*alpha1 - s*beta;
00139 const double tmp2 = c*beta - s*alpha2;
00140 const double tmp3 = s*alpha1 + c*beta;
00141 const double tmp4 = s*beta + c*alpha2;
00142
00143 alpha1 = c * tmp1 - s * tmp2;
00144 alpha2 = s * tmp3 + c * tmp4;
00145 beta = s * tmp1 + c * tmp2;
00146 }
00147
00148 void post_mul_givens(const double c, const double s, double* u, double* v)
00149 {
00150 const double u0 = u[0];
00151 const double u1 = u[1];
00152 const double u2 = u[2];
00153 const double v0 = v[0];
00154 const double v1 = v[1];
00155 const double v2 = v[2];
00156
00157 u[0] = c * u0 - s * v0;
00158 u[1] = c * u1 - s * v1;
00159 u[2] = c * u2 - s * v2;
00160 v[0] = s * u0 + c * v0;
00161 v[1] = s * u1 + c * v1;
00162 v[2] = s * u2 + c * v2;
00163 }
00164
00165 void construct_eigvecs(double alpha1, double alpha2, double alpha3,
00166 double beta, double gamma, double delta,
00167 double& lambda1,
00168 double& lambda2,
00169 double& lambda3,
00170 double* v1, double* v2, double* v3);
00171
00172 };
00173
00174 namespace lsseg {
00175
00176
00177 void analytic_eigvals(double alpha1, double alpha2, double alpha3,
00178 double beta, double gamma, double delta,
00179 double& lambda1, double& lambda2, double& lambda3)
00180
00181 {
00182
00183 const double bgd = beta * gamma * delta;
00184 const double beta2 = beta * beta;
00185 const double gamma2 = gamma * gamma;
00186 const double delta2 = delta * delta;
00187
00188 const double a_2 = -(alpha1 + alpha2 + alpha3);
00189 const double a_1 =
00190 -1*(beta2 + gamma2 + delta2 - alpha1*alpha2 - alpha2*alpha3 - alpha1*alpha3);
00191 const double a_0 =
00192 -1*(alpha1*alpha2*alpha3 - alpha1*delta2 - alpha2*gamma2 - alpha3*beta2 + 2*bgd);
00193
00194
00195
00196
00197
00198
00199 const double a_2_sq = a_2 * a_2;
00200 const double a_2_cub = a_2_sq * a_2;
00201
00202 const double p = a_1 - (a_2_sq) * THIRD;
00203
00204
00205 const double q = -a_0 + (9 * a_1 * a_2 - 2 * a_2_cub) * TW7TH;
00206
00207 const double fac = fabs(p) * THIRD;
00208
00209 double C = (fac > 0) ? 0.5 * q * pow(double(1)/fac, 1.5) : 1;
00210
00211
00212
00213
00214
00215
00216 const double acos_C = (fabs(C) <= 1) ? acos(C) : C > 0 ? 0 : PI;
00217 const double y_1 = cos (THIRD * acos_C);
00218 const double y_2 = cos (THIRD * (acos_C + 2 * PI));
00219 const double y_3 = cos (THIRD * (acos_C + 4 * PI));
00220
00221
00222
00223 const double tmp1 = 2 * sqrt(fac);
00224 const double tmp2 = THIRD * a_2;
00225
00226 lambda1 = tmp1 * y_1 - tmp2;
00227 lambda2 = tmp1 * y_2 - tmp2;
00228 lambda3 = tmp1 * y_3 - tmp2;
00229 }
00230
00231
00232 void analytic_eigsys(double alpha1, double alpha2, double alpha3,
00233 double beta, double gamma, double delta,
00234 double& lambda1, double& lambda2, double& lambda3,
00235 double* v1, double* v2, double* v3)
00236
00237 {
00238
00239 analytic_eigvals(alpha1, alpha2, alpha3, beta, gamma, delta, lambda1, lambda2, lambda3);
00240 construct_eigvecs(alpha1, alpha2, alpha3, beta, gamma, delta, lambda1, lambda2, lambda3,
00241 v1, v2, v3);
00242 }
00243
00244
00245 void numeric_eigsys(double alpha1, double alpha2, double alpha3,
00246 double beta, double gamma, double delta,
00247 double& lambda1, double& lambda2, double& lambda3,
00248 double* u, double* v, double* w)
00249
00250 {
00251 const bool compute_eigvecs = u && v && w;
00252
00253
00254 bool apply_tridiagonalization = fabs(gamma) > EPS * (fabs(alpha1) + fabs(alpha2) + fabs(alpha3)) * THIRD;
00255 double root_sigma, u_1, u_2, H_inv,p_1, p_2, p_3, K, q_1, q_2, q_3;
00256 if (apply_tridiagonalization) {
00257 root_sigma = sqrt(gamma*gamma + delta*delta);
00258 u_1 = gamma;
00259 u_2 = (delta > 0) ? delta + root_sigma : delta - root_sigma;
00260
00261 H_inv = double(2) / (u_1 * u_1 + u_2 * u_2);
00262 p_1 = H_inv * (alpha1 * u_1 + beta * u_2);
00263 p_2 = H_inv * (beta * u_1 + alpha2 * u_2);
00264 p_3 = H_inv * (gamma * u_1 + delta * u_2);
00265 K = 0.5 * H_inv * (u_1 * p_1 + u_2 * p_2);
00266 q_1 = p_1 - K * u_1;
00267 q_2 = p_2 - K * u_2;
00268 q_3 = p_3;
00269
00270
00271 alpha1 -= 2 * (q_1 * u_1);
00272 alpha2 -= 2 * (q_2 * u_2);
00273
00274 beta -= (q_1 * u_2 + u_1 * q_2);
00275 gamma -= q_3 * u_1;
00276 delta -= q_3 * u_2;
00277
00278
00279
00280 } else {
00281 gamma = 0;
00282 }
00283
00284 double corner = 0;
00285 bool beta_zero = fabs(beta) <= EPS * (fabs(alpha1) + fabs(alpha2));
00286 bool delta_zero = fabs(delta) <= EPS * fabs(alpha3);
00287 double d, sign_d, mu, c1, s1, c2, s2;
00288 if (compute_eigvecs) {
00289 u[0] = 1; u[1] = 0; u[2] = 0;
00290 v[0] = 0; v[1] = 1; v[2] = 0;
00291 w[0] = 0; w[1] = 0; w[2] = 1;
00292 }
00293
00294 while(true) {
00295 if (!beta_zero && !delta_zero) {
00296
00297 d = (alpha2 - alpha3) * 0.5;
00298 sign_d = d > 0 ? 1 : -1;
00299 mu = alpha3 - delta * delta / (d + sign_d * sqrt(d*d + delta*delta));
00300
00301 givens(alpha1-mu, beta, c1, s1);
00302 apply_givens(c1,s1,alpha1, alpha2, alpha3, beta, delta, corner);
00303 beta_zero = fabs(beta) <= EPS * (fabs(alpha1) + fabs(alpha2));
00304
00305 givens(beta, corner, c2, s2);
00306 apply_givens(c2, s2, alpha2, alpha3, alpha1, delta, corner, beta);
00307 delta_zero = fabs(delta) <= EPS * (fabs(alpha3));
00308
00309 if (compute_eigvecs) {
00310 post_mul_givens(c1, s1, u, v);
00311 post_mul_givens(c2, s2, v, w);
00312 }
00313
00314 } else if (!beta_zero) {
00315
00316 d = (alpha1 - alpha2) * 0.5;
00317 sign_d = d > 0 ? 1 : -1;
00318 mu = alpha2 - beta * beta / (d + sign_d * sqrt(d*d + beta*beta));
00319 givens(alpha1 - mu, beta, c1, s1);
00320 apply_givens(c1, s1, alpha1, alpha2, beta);
00321 beta_zero = fabs(beta) <= EPS * (fabs(alpha1) + fabs(alpha2));
00322 if (compute_eigvecs) {
00323 post_mul_givens(c1, s1, u, v);
00324 }
00325
00326 } else if (!delta_zero) {
00327
00328 d = (alpha2 - alpha3) * 0.5;
00329 sign_d = d > 0 ? 1 : -1;
00330 mu = alpha3 - delta * delta / (d + sign_d * sqrt(d*d + delta*delta));
00331
00332 givens(alpha2 - mu, delta, c2, s2);
00333 apply_givens(c2, s2, alpha2, alpha3, delta);
00334 delta_zero = fabs(delta) <= EPS * (fabs(alpha3));
00335 if (compute_eigvecs) {
00336 post_mul_givens(c2, s2, v, w);
00337 }
00338 } else {
00339 break;
00340 }
00341 }
00342 lambda1 = alpha1;
00343 lambda2 = alpha2;
00344 lambda3 = alpha3;
00345
00346
00347
00348 if (compute_eigvecs && apply_tridiagonalization) {
00349 const double fac_u = H_inv * (u[0] * u_1 + u[1] * u_2);
00350 u[0] -= fac_u * u_1;
00351 u[1] -= fac_u * u_2;
00352
00353 const double fac_v = H_inv * (v[0] * u_1 + v[1] * u_2);
00354 v[0] -= fac_v * u_1;
00355 v[1] -= fac_v * u_2;
00356
00357 const double fac_w = H_inv * (w[0] * u_1 + w[1] * u_2);
00358 w[0] -= fac_w * u_1;
00359 w[1] -= fac_w * u_2;
00360 }
00361 }
00362
00363 };
00364
00365 namespace {
00366
00367 double tempvec1[3];
00368 double tempvec2[3];
00369 double tempvec3[3];
00370
00371
00372 void find_kernel(const double* const col1,
00373 const double* const col2,
00374 const double* const col3,
00375 double* ker)
00376 {
00377 xprod(col1, col2, tempvec1);
00378 xprod(col2, col3, tempvec2);
00379 xprod(col1, col3, tempvec3);
00380
00381 double n1 = norm2(tempvec1);
00382 double n2 = norm2(tempvec2);
00383 double n3 = norm2(tempvec3);
00384
00385 if (n1 > n2) {
00386 if (n1 > n3) {
00387
00388 n1 = double(1) / sqrt(n1);
00389 for (int i = 0; i < 3; ker[i] = tempvec1[i++] * n1);
00390 } else {
00391
00392 n3 = double(1) / sqrt(n3);
00393 for (int i = 0; i < 3; ker[i] = tempvec3[i++] * n3);
00394 }
00395 } else {
00396 if (n2 > n3) {
00397
00398 n2 = double(1) / sqrt(n2);
00399 for (int i = 0; i < 3; ker[i] = tempvec2[i++] * n2);
00400 } else {
00401
00402 n3 = double(1) / sqrt(n3);
00403 for (int i = 0; i < 3; ker[i] = tempvec3[i++] * n3);
00404 }
00405 }
00406 }
00407
00408
00409 void construct_eigvecs(double alpha1, double alpha2, double alpha3,
00410 double beta, double gamma, double delta,
00411 double& lambda1,
00412 double& lambda2,
00413 double& lambda3,
00414 double* v1, double* v2, double* v3)
00415
00416 {
00417
00418 if (fabs(lambda3) > fabs(lambda2)) swap(lambda3, lambda2);
00419 if (fabs(lambda2) > fabs(lambda1)) {
00420 swap(lambda2, lambda1);
00421 if (fabs(lambda3) > fabs(lambda2)) swap(lambda3, lambda2);
00422 }
00423
00424
00425
00426 double col1[3], col2[3], col3[3];
00427 double tmp[3];
00428 const double numeric_threshold = EPS * (fabs(alpha1) + fabs(alpha2) + fabs(alpha3));
00429
00430
00431 if (fabs(lambda1 - lambda2) > numeric_threshold) {
00432
00433 col1[0] = alpha1-lambda1; col1[1] = beta; col1[2] = gamma;
00434 col2[0] = beta; col2[1] = alpha2-lambda1 ; col2[2] = delta;
00435 col3[0] = gamma; col3[1] = delta; col3[2] = alpha3-lambda1;
00436 find_kernel(col1, col2, col3, v1);
00437
00438 if (fabs(lambda2 - lambda3) > numeric_threshold) {
00439
00440
00441 col1[0] = alpha1-lambda2; col2[1] = alpha2-lambda2 ; col3[2] = alpha3-lambda2;
00442 find_kernel(col1, col2, col3, v2);
00443
00444 col1[0] = alpha1-lambda3; col2[1] = alpha2-lambda3 ; col3[2] = alpha3-lambda3;
00445 find_kernel(col1, col2, col3, v3);
00446
00447 } else {
00448
00449
00450 find_non_collinear(v1, tmp);
00451
00452
00453 xprod(v1, tmp, v2);
00454 normalize(v2);
00455 xprod(v1, v2, v3);
00456 normalize(v3);
00457 }
00458 } else if (fabs(lambda1 - lambda3) > numeric_threshold) {
00459
00460
00461 col1[0] = alpha1-lambda3; col1[1] = beta; col1[2] = gamma;
00462 col2[0] = beta; col2[1] = alpha2-lambda3 ; col2[2] = delta;
00463 col3[0] = gamma; col3[1] = delta; col3[2] = alpha3-lambda3;
00464 find_kernel(col1, col2, col3, v3);
00465
00466 find_non_collinear(v3, tmp);
00467
00468
00469 xprod(v3, tmp, v1);
00470 normalize(v1);
00471 xprod(v3, v1, v2);
00472 normalize(v2);
00473
00474 } else {
00475
00476 v1[0] = 1; v1[1] = 0; v1[2] = 0;
00477 v2[0] = 0; v2[1] = 1; v2[2] = 0;
00478 v3[0] = 0; v3[1] = 0; v3[2] = 1;
00479 }
00480 }
00481
00482 };