Blame eigen/gen.c

Packit 67cb25
/* eigen/gen.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2006, 2007 Patrick Alken
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
#include <stdlib.h>
Packit 67cb25
#include <math.h>
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <gsl/gsl_eigen.h>
Packit 67cb25
#include <gsl/gsl_linalg.h>
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_vector_complex.h>
Packit 67cb25
#include <gsl/gsl_matrix.h>
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
 * This module computes the eigenvalues of a real generalized
Packit 67cb25
 * eigensystem A x = \lambda B x. Left and right Schur vectors
Packit 67cb25
 * are optionally computed as well.
Packit 67cb25
 * 
Packit 67cb25
 * Based on the algorithm from Moler and Stewart
Packit 67cb25
 * [1] C. Moler, G. Stewart, "An Algorithm for Generalized Matrix
Packit 67cb25
 *     Eigenvalue Problems", SIAM J. Numer. Anal., Vol 10, No 2, 1973.
Packit 67cb25
 *
Packit 67cb25
 * This algorithm is also described in the book
Packit 67cb25
 * [2] Golub & Van Loan, "Matrix Computations" (3rd ed), algorithm 7.7.3
Packit 67cb25
 *
Packit 67cb25
 * This file contains routines based on original code from LAPACK
Packit 67cb25
 * which is distributed under the modified BSD license.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#define GEN_ESHIFT_COEFF     (1.736)
Packit 67cb25
Packit 67cb25
static void gen_schur_decomp(gsl_matrix *H, gsl_matrix *R,
Packit 67cb25
                             gsl_vector_complex *alpha, gsl_vector *beta,
Packit 67cb25
                             gsl_eigen_gen_workspace *w);
Packit 67cb25
static inline int gen_qzstep(gsl_matrix *H, gsl_matrix *R,
Packit 67cb25
                             gsl_eigen_gen_workspace *w);
Packit 67cb25
static inline void gen_qzstep_d(gsl_matrix *H, gsl_matrix *R,
Packit 67cb25
                                gsl_eigen_gen_workspace *w);
Packit 67cb25
static void gen_tri_split_top(gsl_matrix *H, gsl_matrix *R,
Packit 67cb25
                              gsl_eigen_gen_workspace *w);
Packit 67cb25
static inline void gen_tri_chase_zero(gsl_matrix *H, gsl_matrix *R,
Packit 67cb25
                                      size_t q,
Packit 67cb25
                                      gsl_eigen_gen_workspace *w);
Packit 67cb25
static inline void gen_tri_zero_H(gsl_matrix *H, gsl_matrix *R,
Packit 67cb25
                                  gsl_eigen_gen_workspace *w);
Packit 67cb25
static inline size_t gen_search_small_elements(gsl_matrix *H,
Packit 67cb25
                                               gsl_matrix *R,
Packit 67cb25
                                               int *flag,
Packit 67cb25
                                               gsl_eigen_gen_workspace *w);
Packit 67cb25
static int gen_schur_standardize1(gsl_matrix *A, gsl_matrix *B,
Packit 67cb25
                                  double *alphar, double *beta,
Packit 67cb25
                                  gsl_eigen_gen_workspace *w);
Packit 67cb25
static int gen_schur_standardize2(gsl_matrix *A, gsl_matrix *B,
Packit 67cb25
                                  gsl_complex *alpha1,
Packit 67cb25
                                  gsl_complex *alpha2,
Packit 67cb25
                                  double *beta1, double *beta2,
Packit 67cb25
                                  gsl_eigen_gen_workspace *w);
Packit 67cb25
static int gen_compute_eigenvals(gsl_matrix *A, gsl_matrix *B,
Packit 67cb25
                                 gsl_complex *alpha1,
Packit 67cb25
                                 gsl_complex *alpha2, double *beta1,
Packit 67cb25
                                 double *beta2);
Packit 67cb25
static void gen_store_eigval1(const gsl_matrix *H, const double a,
Packit 67cb25
                              const double b, gsl_vector_complex *alpha,
Packit 67cb25
                              gsl_vector *beta,
Packit 67cb25
                              gsl_eigen_gen_workspace *w);
Packit 67cb25
static void gen_store_eigval2(const gsl_matrix *H,
Packit 67cb25
                              const gsl_complex *alpha1,
Packit 67cb25
                              const double beta1,
Packit 67cb25
                              const gsl_complex *alpha2,
Packit 67cb25
                              const double beta2,
Packit 67cb25
                              gsl_vector_complex *alpha,
Packit 67cb25
                              gsl_vector *beta,
Packit 67cb25
                              gsl_eigen_gen_workspace *w);
Packit 67cb25
static inline size_t gen_get_submatrix(const gsl_matrix *A,
Packit 67cb25
                                       const gsl_matrix *B);
Packit 67cb25
Packit 67cb25
/*FIX**/
Packit 67cb25
inline static double normF (gsl_matrix * A);
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_eigen_gen_alloc()
Packit 67cb25
Packit 67cb25
Allocate a workspace for solving the generalized eigenvalue problem.
Packit 67cb25
The size of this workspace is O(n)
Packit 67cb25
Packit 67cb25
Inputs: n - size of matrices
Packit 67cb25
Packit 67cb25
Return: pointer to workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
gsl_eigen_gen_workspace *
Packit 67cb25
gsl_eigen_gen_alloc(const size_t n)
Packit 67cb25
{
Packit 67cb25
  gsl_eigen_gen_workspace *w;
Packit 67cb25
Packit 67cb25
  if (n == 0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_NULL ("matrix dimension must be positive integer",
Packit 67cb25
                      GSL_EINVAL);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w = (gsl_eigen_gen_workspace *) calloc (1, sizeof (gsl_eigen_gen_workspace));
Packit 67cb25
Packit 67cb25
  if (w == 0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  w->size = n;
Packit 67cb25
  w->max_iterations = 30 * n;
Packit 67cb25
  w->n_evals = 0;
Packit 67cb25
  w->n_iter = 0;
Packit 67cb25
  w->needtop = 0;
Packit 67cb25
  w->atol = 0.0;
Packit 67cb25
  w->btol = 0.0;
Packit 67cb25
  w->ascale = 0.0;
Packit 67cb25
  w->bscale = 0.0;
Packit 67cb25
  w->eshift = 0.0;
Packit 67cb25
  w->H = NULL;
Packit 67cb25
  w->R = NULL;
Packit 67cb25
  w->compute_s = 0;
Packit 67cb25
  w->compute_t = 0;
Packit 67cb25
  w->Q = NULL;
Packit 67cb25
  w->Z = NULL;
Packit 67cb25
Packit 67cb25
  w->work = gsl_vector_alloc(n);
Packit 67cb25
Packit 67cb25
  if (w->work == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_eigen_gen_free(w);
Packit 67cb25
      GSL_ERROR_NULL ("failed to allocate space for additional workspace", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return (w);
Packit 67cb25
} /* gsl_eigen_gen_alloc() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_eigen_gen_free()
Packit 67cb25
  Free workspace w
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
gsl_eigen_gen_free (gsl_eigen_gen_workspace * w)
Packit 67cb25
{
Packit 67cb25
  RETURN_IF_NULL (w);
Packit 67cb25
Packit 67cb25
  if (w->work)
Packit 67cb25
    gsl_vector_free(w->work);
Packit 67cb25
Packit 67cb25
  free(w);
Packit 67cb25
} /* gsl_eigen_gen_free() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_eigen_gen_params()
Packit 67cb25
  Set parameters which define how we solve the eigenvalue problem
Packit 67cb25
Packit 67cb25
Inputs: compute_s - 1 if we want to compute S, 0 if not
Packit 67cb25
        compute_t - 1 if we want to compute T, 0 if not
Packit 67cb25
        balance   - 1 if we want to balance matrices, 0 if not
Packit 67cb25
        w         - gen workspace
Packit 67cb25
Packit 67cb25
Return: none
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
gsl_eigen_gen_params (const int compute_s, const int compute_t,
Packit 67cb25
                      const int balance, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  w->compute_s = compute_s;
Packit 67cb25
  w->compute_t = compute_t;
Packit 67cb25
} /* gsl_eigen_gen_params() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_eigen_gen()
Packit 67cb25
Packit 67cb25
Solve the generalized eigenvalue problem
Packit 67cb25
Packit 67cb25
A x = \lambda B x
Packit 67cb25
Packit 67cb25
for the eigenvalues \lambda.
Packit 67cb25
Packit 67cb25
Inputs: A     - general real matrix
Packit 67cb25
        B     - general real matrix
Packit 67cb25
        alpha - where to store eigenvalue numerators
Packit 67cb25
        beta  - where to store eigenvalue denominators
Packit 67cb25
        w     - workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_eigen_gen (gsl_matrix * A, gsl_matrix * B, gsl_vector_complex * alpha,
Packit 67cb25
               gsl_vector * beta, gsl_eigen_gen_workspace * w)
Packit 67cb25
{
Packit 67cb25
  const size_t N = A->size1;
Packit 67cb25
Packit 67cb25
  /* check matrix and vector sizes */
Packit 67cb25
Packit 67cb25
  if (N != A->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix must be square to compute eigenvalues", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if ((N != B->size1) || (N != B->size2))
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("B matrix dimensions must match A", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (alpha->size != N || beta->size != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (w->size != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size does not match workspace", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      double anorm, bnorm;
Packit 67cb25
Packit 67cb25
      /* compute the Hessenberg-Triangular reduction of (A, B) */
Packit 67cb25
      gsl_linalg_hesstri_decomp(A, B, w->Q, w->Z, w->work);
Packit 67cb25
Packit 67cb25
      /* save pointers to original matrices */
Packit 67cb25
      w->H = A;
Packit 67cb25
      w->R = B;
Packit 67cb25
Packit 67cb25
      w->n_evals = 0;
Packit 67cb25
      w->n_iter = 0;
Packit 67cb25
      w->eshift = 0.0;
Packit 67cb25
Packit 67cb25
      /* determine if we need to compute top indices in QZ step */
Packit 67cb25
      w->needtop = w->Q != 0 || w->Z != 0 || w->compute_t || w->compute_s;
Packit 67cb25
Packit 67cb25
      /* compute matrix norms */
Packit 67cb25
      anorm = normF(A);
Packit 67cb25
      bnorm = normF(B);
Packit 67cb25
Packit 67cb25
      /* compute tolerances and scaling factors */
Packit 67cb25
      w->atol = GSL_MAX(GSL_DBL_MIN, GSL_DBL_EPSILON * anorm);
Packit 67cb25
      w->btol = GSL_MAX(GSL_DBL_MIN, GSL_DBL_EPSILON * bnorm);
Packit 67cb25
      w->ascale = 1.0 / GSL_MAX(GSL_DBL_MIN, anorm);
Packit 67cb25
      w->bscale = 1.0 / GSL_MAX(GSL_DBL_MIN, bnorm);
Packit 67cb25
Packit 67cb25
      /* compute the generalized Schur decomposition and eigenvalues */
Packit 67cb25
      gen_schur_decomp(A, B, alpha, beta, w);
Packit 67cb25
Packit 67cb25
      if (w->n_evals != N)
Packit 67cb25
        return GSL_EMAXITER;
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_eigen_gen() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_eigen_gen_QZ()
Packit 67cb25
Packit 67cb25
Solve the generalized eigenvalue problem
Packit 67cb25
Packit 67cb25
A x = \lambda B x
Packit 67cb25
Packit 67cb25
for the eigenvalues \lambda. Optionally compute left and/or right
Packit 67cb25
Schur vectors Q and Z which satisfy:
Packit 67cb25
Packit 67cb25
A = Q S Z^t
Packit 67cb25
B = Q T Z^t
Packit 67cb25
Packit 67cb25
where (S, T) is the generalized Schur form of (A, B)
Packit 67cb25
Packit 67cb25
Inputs: A     - general real matrix
Packit 67cb25
        B     - general real matrix
Packit 67cb25
        alpha - where to store eigenvalue numerators
Packit 67cb25
        beta  - where to store eigenvalue denominators
Packit 67cb25
        Q     - if non-null, where to store left Schur vectors
Packit 67cb25
        Z     - if non-null, where to store right Schur vectors
Packit 67cb25
        w     - workspace
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_eigen_gen_QZ (gsl_matrix * A, gsl_matrix * B,
Packit 67cb25
                  gsl_vector_complex * alpha, gsl_vector * beta,
Packit 67cb25
                  gsl_matrix * Q, gsl_matrix * Z,
Packit 67cb25
                  gsl_eigen_gen_workspace * w)
Packit 67cb25
{
Packit 67cb25
  if (Q && (A->size1 != Q->size1 || A->size1 != Q->size2))
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("Q matrix has wrong dimensions", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (Z && (A->size1 != Z->size1 || A->size1 != Z->size2))
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("Z matrix has wrong dimensions", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int s;
Packit 67cb25
Packit 67cb25
      w->Q = Q;
Packit 67cb25
      w->Z = Z;
Packit 67cb25
Packit 67cb25
      s = gsl_eigen_gen(A, B, alpha, beta, w);
Packit 67cb25
Packit 67cb25
      w->Q = NULL;
Packit 67cb25
      w->Z = NULL;
Packit 67cb25
Packit 67cb25
      return s;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_eigen_gen_QZ() */
Packit 67cb25
Packit 67cb25
/********************************************
Packit 67cb25
 *           INTERNAL ROUTINES              *
Packit 67cb25
 ********************************************/
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_schur_decomp()
Packit 67cb25
  Compute the generalized Schur decomposition of the matrix pencil
Packit 67cb25
(H, R) which is in Hessenberg-Triangular form
Packit 67cb25
Packit 67cb25
Inputs: H     - upper hessenberg matrix
Packit 67cb25
        R     - upper triangular matrix
Packit 67cb25
        alpha - (output) where to store eigenvalue numerators
Packit 67cb25
        beta  - (output) where to store eigenvalue denominators
Packit 67cb25
        w     - workspace
Packit 67cb25
Packit 67cb25
Return: none
Packit 67cb25
Packit 67cb25
Notes: 1) w->n_evals is updated to keep track of how many eigenvalues
Packit 67cb25
          are found
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
gen_schur_decomp(gsl_matrix *H, gsl_matrix *R, gsl_vector_complex *alpha,
Packit 67cb25
                 gsl_vector *beta, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  size_t N;
Packit 67cb25
  gsl_matrix_view h, r;
Packit 67cb25
  gsl_matrix_view vh, vr;
Packit 67cb25
  size_t q;             /* index of small subdiagonal element */
Packit 67cb25
  gsl_complex z1, z2;   /* complex values */
Packit 67cb25
  double a, b;
Packit 67cb25
  int s;
Packit 67cb25
  int flag;
Packit 67cb25
Packit 67cb25
  N = H->size1;
Packit 67cb25
Packit 67cb25
  h = gsl_matrix_submatrix(H, 0, 0, N, N);
Packit 67cb25
  r = gsl_matrix_submatrix(R, 0, 0, N, N);
Packit 67cb25
Packit 67cb25
  while ((N > 1) && (w->n_iter)++ < w->max_iterations)
Packit 67cb25
    {
Packit 67cb25
      q = gen_search_small_elements(&h.matrix, &r.matrix, &flag, w);
Packit 67cb25
Packit 67cb25
      if (flag == 0)
Packit 67cb25
        {
Packit 67cb25
          /* no small elements found - do a QZ sweep */
Packit 67cb25
          s = gen_qzstep(&h.matrix, &r.matrix, w);
Packit 67cb25
Packit 67cb25
          if (s == GSL_CONTINUE)
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * (h, r) is a 2-by-2 block with complex eigenvalues -
Packit 67cb25
               * standardize and read off eigenvalues
Packit 67cb25
               */
Packit 67cb25
              s = gen_schur_standardize2(&h.matrix,
Packit 67cb25
                                         &r.matrix,
Packit 67cb25
                                         &z1,
Packit 67cb25
                                         &z2,
Packit 67cb25
                                         &a,
Packit 67cb25
                                         &b,
Packit 67cb25
                                         w);
Packit 67cb25
              if (s != GSL_SUCCESS)
Packit 67cb25
                {
Packit 67cb25
                  /*
Packit 67cb25
                   * if we get here, then the standardization process
Packit 67cb25
                   * perturbed the eigenvalues onto the real line -
Packit 67cb25
                   * continue QZ iteration to break them into 1-by-1
Packit 67cb25
                   * blocks
Packit 67cb25
                   */
Packit 67cb25
                  continue;
Packit 67cb25
                }
Packit 67cb25
Packit 67cb25
              gen_store_eigval2(&h.matrix, &z1, a, &z2, b, alpha, beta, w);
Packit 67cb25
              N = 0;
Packit 67cb25
            } /* if (s) */
Packit 67cb25
Packit 67cb25
          continue;
Packit 67cb25
        } /* if (flag == 0) */
Packit 67cb25
      else if (flag == 2)
Packit 67cb25
        {
Packit 67cb25
          if (q == 0)
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * the leading element of R is zero, split off a block
Packit 67cb25
               * at the top
Packit 67cb25
               */
Packit 67cb25
              gen_tri_split_top(&h.matrix, &r.matrix, w);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * we found a small element on the diagonal of R - chase the
Packit 67cb25
               * zero to the bottom of the active block and then zero
Packit 67cb25
               * H(n, n - 1) to split off a 1-by-1 block
Packit 67cb25
               */
Packit 67cb25
Packit 67cb25
              if (q != N - 1)
Packit 67cb25
                gen_tri_chase_zero(&h.matrix, &r.matrix, q, w);
Packit 67cb25
Packit 67cb25
              /* now zero H(n, n - 1) */
Packit 67cb25
              gen_tri_zero_H(&h.matrix, &r.matrix, w);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          /* continue so the next iteration detects the zero in H */
Packit 67cb25
          continue;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * a small subdiagonal element of H was found - one or two
Packit 67cb25
       * eigenvalues have converged or the matrix has split into
Packit 67cb25
       * two smaller matrices
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      if (q == (N - 1))
Packit 67cb25
        {
Packit 67cb25
          /*
Packit 67cb25
           * the last subdiagonal element of the hessenberg matrix is 0 -
Packit 67cb25
           * H_{NN} / R_{NN} is a real eigenvalue - standardize so
Packit 67cb25
           * R_{NN} > 0
Packit 67cb25
           */
Packit 67cb25
Packit 67cb25
          vh = gsl_matrix_submatrix(&h.matrix, q, q, 1, 1);
Packit 67cb25
          vr = gsl_matrix_submatrix(&r.matrix, q, q, 1, 1);
Packit 67cb25
          gen_schur_standardize1(&vh.matrix, &vr.matrix, &a, &b, w);
Packit 67cb25
Packit 67cb25
          gen_store_eigval1(&vh.matrix, a, b, alpha, beta, w);
Packit 67cb25
Packit 67cb25
          --N;
Packit 67cb25
          h = gsl_matrix_submatrix(&h.matrix, 0, 0, N, N);
Packit 67cb25
          r = gsl_matrix_submatrix(&r.matrix, 0, 0, N, N);
Packit 67cb25
        }
Packit 67cb25
      else if (q == (N - 2))
Packit 67cb25
        {
Packit 67cb25
          /* bottom right 2-by-2 block may have converged */
Packit 67cb25
Packit 67cb25
          vh = gsl_matrix_submatrix(&h.matrix, q, q, 2, 2);
Packit 67cb25
          vr = gsl_matrix_submatrix(&r.matrix, q, q, 2, 2);
Packit 67cb25
          s = gen_schur_standardize2(&vh.matrix,
Packit 67cb25
                                     &vr.matrix,
Packit 67cb25
                                     &z1,
Packit 67cb25
                                     &z2,
Packit 67cb25
                                     &a,
Packit 67cb25
                                     &b,
Packit 67cb25
                                     w);
Packit 67cb25
          if (s != GSL_SUCCESS)
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * this 2-by-2 block contains real eigenvalues that
Packit 67cb25
               * have not yet separated into 1-by-1 blocks -
Packit 67cb25
               * recursively call gen_schur_decomp() to finish off
Packit 67cb25
               * this block
Packit 67cb25
               */
Packit 67cb25
              gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              /* we got 2 complex eigenvalues */
Packit 67cb25
Packit 67cb25
              gen_store_eigval2(&vh.matrix, &z1, a, &z2, b, alpha, beta, w);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          N -= 2;
Packit 67cb25
          h = gsl_matrix_submatrix(&h.matrix, 0, 0, N, N);
Packit 67cb25
          r = gsl_matrix_submatrix(&r.matrix, 0, 0, N, N);
Packit 67cb25
        }
Packit 67cb25
      else if (q == 1)
Packit 67cb25
        {
Packit 67cb25
          /* H_{11} / R_{11} is an eigenvalue */
Packit 67cb25
Packit 67cb25
          vh = gsl_matrix_submatrix(&h.matrix, 0, 0, 1, 1);
Packit 67cb25
          vr = gsl_matrix_submatrix(&r.matrix, 0, 0, 1, 1);
Packit 67cb25
          gen_schur_standardize1(&vh.matrix, &vr.matrix, &a, &b, w);
Packit 67cb25
Packit 67cb25
          gen_store_eigval1(&vh.matrix, a, b, alpha, beta, w);
Packit 67cb25
Packit 67cb25
          --N;
Packit 67cb25
          h = gsl_matrix_submatrix(&h.matrix, 1, 1, N, N);
Packit 67cb25
          r = gsl_matrix_submatrix(&r.matrix, 1, 1, N, N);
Packit 67cb25
        }
Packit 67cb25
      else if (q == 2)
Packit 67cb25
        {
Packit 67cb25
          /* upper left 2-by-2 block may have converged */
Packit 67cb25
Packit 67cb25
          vh = gsl_matrix_submatrix(&h.matrix, 0, 0, 2, 2);
Packit 67cb25
          vr = gsl_matrix_submatrix(&r.matrix, 0, 0, 2, 2);
Packit 67cb25
          s = gen_schur_standardize2(&vh.matrix,
Packit 67cb25
                                     &vr.matrix,
Packit 67cb25
                                     &z1,
Packit 67cb25
                                     &z2,
Packit 67cb25
                                     &a,
Packit 67cb25
                                     &b,
Packit 67cb25
                                     w);
Packit 67cb25
          if (s != GSL_SUCCESS)
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * this 2-by-2 block contains real eigenvalues that
Packit 67cb25
               * have not yet separated into 1-by-1 blocks -
Packit 67cb25
               * recursively call gen_schur_decomp() to finish off
Packit 67cb25
               * this block
Packit 67cb25
               */
Packit 67cb25
              gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              /* we got 2 complex eigenvalues */
Packit 67cb25
              gen_store_eigval2(&vh.matrix, &z1, a, &z2, b, alpha, beta, w);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          N -= 2;
Packit 67cb25
          h = gsl_matrix_submatrix(&h.matrix, 2, 2, N, N);
Packit 67cb25
          r = gsl_matrix_submatrix(&r.matrix, 2, 2, N, N);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          /*
Packit 67cb25
           * There is a zero element on the subdiagonal somewhere
Packit 67cb25
           * in the middle of the matrix - we can now operate
Packit 67cb25
           * separately on the two submatrices split by this
Packit 67cb25
           * element. q is the row index of the zero element.
Packit 67cb25
           */
Packit 67cb25
Packit 67cb25
          /* operate on lower right (N - q)-by-(N - q) block first */
Packit 67cb25
          vh = gsl_matrix_submatrix(&h.matrix, q, q, N - q, N - q);
Packit 67cb25
          vr = gsl_matrix_submatrix(&r.matrix, q, q, N - q, N - q);
Packit 67cb25
          gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
Packit 67cb25
Packit 67cb25
          /* operate on upper left q-by-q block */
Packit 67cb25
          vh = gsl_matrix_submatrix(&h.matrix, 0, 0, q, q);
Packit 67cb25
          vr = gsl_matrix_submatrix(&r.matrix, 0, 0, q, q);
Packit 67cb25
          gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
Packit 67cb25
Packit 67cb25
          N = 0;
Packit 67cb25
        }
Packit 67cb25
    } /* while ((N > 1) && (w->n_iter)++ < w->max_iterations) */
Packit 67cb25
Packit 67cb25
  /* handle special case of N = 1 */
Packit 67cb25
Packit 67cb25
  if (N == 1)
Packit 67cb25
    {
Packit 67cb25
      gen_schur_standardize1(&h.matrix, &r.matrix, &a, &b, w);
Packit 67cb25
      gen_store_eigval1(&h.matrix, a, b, alpha, beta, w);
Packit 67cb25
    }
Packit 67cb25
} /* gen_schur_decomp() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_qzstep()
Packit 67cb25
  This routine determines what type of QZ step to perform on
Packit 67cb25
the generalized matrix pair (H, R). If the pair is 3-by-3 or bigger,
Packit 67cb25
we look at the bottom right 2-by-2 block. If this block has complex
Packit 67cb25
eigenvalues, we perform a Francis double shift QZ sweep. If it
Packit 67cb25
has real eigenvalues, we perform an implicit single shift QZ sweep.
Packit 67cb25
Packit 67cb25
If the pair is 2-by-2 with real eigenvalues, we perform a single
Packit 67cb25
shift sweep. If it has complex eigenvalues, we return GSL_CONTINUE
Packit 67cb25
to notify the calling function that a 2-by-2 block with complex
Packit 67cb25
eigenvalues has converged, so that it may then call
Packit 67cb25
gen_schur_standardize2(). In the real eigenvalue case, we want to
Packit 67cb25
continue doing QZ sweeps to break it up into two 1-by-1 blocks.
Packit 67cb25
Packit 67cb25
See LAPACK routine DHGEQZ and [1] for more information.
Packit 67cb25
Packit 67cb25
Inputs: H - upper Hessenberg matrix (at least 2-by-2)
Packit 67cb25
        R - upper triangular matrix (at least 2-by-2)
Packit 67cb25
        w - workspace
Packit 67cb25
Packit 67cb25
Return: GSL_SUCCESS on normal completion
Packit 67cb25
        GSL_CONTINUE if we detect a 2-by-2 block with complex eigenvalues
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static inline int
Packit 67cb25
gen_qzstep(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  const size_t N = H->size1;
Packit 67cb25
  gsl_matrix_view vh, vr; /* views of bottom right 2-by-2 block */
Packit 67cb25
  double wr1, wr2, wi;
Packit 67cb25
  double scale1, scale2, scale;
Packit 67cb25
  double cs, sn;          /* givens rotation */
Packit 67cb25
  double temp,            /* temporary variables */
Packit 67cb25
         temp2;
Packit 67cb25
  size_t j;               /* looping */
Packit 67cb25
  gsl_vector_view xv, yv; /* temporary views */
Packit 67cb25
  size_t top = 0;
Packit 67cb25
  size_t rows;
Packit 67cb25
Packit 67cb25
  if (w->n_iter % 10 == 0)
Packit 67cb25
    {
Packit 67cb25
      /*
Packit 67cb25
       * Exceptional shift - we have gone 10 iterations without finding
Packit 67cb25
       * a new eigenvalue, do a single shift sweep with an
Packit 67cb25
       * exceptional shift
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      if ((GSL_DBL_MIN * w->max_iterations) *
Packit 67cb25
          fabs(gsl_matrix_get(H, N - 2, N - 1)) <
Packit 67cb25
          fabs(gsl_matrix_get(R, N - 2, N - 2)))
Packit 67cb25
        {
Packit 67cb25
          w->eshift += gsl_matrix_get(H, N - 2, N - 1) /
Packit 67cb25
                       gsl_matrix_get(R, N - 2, N - 2);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        w->eshift += 1.0 / (GSL_DBL_MIN * w->max_iterations);
Packit 67cb25
Packit 67cb25
      if ((w->eshift < GSL_DBL_EPSILON) &&
Packit 67cb25
          (GSL_DBL_MIN * w->max_iterations) *
Packit 67cb25
          fabs(gsl_matrix_get(H, N - 1, N - 2)) <
Packit 67cb25
          fabs(gsl_matrix_get(R, N - 2, N - 2)))
Packit 67cb25
        {
Packit 67cb25
          w->eshift = GEN_ESHIFT_COEFF *
Packit 67cb25
                      (w->ascale * gsl_matrix_get(H, N - 1, N - 2)) /
Packit 67cb25
                      (w->bscale * gsl_matrix_get(R, N - 2, N - 2));
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      scale1 = 1.0;
Packit 67cb25
      wr1 = w->eshift;
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /*
Packit 67cb25
       * Compute generalized eigenvalues of bottom right 2-by-2 block
Packit 67cb25
       * to be used as shifts - wr1 is the Wilkinson shift
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      vh = gsl_matrix_submatrix(H, N - 2, N - 2, 2, 2);
Packit 67cb25
      vr = gsl_matrix_submatrix(R, N - 2, N - 2, 2, 2);
Packit 67cb25
      gsl_schur_gen_eigvals(&vh.matrix,
Packit 67cb25
                            &vr.matrix,
Packit 67cb25
                            &wr1,
Packit 67cb25
                            &wr2,
Packit 67cb25
                            &wi,
Packit 67cb25
                            &scale1,
Packit 67cb25
                            &scale2);
Packit 67cb25
Packit 67cb25
      if (wi != 0.0)
Packit 67cb25
        {
Packit 67cb25
          /* complex eigenvalues */
Packit 67cb25
Packit 67cb25
          if (N == 2)
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * its a 2-by-2 block with complex eigenvalues - notify
Packit 67cb25
               * the calling function to deflate
Packit 67cb25
               */
Packit 67cb25
              return (GSL_CONTINUE);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              /* do a francis double shift sweep */
Packit 67cb25
              gen_qzstep_d(H, R, w);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          return GSL_SUCCESS;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* real eigenvalues - perform single shift QZ step */
Packit 67cb25
Packit 67cb25
  temp = GSL_MIN(w->ascale, 1.0) * (0.5 / GSL_DBL_MIN);
Packit 67cb25
  if (scale1 > temp)
Packit 67cb25
    scale = temp / scale1;
Packit 67cb25
  else
Packit 67cb25
    scale = 1.0;
Packit 67cb25
Packit 67cb25
  temp = GSL_MIN(w->bscale, 1.0) * (0.5 / GSL_DBL_MIN);
Packit 67cb25
  if (fabs(wr1) > temp)
Packit 67cb25
    scale = GSL_MIN(scale, temp / fabs(wr1));
Packit 67cb25
Packit 67cb25
  scale1 *= scale;
Packit 67cb25
  wr1 *= scale;
Packit 67cb25
Packit 67cb25
  if (w->needtop)
Packit 67cb25
    {
Packit 67cb25
      /* get absolute index of this matrix relative to original matrix */
Packit 67cb25
      top = gen_get_submatrix(w->H, H);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  temp = scale1*gsl_matrix_get(H, 0, 0) - wr1*gsl_matrix_get(R, 0, 0);
Packit 67cb25
  temp2 = scale1*gsl_matrix_get(H, 1, 0);
Packit 67cb25
Packit 67cb25
  gsl_linalg_givens(temp, temp2, &cs, &sn;;
Packit 67cb25
  sn = -sn;
Packit 67cb25
Packit 67cb25
  for (j = 0; j < N - 1; ++j)
Packit 67cb25
    {
Packit 67cb25
      if (j > 0)
Packit 67cb25
        {
Packit 67cb25
          temp = gsl_matrix_get(H, j, j - 1);
Packit 67cb25
          temp2 = gsl_matrix_get(H, j + 1, j - 1);
Packit 67cb25
          gsl_linalg_givens(temp, temp2, &cs, &sn;;
Packit 67cb25
          sn = -sn;
Packit 67cb25
Packit 67cb25
          /* apply to column (j - 1) */
Packit 67cb25
          temp = cs * gsl_matrix_get(H, j, j - 1) +
Packit 67cb25
                 sn * gsl_matrix_get(H, j + 1, j - 1);
Packit 67cb25
          gsl_matrix_set(H, j, j - 1, temp);
Packit 67cb25
          gsl_matrix_set(H, j + 1, j - 1, 0.0);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* apply G to H(j:j+1,:) and T(j:j+1,:) */
Packit 67cb25
Packit 67cb25
      if (w->compute_s)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(w->H, top + j, top + j, w->size - top - j);
Packit 67cb25
          yv = gsl_matrix_subrow(w->H, top + j + 1, top + j, w->size - top - j);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(H, j, j, N - j);
Packit 67cb25
          yv = gsl_matrix_subrow(H, j + 1, j, N - j);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
      if (w->compute_t)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(w->R, top + j, top + j, w->size - top - j);
Packit 67cb25
          yv = gsl_matrix_subrow(w->R, top + j + 1, top + j, w->size - top - j);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(R, j, j, N - j);
Packit 67cb25
          yv = gsl_matrix_subrow(R, j + 1, j, N - j);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
      if (w->Q)
Packit 67cb25
        {
Packit 67cb25
          /* accumulate Q: Q -> QG */
Packit 67cb25
          xv = gsl_matrix_column(w->Q, top + j);
Packit 67cb25
          yv = gsl_matrix_column(w->Q, top + j + 1);
Packit 67cb25
          gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      temp = gsl_matrix_get(R, j + 1, j + 1);
Packit 67cb25
      temp2 = gsl_matrix_get(R, j + 1, j);
Packit 67cb25
      gsl_linalg_givens(temp, temp2, &cs, &sn;;
Packit 67cb25
Packit 67cb25
      rows = GSL_MIN(j + 3, N);
Packit 67cb25
Packit 67cb25
      if (w->compute_s)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(w->H, top + j, 0, top + rows);
Packit 67cb25
          yv = gsl_matrix_subcolumn(w->H, top + j + 1, 0, top + rows);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(H, j, 0, rows);
Packit 67cb25
          yv = gsl_matrix_subcolumn(H, j + 1, 0, rows);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
      rows = GSL_MIN(j + 2, N);
Packit 67cb25
Packit 67cb25
      if (w->compute_t)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(w->R, top + j, 0, top + rows);
Packit 67cb25
          yv = gsl_matrix_subcolumn(w->R, top + j + 1, 0, top + rows);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(R, j, 0, rows);
Packit 67cb25
          yv = gsl_matrix_subcolumn(R, j + 1, 0, rows);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
      if (w->Z)
Packit 67cb25
        {
Packit 67cb25
          /* accumulate Z: Z -> ZG */
Packit 67cb25
          xv = gsl_matrix_column(w->Z, top + j);
Packit 67cb25
          yv = gsl_matrix_column(w->Z, top + j + 1);
Packit 67cb25
          gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
        }
Packit 67cb25
    } /* for (j = 0; j < N - 1; ++j) */
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
} /* gen_qzstep() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_qzstep_d()
Packit 67cb25
  Perform an implicit double shift QZ step.
Packit 67cb25
Packit 67cb25
See Golub & Van Loan, "Matrix Computations" (3rd ed), algorithm 7.7.2
Packit 67cb25
Packit 67cb25
Inputs: H - upper Hessenberg matrix (at least 3-by-3)
Packit 67cb25
        R - upper triangular matrix (at least 3-by-3)
Packit 67cb25
        w - workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static inline void
Packit 67cb25
gen_qzstep_d(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  const size_t N = H->size1;
Packit 67cb25
  size_t j;               /* looping */
Packit 67cb25
  double dat[3];          /* householder vector */
Packit 67cb25
  double tau;             /* householder coefficient */
Packit 67cb25
  gsl_vector_view v2, v3; /* views into 'dat' */
Packit 67cb25
  gsl_matrix_view m;      /* temporary view */
Packit 67cb25
  double tmp;
Packit 67cb25
  size_t q, r;
Packit 67cb25
  size_t top = 0;         /* location of H in original matrix */
Packit 67cb25
  double scale;
Packit 67cb25
  double AB11,            /* various matrix element ratios */
Packit 67cb25
         AB22,
Packit 67cb25
         ABNN,
Packit 67cb25
         ABMM,
Packit 67cb25
         AMNBNN,
Packit 67cb25
         ANMBMM,
Packit 67cb25
         A21B11,
Packit 67cb25
         A12B22,
Packit 67cb25
         A32B22,
Packit 67cb25
         B12B22,
Packit 67cb25
         BMNBNN;
Packit 67cb25
Packit 67cb25
  v2 = gsl_vector_view_array(dat, 2);
Packit 67cb25
  v3 = gsl_vector_view_array(dat, 3);
Packit 67cb25
Packit 67cb25
  if (w->needtop)
Packit 67cb25
    {
Packit 67cb25
      /* get absolute index of this matrix relative to original matrix */
Packit 67cb25
      top = gen_get_submatrix(w->H, H);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * Similar to the QR method, we take the shifts to be the two
Packit 67cb25
   * zeros of the problem
Packit 67cb25
   *
Packit 67cb25
   * det[H(n-1:n,n-1:n) - s*R(n-1:n,n-1:n)] = 0
Packit 67cb25
   *
Packit 67cb25
   * The initial householder vector elements are then given by
Packit 67cb25
   * Eq. 4.1 of [1], which are designed to reduce errors when
Packit 67cb25
   * off diagonal elements are small.
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  ABMM = (w->ascale * gsl_matrix_get(H, N - 2, N - 2)) /
Packit 67cb25
         (w->bscale * gsl_matrix_get(R, N - 2, N - 2));
Packit 67cb25
  ABNN = (w->ascale * gsl_matrix_get(H, N - 1, N - 1)) /
Packit 67cb25
         (w->bscale * gsl_matrix_get(R, N - 1, N - 1));
Packit 67cb25
  AB11 = (w->ascale * gsl_matrix_get(H, 0, 0)) /
Packit 67cb25
         (w->bscale * gsl_matrix_get(R, 0, 0));
Packit 67cb25
  AB22 = (w->ascale * gsl_matrix_get(H, 1, 1)) /
Packit 67cb25
         (w->bscale * gsl_matrix_get(R, 1, 1));
Packit 67cb25
  AMNBNN = (w->ascale * gsl_matrix_get(H, N - 2, N - 1)) /
Packit 67cb25
           (w->bscale * gsl_matrix_get(R, N - 1, N - 1));
Packit 67cb25
  ANMBMM = (w->ascale * gsl_matrix_get(H, N - 1, N - 2)) /
Packit 67cb25
           (w->bscale * gsl_matrix_get(R, N - 2, N - 2));
Packit 67cb25
  BMNBNN = gsl_matrix_get(R, N - 2, N - 1) /
Packit 67cb25
           gsl_matrix_get(R, N - 1, N - 1);
Packit 67cb25
  A21B11 = (w->ascale * gsl_matrix_get(H, 1, 0)) /
Packit 67cb25
           (w->bscale * gsl_matrix_get(R, 0, 0));
Packit 67cb25
  A12B22 = (w->ascale * gsl_matrix_get(H, 0, 1)) /
Packit 67cb25
           (w->bscale * gsl_matrix_get(R, 1, 1));
Packit 67cb25
  A32B22 = (w->ascale * gsl_matrix_get(H, 2, 1)) /
Packit 67cb25
           (w->bscale * gsl_matrix_get(R, 1, 1));
Packit 67cb25
  B12B22 = gsl_matrix_get(R, 0, 1) / gsl_matrix_get(R, 1, 1);
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * These are the Eqs (4.1) of [1], just multiplied by the factor
Packit 67cb25
   * (A_{21} / B_{11})
Packit 67cb25
   */
Packit 67cb25
  dat[0] = (ABMM - AB11) * (ABNN - AB11) - (AMNBNN * ANMBMM) +
Packit 67cb25
           (ANMBMM * BMNBNN * AB11) + (A12B22 - (AB11 * B12B22)) * A21B11;
Packit 67cb25
  dat[1] = ((AB22 - AB11) - (A21B11 * B12B22) - (ABMM - AB11) -
Packit 67cb25
            (ABNN - AB11) + (ANMBMM * BMNBNN)) * A21B11;
Packit 67cb25
  dat[2] = A32B22 * A21B11;
Packit 67cb25
Packit 67cb25
  scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]);
Packit 67cb25
  if (scale != 0.0)
Packit 67cb25
    {
Packit 67cb25
      dat[0] /= scale;
Packit 67cb25
      dat[1] /= scale;
Packit 67cb25
      dat[2] /= scale;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  for (j = 0; j < N - 2; ++j)
Packit 67cb25
    {
Packit 67cb25
      r = GSL_MIN(j + 4, N);
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * Find householder Q so that
Packit 67cb25
       *
Packit 67cb25
       * Q [x y z]^t = [ * 0 0 ]^t
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      tau = gsl_linalg_householder_transform(&v3.vector);
Packit 67cb25
Packit 67cb25
      if (tau != 0.0)
Packit 67cb25
        {
Packit 67cb25
          /*
Packit 67cb25
           * q is the initial column to start applying the householder
Packit 67cb25
           * transformation. The GSL_MAX() simply ensures we don't
Packit 67cb25
           * try to apply it to column (-1), since we are zeroing out
Packit 67cb25
           * column (j - 1) except for the first iteration which
Packit 67cb25
           * introduces the bulge.
Packit 67cb25
           */
Packit 67cb25
          q = (size_t) GSL_MAX(0, (int)j - 1);
Packit 67cb25
Packit 67cb25
          /* H -> QH, R -> QR */
Packit 67cb25
Packit 67cb25
          if (w->compute_s)
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * We are computing the Schur form S, so we need to
Packit 67cb25
               * transform the whole matrix H
Packit 67cb25
               */
Packit 67cb25
              m = gsl_matrix_submatrix(w->H,
Packit 67cb25
                                       top + j,
Packit 67cb25
                                       top + q,
Packit 67cb25
                                       3,
Packit 67cb25
                                       w->size - top - q);
Packit 67cb25
              gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              /* just transform the active block */
Packit 67cb25
              m = gsl_matrix_submatrix(H, j, q, 3, N - q);
Packit 67cb25
              gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          if (w->compute_t)
Packit 67cb25
            {
Packit 67cb25
              /*
Packit 67cb25
               * We are computing the Schur form T, so we need to
Packit 67cb25
               * transform the whole matrix R
Packit 67cb25
               */
Packit 67cb25
              m = gsl_matrix_submatrix(w->R,
Packit 67cb25
                                       top + j,
Packit 67cb25
                                       top + j,
Packit 67cb25
                                       3,
Packit 67cb25
                                       w->size - top - j);
Packit 67cb25
              gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              /* just transform the active block */
Packit 67cb25
              m = gsl_matrix_submatrix(R, j, j, 3, N - j);
Packit 67cb25
              gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          if (w->Q)
Packit 67cb25
            {
Packit 67cb25
              /* accumulate the transformation into Q */
Packit 67cb25
              m = gsl_matrix_submatrix(w->Q, 0, top + j, w->size, 3);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
        } /* if (tau != 0.0) */
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * Find householder Z so that
Packit 67cb25
       * 
Packit 67cb25
       * [ r_{j+2,j} r_{j+2, j+1}, r_{j+2, j+2} ] Z = [ 0 0 * ]
Packit 67cb25
       *
Packit 67cb25
       * This isn't exactly what gsl_linalg_householder_transform
Packit 67cb25
       * does, so we need to rotate the input vector so it preserves
Packit 67cb25
       * the last element, and then rotate it back afterwards.
Packit 67cb25
       *
Packit 67cb25
       * So instead of transforming [x y z], we transform [z x y],
Packit 67cb25
       * and the resulting HH vector [1 v2 v3] -> [v2 v3 1] but
Packit 67cb25
       * then needs to be scaled to have the first element = 1, so
Packit 67cb25
       * it becomes [1 v3/v2 1/v2] (tau must also be scaled accordingly).
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      dat[0] = gsl_matrix_get(R, j + 2, j + 2);
Packit 67cb25
      dat[1] = gsl_matrix_get(R, j + 2, j);
Packit 67cb25
      dat[2] = gsl_matrix_get(R, j + 2, j + 1);
Packit 67cb25
      scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]);
Packit 67cb25
      if (scale != 0.0)
Packit 67cb25
        {
Packit 67cb25
          dat[0] /= scale;
Packit 67cb25
          dat[1] /= scale;
Packit 67cb25
          dat[2] /= scale;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      tau = gsl_linalg_householder_transform(&v3.vector);
Packit 67cb25
Packit 67cb25
      if (tau != 0.0)
Packit 67cb25
        {
Packit 67cb25
          /* rotate back */
Packit 67cb25
          tmp = gsl_vector_get(&v3.vector, 1);
Packit 67cb25
          gsl_vector_set(&v3.vector, 1, gsl_vector_get(&v3.vector, 2)/tmp);
Packit 67cb25
          gsl_vector_set(&v3.vector, 2, 1.0 / tmp);
Packit 67cb25
          tau *= tmp * tmp;
Packit 67cb25
Packit 67cb25
          /* H -> HZ, R -> RZ */
Packit 67cb25
Packit 67cb25
          if (w->compute_s)
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(w->H, 0, top + j, top + r, 3);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(H, 0, j, r, 3);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          if (w->compute_t)
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(w->R, 0, top + j, top + j + 3, 3);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(R, 0, j, j + 3, 3);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          if (w->Z)
Packit 67cb25
            {
Packit 67cb25
              /* accumulate transformation into Z */
Packit 67cb25
              m = gsl_matrix_submatrix(w->Z, 0, top + j, w->size, 3);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
        } /* if (tau != 0.0) */
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * Find householder Z so that
Packit 67cb25
       * 
Packit 67cb25
       * [ r_{j+1,j} r_{j+1, j+1} ] Z = [ 0 * ]
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      dat[0] = gsl_matrix_get(R, j + 1, j + 1);
Packit 67cb25
      dat[1] = gsl_matrix_get(R, j + 1, j);
Packit 67cb25
      scale = fabs(dat[0]) + fabs(dat[1]);
Packit 67cb25
      if (scale != 0.0)
Packit 67cb25
        {
Packit 67cb25
          dat[0] /= scale;
Packit 67cb25
          dat[1] /= scale;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      tau = gsl_linalg_householder_transform(&v2.vector);
Packit 67cb25
Packit 67cb25
      if (tau != 0.0)
Packit 67cb25
        {
Packit 67cb25
          /* rotate back */
Packit 67cb25
          tmp = gsl_vector_get(&v2.vector, 1);
Packit 67cb25
          gsl_vector_set(&v2.vector, 1, 1.0 / tmp);
Packit 67cb25
          tau *= tmp * tmp;
Packit 67cb25
Packit 67cb25
          /* H -> HZ, R -> RZ */
Packit 67cb25
Packit 67cb25
          if (w->compute_s)
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(w->H, 0, top + j, top + r, 2);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(H, 0, j, r, 2);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          if (w->compute_t)
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(w->R, 0, top + j, top + j + 3, 2);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              m = gsl_matrix_submatrix(R, 0, j, j + 3, 2);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          if (w->Z)
Packit 67cb25
            {
Packit 67cb25
              /* accumulate transformation into Z */
Packit 67cb25
              m = gsl_matrix_submatrix(w->Z, 0, top + j, w->size, 2);
Packit 67cb25
              gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
            }
Packit 67cb25
        } /* if (tau != 0.0) */
Packit 67cb25
Packit 67cb25
      dat[0] = gsl_matrix_get(H, j + 1, j);
Packit 67cb25
      dat[1] = gsl_matrix_get(H, j + 2, j);
Packit 67cb25
      if (j < N - 3)
Packit 67cb25
        dat[2] = gsl_matrix_get(H, j + 3, j);
Packit 67cb25
Packit 67cb25
      scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]);
Packit 67cb25
      if (scale != 0.0)
Packit 67cb25
        {
Packit 67cb25
          dat[0] /= scale;
Packit 67cb25
          dat[1] /= scale;
Packit 67cb25
          dat[2] /= scale;
Packit 67cb25
        }
Packit 67cb25
    } /* for (j = 0; j < N - 2; ++j) */
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * Find Householder Q so that
Packit 67cb25
   *
Packit 67cb25
   * Q [ x y ]^t = [ * 0 ]^t
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  scale = fabs(dat[0]) + fabs(dat[1]);
Packit 67cb25
  if (scale != 0.0)
Packit 67cb25
    {
Packit 67cb25
      dat[0] /= scale;
Packit 67cb25
      dat[1] /= scale;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  tau = gsl_linalg_householder_transform(&v2.vector);
Packit 67cb25
  
Packit 67cb25
  if (w->compute_s)
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(w->H,
Packit 67cb25
                               top + N - 2,
Packit 67cb25
                               top + N - 3,
Packit 67cb25
                               2,
Packit 67cb25
                               w->size - top - N + 3);
Packit 67cb25
      gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(H, N - 2, N - 3, 2, 3);
Packit 67cb25
      gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (w->compute_t)
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(w->R,
Packit 67cb25
                               top + N - 2,
Packit 67cb25
                               top + N - 2,
Packit 67cb25
                               2,
Packit 67cb25
                               w->size - top - N + 2);
Packit 67cb25
      gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(R, N - 2, N - 2, 2, 2);
Packit 67cb25
      gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (w->Q)
Packit 67cb25
    {
Packit 67cb25
      /* accumulate the transformation into Q */
Packit 67cb25
      m = gsl_matrix_submatrix(w->Q, 0, top + N - 2, w->size, 2);
Packit 67cb25
      gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * Find Householder Z so that
Packit 67cb25
   *
Packit 67cb25
   * [ b_{n,n-1} b_{nn} ] Z = [ 0 * ]
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  dat[0] = gsl_matrix_get(R, N - 1, N - 1);
Packit 67cb25
  dat[1] = gsl_matrix_get(R, N - 1, N - 2);
Packit 67cb25
  scale = fabs(dat[0]) + fabs(dat[1]);
Packit 67cb25
  if (scale != 0.0)
Packit 67cb25
    {
Packit 67cb25
      dat[0] /= scale;
Packit 67cb25
      dat[1] /= scale;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  tau = gsl_linalg_householder_transform(&v2.vector);
Packit 67cb25
Packit 67cb25
  /* rotate back */
Packit 67cb25
  tmp = gsl_vector_get(&v2.vector, 1);
Packit 67cb25
  gsl_vector_set(&v2.vector, 1, 1.0 / tmp);
Packit 67cb25
  tau *= tmp * tmp;
Packit 67cb25
Packit 67cb25
  if (w->compute_s)
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(w->H, 0, top + N - 2, top + N, 2);
Packit 67cb25
      gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(H, 0, N - 2, N, 2);
Packit 67cb25
      gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (w->compute_t)
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(w->R, 0, top + N - 2, top + N, 2);
Packit 67cb25
      gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      m = gsl_matrix_submatrix(R, 0, N - 2, N, 2);
Packit 67cb25
      gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (w->Z)
Packit 67cb25
    {
Packit 67cb25
      /* accumulate the transformation into Z */
Packit 67cb25
      m = gsl_matrix_submatrix(w->Z, 0, top + N - 2, w->size, 2);
Packit 67cb25
      gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
Packit 67cb25
    }
Packit 67cb25
} /* gen_qzstep_d() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_tri_split_top()
Packit 67cb25
  This routine is called when the leading element on the diagonal of R
Packit 67cb25
has become negligible. Split off a 1-by-1 block at the top.
Packit 67cb25
Packit 67cb25
Inputs: H - upper hessenberg matrix
Packit 67cb25
        R - upper triangular matrix
Packit 67cb25
        w - workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
gen_tri_split_top(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  const size_t N = H->size1;
Packit 67cb25
  size_t j, top = 0;
Packit 67cb25
  double cs, sn;
Packit 67cb25
  gsl_vector_view xv, yv;
Packit 67cb25
Packit 67cb25
  if (w->needtop)
Packit 67cb25
    top = gen_get_submatrix(w->H, H);
Packit 67cb25
Packit 67cb25
  j = 0;
Packit 67cb25
Packit 67cb25
  gsl_linalg_givens(gsl_matrix_get(H, j, j),
Packit 67cb25
                    gsl_matrix_get(H, j + 1, j),
Packit 67cb25
                    &cs,
Packit 67cb25
                    &sn;;
Packit 67cb25
  sn = -sn;
Packit 67cb25
Packit 67cb25
  if (w->compute_s)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_subrow(w->H, top + j, top, w->size - top);
Packit 67cb25
      yv = gsl_matrix_subrow(w->H, top + j + 1, top, w->size - top);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_row(H, j);
Packit 67cb25
      yv = gsl_matrix_row(H, j + 1);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
  gsl_matrix_set(H, j + 1, j, 0.0);
Packit 67cb25
Packit 67cb25
  if (w->compute_t)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_subrow(w->R, top + j, top + 1, w->size - top - 1);
Packit 67cb25
      yv = gsl_matrix_subrow(w->R, top + j + 1, top + 1, w->size - top - 1);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_subrow(R, j, 1, N - 1);
Packit 67cb25
      yv = gsl_matrix_subrow(R, j + 1, 1, N - 1);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
  if (w->Q)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_column(w->Q, top + j);
Packit 67cb25
      yv = gsl_matrix_column(w->Q, top + j + 1);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
    }
Packit 67cb25
} /* gen_tri_split_top() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_tri_chase_zero()
Packit 67cb25
  This routine is called when an element on the diagonal of R
Packit 67cb25
has become negligible. Chase the zero to the bottom of the active
Packit 67cb25
block so we can split off a 1-by-1 block.
Packit 67cb25
Packit 67cb25
Inputs: H - upper hessenberg matrix
Packit 67cb25
        R - upper triangular matrix
Packit 67cb25
        q - index such that R(q,q) = 0 (q must be > 0)
Packit 67cb25
        w - workspace
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static inline void
Packit 67cb25
gen_tri_chase_zero(gsl_matrix *H, gsl_matrix *R, size_t q,
Packit 67cb25
                   gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  const size_t N = H->size1;
Packit 67cb25
  size_t j, top = 0;
Packit 67cb25
  double cs, sn;
Packit 67cb25
  gsl_vector_view xv, yv;
Packit 67cb25
Packit 67cb25
  if (w->needtop)
Packit 67cb25
    top = gen_get_submatrix(w->H, H);
Packit 67cb25
Packit 67cb25
  for (j = q; j < N - 1; ++j)
Packit 67cb25
    {
Packit 67cb25
      gsl_linalg_givens(gsl_matrix_get(R, j, j + 1),
Packit 67cb25
                        gsl_matrix_get(R, j + 1, j + 1),
Packit 67cb25
                        &cs,
Packit 67cb25
                        &sn;;
Packit 67cb25
      sn = -sn;
Packit 67cb25
Packit 67cb25
      if (w->compute_t)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(w->R, top + j, top + j + 1, w->size - top - j - 1);
Packit 67cb25
          yv = gsl_matrix_subrow(w->R, top + j + 1, top + j + 1, w->size - top - j - 1);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(R, j, j + 1, N - j - 1);
Packit 67cb25
          yv = gsl_matrix_subrow(R, j + 1, j + 1, N - j - 1);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
      gsl_matrix_set(R, j + 1, j + 1, 0.0);
Packit 67cb25
Packit 67cb25
      if (w->compute_s)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(w->H, top + j, top + j - 1, w->size - top - j + 1);
Packit 67cb25
          yv = gsl_matrix_subrow(w->H, top + j + 1, top + j - 1, w->size - top - j + 1);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(H, j, j - 1, N - j + 1);
Packit 67cb25
          yv = gsl_matrix_subrow(H, j + 1, j - 1, N - j + 1);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
      if (w->Q)
Packit 67cb25
        {
Packit 67cb25
          /* accumulate Q */
Packit 67cb25
          xv = gsl_matrix_column(w->Q, top + j);
Packit 67cb25
          yv = gsl_matrix_column(w->Q, top + j + 1);
Packit 67cb25
          gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_linalg_givens(gsl_matrix_get(H, j + 1, j),
Packit 67cb25
                        gsl_matrix_get(H, j + 1, j - 1),
Packit 67cb25
                        &cs,
Packit 67cb25
                        &sn;;
Packit 67cb25
      sn = -sn;
Packit 67cb25
Packit 67cb25
      if (w->compute_s)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(w->H, top + j, 0, top + j + 2);
Packit 67cb25
          yv = gsl_matrix_subcolumn(w->H, top + j - 1, 0, top + j + 2);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(H, j, 0, j + 2);
Packit 67cb25
          yv = gsl_matrix_subcolumn(H, j - 1, 0, j + 2);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
      gsl_matrix_set(H, j + 1, j - 1, 0.0);
Packit 67cb25
Packit 67cb25
      if (w->compute_t)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(w->R, top + j, 0, top + j + 1);
Packit 67cb25
          yv = gsl_matrix_subcolumn(w->R, top + j - 1, 0, top + j + 1);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(R, j, 0, j + 1);
Packit 67cb25
          yv = gsl_matrix_subcolumn(R, j - 1, 0, j + 1);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
      if (w->Z)
Packit 67cb25
        {
Packit 67cb25
          /* accumulate Z */
Packit 67cb25
          xv = gsl_matrix_column(w->Z, top + j);
Packit 67cb25
          yv = gsl_matrix_column(w->Z, top + j - 1);
Packit 67cb25
          gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
} /* gen_tri_chase_zero() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_tri_zero_H()
Packit 67cb25
  Companion function to get_tri_chase_zero(). After the zero on
Packit 67cb25
the diagonal of R has been chased to the bottom, we zero the element
Packit 67cb25
H(n, n - 1) in order to split off a 1-by-1 block.
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static inline void
Packit 67cb25
gen_tri_zero_H(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  const size_t N = H->size1;
Packit 67cb25
  size_t top = 0;
Packit 67cb25
  double cs, sn;
Packit 67cb25
  gsl_vector_view xv, yv;
Packit 67cb25
Packit 67cb25
  if (w->needtop)
Packit 67cb25
    top = gen_get_submatrix(w->H, H);
Packit 67cb25
Packit 67cb25
  gsl_linalg_givens(gsl_matrix_get(H, N - 1, N - 1),
Packit 67cb25
                    gsl_matrix_get(H, N - 1, N - 2),
Packit 67cb25
                    &cs,
Packit 67cb25
                    &sn;;
Packit 67cb25
  sn = -sn;
Packit 67cb25
Packit 67cb25
  if (w->compute_s)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_subcolumn(w->H, top + N - 1, 0, top + N);
Packit 67cb25
      yv = gsl_matrix_subcolumn(w->H, top + N - 2, 0, top + N);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_column(H, N - 1);
Packit 67cb25
      yv = gsl_matrix_column(H, N - 2);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
  gsl_matrix_set(H, N - 1, N - 2, 0.0);
Packit 67cb25
Packit 67cb25
  if (w->compute_t)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_subcolumn(w->R, top + N - 1, 0, top + N - 1);
Packit 67cb25
      yv = gsl_matrix_subcolumn(w->R, top + N - 2, 0, top + N - 1);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_subcolumn(R, N - 1, 0, N - 1);
Packit 67cb25
      yv = gsl_matrix_subcolumn(R, N - 2, 0, N - 1);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
Packit 67cb25
  if (w->Z)
Packit 67cb25
    {
Packit 67cb25
      /* accumulate Z */
Packit 67cb25
      xv = gsl_matrix_column(w->Z, top + N - 1);
Packit 67cb25
      yv = gsl_matrix_column(w->Z, top + N - 2);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
Packit 67cb25
    }
Packit 67cb25
} /* gen_tri_zero_H() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_search_small_elements()
Packit 67cb25
  This routine searches for small elements in the matrix pencil
Packit 67cb25
(H, R) to determine if any eigenvalues have converged.
Packit 67cb25
Packit 67cb25
Tests:
Packit 67cb25
Packit 67cb25
1. Test if the Hessenberg matrix has a small subdiagonal element:
Packit 67cb25
   H(i, i - 1) < tolerance
Packit 67cb25
Packit 67cb25
2. Test if the Triangular matrix has a small diagonal element:
Packit 67cb25
   R(i, i) < tolerance
Packit 67cb25
Packit 67cb25
Possible outcomes:
Packit 67cb25
Packit 67cb25
(A) Neither test passed: in this case 'flag' is set to 0 and the
Packit 67cb25
    function returns 0
Packit 67cb25
Packit 67cb25
(B) Test 1 passes and 2 does not: in this case 'flag' is set to 1
Packit 67cb25
    and we return the row index i such that H(i, i - 1) < tol
Packit 67cb25
Packit 67cb25
(C) Test 2 passes and 1 does not: in this case 'flag' is set to 2
Packit 67cb25
    and we return the index i such that R(i, i) < tol
Packit 67cb25
Packit 67cb25
(D) Tests 1 and 2 both pass: in this case 'flag' is set to 3 and
Packit 67cb25
    we return the index i such that H(i, i - 1) < tol and R(i, i) < tol
Packit 67cb25
Packit 67cb25
Inputs: H    - upper Hessenberg matrix
Packit 67cb25
        R    - upper Triangular matrix
Packit 67cb25
        flag - (output) flag set on output (see above)
Packit 67cb25
        w    - workspace
Packit 67cb25
Packit 67cb25
Return: see above
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static inline size_t
Packit 67cb25
gen_search_small_elements(gsl_matrix *H, gsl_matrix *R,
Packit 67cb25
                          int *flag, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  const size_t N = H->size1;
Packit 67cb25
  int k;
Packit 67cb25
  size_t i;
Packit 67cb25
  int pass1 = 0;
Packit 67cb25
  int pass2 = 0;
Packit 67cb25
Packit 67cb25
  for (k = (int) N - 1; k >= 0; --k)
Packit 67cb25
    {
Packit 67cb25
      i = (size_t) k;
Packit 67cb25
Packit 67cb25
      if (i != 0 && fabs(gsl_matrix_get(H, i, i - 1)) <= w->atol)
Packit 67cb25
        {
Packit 67cb25
          gsl_matrix_set(H, i, i - 1, 0.0);
Packit 67cb25
          pass1 = 1;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (fabs(gsl_matrix_get(R, i, i)) < w->btol)
Packit 67cb25
        {
Packit 67cb25
          gsl_matrix_set(R, i, i, 0.0);
Packit 67cb25
          pass2 = 1;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (pass1 && !pass2)      /* case B */
Packit 67cb25
        {
Packit 67cb25
          *flag = 1;
Packit 67cb25
          return (i);
Packit 67cb25
        }
Packit 67cb25
      else if (!pass1 && pass2) /* case C */
Packit 67cb25
        {
Packit 67cb25
          *flag = 2;
Packit 67cb25
          return (i);
Packit 67cb25
        }
Packit 67cb25
      else if (pass1 && pass2)  /* case D */
Packit 67cb25
        {
Packit 67cb25
          *flag = 3;
Packit 67cb25
          return (i);
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* neither test passed: case A */
Packit 67cb25
Packit 67cb25
  *flag = 0;
Packit 67cb25
  return (0);
Packit 67cb25
} /* gen_search_subdiag_small_elements() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_schur_standardize1()
Packit 67cb25
  This function is called when a 1-by-1 block has converged -
Packit 67cb25
convert the block to standard form and update the Schur forms and
Packit 67cb25
vectors if required. Standard form here means that the diagonal
Packit 67cb25
element of B is positive.
Packit 67cb25
Packit 67cb25
Inputs: A      - 1-by-1 matrix in Schur form S
Packit 67cb25
        B      - 1-by-1 matrix in Schur form T
Packit 67cb25
        alphar - where to store real part of eigenvalue numerator
Packit 67cb25
        beta   - where to store eigenvalue denominator
Packit 67cb25
        w      - workspace
Packit 67cb25
Packit 67cb25
Return: success
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
gen_schur_standardize1(gsl_matrix *A, gsl_matrix *B, double *alphar,
Packit 67cb25
                       double *beta, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  size_t i;
Packit 67cb25
  size_t top = 0;
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * it is a 1-by-1 block - the only requirement is that
Packit 67cb25
   * B_{00} is > 0, so if it isn't apply a -I transformation
Packit 67cb25
   */
Packit 67cb25
  if (gsl_matrix_get(B, 0, 0) < 0.0)
Packit 67cb25
    {
Packit 67cb25
      if (w->needtop)
Packit 67cb25
        top = gen_get_submatrix(w->H, A);
Packit 67cb25
Packit 67cb25
      if (w->compute_t)
Packit 67cb25
        {
Packit 67cb25
          for (i = 0; i <= top; ++i)
Packit 67cb25
            gsl_matrix_set(w->R, i, top, -gsl_matrix_get(w->R, i, top));
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        gsl_matrix_set(B, 0, 0, -gsl_matrix_get(B, 0, 0));
Packit 67cb25
Packit 67cb25
      if (w->compute_s)
Packit 67cb25
        {
Packit 67cb25
          for (i = 0; i <= top; ++i)
Packit 67cb25
            gsl_matrix_set(w->H, i, top, -gsl_matrix_get(w->H, i, top));
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        gsl_matrix_set(A, 0, 0, -gsl_matrix_get(A, 0, 0));
Packit 67cb25
Packit 67cb25
      if (w->Z)
Packit 67cb25
        {
Packit 67cb25
          for (i = 0; i < w->size; ++i)
Packit 67cb25
            gsl_matrix_set(w->Z, i, top, -gsl_matrix_get(w->Z, i, top));
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  *alphar = gsl_matrix_get(A, 0, 0);
Packit 67cb25
  *beta = gsl_matrix_get(B, 0, 0);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
} /* gen_schur_standardize1() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_schur_standardize2()
Packit 67cb25
  This function is called when a 2-by-2 generalized block has
Packit 67cb25
converged. Convert the block to standard form, which means B
Packit 67cb25
is rotated so that
Packit 67cb25
Packit 67cb25
B = [ B11  0  ] with B11, B22 non-negative
Packit 67cb25
    [  0  B22 ]
Packit 67cb25
Packit 67cb25
If the resulting block (A, B) has complex eigenvalues, they are
Packit 67cb25
computed. Otherwise, the function will return GSL_CONTINUE to
Packit 67cb25
notify caller that we need to do more single shift sweeps to
Packit 67cb25
convert the 2-by-2 block into two 1-by-1 blocks.
Packit 67cb25
Packit 67cb25
Inputs: A      - 2-by-2 submatrix of schur form S
Packit 67cb25
        B      - 2-by-2 submatrix of schur form T
Packit 67cb25
        alpha1 - (output) where to store eigenvalue 1 numerator
Packit 67cb25
        alpha2 - (output) where to store eigenvalue 2 numerator
Packit 67cb25
        beta1  - (output) where to store eigenvalue 1 denominator
Packit 67cb25
        beta2  - (output) where to store eigenvalue 2 denominator
Packit 67cb25
        w      - workspace
Packit 67cb25
Packit 67cb25
Return: GSL_SUCCESS if block has complex eigenvalues (they are computed)
Packit 67cb25
        GSL_CONTINUE if block has real eigenvalues (they are not computed)
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
gen_schur_standardize2(gsl_matrix *A, gsl_matrix *B, gsl_complex *alpha1,
Packit 67cb25
                       gsl_complex *alpha2, double *beta1, double *beta2,
Packit 67cb25
                       gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  double datB[4],
Packit 67cb25
         datV[4],
Packit 67cb25
         datS[2],
Packit 67cb25
         work[2];
Packit 67cb25
  gsl_matrix_view uv = gsl_matrix_view_array(datB, 2, 2);
Packit 67cb25
  gsl_matrix_view vv = gsl_matrix_view_array(datV, 2, 2);
Packit 67cb25
  gsl_vector_view sv = gsl_vector_view_array(datS, 2);
Packit 67cb25
  gsl_vector_view wv = gsl_vector_view_array(work, 2);
Packit 67cb25
  double B11, B22;
Packit 67cb25
  size_t top = 0;
Packit 67cb25
  double det;
Packit 67cb25
  double cr, sr, cl, sl;
Packit 67cb25
  gsl_vector_view xv, yv;
Packit 67cb25
  int s;
Packit 67cb25
Packit 67cb25
  if (w->needtop)
Packit 67cb25
    top = gen_get_submatrix(w->H, A);
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * Rotate B so that
Packit 67cb25
   *
Packit 67cb25
   * B = [ B11  0  ]
Packit 67cb25
   *     [  0  B22 ]
Packit 67cb25
   *
Packit 67cb25
   * with B11 non-negative
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  gsl_matrix_memcpy(&uv.matrix, B);
Packit 67cb25
  gsl_linalg_SV_decomp(&uv.matrix, &vv.matrix, &sv.vector, &wv.vector);
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * Right now, B = U S V^t, where S = diag(s)
Packit 67cb25
   *
Packit 67cb25
   * The SVD routine may have computed reflection matrices U and V,
Packit 67cb25
   * but it would be much nicer to have rotations since we won't have
Packit 67cb25
   * to use BLAS mat-mat multiplications to update our matrices,
Packit 67cb25
   * and can instead use drot. So convert them to rotations if
Packit 67cb25
   * necessary
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  det = gsl_matrix_get(&vv.matrix, 0, 0) * gsl_matrix_get(&vv.matrix, 1, 1) -
Packit 67cb25
        gsl_matrix_get(&vv.matrix, 0, 1) * gsl_matrix_get(&vv.matrix, 1, 0);
Packit 67cb25
  if (det < 0.0)
Packit 67cb25
    {
Packit 67cb25
      /* V is a reflection, convert it to a rotation by inserting
Packit 67cb25
       * F = [1 0; 0 -1] so that:
Packit 67cb25
       *
Packit 67cb25
       * B = U S [1  0] [1  0] V^t
Packit 67cb25
       *         [0 -1] [0 -1]
Packit 67cb25
       *
Packit 67cb25
       * so S -> S F and V -> V F where F is the reflection matrix
Packit 67cb25
       * We just need to invert S22 since the first column of V
Packit 67cb25
       * will remain unchanged and we can just read off the CS and SN
Packit 67cb25
       * parameters.
Packit 67cb25
       */
Packit 67cb25
      datS[1] = -datS[1];
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  cr = gsl_matrix_get(&vv.matrix, 0, 0);
Packit 67cb25
  sr = gsl_matrix_get(&vv.matrix, 1, 0);
Packit 67cb25
Packit 67cb25
  /* same for U */
Packit 67cb25
  det = gsl_matrix_get(&uv.matrix, 0, 0) * gsl_matrix_get(&uv.matrix, 1, 1) -
Packit 67cb25
        gsl_matrix_get(&uv.matrix, 0, 1) * gsl_matrix_get(&uv.matrix, 1, 0);
Packit 67cb25
  if (det < 0.0)
Packit 67cb25
    datS[1] = -datS[1];
Packit 67cb25
Packit 67cb25
  cl = gsl_matrix_get(&uv.matrix, 0, 0);
Packit 67cb25
  sl = gsl_matrix_get(&uv.matrix, 1, 0);
Packit 67cb25
Packit 67cb25
  B11 = gsl_vector_get(&sv.vector, 0);
Packit 67cb25
  B22 = gsl_vector_get(&sv.vector, 1);
Packit 67cb25
Packit 67cb25
  /* make sure B11 is positive */
Packit 67cb25
  if (B11 < 0.0)
Packit 67cb25
    {
Packit 67cb25
      B11 = -B11;
Packit 67cb25
      B22 = -B22;
Packit 67cb25
      cr = -cr;
Packit 67cb25
      sr = -sr;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * At this point,
Packit 67cb25
   *
Packit 67cb25
   * [ S11  0  ] = [ CSL  SNL ] B [ CSR -SNR ]
Packit 67cb25
   * [  0  S22 ]   [-SNL  CSL ]   [ SNR  CSR ]
Packit 67cb25
   *
Packit 67cb25
   * apply rotations to H and rest of R
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  if (w->compute_s)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_subrow(w->H, top, top, w->size - top);
Packit 67cb25
      yv = gsl_matrix_subrow(w->H, top + 1, top, w->size - top);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
Packit 67cb25
Packit 67cb25
      xv = gsl_matrix_subcolumn(w->H, top, 0, top + 2);
Packit 67cb25
      yv = gsl_matrix_subcolumn(w->H, top + 1, 0, top + 2);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_row(A, 0);
Packit 67cb25
      yv = gsl_matrix_row(A, 1);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
Packit 67cb25
Packit 67cb25
      xv = gsl_matrix_column(A, 0);
Packit 67cb25
      yv = gsl_matrix_column(A, 1);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (w->compute_t)
Packit 67cb25
    {
Packit 67cb25
      if (top != (w->size - 2))
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subrow(w->R, top, top + 2, w->size - top - 2);
Packit 67cb25
          yv = gsl_matrix_subrow(w->R, top + 1, top + 2, w->size - top - 2);
Packit 67cb25
          gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (top != 0)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_subcolumn(w->R, top, 0, top);
Packit 67cb25
          yv = gsl_matrix_subcolumn(w->R, top + 1, 0, top);
Packit 67cb25
          gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (w->Q)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_column(w->Q, top);
Packit 67cb25
      yv = gsl_matrix_column(w->Q, top + 1);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (w->Z)
Packit 67cb25
    {
Packit 67cb25
      xv = gsl_matrix_column(w->Z, top);
Packit 67cb25
      yv = gsl_matrix_column(w->Z, top + 1);
Packit 67cb25
      gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_matrix_set(B, 0, 0, B11);
Packit 67cb25
  gsl_matrix_set(B, 0, 1, 0.0);
Packit 67cb25
  gsl_matrix_set(B, 1, 0, 0.0);
Packit 67cb25
  gsl_matrix_set(B, 1, 1, B22);
Packit 67cb25
Packit 67cb25
  /* if B22 is < 0, make it positive by negating its column */
Packit 67cb25
  if (B22 < 0.0)
Packit 67cb25
    {
Packit 67cb25
      size_t i;
Packit 67cb25
Packit 67cb25
      if (w->compute_s)
Packit 67cb25
        {
Packit 67cb25
          for (i = 0; i < top + 2; ++i)
Packit 67cb25
            gsl_matrix_set(w->H, i, top + 1, -gsl_matrix_get(w->H, i, top + 1));
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          gsl_matrix_set(A, 0, 1, -gsl_matrix_get(A, 0, 1));
Packit 67cb25
          gsl_matrix_set(A, 1, 1, -gsl_matrix_get(A, 1, 1));
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (w->compute_t)
Packit 67cb25
        {
Packit 67cb25
          for (i = 0; i < top + 2; ++i)
Packit 67cb25
            gsl_matrix_set(w->R, i, top + 1, -gsl_matrix_get(w->R, i, top + 1));
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          gsl_matrix_set(B, 0, 1, -gsl_matrix_get(B, 0, 1));
Packit 67cb25
          gsl_matrix_set(B, 1, 1, -gsl_matrix_get(B, 1, 1));
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (w->Z)
Packit 67cb25
        {
Packit 67cb25
          xv = gsl_matrix_column(w->Z, top + 1);
Packit 67cb25
          gsl_vector_scale(&xv.vector, -1.0);
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* our block is now in standard form - compute eigenvalues */
Packit 67cb25
Packit 67cb25
  s = gen_compute_eigenvals(A, B, alpha1, alpha2, beta1, beta2);
Packit 67cb25
Packit 67cb25
  return s;
Packit 67cb25
} /* gen_schur_standardize2() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_compute_eigenvals()
Packit 67cb25
  Compute the complex eigenvalues of a 2-by-2 block
Packit 67cb25
Packit 67cb25
Return: GSL_CONTINUE if block contains real eigenvalues (they are not
Packit 67cb25
        computed)
Packit 67cb25
        GSL_SUCCESS on normal completion
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
gen_compute_eigenvals(gsl_matrix *A, gsl_matrix *B, gsl_complex *alpha1,
Packit 67cb25
                      gsl_complex *alpha2, double *beta1, double *beta2)
Packit 67cb25
{
Packit 67cb25
  double wr1, wr2, wi, scale1, scale2;
Packit 67cb25
  double s1inv;
Packit 67cb25
  double A11, A12, A21, A22;
Packit 67cb25
  double B11, B22;
Packit 67cb25
  double c11r, c11i, c12, c21, c22r, c22i;
Packit 67cb25
  double cz, cq;
Packit 67cb25
  double szr, szi, sqr, sqi;
Packit 67cb25
  double a1r, a1i, a2r, a2i, b1r, b1i, b1a, b2r, b2i, b2a;
Packit 67cb25
  double alphar, alphai;
Packit 67cb25
  double t1, an, bn, tempr, tempi, wabs;
Packit 67cb25
Packit 67cb25
  /*
Packit 67cb25
   * This function is called from gen_schur_standardize2() and
Packit 67cb25
   * its possible the standardization has perturbed the eigenvalues
Packit 67cb25
   * onto the real line - so check for this before computing them
Packit 67cb25
   */
Packit 67cb25
Packit 67cb25
  gsl_schur_gen_eigvals(A, B, &wr1, &wr2, &wi, &scale1, &scale2);
Packit 67cb25
  if (wi == 0.0)
Packit 67cb25
    return GSL_CONTINUE; /* real eigenvalues - continue QZ iteration */
Packit 67cb25
Packit 67cb25
  /* complex eigenvalues - compute alpha and beta */
Packit 67cb25
Packit 67cb25
  s1inv = 1.0 / scale1;
Packit 67cb25
Packit 67cb25
  A11 = gsl_matrix_get(A, 0, 0);
Packit 67cb25
  A12 = gsl_matrix_get(A, 0, 1);
Packit 67cb25
  A21 = gsl_matrix_get(A, 1, 0);
Packit 67cb25
  A22 = gsl_matrix_get(A, 1, 1);
Packit 67cb25
Packit 67cb25
  B11 = gsl_matrix_get(B, 0, 0);
Packit 67cb25
  B22 = gsl_matrix_get(B, 1, 1);
Packit 67cb25
Packit 67cb25
  c11r = scale1 * A11 - wr1 * B11;
Packit 67cb25
  c11i = -wi * B11;
Packit 67cb25
  c12 = scale1 * A12;
Packit 67cb25
  c21 = scale1 * A21;
Packit 67cb25
  c22r = scale1 * A22 - wr1 * B22;
Packit 67cb25
  c22i = -wi * B22;
Packit 67cb25
Packit 67cb25
  if (fabs(c11r) + fabs(c11i) + fabs(c12) >
Packit 67cb25
      fabs(c21) + fabs(c22r) + fabs(c22i))
Packit 67cb25
    {
Packit 67cb25
      t1 = gsl_hypot3(c12, c11r, c11i);
Packit 67cb25
      if (t1 != 0.0)
Packit 67cb25
        {
Packit 67cb25
          cz = c12 / t1;
Packit 67cb25
          szr = -c11r / t1;
Packit 67cb25
          szi = -c11i / t1;
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          cz = 0.0;
Packit 67cb25
          szr = 1.0;
Packit 67cb25
          szi = 0.0;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      cz = hypot(c22r, c22i);
Packit 67cb25
      if (cz <= GSL_DBL_MIN)
Packit 67cb25
        {
Packit 67cb25
          cz = 0.0;
Packit 67cb25
          szr = 1.0;
Packit 67cb25
          szi = 0.0;
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          tempr = c22r / cz;
Packit 67cb25
          tempi = c22i / cz;
Packit 67cb25
          t1 = hypot(cz, c21);
Packit 67cb25
          cz /= t1;
Packit 67cb25
          szr = -c21*tempr / t1;
Packit 67cb25
          szi = c21*tempi / t1;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  an = fabs(A11) + fabs(A12) + fabs(A21) + fabs(A22);
Packit 67cb25
  bn = fabs(B11) + fabs(B22);
Packit 67cb25
  wabs = fabs(wr1) + fabs(wi);
Packit 67cb25
  if (scale1*an > wabs*bn)
Packit 67cb25
    {
Packit 67cb25
      cq = cz * B11;
Packit 67cb25
      if (cq <= GSL_DBL_MIN)
Packit 67cb25
        {
Packit 67cb25
          cq = 0.0;
Packit 67cb25
          sqr = 1.0;
Packit 67cb25
          sqi = 0.0;
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          sqr = szr * B22;
Packit 67cb25
          sqi = -szi * B22;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      a1r = cz * A11 + szr * A12;
Packit 67cb25
      a1i = szi * A12;
Packit 67cb25
      a2r = cz * A21 + szr * A22;
Packit 67cb25
      a2i = szi * A22;
Packit 67cb25
      cq = hypot(a1r, a1i);
Packit 67cb25
      if (cq <= GSL_DBL_MIN)
Packit 67cb25
        {
Packit 67cb25
          cq = 0.0;
Packit 67cb25
          sqr = 1.0;
Packit 67cb25
          sqi = 0.0;
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          tempr = a1r / cq;
Packit 67cb25
          tempi = a1i / cq;
Packit 67cb25
          sqr = tempr * a2r + tempi * a2i;
Packit 67cb25
          sqi = tempi * a2r - tempr * a2i;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  t1 = gsl_hypot3(cq, sqr, sqi);
Packit 67cb25
  cq /= t1;
Packit 67cb25
  sqr /= t1;
Packit 67cb25
  sqi /= t1;
Packit 67cb25
Packit 67cb25
  tempr = sqr*szr - sqi*szi;
Packit 67cb25
  tempi = sqr*szi + sqi*szr;
Packit 67cb25
  b1r = cq*cz*B11 + tempr*B22;
Packit 67cb25
  b1i = tempi*B22;
Packit 67cb25
  b1a = hypot(b1r, b1i);
Packit 67cb25
  b2r = cq*cz*B22 + tempr*B11;
Packit 67cb25
  b2i = -tempi*B11;
Packit 67cb25
  b2a = hypot(b2r, b2i);
Packit 67cb25
Packit 67cb25
  *beta1 = b1a;
Packit 67cb25
  *beta2 = b2a;
Packit 67cb25
  
Packit 67cb25
  alphar = (wr1 * b1a) * s1inv;
Packit 67cb25
  alphai = (wi * b1a) * s1inv;
Packit 67cb25
  GSL_SET_COMPLEX(alpha1, alphar, alphai);
Packit 67cb25
Packit 67cb25
  alphar = (wr1 * b2a) * s1inv;
Packit 67cb25
  alphai = -(wi * b2a) * s1inv;
Packit 67cb25
  GSL_SET_COMPLEX(alpha2, alphar, alphai);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
} /* gen_compute_eigenvals() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_store_eigval1()
Packit 67cb25
  Store eigenvalue of a 1-by-1 block into the alpha and beta
Packit 67cb25
output vectors. This routine ensures that eigenvalues are stored
Packit 67cb25
in the same order as they appear in the Schur form and updates
Packit 67cb25
various internal workspace quantities.
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
gen_store_eigval1(const gsl_matrix *H, const double a, const double b,
Packit 67cb25
                  gsl_vector_complex *alpha,
Packit 67cb25
                  gsl_vector *beta, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  size_t top = gen_get_submatrix(w->H, H);
Packit 67cb25
  gsl_complex z;
Packit 67cb25
Packit 67cb25
  GSL_SET_COMPLEX(&z, a, 0.0);
Packit 67cb25
Packit 67cb25
  gsl_vector_complex_set(alpha, top, z);
Packit 67cb25
  gsl_vector_set(beta, top, b);
Packit 67cb25
Packit 67cb25
  w->n_evals += 1;
Packit 67cb25
  w->n_iter = 0;
Packit 67cb25
  w->eshift = 0.0;
Packit 67cb25
} /* gen_store_eigval1() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_store_eigval2()
Packit 67cb25
  Store eigenvalues of a 2-by-2 block into the alpha and beta
Packit 67cb25
output vectors. This routine ensures that eigenvalues are stored
Packit 67cb25
in the same order as they appear in the Schur form and updates
Packit 67cb25
various internal workspace quantities.
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
gen_store_eigval2(const gsl_matrix *H, const gsl_complex *alpha1,
Packit 67cb25
                  const double beta1, const gsl_complex *alpha2,
Packit 67cb25
                  const double beta2, gsl_vector_complex *alpha,
Packit 67cb25
                  gsl_vector *beta, gsl_eigen_gen_workspace *w)
Packit 67cb25
{
Packit 67cb25
  size_t top = gen_get_submatrix(w->H, H);
Packit 67cb25
Packit 67cb25
  gsl_vector_complex_set(alpha, top, *alpha1);
Packit 67cb25
  gsl_vector_set(beta, top, beta1);
Packit 67cb25
Packit 67cb25
  gsl_vector_complex_set(alpha, top + 1, *alpha2);
Packit 67cb25
  gsl_vector_set(beta, top + 1, beta2);
Packit 67cb25
Packit 67cb25
  w->n_evals += 2;
Packit 67cb25
  w->n_iter = 0;
Packit 67cb25
  w->eshift = 0.0;
Packit 67cb25
} /* gen_store_eigval2() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gen_get_submatrix()
Packit 67cb25
  B is a submatrix of A. The goal of this function is to
Packit 67cb25
compute the indices in A of where the matrix B resides
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static inline size_t
Packit 67cb25
gen_get_submatrix(const gsl_matrix *A, const gsl_matrix *B)
Packit 67cb25
{
Packit 67cb25
  size_t diff;
Packit 67cb25
  double ratio;
Packit 67cb25
  size_t top;
Packit 67cb25
Packit 67cb25
  diff = (size_t) (B->data - A->data);
Packit 67cb25
Packit 67cb25
  /* B is on the diagonal of A, so measure distance in units of
Packit 67cb25
     tda+1 */
Packit 67cb25
Packit 67cb25
  ratio = (double)diff / ((double) (A->tda + 1));
Packit 67cb25
Packit 67cb25
  top = (size_t) floor(ratio);
Packit 67cb25
Packit 67cb25
  return top;
Packit 67cb25
} /* gen_get_submatrix() */
Packit 67cb25
Packit 67cb25
/* Frobenius norm */
Packit 67cb25
inline static double
Packit 67cb25
normF (gsl_matrix * A)
Packit 67cb25
{
Packit 67cb25
  size_t i, j, M = A->size1, N = A->size2;
Packit 67cb25
  double sum = 0.0, scale = 0.0, ssq = 1.0;
Packit 67cb25
Packit 67cb25
  for (i = 0; i < M; i++)
Packit 67cb25
    {
Packit 67cb25
      for (j = 0; j < N; j++)
Packit 67cb25
        {
Packit 67cb25
          double Aij = gsl_matrix_get (A, i, j);
Packit 67cb25
Packit 67cb25
          if (Aij != 0.0)
Packit 67cb25
            {
Packit 67cb25
              double ax = fabs (Aij);
Packit 67cb25
Packit 67cb25
              if (scale < ax)
Packit 67cb25
                {
Packit 67cb25
                  ssq = 1.0 + ssq * (scale / ax) * (scale / ax);
Packit 67cb25
                  scale = ax;
Packit 67cb25
                }
Packit 67cb25
              else
Packit 67cb25
                {
Packit 67cb25
                  ssq += (ax / scale) * (ax / scale);
Packit 67cb25
                }
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  sum = scale * sqrt (ssq);
Packit 67cb25
Packit 67cb25
  return sum;
Packit 67cb25
}