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.
21 #include <gsl/gsl_eigen.h>
22 #include <gsl/gsl_math.h>
23 #include <gsl/gsl_matrix.h>
24 #include <gsl/gsl_vector.h>
25 #include <gsl/gsl_vector_complex.h>
26 #include <gsl/gsl_blas.h>
27 #include <gsl/gsl_complex.h>
28 #include <gsl/gsl_complex_math.h>
31 * This module contains some routines related to manipulating the
32 * Schur form of a matrix which are needed by the eigenvalue solvers
34 * This file contains routines based on original code from LAPACK
35 * which is distributed under the modified BSD license.
38 #define GSL_SCHUR_SMLNUM (2.0 * GSL_DBL_MIN)
39 #define GSL_SCHUR_BIGNUM ((1.0 - GSL_DBL_EPSILON) / GSL_SCHUR_SMLNUM)
42 gsl_schur_gen_eigvals()
43 Compute the eigenvalues of a 2-by-2 generalized block.
45 Inputs: A - 2-by-2 matrix
46 B - 2-by-2 upper triangular matrix
47 wr1 - (output) see notes
48 wr2 - (output) see notes
49 wi - (output) see notes
50 scale1 - (output) see notes
51 scale2 - (output) see notes
59 If the block contains real eigenvalues, then wi is set to 0,
60 and wr1, wr2, scale1, and scale2 are set such that:
65 If the block contains complex eigenvalues, then wr1, wr2, scale1,
66 scale2, and wi are set such that:
68 wr1 = wr2 = scale1 * Re(eval)
69 wi = scale1 * Im(eval)
71 wi is always non-negative
73 2) This routine is based on LAPACK DLAG2
77 gsl_schur_gen_eigvals(const gsl_matrix *A, const gsl_matrix *B, double *wr1,
78 double *wr2, double *wi, double *scale1,
81 const double safemin = GSL_DBL_MIN * 1.0e2;
82 const double safemax = 1.0 / safemin;
83 const double rtmin = sqrt(safemin);
84 const double rtmax = 1.0 / rtmin;
86 double ascale, bscale, bsize;
88 double A11, A12, A21, A22;
90 double binv11, binv22;
92 double as11, as12, as22, abi22;
93 double pp, qq, shift, ss, discr, r;
96 anorm = GSL_MAX(GSL_MAX(fabs(gsl_matrix_get(A, 0, 0)) +
97 fabs(gsl_matrix_get(A, 1, 0)),
98 fabs(gsl_matrix_get(A, 0, 1)) +
99 fabs(gsl_matrix_get(A, 1, 1))),
101 ascale = 1.0 / anorm;
102 A11 = ascale * gsl_matrix_get(A, 0, 0);
103 A12 = ascale * gsl_matrix_get(A, 0, 1);
104 A21 = ascale * gsl_matrix_get(A, 1, 0);
105 A22 = ascale * gsl_matrix_get(A, 1, 1);
107 /* perturb B if necessary to ensure non-singularity */
108 B11 = gsl_matrix_get(B, 0, 0);
109 B12 = gsl_matrix_get(B, 0, 1);
110 B22 = gsl_matrix_get(B, 1, 1);
111 bmin = rtmin * GSL_MAX(fabs(B11),
112 GSL_MAX(fabs(B12), GSL_MAX(fabs(B22), rtmin)));
113 if (fabs(B11) < bmin)
114 B11 = GSL_SIGN(B11) * bmin;
115 if (fabs(B22) < bmin)
116 B22 = GSL_SIGN(B22) * bmin;
119 bnorm = GSL_MAX(fabs(B11), GSL_MAX(fabs(B12) + fabs(B22), safemin));
120 bsize = GSL_MAX(fabs(B11), fabs(B22));
121 bscale = 1.0 / bsize;
126 /* compute larger eigenvalue */
132 if (fabs(s1) <= fabs(s2))
134 as12 = A12 - s1 * B12;
135 as22 = A22 - s1 * B22;
136 ss = A21 * (binv11 * binv22);
137 abi22 = as22 * binv22 - ss * B12;
143 as12 = A12 - s2 * B12;
144 as11 = A11 - s2 * B11;
145 ss = A21 * (binv11 * binv22);
147 pp = 0.5 * (as11 * binv11 + abi22);
152 if (fabs(pp * rtmin) >= 1.0)
154 discr = (rtmin * pp) * (rtmin * pp) + qq * safemin;
155 r = sqrt(fabs(discr)) * rtmax;
157 else if (pp * pp + fabs(qq) <= safemin)
159 discr = (rtmax * pp) * (rtmax * pp) + qq * safemax;
160 r = sqrt(fabs(discr)) * rtmin;
164 discr = pp * pp + qq;
165 r = sqrt(fabs(discr));
168 if (discr >= 0.0 || r == 0.0)
170 double sum = pp + GSL_SIGN(pp) * r;
171 double diff = pp - GSL_SIGN(pp) * r;
172 double wbig = shift + sum;
173 double wsmall = shift + diff;
175 /* compute smaller eigenvalue */
177 if (0.5 * fabs(wbig) > GSL_MAX(fabs(wsmall), safemin))
179 double wdet = (A11*A22 - A12*A21) * (binv11 * binv22);
180 wsmall = wdet / wbig;
183 /* choose (real) eigenvalue closest to 2,2 element of AB^{-1} for wr1 */
186 *wr1 = GSL_MIN(wbig, wsmall);
187 *wr2 = GSL_MAX(wbig, wsmall);
191 *wr1 = GSL_MAX(wbig, wsmall);
192 *wr2 = GSL_MIN(wbig, wsmall);
198 /* complex eigenvalues */
204 /* compute scaling */
206 const double fuzzy1 = 1.0 + 1.0e-5;
207 double c1, c2, c3, c4, c5;
208 double wabs, wsize, wscale;
210 c1 = bsize * (safemin * GSL_MAX(1.0, ascale));
211 c2 = safemin * GSL_MAX(1.0, bnorm);
212 c3 = bsize * safemin;
213 if (ascale <= 1.0 && bsize <= 1.0)
214 c4 = GSL_MIN(1.0, (ascale / safemin) * bsize);
218 if (ascale <= 1.0 || bsize <= 1.0)
219 c5 = GSL_MIN(1.0, ascale * bsize);
223 /* scale first eigenvalue */
224 wabs = fabs(*wr1) + fabs(*wi);
225 wsize = GSL_MAX(safemin,
227 GSL_MAX(fuzzy1 * (wabs*c2 + c3),
228 GSL_MIN(c4, 0.5 * GSL_MAX(wabs, c5)))));
231 wscale = 1.0 / wsize;
234 *scale1 = (GSL_MAX(ascale, bsize) * wscale) *
235 GSL_MIN(ascale, bsize);
239 *scale1 = (GSL_MIN(ascale, bsize) * wscale) *
240 GSL_MAX(ascale, bsize);
253 *scale1 = ascale * bsize;
257 /* scale second eigenvalue if real */
260 wsize = GSL_MAX(safemin,
262 GSL_MAX(fuzzy1 * (fabs(*wr2) * c2 + c3),
263 GSL_MIN(c4, 0.5 * GSL_MAX(fabs(*wr2), c5)))));
266 wscale = 1.0 / wsize;
269 *scale2 = (GSL_MAX(ascale, bsize) * wscale) *
270 GSL_MIN(ascale, bsize);
274 *scale2 = (GSL_MIN(ascale, bsize) * wscale) *
275 GSL_MAX(ascale, bsize);
282 *scale2 = ascale * bsize;
288 } /* gsl_schur_gen_eigvals() */
291 gsl_schur_solve_equation()
293 Solve the equation which comes up in the back substitution
294 when computing eigenvectors corresponding to real eigenvalues.
295 The equation that is solved is:
301 A is n-by-n with n = 1 or 2
302 D is a n-by-n diagonal matrix
303 b and x are n-by-1 real vectors
304 s is a scaling factor set by this function to prevent overflow in x
306 Inputs: ca - coefficient multiplying A
307 A - square matrix (n-by-n)
308 z - real scalar (eigenvalue)
309 d1 - (1,1) element in diagonal matrix D
310 d2 - (2,2) element in diagonal matrix D
311 b - right hand side vector
312 x - (output) where to store solution
313 s - (output) scale factor
314 xnorm - (output) infinity norm of X
315 smin - lower bound on singular values of A - if ca*A - z*D
316 is less than this value, we'll use smin*I instead.
317 This value should be a safe distance above underflow.
321 Notes: 1) A and b are not changed on output
322 2) Based on lapack routine DLALN2
326 gsl_schur_solve_equation(double ca, const gsl_matrix *A, double z,
327 double d1, double d2, const gsl_vector *b,
328 gsl_vector *x, double *s, double *xnorm,
337 double c, /* denominator */
340 /* we have a 1-by-1 (real) scalar system to solve */
342 c = ca * gsl_matrix_get(A, 0, 0) - z * d1;
352 /* check scaling for x = b / c */
353 bnorm = fabs(gsl_vector_get(b, 0));
354 if (cnorm < 1.0 && bnorm > 1.0)
356 if (bnorm > GSL_SCHUR_BIGNUM*cnorm)
361 gsl_vector_set(x, 0, gsl_vector_get(b, 0) * scale / c);
362 *xnorm = fabs(gsl_vector_get(x, 0));
371 double ur11, ur12, ur22, ur11r;
377 size_t ipivot[4][4] = { { 0, 1, 2, 3 },
381 int rswap[4] = { 0, 1, 0, 1 };
382 int zswap[4] = { 0, 0, 1, 1 };
385 * we have a 2-by-2 real system to solve:
387 * ( ca * [ A11 A12 ] - z * [ D1 0 ] ) [ x1 ] = [ b1 ]
388 * ( [ A21 A22 ] [ 0 D2 ] ) [ x2 ] [ b2 ]
396 * compute the real part of C = ca*A - z*D - use column ordering
397 * here since porting from lapack
399 cr[0][0] = ca * gsl_matrix_get(A, 0, 0) - z * d1;
400 cr[1][1] = ca * gsl_matrix_get(A, 1, 1) - z * d2;
401 cr[0][1] = ca * gsl_matrix_get(A, 1, 0);
402 cr[1][0] = ca * gsl_matrix_get(A, 0, 1);
404 /* find the largest element in C */
407 for (j = 0; j < 4; ++j)
409 if (fabs(crv[j]) > cmax)
416 bval1 = gsl_vector_get(b, 0);
417 bval2 = gsl_vector_get(b, 1);
419 /* if norm(C) < smin, use smin*I */
423 bnorm = GSL_MAX(fabs(bval1), fabs(bval2));
424 if (smin < 1.0 && bnorm > 1.0)
426 if (bnorm > GSL_SCHUR_BIGNUM*smin)
430 gsl_vector_set(x, 0, temp * bval1);
431 gsl_vector_set(x, 1, temp * bval2);
432 *xnorm = temp * bnorm;
437 /* gaussian elimination with complete pivoting */
439 cr21 = crv[ipivot[1][icmax]];
440 ur12 = crv[ipivot[2][icmax]];
441 cr22 = crv[ipivot[3][icmax]];
444 ur22 = cr22 - ur12 * lr21;
446 /* if smaller pivot < smin, use smin */
447 if (fabs(ur22) < smin)
462 bbnd = GSL_MAX(fabs(b1 * (ur22 * ur11r)), fabs(b2));
463 if (bbnd > 1.0 && fabs(ur22) < 1.0)
465 if (bbnd >= GSL_SCHUR_BIGNUM * fabs(ur22))
469 x2 = (b2 * scale) / ur22;
470 x1 = (scale * b1) * ur11r - x2 * (ur11r * ur12);
473 gsl_vector_set(x, 0, x2);
474 gsl_vector_set(x, 1, x1);
478 gsl_vector_set(x, 0, x1);
479 gsl_vector_set(x, 1, x2);
482 *xnorm = GSL_MAX(fabs(x1), fabs(x2));
484 /* further scaling if norm(A) norm(X) > overflow */
485 if (*xnorm > 1.0 && cmax > 1.0)
487 if (*xnorm > GSL_SCHUR_BIGNUM / cmax)
489 temp = cmax / GSL_SCHUR_BIGNUM;
490 gsl_blas_dscal(temp, x);
499 } /* gsl_schur_solve_equation() */
502 gsl_schur_solve_equation_z()
504 Solve the equation which comes up in the back substitution
505 when computing eigenvectors corresponding to complex eigenvalues.
506 The equation that is solved is:
512 A is n-by-n with n = 1 or 2
513 D is a n-by-n diagonal matrix
514 b and x are n-by-1 complex vectors
515 s is a scaling factor set by this function to prevent overflow in x
517 Inputs: ca - coefficient multiplying A
518 A - square matrix (n-by-n)
519 z - complex scalar (eigenvalue)
520 d1 - (1,1) element in diagonal matrix D
521 d2 - (2,2) element in diagonal matrix D
522 b - right hand side vector
523 x - (output) where to store solution
524 s - (output) scale factor
525 xnorm - (output) infinity norm of X
526 smin - lower bound on singular values of A - if ca*A - z*D
527 is less than this value, we'll use smin*I instead.
528 This value should be a safe distance above underflow.
530 Notes: 1) A and b are not changed on output
531 2) Based on lapack routine DLALN2
535 gsl_schur_solve_equation_z(double ca, const gsl_matrix *A, gsl_complex *z,
536 double d1, double d2,
537 const gsl_vector_complex *b,
538 gsl_vector_complex *x, double *s, double *xnorm,
547 double cr, /* denominator */
550 gsl_complex bval, c, xval, tmp;
552 /* we have a 1-by-1 (complex) scalar system to solve */
554 /* c = ca*a - z*d1 */
555 cr = ca * gsl_matrix_get(A, 0, 0) - GSL_REAL(*z) * d1;
556 ci = -GSL_IMAG(*z) * d1;
557 cnorm = fabs(cr) + fabs(ci);
567 /* check scaling for x = b / c */
568 bval = gsl_vector_complex_get(b, 0);
569 bnorm = fabs(GSL_REAL(bval)) + fabs(GSL_IMAG(bval));
570 if (cnorm < 1.0 && bnorm > 1.0)
572 if (bnorm > GSL_SCHUR_BIGNUM*cnorm)
577 GSL_SET_COMPLEX(&tmp, scale*GSL_REAL(bval), scale*GSL_IMAG(bval));
578 GSL_SET_COMPLEX(&c, cr, ci);
579 xval = gsl_complex_div(tmp, c);
581 gsl_vector_complex_set(x, 0, xval);
583 *xnorm = fabs(GSL_REAL(xval)) + fabs(GSL_IMAG(xval));
587 double cr[2][2], ci[2][2];
590 gsl_complex bval1, bval2;
591 gsl_complex xval1, xval2;
596 double ur11, ur12, ur22, ui11, ui12, ui22, ur11r, ui11r;
600 double cr21, cr22, ci21, ci22;
601 double br1, bi1, br2, bi2, bbnd;
603 size_t ipivot[4][4] = { { 0, 1, 2, 3 },
607 int rswap[4] = { 0, 1, 0, 1 };
608 int zswap[4] = { 0, 0, 1, 1 };
611 * complex 2-by-2 system:
613 * ( ca * [ A11 A12 ] - z * [ D1 0 ] ) [ X1 ] = [ B1 ]
614 * ( [ A21 A22 ] [ 0 D2] ) [ X2 ] [ B2 ]
618 * where the X and B values are complex.
625 * compute the real part of C = ca*A - z*D - use column ordering
626 * here since porting from lapack
628 cr[0][0] = ca*gsl_matrix_get(A, 0, 0) - GSL_REAL(*z)*d1;
629 cr[1][1] = ca*gsl_matrix_get(A, 1, 1) - GSL_REAL(*z)*d2;
630 cr[0][1] = ca*gsl_matrix_get(A, 1, 0);
631 cr[1][0] = ca*gsl_matrix_get(A, 0, 1);
633 /* compute the imaginary part */
634 ci[0][0] = -GSL_IMAG(*z) * d1;
637 ci[1][1] = -GSL_IMAG(*z) * d2;
642 for (j = 0; j < 4; ++j)
644 if (fabs(crv[j]) + fabs(civ[j]) > cmax)
646 cmax = fabs(crv[j]) + fabs(civ[j]);
651 bval1 = gsl_vector_complex_get(b, 0);
652 bval2 = gsl_vector_complex_get(b, 1);
654 /* if norm(C) < smin, use smin*I */
657 bnorm = GSL_MAX(fabs(GSL_REAL(bval1)) + fabs(GSL_IMAG(bval1)),
658 fabs(GSL_REAL(bval2)) + fabs(GSL_IMAG(bval2)));
659 if (smin < 1.0 && bnorm > 1.0)
661 if (bnorm > GSL_SCHUR_BIGNUM*smin)
666 xval1 = gsl_complex_mul_real(bval1, temp);
667 xval2 = gsl_complex_mul_real(bval2, temp);
668 gsl_vector_complex_set(x, 0, xval1);
669 gsl_vector_complex_set(x, 1, xval2);
670 *xnorm = temp * bnorm;
675 /* gaussian elimination with complete pivoting */
678 cr21 = crv[ipivot[1][icmax]];
679 ci21 = civ[ipivot[1][icmax]];
680 ur12 = crv[ipivot[2][icmax]];
681 ui12 = civ[ipivot[2][icmax]];
682 cr22 = crv[ipivot[3][icmax]];
683 ci22 = civ[ipivot[3][icmax]];
685 if (icmax == 0 || icmax == 3)
687 /* off diagonals of pivoted C are real */
688 if (fabs(ur11) > fabs(ui11))
691 ur11r = 1.0 / (ur11 * (1.0 + temp*temp));
692 ui11r = -temp * ur11r;
697 ui11r = -1.0 / (ui11 * (1.0 + temp*temp));
702 ur12s = ur12 * ur11r;
703 ui12s = ur12 * ui11r;
704 ur22 = cr22 - ur12 * lr21;
705 ui22 = ci22 - ur12 * li21;
709 /* diagonals of pivoted C are real */
714 ur12s = ur12 * ur11r;
715 ui12s = ui12 * ur11r;
716 ur22 = cr22 - ur12 * lr21 + ui12 * li21;
717 ui22 = -ur12 * li21 - ui12 * lr21;
720 u22abs = fabs(ur22) + fabs(ui22);
722 /* if smaller pivot < smin, use smin */
731 br2 = GSL_REAL(bval1);
732 bi2 = GSL_IMAG(bval1);
733 br1 = GSL_REAL(bval2);
734 bi1 = GSL_IMAG(bval2);
738 br1 = GSL_REAL(bval1);
739 bi1 = GSL_IMAG(bval1);
740 br2 = GSL_REAL(bval2);
741 bi2 = GSL_IMAG(bval2);
744 br2 += li21*bi1 - lr21*br1;
745 bi2 -= li21*br1 + lr21*bi1;
746 bbnd = GSL_MAX((fabs(br1) + fabs(bi1)) *
747 (u22abs * (fabs(ur11r) + fabs(ui11r))),
748 fabs(br2) + fabs(bi2));
749 if (bbnd > 1.0 && u22abs < 1.0)
751 if (bbnd >= GSL_SCHUR_BIGNUM*u22abs)
761 GSL_SET_COMPLEX(&b1, br2, bi2);
762 GSL_SET_COMPLEX(&b2, ur22, ui22);
763 xval2 = gsl_complex_div(b1, b2);
765 xr1 = ur11r*br1 - ui11r*bi1 - ur12s*GSL_REAL(xval2) + ui12s*GSL_IMAG(xval2);
766 xi1 = ui11r*br1 + ur11r*bi1 - ui12s*GSL_REAL(xval2) - ur12s*GSL_IMAG(xval2);
767 GSL_SET_COMPLEX(&xval1, xr1, xi1);
771 gsl_vector_complex_set(x, 0, xval2);
772 gsl_vector_complex_set(x, 1, xval1);
776 gsl_vector_complex_set(x, 0, xval1);
777 gsl_vector_complex_set(x, 1, xval2);
780 *xnorm = GSL_MAX(fabs(GSL_REAL(xval1)) + fabs(GSL_IMAG(xval1)),
781 fabs(GSL_REAL(xval2)) + fabs(GSL_IMAG(xval2)));
783 /* further scaling if norm(A) norm(X) > overflow */
784 if (*xnorm > 1.0 && cmax > 1.0)
786 if (*xnorm > GSL_SCHUR_BIGNUM / cmax)
788 temp = cmax / GSL_SCHUR_BIGNUM;
789 gsl_blas_zdscal(temp, x);
798 } /* gsl_schur_solve_equation_z() */