From 2743b0f334b32804ca588da1d3fca9d6e0d507e7 Mon Sep 17 00:00:00 2001 From: Christophe Geuzaine <cgeuzaine@ulg.ac.be> Date: Wed, 8 Dec 2004 03:08:12 +0000 Subject: [PATCH] complete JF's FindCubicRoots routine (complex roots) --- Numeric/Numeric.cpp | 188 +++++++++++++++++++++----------------------- Numeric/Numeric.h | 12 +-- 2 files changed, 97 insertions(+), 103 deletions(-) diff --git a/Numeric/Numeric.cpp b/Numeric/Numeric.cpp index 722fc463a6..be1b44a575 100644 --- a/Numeric/Numeric.cpp +++ b/Numeric/Numeric.cpp @@ -1,4 +1,4 @@ -// $Id: Numeric.cpp,v 1.18 2004-11-25 16:22:45 remacle Exp $ +// $Id: Numeric.cpp,v 1.19 2004-12-08 03:08:12 geuzaine Exp $ // // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle // @@ -152,6 +152,20 @@ double det3x3(double mat[3][3]) mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0])); } +double trace3x3(double mat[3][3]) +{ + return mat[0][0] + mat[1][1] + mat[2][2]; +} + +double trace2 (double mat[3][3]) +{ + double a00 = mat[0][0] * mat[0][0] + mat[1][0] * mat[0][1] + mat[2][0] * mat[0][2]; + double a11 = mat[1][0] * mat[0][1] + mat[1][1] * mat[1][1] + mat[1][2] * mat[2][1]; + double a22 = mat[2][0] * mat[0][2] + mat[2][1] * mat[1][2] + mat[2][2] * mat[2][2]; + + return a00 + a11 + a22; +} + int sys3x3(double mat[3][3], double b[3], double res[3], double *det) { double ud; @@ -283,112 +297,92 @@ void gradSimplex(double *x, double *y, double *z, double *v, double *grad) } void eigenvalue(double mat[3][3], double v[3]) - { - /// characteristic polynomial of T : find v root of - /// v^3 - I1 v^2 + I2 T + I3 = 0 - /// I1 : first invariant , trace(T) - /// I2 : second invariant , 1/2 (I1^2 -trace(T^2)) - /// I3 : third invariant , det T - - double In[4]; - In[3] = 1.0; - In[2] = - trace(mat); - In[1] = 0.5 * (In[2]*In[2] - trace2(mat)); - In[0] = - det(mat); - - // printf (" %lf x^3 + %lf x^2 + %lf x + %lf = 0\n", - // I[3],I[2],I[1],I[0]); - - FindCubicRoots(In,v); - - eigsort(v); - - } - -void FindCubicRoots(const double coeff[4], double x[3]) - { - double a1 = coeff[2] / coeff[3]; - double a2 = coeff[1] / coeff[3]; - double a3 = coeff[0] / coeff[3]; - - double Q = (a1 * a1 - 3 * a2) / 9.; - double R = (2. * a1 * a1 * a1 - 9. * a1 * a2 + 27. * a3) / 54.; - double Qcubed = Q * Q * Q; - double d = Qcubed - R * R; - - // printf ("d = %22.15e Q = %12.5E R = %12.5E Qcubed %12.5E\n",d,Q,R,Qcubed); - - /// three roots, 2 equal - if(Qcubed == 0.0 || fabs ( Qcubed - R * R ) < 1.e-8 * (fabs ( Qcubed) + fabs( R * R)) ) - { - double theta; - if (Qcubed <= 0.0)theta = acos(1.0); - else if (R / sqrt(Qcubed) > 1.0)theta = acos(1.0); - else if (R / sqrt(Qcubed) < -1.0)theta = acos(-1.0); - else theta = acos(R / sqrt(Qcubed)); - double sqrtQ = sqrt(Q); - // printf("sqrtQ = %12.5E teta=%12.5E a1=%12.5E\n",sqrt(Q),theta,a1); - x[0] = -2 * sqrtQ * cos( theta / 3) - a1 / 3; - x[1] = -2 * sqrtQ * cos((theta + 2 * M_PI) / 3) - a1 / 3; - x[2] = -2 * sqrtQ * cos((theta + 4 * M_PI) / 3) - a1 / 3; - // return (3); - } - - /* Three real roots */ - if (d >= 0.0) { - double theta = acos(R / sqrt(Qcubed)); - double sqrtQ = sqrt(Q); - x[0] = -2 * sqrtQ * cos( theta / 3) - a1 / 3; - x[1] = -2 * sqrtQ * cos((theta + 2 * M_PI) / 3) - a1 / 3; - x[2] = -2 * sqrtQ * cos((theta + 4 * M_PI) / 3) - a1 / 3; - // return (3); - } - - /* One real root */ - else { - printf("IMPOSSIBLE !!!\n"); - - double e = pow(sqrt(-d) + fabs(R), 1. / 3.); - if (R > 0) - e = -e; - x[0] = (e + Q / e) - a1 / 3.; - // return (1); - } +{ + // characteristic polynomial of T : find v root of + // v^3 - I1 v^2 + I2 T - I3 = 0 + // I1 : first invariant , trace(T) + // I2 : second invariant , 1/2 (I1^2 -trace(T^2)) + // I3 : third invariant , det T + + double c[4]; + c[3] = 1.0; + c[2] = - trace3x3(mat); + c[1] = 0.5 * (c[2]*c[2] - trace2(mat)); + c[0] = - det3x3(mat); + + //printf("%g %g %g\n", mat[0][0], mat[0][1], mat[0][2]); + //printf("%g %g %g\n", mat[1][0], mat[1][1], mat[1][2]); + //printf("%g %g %g\n", mat[2][0], mat[2][1], mat[2][2]); + //printf("%g x^3 + %g x^2 + %g x + %g = 0\n", c[3], c[2], c[1], c[0]); + + double imag[3]; + FindCubicRoots(c, v, imag); + eigsort(v); +} +void FindCubicRoots(const double coef[4], double real[3], double imag[3]) +{ + double a = coef[3]; + double b = coef[2]; + double c = coef[1]; + double d = coef[0]; + + if(!a || !d){ + Msg(GERROR, "Degenerate cubic: use a second degree solver!"); + return; } -double trace(double mat[3][3]) - { - return mat[0][0] + mat[1][1] + mat[2][2]; + b /= a; + c /= a; + d /= a; + + double q = (3.0*c - (b*b))/9.0; + double r = -(27.0*d) + b*(9.0*c - 2.0*(b*b)); + r /= 54.0; + + double discrim = q*q*q + r*r; + imag[0] = 0.0; // The first root is always real. + double term1 = (b/3.0); + + if (discrim > 0) { // one root is real, two are complex + double s = r + sqrt(discrim); + s = ((s < 0) ? -pow(-s, (1.0/3.0)) : pow(s, (1.0/3.0))); + double t = r - sqrt(discrim); + t = ((t < 0) ? -pow(-t, (1.0/3.0)) : pow(t, (1.0/3.0))); + real[0] = -term1 + s + t; + term1 += (s + t)/2.0; + real[1] = real[2] = -term1; + term1 = sqrt(3.0)*(-t + s)/2; + imag[1] = term1; + imag[2] = -term1; + return; } - -double det(double mat[3][3]) - { - return mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) - - mat[0][1] * (mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) + - mat[0][2] * (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]); + // The remaining options are all real + imag[1] = imag[2] = 0.0; + + double r13; + if (discrim == 0){ // All roots real, at least two are equal. + r13 = ((r < 0) ? -pow(-r,(1.0/3.0)) : pow(r,(1.0/3.0))); + real[0] = -term1 + 2.0*r13; + real[1] = real[2] = -(r13 + term1); + return; } -double trace2 (double mat[3][3]) - { - double a00 = mat[0][0] * mat[0][0] + - mat[1][0] * mat[0][1] + - mat[2][0] * mat[0][2]; - double a11 = mat[1][0] * mat[0][1] + - mat[1][1] * mat[1][1] + - mat[1][2] * mat[2][1]; - double a22 = mat[2][0] * mat[0][2] + - mat[2][1] * mat[1][2] + - mat[2][2] * mat[2][2]; - - return a00 + a11 + a22; - } + // Only option left is that all roots are real and unequal (to get + // here, q < 0) + q = -q; + double dum1 = q*q*q; + dum1 = acos(r/sqrt(dum1)); + r13 = 2.0*sqrt(q); + real[0] = -term1 + r13*cos(dum1/3.0); + real[1] = -term1 + r13*cos((dum1 + 2.0*M_PI)/3.0); + real[2] = -term1 + r13*cos((dum1 + 4.0*M_PI)/3.0); +} void eigsort(double d[3]) - { - int k,j,i; + int k, j, i; double p; for (i=0; i<3; i++) { diff --git a/Numeric/Numeric.h b/Numeric/Numeric.h index b3fcd9664b..b8ec30a44f 100644 --- a/Numeric/Numeric.h +++ b/Numeric/Numeric.h @@ -66,8 +66,14 @@ int sys2x2(double mat[2][2], double b[2], double res[2]); int sys3x3(double mat[3][3], double b[3], double res[3], double *det); int sys3x3_with_tol(double mat[3][3], double b[3], double res[3], double *det); double det3x3(double mat[3][3]); +double trace3x3(double mat[3][3]); +double trace2 (double mat[3][3]); int inv3x3(double mat[3][3], double inv[3][3], double *det); double angle_02pi(double A3); +void eigenvalue(double mat[3][3], double re[3]); +void FindCubicRoots(const double coeff[4], double re[3], double im[3]); +void eigsort(double d[3]); +int EigenSolve3x3(double A[3][3], double wr[3], double wi[3], double B[3][3]); double InterpolateIso(double *X, double *Y, double *Z, double *Val, double V, int I1, int I2, @@ -84,11 +90,5 @@ void mnbrak(double *ax, double *bx, double *cx, double *fa, double *fb, double *fc, double (*func)(double)); void newt(double x[], int n, int *check, void (*vecfunc)(int, double [], double [])); -void eigenvalue(double mat[3][3], double v[3]); -void FindCubicRoots(const double coeff[4], double x[3]); -double trace(double mat[3][3]); -double det(double mat[3][3]); -double trace2 (double mat[3][3]); -void eigsort(double d[3]); #endif -- GitLab