3 * Copyright (C) 2006, 2007 Patrick Alken
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or (at
8 * your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <gsl/gsl_eigen.h>
25 #include <gsl/gsl_linalg.h>
26 #include <gsl/gsl_math.h>
27 #include <gsl/gsl_blas.h>
28 #include <gsl/gsl_vector.h>
29 #include <gsl/gsl_vector_complex.h>
30 #include <gsl/gsl_matrix.h>
33 * This module computes the eigenvalues of a real generalized
34 * eigensystem A x = \lambda B x. Left and right Schur vectors
35 * are optionally computed as well.
37 * Based on the algorithm from Moler and Stewart
38 * [1] C. Moler, G. Stewart, "An Algorithm for Generalized Matrix
39 * Eigenvalue Problems", SIAM J. Numer. Anal., Vol 10, No 2, 1973.
41 * This algorithm is also described in the book
42 * [2] Golub & Van Loan, "Matrix Computations" (3rd ed), algorithm 7.7.3
44 * This file contains routines based on original code from LAPACK
45 * which is distributed under the modified BSD license.
48 #define GEN_ESHIFT_COEFF (1.736)
50 static void gen_schur_decomp(gsl_matrix *H, gsl_matrix *R,
51 gsl_vector_complex *alpha, gsl_vector *beta,
52 gsl_eigen_gen_workspace *w);
53 static inline int gen_qzstep(gsl_matrix *H, gsl_matrix *R,
54 gsl_eigen_gen_workspace *w);
55 static inline void gen_qzstep_d(gsl_matrix *H, gsl_matrix *R,
56 gsl_eigen_gen_workspace *w);
57 static void gen_tri_split_top(gsl_matrix *H, gsl_matrix *R,
58 gsl_eigen_gen_workspace *w);
59 static inline void gen_tri_chase_zero(gsl_matrix *H, gsl_matrix *R,
61 gsl_eigen_gen_workspace *w);
62 static inline void gen_tri_zero_H(gsl_matrix *H, gsl_matrix *R,
63 gsl_eigen_gen_workspace *w);
64 static inline size_t gen_search_small_elements(gsl_matrix *H,
67 gsl_eigen_gen_workspace *w);
68 static int gen_schur_standardize1(gsl_matrix *A, gsl_matrix *B,
69 double *alphar, double *beta,
70 gsl_eigen_gen_workspace *w);
71 static int gen_schur_standardize2(gsl_matrix *A, gsl_matrix *B,
74 double *beta1, double *beta2,
75 gsl_eigen_gen_workspace *w);
76 static int gen_compute_eigenvals(gsl_matrix *A, gsl_matrix *B,
78 gsl_complex *alpha2, double *beta1,
80 static void gen_store_eigval1(const gsl_matrix *H, const double a,
81 const double b, gsl_vector_complex *alpha,
83 gsl_eigen_gen_workspace *w);
84 static void gen_store_eigval2(const gsl_matrix *H,
85 const gsl_complex *alpha1,
87 const gsl_complex *alpha2,
89 gsl_vector_complex *alpha,
91 gsl_eigen_gen_workspace *w);
92 static inline size_t gen_get_submatrix(const gsl_matrix *A,
96 inline static double normF (gsl_matrix * A);
97 inline static void create_givens (const double a, const double b, double *c, double *s);
100 gsl_eigen_gen_alloc()
102 Allocate a workspace for solving the generalized eigenvalue problem.
103 The size of this workspace is O(n)
105 Inputs: n - size of matrices
107 Return: pointer to workspace
110 gsl_eigen_gen_workspace *
111 gsl_eigen_gen_alloc(const size_t n)
113 gsl_eigen_gen_workspace *w;
117 GSL_ERROR_NULL ("matrix dimension must be positive integer",
121 w = calloc (1, sizeof (gsl_eigen_gen_workspace));
125 GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
129 w->max_iterations = 30 * n;
145 w->work = gsl_vector_alloc(n);
149 gsl_eigen_gen_free(w);
150 GSL_ERROR_NULL ("failed to allocate space for additional workspace", GSL_ENOMEM);
154 } /* gsl_eigen_gen_alloc() */
162 gsl_eigen_gen_free (gsl_eigen_gen_workspace * w)
165 gsl_vector_free(w->work);
168 } /* gsl_eigen_gen_free() */
171 gsl_eigen_gen_params()
172 Set parameters which define how we solve the eigenvalue problem
174 Inputs: compute_s - 1 if we want to compute S, 0 if not
175 compute_t - 1 if we want to compute T, 0 if not
176 balance - 1 if we want to balance matrices, 0 if not
183 gsl_eigen_gen_params (const int compute_s, const int compute_t,
184 const int balance, gsl_eigen_gen_workspace *w)
186 w->compute_s = compute_s;
187 w->compute_t = compute_t;
188 } /* gsl_eigen_gen_params() */
193 Solve the generalized eigenvalue problem
197 for the eigenvalues \lambda.
199 Inputs: A - general real matrix
200 B - general real matrix
201 alpha - where to store eigenvalue numerators
202 beta - where to store eigenvalue denominators
205 Return: success or error
209 gsl_eigen_gen (gsl_matrix * A, gsl_matrix * B, gsl_vector_complex * alpha,
210 gsl_vector * beta, gsl_eigen_gen_workspace * w)
212 const size_t N = A->size1;
214 /* check matrix and vector sizes */
218 GSL_ERROR ("matrix must be square to compute eigenvalues", GSL_ENOTSQR);
220 else if ((N != B->size1) || (N != B->size2))
222 GSL_ERROR ("B matrix dimensions must match A", GSL_EBADLEN);
224 else if (alpha->size != N || beta->size != N)
226 GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
228 else if (w->size != N)
230 GSL_ERROR ("matrix size does not match workspace", GSL_EBADLEN);
236 /* compute the Hessenberg-Triangular reduction of (A, B) */
237 gsl_linalg_hesstri_decomp(A, B, w->Q, w->Z, w->work);
239 /* save pointers to original matrices */
247 /* determine if we need to compute top indices in QZ step */
248 w->needtop = w->Q != 0 || w->Z != 0 || w->compute_t || w->compute_s;
250 /* compute matrix norms */
254 /* compute tolerances and scaling factors */
255 w->atol = GSL_MAX(GSL_DBL_MIN, GSL_DBL_EPSILON * anorm);
256 w->btol = GSL_MAX(GSL_DBL_MIN, GSL_DBL_EPSILON * bnorm);
257 w->ascale = 1.0 / GSL_MAX(GSL_DBL_MIN, anorm);
258 w->bscale = 1.0 / GSL_MAX(GSL_DBL_MIN, bnorm);
260 /* compute the generalized Schur decomposition and eigenvalues */
261 gen_schur_decomp(A, B, alpha, beta, w);
268 } /* gsl_eigen_gen() */
273 Solve the generalized eigenvalue problem
277 for the eigenvalues \lambda. Optionally compute left and/or right
278 Schur vectors Q and Z which satisfy:
283 where (S, T) is the generalized Schur form of (A, B)
285 Inputs: A - general real matrix
286 B - general real matrix
287 alpha - where to store eigenvalue numerators
288 beta - where to store eigenvalue denominators
289 Q - if non-null, where to store left Schur vectors
290 Z - if non-null, where to store right Schur vectors
293 Return: success or error
297 gsl_eigen_gen_QZ (gsl_matrix * A, gsl_matrix * B,
298 gsl_vector_complex * alpha, gsl_vector * beta,
299 gsl_matrix * Q, gsl_matrix * Z,
300 gsl_eigen_gen_workspace * w)
302 if (Q && (A->size1 != Q->size1 || A->size1 != Q->size2))
304 GSL_ERROR("Q matrix has wrong dimensions", GSL_EBADLEN);
306 else if (Z && (A->size1 != Z->size1 || A->size1 != Z->size2))
308 GSL_ERROR("Z matrix has wrong dimensions", GSL_EBADLEN);
317 s = gsl_eigen_gen(A, B, alpha, beta, w);
324 } /* gsl_eigen_gen_QZ() */
326 /********************************************
327 * INTERNAL ROUTINES *
328 ********************************************/
332 Compute the generalized Schur decomposition of the matrix pencil
333 (H, R) which is in Hessenberg-Triangular form
335 Inputs: H - upper hessenberg matrix
336 R - upper triangular matrix
337 alpha - (output) where to store eigenvalue numerators
338 beta - (output) where to store eigenvalue denominators
343 Notes: 1) w->n_evals is updated to keep track of how many eigenvalues
348 gen_schur_decomp(gsl_matrix *H, gsl_matrix *R, gsl_vector_complex *alpha,
349 gsl_vector *beta, gsl_eigen_gen_workspace *w)
352 gsl_matrix_view h, r;
353 gsl_matrix_view vh, vr;
354 size_t q; /* index of small subdiagonal element */
355 gsl_complex z1, z2; /* complex values */
362 h = gsl_matrix_submatrix(H, 0, 0, N, N);
363 r = gsl_matrix_submatrix(R, 0, 0, N, N);
365 while ((N > 1) && (w->n_iter)++ < w->max_iterations)
367 q = gen_search_small_elements(&h.matrix, &r.matrix, &flag, w);
371 /* no small elements found - do a QZ sweep */
372 s = gen_qzstep(&h.matrix, &r.matrix, w);
374 if (s == GSL_CONTINUE)
377 * (h, r) is a 2-by-2 block with complex eigenvalues -
378 * standardize and read off eigenvalues
380 s = gen_schur_standardize2(&h.matrix,
387 if (s != GSL_SUCCESS)
390 * if we get here, then the standardization process
391 * perturbed the eigenvalues onto the real line -
392 * continue QZ iteration to break them into 1-by-1
398 gen_store_eigval2(&h.matrix, &z1, a, &z2, b, alpha, beta, w);
403 } /* if (flag == 0) */
409 * the leading element of R is zero, split off a block
412 gen_tri_split_top(&h.matrix, &r.matrix, w);
417 * we found a small element on the diagonal of R - chase the
418 * zero to the bottom of the active block and then zero
419 * H(n, n - 1) to split off a 1-by-1 block
423 gen_tri_chase_zero(&h.matrix, &r.matrix, q, w);
425 /* now zero H(n, n - 1) */
426 gen_tri_zero_H(&h.matrix, &r.matrix, w);
429 /* continue so the next iteration detects the zero in H */
434 * a small subdiagonal element of H was found - one or two
435 * eigenvalues have converged or the matrix has split into
436 * two smaller matrices
442 * the last subdiagonal element of the hessenberg matrix is 0 -
443 * H_{NN} / R_{NN} is a real eigenvalue - standardize so
447 vh = gsl_matrix_submatrix(&h.matrix, q, q, 1, 1);
448 vr = gsl_matrix_submatrix(&r.matrix, q, q, 1, 1);
449 gen_schur_standardize1(&vh.matrix, &vr.matrix, &a, &b, w);
451 gen_store_eigval1(&vh.matrix, a, b, alpha, beta, w);
454 h = gsl_matrix_submatrix(&h.matrix, 0, 0, N, N);
455 r = gsl_matrix_submatrix(&r.matrix, 0, 0, N, N);
457 else if (q == (N - 2))
459 /* bottom right 2-by-2 block may have converged */
461 vh = gsl_matrix_submatrix(&h.matrix, q, q, 2, 2);
462 vr = gsl_matrix_submatrix(&r.matrix, q, q, 2, 2);
463 s = gen_schur_standardize2(&vh.matrix,
470 if (s != GSL_SUCCESS)
473 * this 2-by-2 block contains real eigenvalues that
474 * have not yet separated into 1-by-1 blocks -
475 * recursively call gen_schur_decomp() to finish off
478 gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
482 /* we got 2 complex eigenvalues */
484 gen_store_eigval2(&vh.matrix, &z1, a, &z2, b, alpha, beta, w);
488 h = gsl_matrix_submatrix(&h.matrix, 0, 0, N, N);
489 r = gsl_matrix_submatrix(&r.matrix, 0, 0, N, N);
493 /* H_{11} / R_{11} is an eigenvalue */
495 vh = gsl_matrix_submatrix(&h.matrix, 0, 0, 1, 1);
496 vr = gsl_matrix_submatrix(&r.matrix, 0, 0, 1, 1);
497 gen_schur_standardize1(&vh.matrix, &vr.matrix, &a, &b, w);
499 gen_store_eigval1(&vh.matrix, a, b, alpha, beta, w);
502 h = gsl_matrix_submatrix(&h.matrix, 1, 1, N, N);
503 r = gsl_matrix_submatrix(&r.matrix, 1, 1, N, N);
507 /* upper left 2-by-2 block may have converged */
509 vh = gsl_matrix_submatrix(&h.matrix, 0, 0, 2, 2);
510 vr = gsl_matrix_submatrix(&r.matrix, 0, 0, 2, 2);
511 s = gen_schur_standardize2(&vh.matrix,
518 if (s != GSL_SUCCESS)
521 * this 2-by-2 block contains real eigenvalues that
522 * have not yet separated into 1-by-1 blocks -
523 * recursively call gen_schur_decomp() to finish off
526 gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
530 /* we got 2 complex eigenvalues */
531 gen_store_eigval2(&vh.matrix, &z1, a, &z2, b, alpha, beta, w);
535 h = gsl_matrix_submatrix(&h.matrix, 2, 2, N, N);
536 r = gsl_matrix_submatrix(&r.matrix, 2, 2, N, N);
541 * There is a zero element on the subdiagonal somewhere
542 * in the middle of the matrix - we can now operate
543 * separately on the two submatrices split by this
544 * element. q is the row index of the zero element.
547 /* operate on lower right (N - q)-by-(N - q) block first */
548 vh = gsl_matrix_submatrix(&h.matrix, q, q, N - q, N - q);
549 vr = gsl_matrix_submatrix(&r.matrix, q, q, N - q, N - q);
550 gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
552 /* operate on upper left q-by-q block */
553 vh = gsl_matrix_submatrix(&h.matrix, 0, 0, q, q);
554 vr = gsl_matrix_submatrix(&r.matrix, 0, 0, q, q);
555 gen_schur_decomp(&vh.matrix, &vr.matrix, alpha, beta, w);
559 } /* while ((N > 1) && (w->n_iter)++ < w->max_iterations) */
561 /* handle special case of N = 1 */
565 gen_schur_standardize1(&h.matrix, &r.matrix, &a, &b, w);
566 gen_store_eigval1(&h.matrix, a, b, alpha, beta, w);
568 } /* gen_schur_decomp() */
572 This routine determines what type of QZ step to perform on
573 the generalized matrix pair (H, R). If the pair is 3-by-3 or bigger,
574 we look at the bottom right 2-by-2 block. If this block has complex
575 eigenvalues, we perform a Francis double shift QZ sweep. If it
576 has real eigenvalues, we perform an implicit single shift QZ sweep.
578 If the pair is 2-by-2 with real eigenvalues, we perform a single
579 shift sweep. If it has complex eigenvalues, we return GSL_CONTINUE
580 to notify the calling function that a 2-by-2 block with complex
581 eigenvalues has converged, so that it may then call
582 gen_schur_standardize2(). In the real eigenvalue case, we want to
583 continue doing QZ sweeps to break it up into two 1-by-1 blocks.
585 See LAPACK routine DHGEQZ and [1] for more information.
587 Inputs: H - upper Hessenberg matrix (at least 2-by-2)
588 R - upper triangular matrix (at least 2-by-2)
591 Return: GSL_SUCCESS on normal completion
592 GSL_CONTINUE if we detect a 2-by-2 block with complex eigenvalues
596 gen_qzstep(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
598 const size_t N = H->size1;
599 gsl_matrix_view vh, vr; /* views of bottom right 2-by-2 block */
601 double scale1, scale2, scale;
602 double cs, sn; /* givens rotation */
603 double temp, /* temporary variables */
605 size_t j; /* looping */
606 gsl_vector_view xv, yv; /* temporary views */
610 if (w->n_iter % 10 == 0)
613 * Exceptional shift - we have gone 10 iterations without finding
614 * a new eigenvalue, do a single shift sweep with an
618 if ((GSL_DBL_MIN * w->max_iterations) *
619 fabs(gsl_matrix_get(H, N - 2, N - 1)) <
620 fabs(gsl_matrix_get(R, N - 2, N - 2)))
622 w->eshift += gsl_matrix_get(H, N - 2, N - 1) /
623 gsl_matrix_get(R, N - 2, N - 2);
626 w->eshift += 1.0 / (GSL_DBL_MIN * w->max_iterations);
628 if ((w->eshift < GSL_DBL_EPSILON) &&
629 (GSL_DBL_MIN * w->max_iterations) *
630 fabs(gsl_matrix_get(H, N - 1, N - 2)) <
631 fabs(gsl_matrix_get(R, N - 2, N - 2)))
633 w->eshift = GEN_ESHIFT_COEFF *
634 (w->ascale * gsl_matrix_get(H, N - 1, N - 2)) /
635 (w->bscale * gsl_matrix_get(R, N - 2, N - 2));
644 * Compute generalized eigenvalues of bottom right 2-by-2 block
645 * to be used as shifts - wr1 is the Wilkinson shift
648 vh = gsl_matrix_submatrix(H, N - 2, N - 2, 2, 2);
649 vr = gsl_matrix_submatrix(R, N - 2, N - 2, 2, 2);
650 gsl_schur_gen_eigvals(&vh.matrix,
660 /* complex eigenvalues */
665 * its a 2-by-2 block with complex eigenvalues - notify
666 * the calling function to deflate
668 return (GSL_CONTINUE);
672 /* do a francis double shift sweep */
673 gen_qzstep_d(H, R, w);
680 /* real eigenvalues - perform single shift QZ step */
682 temp = GSL_MIN(w->ascale, 1.0) * (0.5 / GSL_DBL_MIN);
684 scale = temp / scale1;
688 temp = GSL_MIN(w->bscale, 1.0) * (0.5 / GSL_DBL_MIN);
689 if (fabs(wr1) > temp)
690 scale = GSL_MIN(scale, temp / fabs(wr1));
697 /* get absolute index of this matrix relative to original matrix */
698 top = gen_get_submatrix(w->H, H);
701 temp = scale1*gsl_matrix_get(H, 0, 0) - wr1*gsl_matrix_get(R, 0, 0);
702 temp2 = scale1*gsl_matrix_get(H, 1, 0);
704 create_givens(temp, temp2, &cs, &sn);
707 for (j = 0; j < N - 1; ++j)
711 temp = gsl_matrix_get(H, j, j - 1);
712 temp2 = gsl_matrix_get(H, j + 1, j - 1);
713 create_givens(temp, temp2, &cs, &sn);
716 /* apply to column (j - 1) */
717 temp = cs * gsl_matrix_get(H, j, j - 1) +
718 sn * gsl_matrix_get(H, j + 1, j - 1);
719 gsl_matrix_set(H, j, j - 1, temp);
720 gsl_matrix_set(H, j + 1, j - 1, 0.0);
723 /* apply G to H(j:j+1,:) and T(j:j+1,:) */
727 xv = gsl_matrix_subrow(w->H, top + j, top + j, w->size - top - j);
728 yv = gsl_matrix_subrow(w->H, top + j + 1, top + j, w->size - top - j);
732 xv = gsl_matrix_subrow(H, j, j, N - j);
733 yv = gsl_matrix_subrow(H, j + 1, j, N - j);
736 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
740 xv = gsl_matrix_subrow(w->R, top + j, top + j, w->size - top - j);
741 yv = gsl_matrix_subrow(w->R, top + j + 1, top + j, w->size - top - j);
745 xv = gsl_matrix_subrow(R, j, j, N - j);
746 yv = gsl_matrix_subrow(R, j + 1, j, N - j);
749 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
753 /* accumulate Q: Q -> QG */
754 xv = gsl_matrix_column(w->Q, top + j);
755 yv = gsl_matrix_column(w->Q, top + j + 1);
756 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
759 temp = gsl_matrix_get(R, j + 1, j + 1);
760 temp2 = gsl_matrix_get(R, j + 1, j);
761 create_givens(temp, temp2, &cs, &sn);
763 rows = GSL_MIN(j + 3, N);
767 xv = gsl_matrix_subcolumn(w->H, top + j, 0, top + rows);
768 yv = gsl_matrix_subcolumn(w->H, top + j + 1, 0, top + rows);
772 xv = gsl_matrix_subcolumn(H, j, 0, rows);
773 yv = gsl_matrix_subcolumn(H, j + 1, 0, rows);
776 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
778 rows = GSL_MIN(j + 2, N);
782 xv = gsl_matrix_subcolumn(w->R, top + j, 0, top + rows);
783 yv = gsl_matrix_subcolumn(w->R, top + j + 1, 0, top + rows);
787 xv = gsl_matrix_subcolumn(R, j, 0, rows);
788 yv = gsl_matrix_subcolumn(R, j + 1, 0, rows);
791 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
795 /* accumulate Z: Z -> ZG */
796 xv = gsl_matrix_column(w->Z, top + j);
797 yv = gsl_matrix_column(w->Z, top + j + 1);
798 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
800 } /* for (j = 0; j < N - 1; ++j) */
807 Perform an implicit double shift QZ step.
809 See Golub & Van Loan, "Matrix Computations" (3rd ed), algorithm 7.7.2
811 Inputs: H - upper Hessenberg matrix (at least 3-by-3)
812 R - upper triangular matrix (at least 3-by-3)
817 gen_qzstep_d(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
819 const size_t N = H->size1;
820 size_t j; /* looping */
821 double dat[3]; /* householder vector */
822 double tau; /* householder coefficient */
823 gsl_vector_view v2, v3; /* views into 'dat' */
824 gsl_matrix_view m; /* temporary view */
827 size_t top; /* location of H in original matrix */
829 double AB11, /* various matrix element ratios */
841 v2 = gsl_vector_view_array(dat, 2);
842 v3 = gsl_vector_view_array(dat, 3);
846 /* get absolute index of this matrix relative to original matrix */
847 top = gen_get_submatrix(w->H, H);
851 * Similar to the QR method, we take the shifts to be the two
852 * zeros of the problem
854 * det[H(n-1:n,n-1:n) - s*R(n-1:n,n-1:n)] = 0
856 * The initial householder vector elements are then given by
857 * Eq. 4.1 of [1], which are designed to reduce errors when
858 * off diagonal elements are small.
861 ABMM = (w->ascale * gsl_matrix_get(H, N - 2, N - 2)) /
862 (w->bscale * gsl_matrix_get(R, N - 2, N - 2));
863 ABNN = (w->ascale * gsl_matrix_get(H, N - 1, N - 1)) /
864 (w->bscale * gsl_matrix_get(R, N - 1, N - 1));
865 AB11 = (w->ascale * gsl_matrix_get(H, 0, 0)) /
866 (w->bscale * gsl_matrix_get(R, 0, 0));
867 AB22 = (w->ascale * gsl_matrix_get(H, 1, 1)) /
868 (w->bscale * gsl_matrix_get(R, 1, 1));
869 AMNBNN = (w->ascale * gsl_matrix_get(H, N - 2, N - 1)) /
870 (w->bscale * gsl_matrix_get(R, N - 1, N - 1));
871 ANMBMM = (w->ascale * gsl_matrix_get(H, N - 1, N - 2)) /
872 (w->bscale * gsl_matrix_get(R, N - 2, N - 2));
873 BMNBNN = gsl_matrix_get(R, N - 2, N - 1) /
874 gsl_matrix_get(R, N - 1, N - 1);
875 A21B11 = (w->ascale * gsl_matrix_get(H, 1, 0)) /
876 (w->bscale * gsl_matrix_get(R, 0, 0));
877 A12B22 = (w->ascale * gsl_matrix_get(H, 0, 1)) /
878 (w->bscale * gsl_matrix_get(R, 1, 1));
879 A32B22 = (w->ascale * gsl_matrix_get(H, 2, 1)) /
880 (w->bscale * gsl_matrix_get(R, 1, 1));
881 B12B22 = gsl_matrix_get(R, 0, 1) / gsl_matrix_get(R, 1, 1);
884 * These are the Eqs (4.1) of [1], just multiplied by the factor
887 dat[0] = (ABMM - AB11) * (ABNN - AB11) - (AMNBNN * ANMBMM) +
888 (ANMBMM * BMNBNN * AB11) + (A12B22 - (AB11 * B12B22)) * A21B11;
889 dat[1] = ((AB22 - AB11) - (A21B11 * B12B22) - (ABMM - AB11) -
890 (ABNN - AB11) + (ANMBMM * BMNBNN)) * A21B11;
891 dat[2] = A32B22 * A21B11;
893 scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]);
901 for (j = 0; j < N - 2; ++j)
903 r = GSL_MIN(j + 4, N);
906 * Find householder Q so that
908 * Q [x y z]^t = [ * 0 0 ]^t
911 tau = gsl_linalg_householder_transform(&v3.vector);
916 * q is the initial column to start applying the householder
917 * transformation. The GSL_MAX() simply ensures we don't
918 * try to apply it to column (-1), since we are zeroing out
919 * column (j - 1) except for the first iteration which
920 * introduces the bulge.
922 q = (size_t) GSL_MAX(0, (int)j - 1);
924 /* H -> QH, R -> QR */
929 * We are computing the Schur form S, so we need to
930 * transform the whole matrix H
932 m = gsl_matrix_submatrix(w->H,
937 gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
941 /* just transform the active block */
942 m = gsl_matrix_submatrix(H, j, q, 3, N - q);
943 gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
949 * We are computing the Schur form T, so we need to
950 * transform the whole matrix R
952 m = gsl_matrix_submatrix(w->R,
957 gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
961 /* just transform the active block */
962 m = gsl_matrix_submatrix(R, j, j, 3, N - j);
963 gsl_linalg_householder_hm(tau, &v3.vector, &m.matrix);
968 /* accumulate the transformation into Q */
969 m = gsl_matrix_submatrix(w->Q, 0, top + j, w->size, 3);
970 gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
972 } /* if (tau != 0.0) */
975 * Find householder Z so that
977 * [ r_{j+2,j} r_{j+2, j+1}, r_{j+2, j+2} ] Z = [ 0 0 * ]
979 * This isn't exactly what gsl_linalg_householder_transform
980 * does, so we need to rotate the input vector so it preserves
981 * the last element, and then rotate it back afterwards.
983 * So instead of transforming [x y z], we transform [z x y],
984 * and the resulting HH vector [1 v2 v3] -> [v2 v3 1] but
985 * then needs to be scaled to have the first element = 1, so
986 * it becomes [1 v3/v2 1/v2] (tau must also be scaled accordingly).
989 dat[0] = gsl_matrix_get(R, j + 2, j + 2);
990 dat[1] = gsl_matrix_get(R, j + 2, j);
991 dat[2] = gsl_matrix_get(R, j + 2, j + 1);
992 scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]);
1000 tau = gsl_linalg_householder_transform(&v3.vector);
1005 tmp = gsl_vector_get(&v3.vector, 1);
1006 gsl_vector_set(&v3.vector, 1, gsl_vector_get(&v3.vector, 2)/tmp);
1007 gsl_vector_set(&v3.vector, 2, 1.0 / tmp);
1010 /* H -> HZ, R -> RZ */
1014 m = gsl_matrix_submatrix(w->H, 0, top + j, top + r, 3);
1015 gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
1019 m = gsl_matrix_submatrix(H, 0, j, r, 3);
1020 gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
1025 m = gsl_matrix_submatrix(w->R, 0, top + j, top + j + 3, 3);
1026 gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
1030 m = gsl_matrix_submatrix(R, 0, j, j + 3, 3);
1031 gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
1036 /* accumulate transformation into Z */
1037 m = gsl_matrix_submatrix(w->Z, 0, top + j, w->size, 3);
1038 gsl_linalg_householder_mh(tau, &v3.vector, &m.matrix);
1040 } /* if (tau != 0.0) */
1043 * Find householder Z so that
1045 * [ r_{j+1,j} r_{j+1, j+1} ] Z = [ 0 * ]
1048 dat[0] = gsl_matrix_get(R, j + 1, j + 1);
1049 dat[1] = gsl_matrix_get(R, j + 1, j);
1050 scale = fabs(dat[0]) + fabs(dat[1]);
1057 tau = gsl_linalg_householder_transform(&v2.vector);
1062 tmp = gsl_vector_get(&v2.vector, 1);
1063 gsl_vector_set(&v2.vector, 1, 1.0 / tmp);
1066 /* H -> HZ, R -> RZ */
1070 m = gsl_matrix_submatrix(w->H, 0, top + j, top + r, 2);
1071 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1075 m = gsl_matrix_submatrix(H, 0, j, r, 2);
1076 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1081 m = gsl_matrix_submatrix(w->R, 0, top + j, top + j + 3, 2);
1082 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1086 m = gsl_matrix_submatrix(R, 0, j, j + 3, 2);
1087 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1092 /* accumulate transformation into Z */
1093 m = gsl_matrix_submatrix(w->Z, 0, top + j, w->size, 2);
1094 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1096 } /* if (tau != 0.0) */
1098 dat[0] = gsl_matrix_get(H, j + 1, j);
1099 dat[1] = gsl_matrix_get(H, j + 2, j);
1101 dat[2] = gsl_matrix_get(H, j + 3, j);
1103 scale = fabs(dat[0]) + fabs(dat[1]) + fabs(dat[2]);
1110 } /* for (j = 0; j < N - 2; ++j) */
1113 * Find Householder Q so that
1115 * Q [ x y ]^t = [ * 0 ]^t
1118 scale = fabs(dat[0]) + fabs(dat[1]);
1125 tau = gsl_linalg_householder_transform(&v2.vector);
1129 m = gsl_matrix_submatrix(w->H,
1133 w->size - top - N + 3);
1134 gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
1138 m = gsl_matrix_submatrix(H, N - 2, N - 3, 2, 3);
1139 gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
1144 m = gsl_matrix_submatrix(w->R,
1148 w->size - top - N + 2);
1149 gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
1153 m = gsl_matrix_submatrix(R, N - 2, N - 2, 2, 2);
1154 gsl_linalg_householder_hm(tau, &v2.vector, &m.matrix);
1159 /* accumulate the transformation into Q */
1160 m = gsl_matrix_submatrix(w->Q, 0, top + N - 2, w->size, 2);
1161 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1165 * Find Householder Z so that
1167 * [ b_{n,n-1} b_{nn} ] Z = [ 0 * ]
1170 dat[0] = gsl_matrix_get(R, N - 1, N - 1);
1171 dat[1] = gsl_matrix_get(R, N - 1, N - 2);
1172 scale = fabs(dat[0]) + fabs(dat[1]);
1179 tau = gsl_linalg_householder_transform(&v2.vector);
1182 tmp = gsl_vector_get(&v2.vector, 1);
1183 gsl_vector_set(&v2.vector, 1, 1.0 / tmp);
1188 m = gsl_matrix_submatrix(w->H, 0, top + N - 2, top + N, 2);
1189 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1193 m = gsl_matrix_submatrix(H, 0, N - 2, N, 2);
1194 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1199 m = gsl_matrix_submatrix(w->R, 0, top + N - 2, top + N, 2);
1200 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1204 m = gsl_matrix_submatrix(R, 0, N - 2, N, 2);
1205 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1210 /* accumulate the transformation into Z */
1211 m = gsl_matrix_submatrix(w->Z, 0, top + N - 2, w->size, 2);
1212 gsl_linalg_householder_mh(tau, &v2.vector, &m.matrix);
1214 } /* gen_qzstep_d() */
1218 This routine is called when the leading element on the diagonal of R
1219 has become negligible. Split off a 1-by-1 block at the top.
1221 Inputs: H - upper hessenberg matrix
1222 R - upper triangular matrix
1227 gen_tri_split_top(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
1229 const size_t N = H->size1;
1232 gsl_vector_view xv, yv;
1235 top = gen_get_submatrix(w->H, H);
1239 create_givens(gsl_matrix_get(H, j, j),
1240 gsl_matrix_get(H, j + 1, j),
1247 xv = gsl_matrix_subrow(w->H, top + j, top, w->size - top);
1248 yv = gsl_matrix_subrow(w->H, top + j + 1, top, w->size - top);
1252 xv = gsl_matrix_row(H, j);
1253 yv = gsl_matrix_row(H, j + 1);
1256 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1257 gsl_matrix_set(H, j + 1, j, 0.0);
1261 xv = gsl_matrix_subrow(w->R, top + j, top + 1, w->size - top - 1);
1262 yv = gsl_matrix_subrow(w->R, top + j + 1, top + 1, w->size - top - 1);
1266 xv = gsl_matrix_subrow(R, j, 1, N - 1);
1267 yv = gsl_matrix_subrow(R, j + 1, 1, N - 1);
1270 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1274 xv = gsl_matrix_column(w->Q, top + j);
1275 yv = gsl_matrix_column(w->Q, top + j + 1);
1276 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1278 } /* gen_tri_split_top() */
1281 gen_tri_chase_zero()
1282 This routine is called when an element on the diagonal of R
1283 has become negligible. Chase the zero to the bottom of the active
1284 block so we can split off a 1-by-1 block.
1286 Inputs: H - upper hessenberg matrix
1287 R - upper triangular matrix
1288 q - index such that R(q,q) = 0 (q must be > 0)
1293 gen_tri_chase_zero(gsl_matrix *H, gsl_matrix *R, size_t q,
1294 gsl_eigen_gen_workspace *w)
1296 const size_t N = H->size1;
1299 gsl_vector_view xv, yv;
1302 top = gen_get_submatrix(w->H, H);
1304 for (j = q; j < N - 1; ++j)
1306 create_givens(gsl_matrix_get(R, j, j + 1),
1307 gsl_matrix_get(R, j + 1, j + 1),
1314 xv = gsl_matrix_subrow(w->R, top + j, top + j + 1, w->size - top - j - 1);
1315 yv = gsl_matrix_subrow(w->R, top + j + 1, top + j + 1, w->size - top - j - 1);
1319 xv = gsl_matrix_subrow(R, j, j + 1, N - j - 1);
1320 yv = gsl_matrix_subrow(R, j + 1, j + 1, N - j - 1);
1323 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1324 gsl_matrix_set(R, j + 1, j + 1, 0.0);
1328 xv = gsl_matrix_subrow(w->H, top + j, top + j - 1, w->size - top - j + 1);
1329 yv = gsl_matrix_subrow(w->H, top + j + 1, top + j - 1, w->size - top - j + 1);
1333 xv = gsl_matrix_subrow(H, j, j - 1, N - j + 1);
1334 yv = gsl_matrix_subrow(H, j + 1, j - 1, N - j + 1);
1337 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1342 xv = gsl_matrix_column(w->Q, top + j);
1343 yv = gsl_matrix_column(w->Q, top + j + 1);
1344 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1347 create_givens(gsl_matrix_get(H, j + 1, j),
1348 gsl_matrix_get(H, j + 1, j - 1),
1355 xv = gsl_matrix_subcolumn(w->H, top + j, 0, top + j + 2);
1356 yv = gsl_matrix_subcolumn(w->H, top + j - 1, 0, top + j + 2);
1360 xv = gsl_matrix_subcolumn(H, j, 0, j + 2);
1361 yv = gsl_matrix_subcolumn(H, j - 1, 0, j + 2);
1364 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1365 gsl_matrix_set(H, j + 1, j - 1, 0.0);
1369 xv = gsl_matrix_subcolumn(w->R, top + j, 0, top + j + 1);
1370 yv = gsl_matrix_subcolumn(w->R, top + j - 1, 0, top + j + 1);
1374 xv = gsl_matrix_subcolumn(R, j, 0, j + 1);
1375 yv = gsl_matrix_subcolumn(R, j - 1, 0, j + 1);
1378 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1383 xv = gsl_matrix_column(w->Z, top + j);
1384 yv = gsl_matrix_column(w->Z, top + j - 1);
1385 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1388 } /* gen_tri_chase_zero() */
1392 Companion function to get_tri_chase_zero(). After the zero on
1393 the diagonal of R has been chased to the bottom, we zero the element
1394 H(n, n - 1) in order to split off a 1-by-1 block.
1398 gen_tri_zero_H(gsl_matrix *H, gsl_matrix *R, gsl_eigen_gen_workspace *w)
1400 const size_t N = H->size1;
1403 gsl_vector_view xv, yv;
1406 top = gen_get_submatrix(w->H, H);
1408 create_givens(gsl_matrix_get(H, N - 1, N - 1),
1409 gsl_matrix_get(H, N - 1, N - 2),
1416 xv = gsl_matrix_subcolumn(w->H, top + N - 1, 0, top + N);
1417 yv = gsl_matrix_subcolumn(w->H, top + N - 2, 0, top + N);
1421 xv = gsl_matrix_column(H, N - 1);
1422 yv = gsl_matrix_column(H, N - 2);
1425 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1427 gsl_matrix_set(H, N - 1, N - 2, 0.0);
1431 xv = gsl_matrix_subcolumn(w->R, top + N - 1, 0, top + N - 1);
1432 yv = gsl_matrix_subcolumn(w->R, top + N - 2, 0, top + N - 1);
1436 xv = gsl_matrix_subcolumn(R, N - 1, 0, N - 1);
1437 yv = gsl_matrix_subcolumn(R, N - 2, 0, N - 1);
1440 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1445 xv = gsl_matrix_column(w->Z, top + N - 1);
1446 yv = gsl_matrix_column(w->Z, top + N - 2);
1447 gsl_blas_drot(&xv.vector, &yv.vector, cs, sn);
1449 } /* gen_tri_zero_H() */
1452 gen_search_small_elements()
1453 This routine searches for small elements in the matrix pencil
1454 (H, R) to determine if any eigenvalues have converged.
1458 1. Test if the Hessenberg matrix has a small subdiagonal element:
1459 H(i, i - 1) < tolerance
1461 2. Test if the Triangular matrix has a small diagonal element:
1466 (A) Neither test passed: in this case 'flag' is set to 0 and the
1469 (B) Test 1 passes and 2 does not: in this case 'flag' is set to 1
1470 and we return the row index i such that H(i, i - 1) < tol
1472 (C) Test 2 passes and 1 does not: in this case 'flag' is set to 2
1473 and we return the index i such that R(i, i) < tol
1475 (D) Tests 1 and 2 both pass: in this case 'flag' is set to 3 and
1476 we return the index i such that H(i, i - 1) < tol and R(i, i) < tol
1478 Inputs: H - upper Hessenberg matrix
1479 R - upper Triangular matrix
1480 flag - (output) flag set on output (see above)
1486 static inline size_t
1487 gen_search_small_elements(gsl_matrix *H, gsl_matrix *R,
1488 int *flag, gsl_eigen_gen_workspace *w)
1490 const size_t N = H->size1;
1496 for (k = (int) N - 1; k >= 0; --k)
1500 if (i != 0 && fabs(gsl_matrix_get(H, i, i - 1)) <= w->atol)
1502 gsl_matrix_set(H, i, i - 1, 0.0);
1506 if (fabs(gsl_matrix_get(R, i, i)) < w->btol)
1508 gsl_matrix_set(R, i, i, 0.0);
1512 if (pass1 && !pass2) /* case B */
1517 else if (!pass1 && pass2) /* case C */
1522 else if (pass1 && pass2) /* case D */
1529 /* neither test passed: case A */
1533 } /* gen_search_subdiag_small_elements() */
1536 gen_schur_standardize1()
1537 This function is called when a 1-by-1 block has converged -
1538 convert the block to standard form and update the Schur forms and
1539 vectors if required. Standard form here means that the diagonal
1540 element of B is positive.
1542 Inputs: A - 1-by-1 matrix in Schur form S
1543 B - 1-by-1 matrix in Schur form T
1544 alphar - where to store real part of eigenvalue numerator
1545 beta - where to store eigenvalue denominator
1552 gen_schur_standardize1(gsl_matrix *A, gsl_matrix *B, double *alphar,
1553 double *beta, gsl_eigen_gen_workspace *w)
1559 * it is a 1-by-1 block - the only requirement is that
1560 * B_{00} is > 0, so if it isn't apply a -I transformation
1562 if (gsl_matrix_get(B, 0, 0) < 0.0)
1565 top = gen_get_submatrix(w->H, A);
1569 for (i = 0; i <= top; ++i)
1570 gsl_matrix_set(w->R, i, top, -gsl_matrix_get(w->R, i, top));
1573 gsl_matrix_set(B, 0, 0, -gsl_matrix_get(B, 0, 0));
1577 for (i = 0; i <= top; ++i)
1578 gsl_matrix_set(w->H, i, top, -gsl_matrix_get(w->H, i, top));
1581 gsl_matrix_set(A, 0, 0, -gsl_matrix_get(A, 0, 0));
1585 for (i = 0; i < w->size; ++i)
1586 gsl_matrix_set(w->Z, i, top, -gsl_matrix_get(w->Z, i, top));
1590 *alphar = gsl_matrix_get(A, 0, 0);
1591 *beta = gsl_matrix_get(B, 0, 0);
1594 } /* gen_schur_standardize1() */
1597 gen_schur_standardize2()
1598 This function is called when a 2-by-2 generalized block has
1599 converged. Convert the block to standard form, which means B
1602 B = [ B11 0 ] with B11, B22 non-negative
1605 If the resulting block (A, B) has complex eigenvalues, they are
1606 computed. Otherwise, the function will return GSL_CONTINUE to
1607 notify caller that we need to do more single shift sweeps to
1608 convert the 2-by-2 block into two 1-by-1 blocks.
1610 Inputs: A - 2-by-2 submatrix of schur form S
1611 B - 2-by-2 submatrix of schur form T
1612 alpha1 - (output) where to store eigenvalue 1 numerator
1613 alpha2 - (output) where to store eigenvalue 2 numerator
1614 beta1 - (output) where to store eigenvalue 1 denominator
1615 beta2 - (output) where to store eigenvalue 2 denominator
1618 Return: GSL_SUCCESS if block has complex eigenvalues (they are computed)
1619 GSL_CONTINUE if block has real eigenvalues (they are not computed)
1623 gen_schur_standardize2(gsl_matrix *A, gsl_matrix *B, gsl_complex *alpha1,
1624 gsl_complex *alpha2, double *beta1, double *beta2,
1625 gsl_eigen_gen_workspace *w)
1631 gsl_matrix_view uv = gsl_matrix_view_array(datB, 2, 2);
1632 gsl_matrix_view vv = gsl_matrix_view_array(datV, 2, 2);
1633 gsl_vector_view sv = gsl_vector_view_array(datS, 2);
1634 gsl_vector_view wv = gsl_vector_view_array(work, 2);
1638 double cr, sr, cl, sl;
1639 gsl_vector_view xv, yv;
1643 top = gen_get_submatrix(w->H, A);
1651 * with B11 non-negative
1654 gsl_matrix_memcpy(&uv.matrix, B);
1655 gsl_linalg_SV_decomp(&uv.matrix, &vv.matrix, &sv.vector, &wv.vector);
1658 * Right now, B = U S V^t, where S = diag(s)
1660 * The SVD routine may have computed reflection matrices U and V,
1661 * but it would be much nicer to have rotations since we won't have
1662 * to use BLAS mat-mat multiplications to update our matrices,
1663 * and can instead use drot. So convert them to rotations if
1667 det = gsl_matrix_get(&vv.matrix, 0, 0) * gsl_matrix_get(&vv.matrix, 1, 1) -
1668 gsl_matrix_get(&vv.matrix, 0, 1) * gsl_matrix_get(&vv.matrix, 1, 0);
1671 /* V is a reflection, convert it to a rotation by inserting
1672 * F = [1 0; 0 -1] so that:
1674 * B = U S [1 0] [1 0] V^t
1677 * so S -> S F and V -> V F where F is the reflection matrix
1678 * We just need to invert S22 since the first column of V
1679 * will remain unchanged and we can just read off the CS and SN
1685 cr = gsl_matrix_get(&vv.matrix, 0, 0);
1686 sr = gsl_matrix_get(&vv.matrix, 1, 0);
1689 det = gsl_matrix_get(&uv.matrix, 0, 0) * gsl_matrix_get(&uv.matrix, 1, 1) -
1690 gsl_matrix_get(&uv.matrix, 0, 1) * gsl_matrix_get(&uv.matrix, 1, 0);
1694 cl = gsl_matrix_get(&uv.matrix, 0, 0);
1695 sl = gsl_matrix_get(&uv.matrix, 1, 0);
1697 B11 = gsl_vector_get(&sv.vector, 0);
1698 B22 = gsl_vector_get(&sv.vector, 1);
1700 /* make sure B11 is positive */
1712 * [ S11 0 ] = [ CSL SNL ] B [ CSR -SNR ]
1713 * [ 0 S22 ] [-SNL CSL ] [ SNR CSR ]
1715 * apply rotations to H and rest of R
1720 xv = gsl_matrix_subrow(w->H, top, top, w->size - top);
1721 yv = gsl_matrix_subrow(w->H, top + 1, top, w->size - top);
1722 gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
1724 xv = gsl_matrix_subcolumn(w->H, top, 0, top + 2);
1725 yv = gsl_matrix_subcolumn(w->H, top + 1, 0, top + 2);
1726 gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
1730 xv = gsl_matrix_row(A, 0);
1731 yv = gsl_matrix_row(A, 1);
1732 gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
1734 xv = gsl_matrix_column(A, 0);
1735 yv = gsl_matrix_column(A, 1);
1736 gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
1741 if (top != (w->size - 2))
1743 xv = gsl_matrix_subrow(w->R, top, top + 2, w->size - top - 2);
1744 yv = gsl_matrix_subrow(w->R, top + 1, top + 2, w->size - top - 2);
1745 gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
1750 xv = gsl_matrix_subcolumn(w->R, top, 0, top);
1751 yv = gsl_matrix_subcolumn(w->R, top + 1, 0, top);
1752 gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
1758 xv = gsl_matrix_column(w->Q, top);
1759 yv = gsl_matrix_column(w->Q, top + 1);
1760 gsl_blas_drot(&xv.vector, &yv.vector, cl, sl);
1765 xv = gsl_matrix_column(w->Z, top);
1766 yv = gsl_matrix_column(w->Z, top + 1);
1767 gsl_blas_drot(&xv.vector, &yv.vector, cr, sr);
1770 gsl_matrix_set(B, 0, 0, B11);
1771 gsl_matrix_set(B, 0, 1, 0.0);
1772 gsl_matrix_set(B, 1, 0, 0.0);
1773 gsl_matrix_set(B, 1, 1, B22);
1775 /* if B22 is < 0, make it positive by negating its column */
1782 for (i = 0; i < top + 2; ++i)
1783 gsl_matrix_set(w->H, i, top + 1, -gsl_matrix_get(w->H, i, top + 1));
1787 gsl_matrix_set(A, 0, 1, -gsl_matrix_get(A, 0, 1));
1788 gsl_matrix_set(A, 1, 1, -gsl_matrix_get(A, 1, 1));
1793 for (i = 0; i < top + 2; ++i)
1794 gsl_matrix_set(w->R, i, top + 1, -gsl_matrix_get(w->R, i, top + 1));
1798 gsl_matrix_set(B, 0, 1, -gsl_matrix_get(B, 0, 1));
1799 gsl_matrix_set(B, 1, 1, -gsl_matrix_get(B, 1, 1));
1804 xv = gsl_matrix_column(w->Z, top + 1);
1805 gsl_vector_scale(&xv.vector, -1.0);
1809 /* our block is now in standard form - compute eigenvalues */
1811 s = gen_compute_eigenvals(A, B, alpha1, alpha2, beta1, beta2);
1814 } /* gen_schur_standardize2() */
1817 gen_compute_eigenvals()
1818 Compute the complex eigenvalues of a 2-by-2 block
1820 Return: GSL_CONTINUE if block contains real eigenvalues (they are not
1822 GSL_SUCCESS on normal completion
1826 gen_compute_eigenvals(gsl_matrix *A, gsl_matrix *B, gsl_complex *alpha1,
1827 gsl_complex *alpha2, double *beta1, double *beta2)
1829 double wr1, wr2, wi, scale1, scale2;
1831 double A11, A12, A21, A22;
1833 double c11r, c11i, c12, c21, c22r, c22i;
1835 double szr, szi, sqr, sqi;
1836 double a1r, a1i, a2r, a2i, b1r, b1i, b1a, b2r, b2i, b2a;
1837 double alphar, alphai;
1838 double t1, an, bn, tempr, tempi, wabs;
1841 * This function is called from gen_schur_standardize2() and
1842 * its possible the standardization has perturbed the eigenvalues
1843 * onto the real line - so check for this before computing them
1846 gsl_schur_gen_eigvals(A, B, &wr1, &wr2, &wi, &scale1, &scale2);
1848 return GSL_CONTINUE; /* real eigenvalues - continue QZ iteration */
1850 /* complex eigenvalues - compute alpha and beta */
1852 s1inv = 1.0 / scale1;
1854 A11 = gsl_matrix_get(A, 0, 0);
1855 A12 = gsl_matrix_get(A, 0, 1);
1856 A21 = gsl_matrix_get(A, 1, 0);
1857 A22 = gsl_matrix_get(A, 1, 1);
1859 B11 = gsl_matrix_get(B, 0, 0);
1860 B22 = gsl_matrix_get(B, 1, 1);
1862 c11r = scale1 * A11 - wr1 * B11;
1866 c22r = scale1 * A22 - wr1 * B22;
1869 if (fabs(c11r) + fabs(c11i) + fabs(c12) >
1870 fabs(c21) + fabs(c22r) + fabs(c22i))
1872 t1 = gsl_hypot3(c12, c11r, c11i);
1888 cz = hypot(c22r, c22i);
1889 if (cz <= GSL_DBL_MIN)
1899 t1 = hypot(cz, c21);
1901 szr = -c21*tempr / t1;
1902 szi = c21*tempi / t1;
1906 an = fabs(A11) + fabs(A12) + fabs(A21) + fabs(A22);
1907 bn = fabs(B11) + fabs(B22);
1908 wabs = fabs(wr1) + fabs(wi);
1909 if (scale1*an > wabs*bn)
1912 if (cq <= GSL_DBL_MIN)
1926 a1r = cz * A11 + szr * A12;
1928 a2r = cz * A21 + szr * A22;
1930 cq = hypot(a1r, a1i);
1931 if (cq <= GSL_DBL_MIN)
1941 sqr = tempr * a2r + tempi * a2i;
1942 sqi = tempi * a2r - tempr * a2i;
1946 t1 = gsl_hypot3(cq, sqr, sqi);
1951 tempr = sqr*szr - sqi*szi;
1952 tempi = sqr*szi + sqi*szr;
1953 b1r = cq*cz*B11 + tempr*B22;
1955 b1a = hypot(b1r, b1i);
1956 b2r = cq*cz*B22 + tempr*B11;
1958 b2a = hypot(b2r, b2i);
1963 alphar = (wr1 * b1a) * s1inv;
1964 alphai = (wi * b1a) * s1inv;
1965 GSL_SET_COMPLEX(alpha1, alphar, alphai);
1967 alphar = (wr1 * b2a) * s1inv;
1968 alphai = -(wi * b2a) * s1inv;
1969 GSL_SET_COMPLEX(alpha2, alphar, alphai);
1972 } /* gen_compute_eigenvals() */
1976 Store eigenvalue of a 1-by-1 block into the alpha and beta
1977 output vectors. This routine ensures that eigenvalues are stored
1978 in the same order as they appear in the Schur form and updates
1979 various internal workspace quantities.
1983 gen_store_eigval1(const gsl_matrix *H, const double a, const double b,
1984 gsl_vector_complex *alpha,
1985 gsl_vector *beta, gsl_eigen_gen_workspace *w)
1987 size_t top = gen_get_submatrix(w->H, H);
1990 GSL_SET_COMPLEX(&z, a, 0.0);
1992 gsl_vector_complex_set(alpha, top, z);
1993 gsl_vector_set(beta, top, b);
1998 } /* gen_store_eigval1() */
2002 Store eigenvalues of a 2-by-2 block into the alpha and beta
2003 output vectors. This routine ensures that eigenvalues are stored
2004 in the same order as they appear in the Schur form and updates
2005 various internal workspace quantities.
2009 gen_store_eigval2(const gsl_matrix *H, const gsl_complex *alpha1,
2010 const double beta1, const gsl_complex *alpha2,
2011 const double beta2, gsl_vector_complex *alpha,
2012 gsl_vector *beta, gsl_eigen_gen_workspace *w)
2014 size_t top = gen_get_submatrix(w->H, H);
2016 gsl_vector_complex_set(alpha, top, *alpha1);
2017 gsl_vector_set(beta, top, beta1);
2019 gsl_vector_complex_set(alpha, top + 1, *alpha2);
2020 gsl_vector_set(beta, top + 1, beta2);
2025 } /* gen_store_eigval2() */
2029 B is a submatrix of A. The goal of this function is to
2030 compute the indices in A of where the matrix B resides
2033 static inline size_t
2034 gen_get_submatrix(const gsl_matrix *A, const gsl_matrix *B)
2040 diff = (size_t) (B->data - A->data);
2042 /* B is on the diagonal of A, so measure distance in units of
2045 ratio = (double)diff / ((double) (A->tda + 1));
2047 top = (size_t) floor(ratio);
2050 } /* gen_get_submatrix() */
2052 /* Frobenius norm */
2053 inline static double
2054 normF (gsl_matrix * A)
2056 size_t i, j, M = A->size1, N = A->size2;
2057 double sum = 0.0, scale = 0.0, ssq = 1.0;
2059 for (i = 0; i < M; i++)
2061 for (j = 0; j < N; j++)
2063 double Aij = gsl_matrix_get (A, i, j);
2067 double ax = fabs (Aij);
2071 ssq = 1.0 + ssq * (scale / ax) * (scale / ax);
2076 ssq += (ax / scale) * (ax / scale);
2083 sum = scale * sqrt (ssq);
2088 /* Generate a Givens rotation (cos,sin) which takes v=(x,y) to (|v|,0)
2090 From Golub and Van Loan, "Matrix Computations", Section 5.1.8 */
2093 create_givens (const double a, const double b, double *c, double *s)
2100 else if (fabs (b) > fabs (a))
2103 double s1 = 1.0 / sqrt (1 + t * t);
2110 double c1 = 1.0 / sqrt (1 + t * t);