Пример #1
0
/*
******** ell_cubic(): 
**
** finds real roots of x^3 + A*x^2 + B*x + C.
**
** records the found real roots in the given root array. 
**
** returns information about the roots according to ellCubicRoot enum,
** the set the following values in given root[] array:
**   ellCubicRootSingle: root[0], root[1] == root[2] == AIR_NAN
**   ellCubicRootTriple: root[0] == root[1] == root[2]
**   ellCubicRootSingleDouble: single root[0]; double root[1] == root[2]
**                          or double root[0] == root[1], single root[2]
**   ellCubicRootThree: root[0], root[1], root[2]
**
** The values stored in root[] are, in a change from the past, sorted
** in descending order!  No need to sort them any more!
**
** This does NOT use biff
*/
int
ell_cubic(double root[3], double A, double B, double C, int newton) {
  char me[]="ell_cubic";
  double epsilon = 1.0E-11, AA, Q, R, QQQ, D, sqrt_D, der,
    u, v, x, theta, t, sub;

  sub = A/3.0;
  AA = A*A;
  Q = (AA/3.0 - B)/3.0;
  R = (-2.0*A*AA/27.0 + A*B/3.0 - C)/2.0;
  QQQ = Q*Q*Q;
  D = R*R - QQQ;
  if (D < -epsilon) {
    /* three distinct roots- this is the most common case, it has 
       been tested the most, its code should go first */
    theta = acos(R/sqrt(QQQ))/3.0;
    t = 2*sqrt(Q);
    /* yes, these are sorted, because the C definition of acos says
       that it returns values in in [0, pi] */
    root[0] = t*cos(theta) - sub;
    root[1] = t*cos(theta - 2*AIR_PI/3.0) - sub;
    root[2] = t*cos(theta + 2*AIR_PI/3.0) - sub;
    /*
    if (!AIR_EXISTS(root[0])) {
      fprintf(stderr, "%s: %g %g %g --> nan!!!\n", me, A, B, C);
    }
    */
    return ell_cubic_root_three;
  }
  else if (D > epsilon) {
    double nr, fnr;
    /* one real solution, except maybe also a "rescued" double root */
    sqrt_D = sqrt(D);
    u = airCbrt(sqrt_D+R);
    v = -airCbrt(sqrt_D-R);
    x = u+v - sub;
    if (!newton) {
      root[0] = x;
      root[1] = root[2] = AIR_NAN;
      return ell_cubic_root_single;
    }

    /* else refine x, the known root, with newton-raphson, so as to get the 
       most accurate possible calculation for nr, the possible new root */
    x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der);
    x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der);
    x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der);
    x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der);
    x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der);
    x -= (der = (3*x + 2*A)*x + B, ((x/der + A/der)*x + B/der)*x + C/der);
    nr = -(A + x)/2.0;
    fnr = ((nr + A)*nr + B)*nr + C;  /* the polynomial evaluated at nr */
    /*
    if (ell_debug) {
      fprintf(stderr, "%s: root = %g -> %g, nr=% 20.15f\n"
              "   fnr=% 20.15f\n", me,
              x, (((x + A)*x + B)*x + C), nr, fnr);
    }
    */
    if (fnr < -epsilon || fnr > epsilon) {
      root[0] = x;
      root[1] = root[2] = AIR_NAN;
      return ell_cubic_root_single;
    }
    else {
      if (ell_debug) {
        fprintf(stderr, "%s: rescued double root:% 20.15f\n", me, nr);
      } 
      if (x > nr) {
        root[0] = x;
        root[1] = nr;
        root[2] = nr;
      } else {
        root[0] = nr;
        root[1] = nr;
        root[2] = x;
      }
      return ell_cubic_root_single_double;
    }
  } 
  else {
    /* else D is in the interval [-epsilon, +epsilon] */
    if (R < -epsilon || epsilon < R) {
      /* one double root and one single root */
      u = airCbrt(R);
      if (u > 0) {
        root[0] = 2*u - sub;
        root[1] = -u - sub;
        root[2] = -u - sub;
      } else {
        root[0] = -u - sub;
        root[1] = -u - sub;
        root[2] = 2*u - sub;
      }
      return ell_cubic_root_single_double;
    } 
    else {
      /* one triple root */
      root[0] = root[1] = root[2] = -sub;
      return ell_cubic_root_triple;
    }
  }
  /* shouldn't ever get here */
  /* return ell_cubic_root_unknown; */
}
float
dyeLcbrt(float t) {
  return AIR_CAST(float, (t > 0.008856 
                          ? airCbrt(t)
                          : 7.787*t + 16.0/116.0));
}
Пример #3
0
int
evals_evecs(double eval[3], double evec[9],
            const double _M00, const double _M01, const double _M02,
            const double _M11, const double _M12,
            const double _M22) {
  double r0[3], r1[3], r2[3], crs[3], len, dot;

  double mean, norm, rnorm, Q, R, QQQ, D, theta,
    M00, M01, M02, M11, M12, M22;
  double epsilon = 1.0E-12;
  int roots;

  /* copy the given matrix elements */
  M00 = _M00;
  M01 = _M01;
  M02 = _M02;
  M11 = _M11;
  M12 = _M12;
  M22 = _M22;

  /*
  ** subtract out the eigenvalue mean (will add back to evals later);
  ** helps with numerical stability
  */
  mean = (M00 + M11 + M22)/3.0;
  M00 -= mean;
  M11 -= mean;
  M22 -= mean;

  /*
  ** divide out L2 norm of eigenvalues (will multiply back later);
  ** this too seems to help with stability
  */
  norm = sqrt(M00*M00 + 2*M01*M01 + 2*M02*M02 +
              M11*M11 + 2*M12*M12 +
              M22*M22);
  rnorm = norm ? 1.0/norm : 1.0;
  M00 *= rnorm;
  M01 *= rnorm;
  M02 *= rnorm;
  M11 *= rnorm;
  M12 *= rnorm;
  M22 *= rnorm;

  /* this code is a mix of prior Teem code and ideas from Eberly's
     "Eigensystems for 3 x 3 Symmetric Matrices (Revisited)" */
  Q = (M01*M01 + M02*M02 + M12*M12 - M00*M11 - M00*M22 - M11*M22)/3.0;
  QQQ = Q*Q*Q;
  R = (M00*M11*M22 + M02*(2*M01*M12 - M02*M11)
       - M00*M12*M12 - M01*M01*M22)/2.0;
  D = QQQ - R*R;
  if (D > epsilon) {
    /* three distinct roots- this is the most common case */
    double mm, ss, cc;
    theta = atan2(sqrt(D), R)/3.0;
    mm = sqrt(Q);
    ss = sin(theta);
    cc = cos(theta);
    eval[0] = 2*mm*cc;
    eval[1] = mm*(-cc + sqrt(3.0)*ss);
    eval[2] = mm*(-cc - sqrt(3.0)*ss);
    roots = ROOT_THREE;
    /* else D is near enough to zero */
  } else if (R < -epsilon || epsilon < R) {
    double U;
    /* one double root and one single root */
    U = airCbrt(R); /* cube root function */
    if (U > 0) {
      eval[0] = 2*U;
      eval[1] = -U;
      eval[2] = -U;
    } else {
      eval[0] = -U;
      eval[1] = -U;
      eval[2] = 2*U;
    }
    roots = ROOT_SINGLE_DOUBLE;
  } else {
    /* a triple root! */
    eval[0] = eval[1] = eval[2] = 0.0;
    roots = ROOT_TRIPLE;
  }

  /* r0, r1, r2 are the vectors we manipulate to
     find the nullspaces of M - lambda*I */
  VEC_SET(r0, 0.0, M01, M02);
  VEC_SET(r1, M01, 0.0, M12);
  VEC_SET(r2, M02, M12, 0.0);
  if (ROOT_THREE == roots) {
    r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0];
    nullspace1(evec+0, r0, r1, r2);
    r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1];
    nullspace1(evec+3, r0, r1, r2);
    r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2];
    nullspace1(evec+6, r0, r1, r2);
  } else if (ROOT_SINGLE_DOUBLE == roots) {
    if (eval[1] == eval[2]) {
      /* one big (eval[0]) , two small (eval[1,2]) */
      r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0];
      nullspace1(evec+0, r0, r1, r2);
      r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1];
      nullspace2(evec+3, evec+6, r0, r1, r2);
    }
    else {
      /* two big (eval[0,1]), one small (eval[2]) */
      r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0];
      nullspace2(evec+0, evec+3, r0, r1, r2);
      r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2];
      nullspace1(evec+6, r0, r1, r2);
    }
  } else {
    /* ROOT_TRIPLE == roots; use any basis for eigenvectors */
    VEC_SET(evec+0, 1, 0, 0);
    VEC_SET(evec+3, 0, 1, 0);
    VEC_SET(evec+6, 0, 0, 1);
  }
  /* we always make sure its really orthonormal; keeping fixed the
     eigenvector associated with the largest-magnitude eigenvalue */
  if (ABS(eval[0]) > ABS(eval[2])) {
    /* normalize evec+0 but don't move it */
    VEC_NORM(evec+0, len);
    dot = VEC_DOT(evec+0, evec+3); VEC_SCL_SUB(evec+3, dot, evec+0);
    VEC_NORM(evec+3, len);
    dot = VEC_DOT(evec+0, evec+6); VEC_SCL_SUB(evec+6, dot, evec+0);
    dot = VEC_DOT(evec+3, evec+6); VEC_SCL_SUB(evec+6, dot, evec+3);
    VEC_NORM(evec+6, len);
  } else {
    /* normalize evec+6 but don't move it */
    VEC_NORM(evec+6, len);
    dot = VEC_DOT(evec+6, evec+3); VEC_SCL_SUB(evec+3, dot, evec+6);
    VEC_NORM(evec+3, len);
    dot = VEC_DOT(evec+3, evec+0); VEC_SCL_SUB(evec+0, dot, evec+3);
    dot = VEC_DOT(evec+6, evec+0); VEC_SCL_SUB(evec+0, dot, evec+6);
    VEC_NORM(evec+0, len);
  }

  /* to be nice, make it right-handed */
  VEC_CROSS(crs, evec+0, evec+3);
  if (0 > VEC_DOT(crs, evec+6)) {
    VEC_SCL(evec+6, -1);
  }

  /* multiply back by eigenvalue L2 norm */
  eval[0] /= rnorm;
  eval[1] /= rnorm;
  eval[2] /= rnorm;

  /* add back in the eigenvalue mean */
  eval[0] += mean;
  eval[1] += mean;
  eval[2] += mean;

  return roots;
}
Пример #4
0
static double _nrrdUnaryOpCbrt(double a)       {return airCbrt(a);}