3 * Copyright (C) 2006 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_complex.h>
25 #include <gsl/gsl_complex_math.h>
26 #include <gsl/gsl_eigen.h>
27 #include <gsl/gsl_linalg.h>
28 #include <gsl/gsl_math.h>
29 #include <gsl/gsl_blas.h>
30 #include <gsl/gsl_cblas.h>
31 #include <gsl/gsl_vector.h>
32 #include <gsl/gsl_vector_complex.h>
33 #include <gsl/gsl_matrix.h>
36 * This module computes the eigenvalues and eigenvectors of a real
37 * nonsymmetric matrix.
39 * This file contains routines based on original code from LAPACK
40 * which is distributed under the modified BSD license. The LAPACK
41 * routines used are DTREVC and DLALN2.
44 #define GSL_NONSYMMV_SMLNUM (2.0 * GSL_DBL_MIN)
45 #define GSL_NONSYMMV_BIGNUM ((1.0 - GSL_DBL_EPSILON) / GSL_NONSYMMV_SMLNUM)
47 static void nonsymmv_get_right_eigenvectors(gsl_matrix *T, gsl_matrix *Z,
48 gsl_vector_complex *eval,
49 gsl_matrix_complex *evec,
50 gsl_eigen_nonsymmv_workspace *w);
51 static void nonsymmv_normalize_eigenvectors(gsl_vector_complex *eval,
52 gsl_matrix_complex *evec);
55 gsl_eigen_nonsymmv_alloc()
57 Allocate a workspace for solving the nonsymmetric eigenvalue problem.
58 The size of this workspace is O(5n).
60 Inputs: n - size of matrices
62 Return: pointer to workspace
65 gsl_eigen_nonsymmv_workspace *
66 gsl_eigen_nonsymmv_alloc(const size_t n)
68 gsl_eigen_nonsymmv_workspace *w;
72 GSL_ERROR_NULL ("matrix dimension must be positive integer",
76 w = (gsl_eigen_nonsymmv_workspace *)
77 calloc (1, sizeof (gsl_eigen_nonsymmv_workspace));
81 GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
86 w->nonsymm_workspace_p = gsl_eigen_nonsymm_alloc(n);
88 if (w->nonsymm_workspace_p == 0)
90 gsl_eigen_nonsymmv_free(w);
91 GSL_ERROR_NULL ("failed to allocate space for nonsymm workspace", GSL_ENOMEM);
95 * set parameters to compute the full Schur form T and balance
98 gsl_eigen_nonsymm_params(1, 1, w->nonsymm_workspace_p);
100 w->work = gsl_vector_alloc(n);
101 w->work2 = gsl_vector_alloc(n);
102 w->work3 = gsl_vector_alloc(n);
103 if (w->work == 0 || w->work2 == 0 || w->work3 == 0)
105 gsl_eigen_nonsymmv_free(w);
106 GSL_ERROR_NULL ("failed to allocate space for nonsymmv additional workspace", GSL_ENOMEM);
110 } /* gsl_eigen_nonsymmv_alloc() */
113 gsl_eigen_nonsymmv_free()
118 gsl_eigen_nonsymmv_free (gsl_eigen_nonsymmv_workspace * w)
120 if (w->nonsymm_workspace_p)
121 gsl_eigen_nonsymm_free(w->nonsymm_workspace_p);
124 gsl_vector_free(w->work);
127 gsl_vector_free(w->work2);
130 gsl_vector_free(w->work3);
133 } /* gsl_eigen_nonsymmv_free() */
138 Solve the nonsymmetric eigensystem problem
142 for the eigenvalues \lambda and right eigenvectors x
144 Inputs: A - general real matrix
145 eval - where to store eigenvalues
146 evec - where to store eigenvectors
149 Return: success or error
153 gsl_eigen_nonsymmv (gsl_matrix * A, gsl_vector_complex * eval,
154 gsl_matrix_complex * evec,
155 gsl_eigen_nonsymmv_workspace * w)
157 const size_t N = A->size1;
159 /* check matrix and vector sizes */
163 GSL_ERROR ("matrix must be square to compute eigenvalues", GSL_ENOTSQR);
165 else if (eval->size != N)
167 GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
169 else if (evec->size1 != evec->size2)
171 GSL_ERROR ("eigenvector matrix must be square", GSL_ENOTSQR);
173 else if (evec->size1 != N)
175 GSL_ERROR ("eigenvector matrix has wrong size", GSL_EBADLEN);
183 * We need a place to store the Schur vectors, so we will
184 * treat evec as a real matrix and store them in the left
185 * half - the factor of 2 in the tda corresponds to the
186 * complex multiplicity
195 /* compute eigenvalues, Schur form, and Schur vectors */
196 s = gsl_eigen_nonsymm_Z(A, eval, &Z, w->nonsymm_workspace_p);
201 * save the Schur vectors in user supplied matrix, since
202 * they will be destroyed when computing eigenvectors
204 gsl_matrix_memcpy(w->Z, &Z);
207 /* only compute eigenvectors if we found all eigenvalues */
208 if (s == GSL_SUCCESS)
210 /* compute eigenvectors */
211 nonsymmv_get_right_eigenvectors(A, &Z, eval, evec, w);
213 /* normalize so that Euclidean norm is 1 */
214 nonsymmv_normalize_eigenvectors(eval, evec);
219 } /* gsl_eigen_nonsymmv() */
222 gsl_eigen_nonsymmv_Z()
223 Compute eigenvalues and eigenvectors of a real nonsymmetric matrix
224 and also save the Schur vectors. See comments in gsl_eigen_nonsymm_Z
225 for more information.
227 Inputs: A - real nonsymmetric matrix
228 eval - where to store eigenvalues
229 evec - where to store eigenvectors
230 Z - where to store Schur vectors
231 w - nonsymmv workspace
233 Return: success or error
237 gsl_eigen_nonsymmv_Z (gsl_matrix * A, gsl_vector_complex * eval,
238 gsl_matrix_complex * evec, gsl_matrix * Z,
239 gsl_eigen_nonsymmv_workspace * w)
241 /* check matrix and vector sizes */
243 if (A->size1 != A->size2)
245 GSL_ERROR ("matrix must be square to compute eigenvalues/eigenvectors", GSL_ENOTSQR);
247 else if (eval->size != A->size1)
249 GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
251 else if (evec->size1 != evec->size2)
253 GSL_ERROR ("eigenvector matrix must be square", GSL_ENOTSQR);
255 else if (evec->size1 != A->size1)
257 GSL_ERROR ("eigenvector matrix has wrong size", GSL_EBADLEN);
259 else if ((Z->size1 != Z->size2) || (Z->size1 != A->size1))
261 GSL_ERROR ("Z matrix has wrong dimensions", GSL_EBADLEN);
269 s = gsl_eigen_nonsymmv(A, eval, evec, w);
275 } /* gsl_eigen_nonsymmv_Z() */
277 /********************************************
278 * INTERNAL ROUTINES *
279 ********************************************/
282 nonsymmv_get_right_eigenvectors()
283 Compute the right eigenvectors of the Schur form T and then
284 backtransform them using the Schur vectors to get right eigenvectors of
287 Inputs: T - Schur form
289 eval - where to store eigenvalues (to ensure that the
290 correct eigenvalue is stored in the same position
292 evec - where to store eigenvectors
293 w - nonsymmv workspace
297 Notes: 1) based on LAPACK routine DTREVC - the algorithm used is
298 backsubstitution on the upper quasi triangular system T
299 followed by backtransformation by Z to get vectors of the
302 2) The Schur vectors in Z are destroyed and replaced with
303 eigenvectors stored with the same storage scheme as DTREVC.
304 The eigenvectors are also stored in 'evec'
306 3) The matrix T is unchanged on output
308 4) Each eigenvector is normalized so that the element of
309 largest magnitude has magnitude 1; here the magnitude of
310 a complex number (x,y) is taken to be |x| + |y|
314 nonsymmv_get_right_eigenvectors(gsl_matrix *T, gsl_matrix *Z,
315 gsl_vector_complex *eval,
316 gsl_matrix_complex *evec,
317 gsl_eigen_nonsymmv_workspace *w)
319 const size_t N = T->size1;
320 const double smlnum = GSL_DBL_MIN * N / GSL_DBL_EPSILON;
321 const double bignum = (1.0 - GSL_DBL_EPSILON) / smlnum;
323 size_t iu, /* looping */
326 gsl_complex lambda; /* current eigenvalue */
327 double lambda_re, /* Re(lambda) */
328 lambda_im; /* Im(lambda) */
329 gsl_matrix_view Tv, /* temporary views */
331 gsl_vector_view y, /* temporary views */
335 double dat[4], /* scratch arrays */
337 double scale; /* scale factor */
338 double xnorm; /* |X| */
339 gsl_vector_complex_view ecol, /* column of evec */
341 int complex_pair; /* complex eigenvalue pair? */
345 * Compute 1-norm of each column of upper triangular part of T
346 * to control overflow in triangular solver
349 gsl_vector_set(w->work3, 0, 0.0);
350 for (ju = 1; ju < N; ++ju)
352 gsl_vector_set(w->work3, ju, 0.0);
353 for (iu = 0; iu < ju; ++iu)
355 gsl_vector_set(w->work3, ju,
356 gsl_vector_get(w->work3, ju) +
357 fabs(gsl_matrix_get(T, iu, ju)));
361 for (i = (int) N - 1; i >= 0; --i)
365 /* get current eigenvalue and store it in lambda */
366 lambda_re = gsl_matrix_get(T, iu, iu);
368 if (iu != 0 && gsl_matrix_get(T, iu, iu - 1) != 0.0)
370 lambda_im = sqrt(fabs(gsl_matrix_get(T, iu, iu - 1))) *
371 sqrt(fabs(gsl_matrix_get(T, iu - 1, iu)));
378 GSL_SET_COMPLEX(&lambda, lambda_re, lambda_im);
380 smin = GSL_MAX(GSL_DBL_EPSILON * (fabs(lambda_re) + fabs(lambda_im)),
382 smin = GSL_MAX(smin, GSL_NONSYMMV_SMLNUM);
384 if (lambda_im == 0.0)
387 gsl_vector_view bv, xv;
389 /* real eigenvector */
392 * The ordering of eigenvalues in 'eval' is arbitrary and
393 * does not necessarily follow the Schur form T, so store
394 * lambda in the right slot in eval to ensure it corresponds
395 * to the eigenvector we are about to compute
397 gsl_vector_complex_set(eval, iu, lambda);
400 * We need to solve the system:
402 * (T(1:iu-1, 1:iu-1) - lambda*I)*X = -T(1:iu-1,iu)
405 /* construct right hand side */
406 for (k = 0; k < i; ++k)
408 gsl_vector_set(w->work,
410 -gsl_matrix_get(T, (size_t) k, iu));
413 gsl_vector_set(w->work, iu, 1.0);
415 for (l = i - 1; l >= 0; --l)
417 size_t lu = (size_t) l;
422 complex_pair = gsl_matrix_get(T, lu, lu - 1) != 0.0;
429 * 1-by-1 diagonal block - solve the system:
431 * (T_{ll} - lambda)*x = -T_{l(iu)}
434 Tv = gsl_matrix_submatrix(T, lu, lu, 1, 1);
435 bv = gsl_vector_view_array(dat, 1);
436 gsl_vector_set(&bv.vector, 0,
437 gsl_vector_get(w->work, lu));
438 xv = gsl_vector_view_array(dat_X, 1);
440 gsl_schur_solve_equation(1.0,
451 /* scale x to avoid overflow */
452 x = gsl_vector_get(&xv.vector, 0);
455 if (gsl_vector_get(w->work3, lu) > bignum / xnorm)
466 wv = gsl_vector_subvector(w->work, 0, iu + 1);
467 gsl_blas_dscal(scale, &wv.vector);
470 gsl_vector_set(w->work, lu, x);
474 gsl_vector_view v1, v2;
476 /* update right hand side */
478 v1 = gsl_matrix_subcolumn(T, lu, 0, lu);
479 v2 = gsl_vector_subvector(w->work, 0, lu);
480 gsl_blas_daxpy(-x, &v1.vector, &v2.vector);
482 } /* if (!complex_pair) */
488 * 2-by-2 diagonal block
491 Tv = gsl_matrix_submatrix(T, lu - 1, lu - 1, 2, 2);
492 bv = gsl_vector_view_array(dat, 2);
493 gsl_vector_set(&bv.vector, 0,
494 gsl_vector_get(w->work, lu - 1));
495 gsl_vector_set(&bv.vector, 1,
496 gsl_vector_get(w->work, lu));
497 xv = gsl_vector_view_array(dat_X, 2);
499 gsl_schur_solve_equation(1.0,
510 /* scale X(1,1) and X(2,1) to avoid overflow */
511 x11 = gsl_vector_get(&xv.vector, 0);
512 x21 = gsl_vector_get(&xv.vector, 1);
518 beta = GSL_MAX(gsl_vector_get(w->work3, lu - 1),
519 gsl_vector_get(w->work3, lu));
520 if (beta > bignum / xnorm)
528 /* scale if necessary */
533 wv = gsl_vector_subvector(w->work, 0, iu + 1);
534 gsl_blas_dscal(scale, &wv.vector);
537 gsl_vector_set(w->work, lu - 1, x11);
538 gsl_vector_set(w->work, lu, x21);
540 /* update right hand side */
543 gsl_vector_view v1, v2;
545 v1 = gsl_matrix_subcolumn(T, lu - 1, 0, lu - 1);
546 v2 = gsl_vector_subvector(w->work, 0, lu - 1);
547 gsl_blas_daxpy(-x11, &v1.vector, &v2.vector);
549 v1 = gsl_matrix_subcolumn(T, lu, 0, lu - 1);
550 gsl_blas_daxpy(-x21, &v1.vector, &v2.vector);
554 } /* if (complex_pair) */
555 } /* for (l = i - 1; l >= 0; --l) */
558 * At this point, w->work is an eigenvector of the
559 * Schur form T. To get an eigenvector of the original
560 * matrix, we multiply on the left by Z, the matrix of
564 ecol = gsl_matrix_complex_column(evec, iu);
565 y = gsl_matrix_column(Z, iu);
571 Zv = gsl_matrix_submatrix(Z, 0, 0, N, iu);
573 x = gsl_vector_subvector(w->work, 0, iu);
575 /* compute Z * w->work and store it in Z(:,iu) */
576 gsl_blas_dgemv(CblasNoTrans,
580 gsl_vector_get(w->work, iu),
584 /* store eigenvector into evec */
586 ev = gsl_vector_complex_real(&ecol.vector);
587 ev2 = gsl_vector_complex_imag(&ecol.vector);
590 for (ii = 0; ii < N; ++ii)
592 double a = gsl_vector_get(&y.vector, ii);
594 /* store real part of eigenvector */
595 gsl_vector_set(&ev.vector, ii, a);
597 /* set imaginary part to 0 */
598 gsl_vector_set(&ev2.vector, ii, 0.0);
607 /* scale by magnitude of largest element */
608 gsl_blas_dscal(scale, &ev.vector);
609 } /* if (GSL_IMAG(lambda) == 0.0) */
612 gsl_vector_complex_view bv, xv;
617 /* complex eigenvector */
620 * Store the complex conjugate eigenvalues in the right
623 GSL_SET_REAL(&lambda2, GSL_REAL(lambda));
624 GSL_SET_IMAG(&lambda2, -GSL_IMAG(lambda));
625 gsl_vector_complex_set(eval, iu - 1, lambda);
626 gsl_vector_complex_set(eval, iu, lambda2);
631 * [ T(i:i+1,i:i+1) - lambda*I ] * X = 0
634 if (fabs(gsl_matrix_get(T, iu - 1, iu)) >=
635 fabs(gsl_matrix_get(T, iu, iu - 1)))
637 gsl_vector_set(w->work, iu - 1, 1.0);
638 gsl_vector_set(w->work2, iu,
639 lambda_im / gsl_matrix_get(T, iu - 1, iu));
643 gsl_vector_set(w->work, iu - 1,
644 -lambda_im / gsl_matrix_get(T, iu, iu - 1));
645 gsl_vector_set(w->work2, iu, 1.0);
647 gsl_vector_set(w->work, iu, 0.0);
648 gsl_vector_set(w->work2, iu - 1, 0.0);
650 /* construct right hand side */
651 for (k = 0; k < iu - 1; ++k)
653 gsl_vector_set(w->work, k,
654 -gsl_vector_get(w->work, iu - 1) *
655 gsl_matrix_get(T, k, iu - 1));
656 gsl_vector_set(w->work2, k,
657 -gsl_vector_get(w->work2, iu) *
658 gsl_matrix_get(T, k, iu));
662 * We must solve the upper quasi-triangular system:
664 * [ T(1:i-2,1:i-2) - lambda*I ] * X = s*(work + i*work2)
667 for (l = i - 2; l >= 0; --l)
669 size_t lu = (size_t) l;
674 complex_pair = gsl_matrix_get(T, lu, lu - 1) != 0.0;
682 * 1-by-1 diagonal block - solve the system:
684 * (T_{ll} - lambda)*x = work + i*work2
687 Tv = gsl_matrix_submatrix(T, lu, lu, 1, 1);
688 bv = gsl_vector_complex_view_array(dat, 1);
689 xv = gsl_vector_complex_view_array(dat_X, 1);
691 GSL_SET_COMPLEX(&bval,
692 gsl_vector_get(w->work, lu),
693 gsl_vector_get(w->work2, lu));
694 gsl_vector_complex_set(&bv.vector, 0, bval);
696 gsl_schur_solve_equation_z(1.0,
709 if (gsl_vector_get(w->work3, lu) > bignum / xnorm)
711 gsl_blas_zdscal(1.0/xnorm, &xv.vector);
716 /* scale if necessary */
721 wv = gsl_vector_subvector(w->work, 0, iu + 1);
722 gsl_blas_dscal(scale, &wv.vector);
723 wv = gsl_vector_subvector(w->work2, 0, iu + 1);
724 gsl_blas_dscal(scale, &wv.vector);
727 x = gsl_vector_complex_get(&xv.vector, 0);
728 gsl_vector_set(w->work, lu, GSL_REAL(x));
729 gsl_vector_set(w->work2, lu, GSL_IMAG(x));
731 /* update the right hand side */
734 gsl_vector_view v1, v2;
736 v1 = gsl_matrix_subcolumn(T, lu, 0, lu);
737 v2 = gsl_vector_subvector(w->work, 0, lu);
738 gsl_blas_daxpy(-GSL_REAL(x), &v1.vector, &v2.vector);
740 v2 = gsl_vector_subvector(w->work2, 0, lu);
741 gsl_blas_daxpy(-GSL_IMAG(x), &v1.vector, &v2.vector);
743 } /* if (!complex_pair) */
746 gsl_complex b1, b2, x1, x2;
749 * 2-by-2 diagonal block - solve the system
752 Tv = gsl_matrix_submatrix(T, lu - 1, lu - 1, 2, 2);
753 bv = gsl_vector_complex_view_array(dat, 2);
754 xv = gsl_vector_complex_view_array(dat_X, 2);
757 gsl_vector_get(w->work, lu - 1),
758 gsl_vector_get(w->work2, lu - 1));
760 gsl_vector_get(w->work, lu),
761 gsl_vector_get(w->work2, lu));
762 gsl_vector_complex_set(&bv.vector, 0, b1);
763 gsl_vector_complex_set(&bv.vector, 1, b2);
765 gsl_schur_solve_equation_z(1.0,
776 x1 = gsl_vector_complex_get(&xv.vector, 0);
777 x2 = gsl_vector_complex_get(&xv.vector, 1);
783 beta = GSL_MAX(gsl_vector_get(w->work3, lu - 1),
784 gsl_vector_get(w->work3, lu));
785 if (beta > bignum / xnorm)
787 gsl_blas_zdscal(1.0/xnorm, &xv.vector);
792 /* scale if necessary */
797 wv = gsl_vector_subvector(w->work, 0, iu + 1);
798 gsl_blas_dscal(scale, &wv.vector);
799 wv = gsl_vector_subvector(w->work2, 0, iu + 1);
800 gsl_blas_dscal(scale, &wv.vector);
802 gsl_vector_set(w->work, lu - 1, GSL_REAL(x1));
803 gsl_vector_set(w->work, lu, GSL_REAL(x2));
804 gsl_vector_set(w->work2, lu - 1, GSL_IMAG(x1));
805 gsl_vector_set(w->work2, lu, GSL_IMAG(x2));
807 /* update right hand side */
810 gsl_vector_view v1, v2, v3, v4;
812 v1 = gsl_matrix_subcolumn(T, lu - 1, 0, lu - 1);
813 v4 = gsl_matrix_subcolumn(T, lu, 0, lu - 1);
814 v2 = gsl_vector_subvector(w->work, 0, lu - 1);
815 v3 = gsl_vector_subvector(w->work2, 0, lu - 1);
817 gsl_blas_daxpy(-GSL_REAL(x1), &v1.vector, &v2.vector);
818 gsl_blas_daxpy(-GSL_REAL(x2), &v4.vector, &v2.vector);
819 gsl_blas_daxpy(-GSL_IMAG(x1), &v1.vector, &v3.vector);
820 gsl_blas_daxpy(-GSL_IMAG(x2), &v4.vector, &v3.vector);
824 } /* if (complex_pair) */
825 } /* for (l = i - 2; l >= 0; --l) */
828 * At this point, work + i*work2 is an eigenvector
829 * of T - backtransform to get an eigenvector of the
833 y = gsl_matrix_column(Z, iu - 1);
834 y2 = gsl_matrix_column(Z, iu);
840 /* compute real part of eigenvectors */
842 Zv = gsl_matrix_submatrix(Z, 0, 0, N, iu - 1);
843 x = gsl_vector_subvector(w->work, 0, iu - 1);
845 gsl_blas_dgemv(CblasNoTrans,
849 gsl_vector_get(w->work, iu - 1),
853 /* now compute the imaginary part */
854 x = gsl_vector_subvector(w->work2, 0, iu - 1);
856 gsl_blas_dgemv(CblasNoTrans,
860 gsl_vector_get(w->work2, iu),
865 gsl_blas_dscal(gsl_vector_get(w->work, iu - 1), &y.vector);
866 gsl_blas_dscal(gsl_vector_get(w->work2, iu), &y2.vector);
870 * Now store the eigenvectors into evec - the real parts
871 * are Z(:,iu - 1) and the imaginary parts are
875 /* get views of the two eigenvector slots */
876 ecol = gsl_matrix_complex_column(evec, iu - 1);
877 ecol2 = gsl_matrix_complex_column(evec, iu);
880 * save imaginary part first as it may get overwritten
881 * when copying the real part due to our storage scheme
884 ev = gsl_vector_complex_imag(&ecol.vector);
885 ev2 = gsl_vector_complex_imag(&ecol2.vector);
887 for (ii = 0; ii < N; ++ii)
889 double a = gsl_vector_get(&y2.vector, ii);
891 scale = GSL_MAX(scale,
892 fabs(a) + fabs(gsl_vector_get(&y.vector, ii)));
894 gsl_vector_set(&ev.vector, ii, a);
895 gsl_vector_set(&ev2.vector, ii, -a);
898 /* now save the real part */
899 ev = gsl_vector_complex_real(&ecol.vector);
900 ev2 = gsl_vector_complex_real(&ecol2.vector);
901 for (ii = 0; ii < N; ++ii)
903 double a = gsl_vector_get(&y.vector, ii);
905 gsl_vector_set(&ev.vector, ii, a);
906 gsl_vector_set(&ev2.vector, ii, a);
912 /* scale by largest element magnitude */
914 gsl_blas_zdscal(scale, &ecol.vector);
915 gsl_blas_zdscal(scale, &ecol2.vector);
918 * decrement i since we took care of two eigenvalues at
922 } /* if (GSL_IMAG(lambda) != 0.0) */
923 } /* for (i = (int) N - 1; i >= 0; --i) */
924 } /* nonsymmv_get_right_eigenvectors() */
927 nonsymmv_normalize_eigenvectors()
928 Normalize eigenvectors so that their Euclidean norm is 1
930 Inputs: eval - eigenvalues
935 nonsymmv_normalize_eigenvectors(gsl_vector_complex *eval,
936 gsl_matrix_complex *evec)
938 const size_t N = evec->size1;
939 size_t i; /* looping */
941 gsl_vector_complex_view vi;
942 gsl_vector_view re, im;
943 double scale; /* scaling factor */
945 for (i = 0; i < N; ++i)
947 ei = gsl_vector_complex_get(eval, i);
948 vi = gsl_matrix_complex_column(evec, i);
950 re = gsl_vector_complex_real(&vi.vector);
952 if (GSL_IMAG(ei) == 0.0)
954 scale = 1.0 / gsl_blas_dnrm2(&re.vector);
955 gsl_blas_dscal(scale, &re.vector);
957 else if (GSL_IMAG(ei) > 0.0)
959 im = gsl_vector_complex_imag(&vi.vector);
961 scale = 1.0 / gsl_hypot(gsl_blas_dnrm2(&re.vector),
962 gsl_blas_dnrm2(&im.vector));
963 gsl_blas_zdscal(scale, &vi.vector);
965 vi = gsl_matrix_complex_column(evec, i + 1);
966 gsl_blas_zdscal(scale, &vi.vector);
969 } /* nonsymmv_normalize_eigenvectors() */