Blame eigen/qrstep.c

Packit 67cb25
/* eigen/qrstep.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2007, 2010 Brian Gough
Packit 67cb25
 * 
Packit 67cb25
 * This program is free software; you can redistribute it and/or modify
Packit 67cb25
 * it under the terms of the GNU General Public License as published by
Packit 67cb25
 * the Free Software Foundation; either version 3 of the License, or (at
Packit 67cb25
 * your option) any later version.
Packit 67cb25
 * 
Packit 67cb25
 * This program is distributed in the hope that it will be useful, but
Packit 67cb25
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
 * General Public License for more details.
Packit 67cb25
 * 
Packit 67cb25
 * You should have received a copy of the GNU General Public License
Packit 67cb25
 * along with this program; if not, write to the Free Software
Packit 67cb25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
/* remove off-diagonal elements which are neglegible compared with the
Packit 67cb25
   neighboring diagonal elements */
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
chop_small_elements (const size_t N, const double d[], double sd[])
Packit 67cb25
{
Packit 67cb25
  double d_i = d[0];
Packit 67cb25
Packit 67cb25
  size_t i;
Packit 67cb25
Packit 67cb25
  for (i = 0; i < N - 1; i++)
Packit 67cb25
    {
Packit 67cb25
      double sd_i = sd[i];
Packit 67cb25
      double d_ip1 = d[i + 1];
Packit 67cb25
Packit 67cb25
      if (fabs (sd_i) < GSL_DBL_EPSILON * (fabs (d_i) + fabs (d_ip1)))
Packit 67cb25
        {
Packit 67cb25
          sd[i] = 0.0;
Packit 67cb25
        }
Packit 67cb25
      d_i = d_ip1;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* Generate a Givens rotation (cos,sin) which takes v=(x,y) to (|v|,0) 
Packit 67cb25
Packit 67cb25
   From Golub and Van Loan, "Matrix Computations", Section 5.1.8 */
Packit 67cb25
Packit 67cb25
inline static void
Packit 67cb25
create_givens (const double a, const double b, double *c, double *s)
Packit 67cb25
{
Packit 67cb25
  if (b == 0)
Packit 67cb25
    {
Packit 67cb25
      *c = 1;
Packit 67cb25
      *s = 0;
Packit 67cb25
    }
Packit 67cb25
  else if (fabs (b) > fabs (a))
Packit 67cb25
    {
Packit 67cb25
      double t = -a / b;
Packit 67cb25
      double s1 = 1.0 / sqrt (1 + t * t);
Packit 67cb25
      *s = s1;
Packit 67cb25
      *c = s1 * t;
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      double t = -b / a;
Packit 67cb25
      double c1 = 1.0 / sqrt (1 + t * t);
Packit 67cb25
      *c = c1;
Packit 67cb25
      *s = c1 * t;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
inline static double
Packit 67cb25
trailing_eigenvalue (const size_t n, const double d[], const double sd[])
Packit 67cb25
{
Packit 67cb25
  double ta = d[n - 2];
Packit 67cb25
  double tb = d[n - 1];
Packit 67cb25
  double tab = sd[n - 2];
Packit 67cb25
Packit 67cb25
  double dt = (ta - tb) / 2.0;
Packit 67cb25
Packit 67cb25
  double mu;
Packit 67cb25
Packit 67cb25
  if (dt > 0)
Packit 67cb25
    {
Packit 67cb25
      mu = tb - tab * (tab / (dt + hypot (dt, tab)));
Packit 67cb25
    }
Packit 67cb25
  else if (dt == 0) 
Packit 67cb25
    {
Packit 67cb25
      mu = tb - fabs(tab);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      mu = tb + tab * (tab / ((-dt) + hypot (dt, tab)));
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return mu;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
qrstep (const size_t n, double d[], double sd[], double gc[], double gs[])
Packit 67cb25
{
Packit 67cb25
  double x, z;
Packit 67cb25
  double ak, bk, zk, ap, bp, aq, bq;
Packit 67cb25
  size_t k;
Packit 67cb25
Packit 67cb25
  double mu = trailing_eigenvalue (n, d, sd);
Packit 67cb25
Packit 67cb25
  /* If mu is large relative to d_0 and sd_0 then the Givens rotation
Packit 67cb25
     will have no effect, leading to an infinite loop.  
Packit 67cb25
Packit 67cb25
     We set mu to zero in this case, which at least diagonalises the
Packit 67cb25
     submatrix [d_0, sd_0 ; sd_0, d_0] and allows further progress. */
Packit 67cb25
Packit 67cb25
  if (GSL_DBL_EPSILON * fabs(mu) > (fabs(d[0]) + fabs(sd[0]))) { 
Packit 67cb25
    mu = 0;
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  x = d[0] - mu;
Packit 67cb25
  z = sd[0];
Packit 67cb25
Packit 67cb25
  ak = 0;
Packit 67cb25
  bk = 0;
Packit 67cb25
  zk = 0;
Packit 67cb25
Packit 67cb25
  ap = d[0];
Packit 67cb25
  bp = sd[0];
Packit 67cb25
Packit 67cb25
  aq = d[1];
Packit 67cb25
Packit 67cb25
  if (n == 2)
Packit 67cb25
    {
Packit 67cb25
      double c, s;
Packit 67cb25
      create_givens (x, z, &c, &s);
Packit 67cb25
Packit 67cb25
      if (gc != NULL)
Packit 67cb25
        gc[0] = c; 
Packit 67cb25
      if (gs != NULL)
Packit 67cb25
        gs[0] = s;
Packit 67cb25
Packit 67cb25
      {
Packit 67cb25
        double ap1 = c * (c * ap - s * bp) + s * (s * aq - c * bp);
Packit 67cb25
        double bp1 = c * (s * ap + c * bp) - s * (s * bp + c * aq);
Packit 67cb25
Packit 67cb25
        double aq1 = s * (s * ap + c * bp) + c * (s * bp + c * aq);
Packit 67cb25
Packit 67cb25
        ak = ap1;
Packit 67cb25
        bk = bp1;
Packit 67cb25
Packit 67cb25
        ap = aq1;
Packit 67cb25
      }
Packit 67cb25
Packit 67cb25
      d[0] = ak;
Packit 67cb25
      sd[0] = bk;
Packit 67cb25
      d[1] = ap;
Packit 67cb25
Packit 67cb25
      return;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  bq = sd[1];
Packit 67cb25
Packit 67cb25
  for (k = 0; k < n - 1; k++)
Packit 67cb25
    {
Packit 67cb25
      double c, s;
Packit 67cb25
      create_givens (x, z, &c, &s);
Packit 67cb25
Packit 67cb25
      /* store Givens rotation */
Packit 67cb25
      if (gc != NULL)
Packit 67cb25
        gc[k] = c; 
Packit 67cb25
      if (gs != NULL)
Packit 67cb25
        gs[k] = s;
Packit 67cb25
Packit 67cb25
      /* compute G' T G */
Packit 67cb25
Packit 67cb25
      {
Packit 67cb25
        double bk1 = c * bk - s * zk;
Packit 67cb25
Packit 67cb25
        double ap1 = c * (c * ap - s * bp) + s * (s * aq - c * bp);
Packit 67cb25
        double bp1 = c * (s * ap + c * bp) - s * (s * bp + c * aq);
Packit 67cb25
        double zp1 = -s * bq;
Packit 67cb25
Packit 67cb25
        double aq1 = s * (s * ap + c * bp) + c * (s * bp + c * aq);
Packit 67cb25
        double bq1 = c * bq;
Packit 67cb25
Packit 67cb25
        ak = ap1;
Packit 67cb25
        bk = bp1;
Packit 67cb25
        zk = zp1;
Packit 67cb25
Packit 67cb25
        ap = aq1;
Packit 67cb25
        bp = bq1;
Packit 67cb25
Packit 67cb25
        if (k < n - 2)
Packit 67cb25
          aq = d[k + 2];
Packit 67cb25
        if (k < n - 3)
Packit 67cb25
          bq = sd[k + 2];
Packit 67cb25
Packit 67cb25
        d[k] = ak;
Packit 67cb25
Packit 67cb25
        if (k > 0)
Packit 67cb25
          sd[k - 1] = bk1;
Packit 67cb25
Packit 67cb25
        if (k < n - 2)
Packit 67cb25
          sd[k + 1] = bp;
Packit 67cb25
Packit 67cb25
        x = bk;
Packit 67cb25
        z = zk;
Packit 67cb25
      }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* k = n - 1 */
Packit 67cb25
  d[k] = ap;
Packit 67cb25
  sd[k - 1] = bk;
Packit 67cb25
}