/* eigen/genv.c
*
* Copyright (C) 2007 Patrick Alken
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <math.h>
#include <config.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_errno.h>
/*
* This module computes the eigenvalues and eigenvectors of a
* real generalized eigensystem A x = \lambda B x. Left and right
* Schur vectors are optionally computed as well.
*
* This file contains routines based on original code from LAPACK
* which is distributed under the modified BSD license.
*/
static int genv_get_right_eigenvectors(const gsl_matrix *S,
const gsl_matrix *T,
gsl_matrix *Z,
gsl_matrix_complex *evec,
gsl_eigen_genv_workspace *w);
static void genv_normalize_eigenvectors(gsl_vector_complex *alpha,
gsl_matrix_complex *evec);
/*
gsl_eigen_genv_alloc()
Allocate a workspace for solving the generalized eigenvalue problem.
The size of this workspace is O(7n).
Inputs: n - size of matrices
Return: pointer to workspace
*/
gsl_eigen_genv_workspace *
gsl_eigen_genv_alloc(const size_t n)
{
gsl_eigen_genv_workspace *w;
if (n == 0)
{
GSL_ERROR_NULL ("matrix dimension must be positive integer",
GSL_EINVAL);
}
w = (gsl_eigen_genv_workspace *) calloc (1, sizeof (gsl_eigen_genv_workspace));
if (w == 0)
{
GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
}
w->size = n;
w->Q = NULL;
w->Z = NULL;
w->gen_workspace_p = gsl_eigen_gen_alloc(n);
if (w->gen_workspace_p == 0)
{
gsl_eigen_genv_free(w);
GSL_ERROR_NULL ("failed to allocate space for gen workspace", GSL_ENOMEM);
}
/* compute the full Schur forms */
gsl_eigen_gen_params(1, 1, 1, w->gen_workspace_p);
w->work1 = gsl_vector_alloc(n);
w->work2 = gsl_vector_alloc(n);
w->work3 = gsl_vector_alloc(n);
w->work4 = gsl_vector_alloc(n);
w->work5 = gsl_vector_alloc(n);
w->work6 = gsl_vector_alloc(n);
if (w->work1 == 0 || w->work2 == 0 || w->work3 == 0 ||
w->work4 == 0 || w->work5 == 0 || w->work6 == 0)
{
gsl_eigen_genv_free(w);
GSL_ERROR_NULL ("failed to allocate space for additional workspace", GSL_ENOMEM);
}
return (w);
} /* gsl_eigen_genv_alloc() */
/*
gsl_eigen_genv_free()
Free workspace w
*/
void
gsl_eigen_genv_free(gsl_eigen_genv_workspace *w)
{
RETURN_IF_NULL (w);
if (w->gen_workspace_p)
gsl_eigen_gen_free(w->gen_workspace_p);
if (w->work1)
gsl_vector_free(w->work1);
if (w->work2)
gsl_vector_free(w->work2);
if (w->work3)
gsl_vector_free(w->work3);
if (w->work4)
gsl_vector_free(w->work4);
if (w->work5)
gsl_vector_free(w->work5);
if (w->work6)
gsl_vector_free(w->work6);
free(w);
} /* gsl_eigen_genv_free() */
/*
gsl_eigen_genv()
Solve the generalized eigenvalue problem
A x = \lambda B x
for the eigenvalues \lambda and right eigenvectors x.
Inputs: A - general real matrix
B - general real matrix
alpha - (output) where to store eigenvalue numerators
beta - (output) where to store eigenvalue denominators
evec - (output) where to store eigenvectors
w - workspace
Return: success or error
*/
int
gsl_eigen_genv (gsl_matrix * A, gsl_matrix * B, gsl_vector_complex * alpha,
gsl_vector * beta, gsl_matrix_complex *evec,
gsl_eigen_genv_workspace * w)
{
const size_t N = A->size1;
/* check matrix and vector sizes */
if (N != A->size2)
{
GSL_ERROR ("matrix must be square to compute eigenvalues", GSL_ENOTSQR);
}
else if ((N != B->size1) || (N != B->size2))
{
GSL_ERROR ("B matrix dimensions must match A", GSL_EBADLEN);
}
else if (alpha->size != N || beta->size != N)
{
GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
}
else if (w->size != N)
{
GSL_ERROR ("matrix size does not match workspace", GSL_EBADLEN);
}
else if (evec->size1 != N)
{
GSL_ERROR ("eigenvector matrix has wrong size", GSL_EBADLEN);
}
else
{
int s;
gsl_matrix Z;
/*
* We need a place to store the right Schur vectors, so we will
* treat evec as a real matrix and store them in the left
* half - the factor of 2 in the tda corresponds to the
* complex multiplicity
*/
Z.size1 = N;
Z.size2 = N;
Z.tda = 2 * N;
Z.data = evec->data;
Z.block = 0;
Z.owner = 0;
s = gsl_eigen_gen_QZ(A, B, alpha, beta, w->Q, &Z, w->gen_workspace_p);
if (w->Z)
{
/* save right Schur vectors */
gsl_matrix_memcpy(w->Z, &Z);
}
/* only compute eigenvectors if we found all eigenvalues */
if (s == GSL_SUCCESS)
{
/* compute eigenvectors */
s = genv_get_right_eigenvectors(A, B, &Z, evec, w);
if (s == GSL_SUCCESS)
genv_normalize_eigenvectors(alpha, evec);
}
return s;
}
} /* gsl_eigen_genv() */
/*
gsl_eigen_genv_QZ()
Solve the generalized eigenvalue problem
A x = \lambda B x
for the eigenvalues \lambda and right eigenvectors x. Optionally
compute left and/or right Schur vectors Q and Z which satisfy:
A = Q S Z^t
B = Q T Z^t
where (S, T) is the generalized Schur form of (A, B)
Inputs: A - general real matrix
B - general real matrix
alpha - (output) where to store eigenvalue numerators
beta - (output) where to store eigenvalue denominators
evec - (output) where to store eigenvectors
Q - (output) if non-null, where to store left Schur vectors
Z - (output) if non-null, where to store right Schur vectors
w - workspace
Return: success or error
*/
int
gsl_eigen_genv_QZ (gsl_matrix * A, gsl_matrix * B,
gsl_vector_complex * alpha, gsl_vector * beta,
gsl_matrix_complex * evec,
gsl_matrix * Q, gsl_matrix * Z,
gsl_eigen_genv_workspace * w)
{
if (Q && (A->size1 != Q->size1 || A->size1 != Q->size2))
{
GSL_ERROR("Q matrix has wrong dimensions", GSL_EBADLEN);
}
else if (Z && (A->size1 != Z->size1 || A->size1 != Z->size2))
{
GSL_ERROR("Z matrix has wrong dimensions", GSL_EBADLEN);
}
else
{
int s;
w->Q = Q;
w->Z = Z;
s = gsl_eigen_genv(A, B, alpha, beta, evec, w);
w->Q = NULL;
w->Z = NULL;
return s;
}
} /* gsl_eigen_genv_QZ() */
/********************************************
* INTERNAL ROUTINES *
********************************************/
/*
genv_get_right_eigenvectors()
Compute right eigenvectors of the Schur form (S, T) and then
backtransform them using the right Schur vectors to get right
eigenvectors of the original system.
Inputs: S - upper quasi-triangular Schur form of A
T - upper triangular Schur form of B
Z - right Schur vectors
evec - (output) where to store eigenvectors
w - workspace
Return: success or error
Notes: 1) based on LAPACK routine DTGEVC
2) eigenvectors are stored in the order that their
eigenvalues appear in the Schur form
*/
static int
genv_get_right_eigenvectors(const gsl_matrix *S, const gsl_matrix *T,
gsl_matrix *Z,
gsl_matrix_complex *evec,
gsl_eigen_genv_workspace *w)
{
const size_t N = w->size;
const double small = GSL_DBL_MIN * N / GSL_DBL_EPSILON;
const double big = 1.0 / small;
const double bignum = 1.0 / (GSL_DBL_MIN * N);
size_t i, j, k, end;
int is;
double anorm, bnorm;
double temp, temp2, temp2r, temp2i;
double ascale, bscale;
double salfar, sbeta;
double acoef, bcoefr, bcoefi, acoefa, bcoefa;
double creala, cimaga, crealb, cimagb, cre2a, cim2a, cre2b, cim2b;
double dmin, xmax;
double scale;
size_t nw, na;
int lsa, lsb;
int complex_pair;
gsl_complex z_zero, z_one;
double bdiag[2] = { 0.0, 0.0 };
double sum[4];
int il2by2;
size_t jr, jc, ja;
double xscale;
gsl_vector_complex_view ecol;
gsl_vector_view re, im, re2, im2;
GSL_SET_COMPLEX(&z_zero, 0.0, 0.0);
GSL_SET_COMPLEX(&z_one, 1.0, 0.0);
/*
* Compute the 1-norm of each column of (S, T) excluding elements
* belonging to the diagonal blocks to check for possible overflow
* in the triangular solver
*/
anorm = fabs(gsl_matrix_get(S, 0, 0));
if (N > 1)
anorm += fabs(gsl_matrix_get(S, 1, 0));
bnorm = fabs(gsl_matrix_get(T, 0, 0));
gsl_vector_set(w->work1, 0, 0.0);
gsl_vector_set(w->work2, 0, 0.0);
for (j = 1; j < N; ++j)
{
temp = temp2 = 0.0;
if (gsl_matrix_get(S, j, j - 1) == 0.0)
end = j;
else
end = j - 1;
for (i = 0; i < end; ++i)
{
temp += fabs(gsl_matrix_get(S, i, j));
temp2 += fabs(gsl_matrix_get(T, i, j));
}
gsl_vector_set(w->work1, j, temp);
gsl_vector_set(w->work2, j, temp2);
for (i = end; i < GSL_MIN(j + 2, N); ++i)
{
temp += fabs(gsl_matrix_get(S, i, j));
temp2 += fabs(gsl_matrix_get(T, i, j));
}
anorm = GSL_MAX(anorm, temp);
bnorm = GSL_MAX(bnorm, temp2);
}
ascale = 1.0 / GSL_MAX(anorm, GSL_DBL_MIN);
bscale = 1.0 / GSL_MAX(bnorm, GSL_DBL_MIN);
complex_pair = 0;
for (k = 0; k < N; ++k)
{
size_t je = N - 1 - k;
if (complex_pair)
{
complex_pair = 0;
continue;
}
nw = 1;
if (je > 0)
{
if (gsl_matrix_get(S, je, je - 1) != 0.0)
{
complex_pair = 1;
nw = 2;
}
}
if (!complex_pair)
{
if (fabs(gsl_matrix_get(S, je, je)) <= GSL_DBL_MIN &&
fabs(gsl_matrix_get(T, je, je)) <= GSL_DBL_MIN)
{
/* singular matrix pencil - unit eigenvector */
for (i = 0; i < N; ++i)
gsl_matrix_complex_set(evec, i, je, z_zero);
gsl_matrix_complex_set(evec, je, je, z_one);
continue;
}
/* clear vector */
for (i = 0; i < N; ++i)
gsl_vector_set(w->work3, i, 0.0);
}
else
{
/* clear vectors */
for (i = 0; i < N; ++i)
{
gsl_vector_set(w->work3, i, 0.0);
gsl_vector_set(w->work4, i, 0.0);
}
}
if (!complex_pair)
{
/* real eigenvalue */
temp = 1.0 / GSL_MAX(GSL_DBL_MIN,
GSL_MAX(fabs(gsl_matrix_get(S, je, je)) * ascale,
fabs(gsl_matrix_get(T, je, je)) * bscale));
salfar = (temp * gsl_matrix_get(S, je, je)) * ascale;
sbeta = (temp * gsl_matrix_get(T, je, je)) * bscale;
acoef = sbeta * ascale;
bcoefr = salfar * bscale;
bcoefi = 0.0;
/* scale to avoid underflow */
scale = 1.0;
lsa = fabs(sbeta) >= GSL_DBL_MIN && fabs(acoef) < small;
lsb = fabs(salfar) >= GSL_DBL_MIN && fabs(bcoefr) < small;
if (lsa)
scale = (small / fabs(sbeta)) * GSL_MIN(anorm, big);
if (lsb)
scale = GSL_MAX(scale, (small / fabs(salfar)) * GSL_MIN(bnorm, big));
if (lsa || lsb)
{
scale = GSL_MIN(scale,
1.0 / (GSL_DBL_MIN *
GSL_MAX(1.0,
GSL_MAX(fabs(acoef), fabs(bcoefr)))));
if (lsa)
acoef = ascale * (scale * sbeta);
else
acoef *= scale;
if (lsb)
bcoefr = bscale * (scale * salfar);
else
bcoefr *= scale;
}
acoefa = fabs(acoef);
bcoefa = fabs(bcoefr);
/* first component is 1 */
gsl_vector_set(w->work3, je, 1.0);
xmax = 1.0;
/* compute contribution from column je of A and B to sum */
for (i = 0; i < je; ++i)
{
gsl_vector_set(w->work3, i,
bcoefr*gsl_matrix_get(T, i, je) -
acoef * gsl_matrix_get(S, i, je));
}
}
else
{
gsl_matrix_const_view vs =
gsl_matrix_const_submatrix(S, je - 1, je - 1, 2, 2);
gsl_matrix_const_view vt =
gsl_matrix_const_submatrix(T, je - 1, je - 1, 2, 2);
/* complex eigenvalue */
gsl_schur_gen_eigvals(&vs.matrix,
&vt.matrix,
&bcoefr,
&temp2,
&bcoefi,
&acoef,
&temp);
if (bcoefi == 0.0)
{
GSL_ERROR("gsl_schur_gen_eigvals failed on complex block", GSL_FAILURE);
}
/* scale to avoid over/underflow */
acoefa = fabs(acoef);
bcoefa = fabs(bcoefr) + fabs(bcoefi);
scale = 1.0;
if (acoefa*GSL_DBL_EPSILON < GSL_DBL_MIN && acoefa >= GSL_DBL_MIN)
scale = (GSL_DBL_MIN / GSL_DBL_EPSILON) / acoefa;
if (bcoefa*GSL_DBL_EPSILON < GSL_DBL_MIN && bcoefa >= GSL_DBL_MIN)
scale = GSL_MAX(scale, (GSL_DBL_MIN/GSL_DBL_EPSILON) / bcoefa);
if (GSL_DBL_MIN*acoefa > ascale)
scale = ascale / (GSL_DBL_MIN * acoefa);
if (GSL_DBL_MIN*bcoefa > bscale)
scale = GSL_MIN(scale, bscale / (GSL_DBL_MIN*bcoefa));
if (scale != 1.0)
{
acoef *= scale;
acoefa = fabs(acoef);
bcoefr *= scale;
bcoefi *= scale;
bcoefa = fabs(bcoefr) + fabs(bcoefi);
}
/* compute first two components of eigenvector */
temp = acoef * gsl_matrix_get(S, je, je - 1);
temp2r = acoef * gsl_matrix_get(S, je, je) -
bcoefr * gsl_matrix_get(T, je, je);
temp2i = -bcoefi * gsl_matrix_get(T, je, je);
if (fabs(temp) >= fabs(temp2r) + fabs(temp2i))
{
gsl_vector_set(w->work3, je, 1.0);
gsl_vector_set(w->work4, je, 0.0);
gsl_vector_set(w->work3, je - 1, -temp2r / temp);
gsl_vector_set(w->work4, je - 1, -temp2i / temp);
}
else
{
gsl_vector_set(w->work3, je - 1, 1.0);
gsl_vector_set(w->work4, je - 1, 0.0);
temp = acoef * gsl_matrix_get(S, je - 1, je);
gsl_vector_set(w->work3, je,
(bcoefr*gsl_matrix_get(T, je - 1, je - 1) -
acoef*gsl_matrix_get(S, je - 1, je - 1)) / temp);
gsl_vector_set(w->work4, je,
bcoefi*gsl_matrix_get(T, je - 1, je - 1) / temp);
}
xmax = GSL_MAX(fabs(gsl_vector_get(w->work3, je)) +
fabs(gsl_vector_get(w->work4, je)),
fabs(gsl_vector_get(w->work3, je - 1)) +
fabs(gsl_vector_get(w->work4, je - 1)));
/* compute contribution from column je and je - 1 */
creala = acoef * gsl_vector_get(w->work3, je - 1);
cimaga = acoef * gsl_vector_get(w->work4, je - 1);
crealb = bcoefr * gsl_vector_get(w->work3, je - 1) -
bcoefi * gsl_vector_get(w->work4, je - 1);
cimagb = bcoefi * gsl_vector_get(w->work3, je - 1) +
bcoefr * gsl_vector_get(w->work4, je - 1);
cre2a = acoef * gsl_vector_get(w->work3, je);
cim2a = acoef * gsl_vector_get(w->work4, je);
cre2b = bcoefr * gsl_vector_get(w->work3, je) -
bcoefi * gsl_vector_get(w->work4, je);
cim2b = bcoefi * gsl_vector_get(w->work3, je) +
bcoefr * gsl_vector_get(w->work4, je);
for (i = 0; i < je - 1; ++i)
{
gsl_vector_set(w->work3, i,
-creala * gsl_matrix_get(S, i, je - 1) +
crealb * gsl_matrix_get(T, i, je - 1) -
cre2a * gsl_matrix_get(S, i, je) +
cre2b * gsl_matrix_get(T, i, je));
gsl_vector_set(w->work4, i,
-cimaga * gsl_matrix_get(S, i, je - 1) +
cimagb * gsl_matrix_get(T, i, je - 1) -
cim2a * gsl_matrix_get(S, i, je) +
cim2b * gsl_matrix_get(T, i, je));
}
}
dmin = GSL_MAX(GSL_DBL_MIN,
GSL_MAX(GSL_DBL_EPSILON*acoefa*anorm,
GSL_DBL_EPSILON*bcoefa*bnorm));
/* triangular solve of (a A - b B) x = 0 */
il2by2 = 0;
for (is = (int) je - (int) nw; is >= 0; --is)
{
j = (size_t) is;
if (!il2by2 && j > 0)
{
if (gsl_matrix_get(S, j, j - 1) != 0.0)
{
il2by2 = 1;
continue;
}
}
bdiag[0] = gsl_matrix_get(T, j, j);
if (il2by2)
{
na = 2;
bdiag[1] = gsl_matrix_get(T, j + 1, j + 1);
}
else
na = 1;
if (nw == 1)
{
gsl_matrix_const_view sv =
gsl_matrix_const_submatrix(S, j, j, na, na);
gsl_vector_view xv, bv;
bv = gsl_vector_subvector(w->work3, j, na);
/*
* the loop below expects the solution in the first column
* of sum, so set stride to 2
*/
xv = gsl_vector_view_array_with_stride(sum, 2, na);
gsl_schur_solve_equation(acoef,
&sv.matrix,
bcoefr,
bdiag[0],
bdiag[1],
&bv.vector,
&xv.vector,
&scale,
&temp,
dmin);
}
else
{
double bdat[4];
gsl_matrix_const_view sv =
gsl_matrix_const_submatrix(S, j, j, na, na);
gsl_vector_complex_view xv =
gsl_vector_complex_view_array(sum, na);
gsl_vector_complex_view bv =
gsl_vector_complex_view_array(bdat, na);
gsl_complex z;
bdat[0] = gsl_vector_get(w->work3, j);
bdat[1] = gsl_vector_get(w->work4, j);
if (na == 2)
{
bdat[2] = gsl_vector_get(w->work3, j + 1);
bdat[3] = gsl_vector_get(w->work4, j + 1);
}
GSL_SET_COMPLEX(&z, bcoefr, bcoefi);
gsl_schur_solve_equation_z(acoef,
&sv.matrix,
&z,
bdiag[0],
bdiag[1],
&bv.vector,
&xv.vector,
&scale,
&temp,
dmin);
}
if (scale < 1.0)
{
for (jr = 0; jr <= je; ++jr)
{
gsl_vector_set(w->work3, jr,
scale * gsl_vector_get(w->work3, jr));
if (nw == 2)
{
gsl_vector_set(w->work4, jr,
scale * gsl_vector_get(w->work4, jr));
}
}
}
xmax = GSL_MAX(scale * xmax, temp);
for (jr = 0; jr < na; ++jr)
{
gsl_vector_set(w->work3, j + jr, sum[jr*na]);
if (nw == 2)
gsl_vector_set(w->work4, j + jr, sum[jr*na + 1]);
}
if (j > 0)
{
xscale = 1.0 / GSL_MAX(1.0, xmax);
temp = acoefa * gsl_vector_get(w->work1, j) +
bcoefa * gsl_vector_get(w->work2, j);
if (il2by2)
{
temp = GSL_MAX(temp,
acoefa * gsl_vector_get(w->work1, j + 1) +
bcoefa * gsl_vector_get(w->work2, j + 1));
}
temp = GSL_MAX(temp, GSL_MAX(acoefa, bcoefa));
if (temp > bignum * xscale)
{
for (jr = 0; jr <= je; ++jr)
{
gsl_vector_set(w->work3, jr,
xscale * gsl_vector_get(w->work3, jr));
if (nw == 2)
{
gsl_vector_set(w->work4, jr,
xscale * gsl_vector_get(w->work4, jr));
}
}
xmax *= xscale;
}
for (ja = 0; ja < na; ++ja)
{
if (complex_pair)
{
creala = acoef * gsl_vector_get(w->work3, j + ja);
cimaga = acoef * gsl_vector_get(w->work4, j + ja);
crealb = bcoefr * gsl_vector_get(w->work3, j + ja) -
bcoefi * gsl_vector_get(w->work4, j + ja);
cimagb = bcoefi * gsl_vector_get(w->work3, j + ja) +
bcoefr * gsl_vector_get(w->work4, j + ja);
for (jr = 0; jr <= j - 1; ++jr)
{
gsl_vector_set(w->work3, jr,
gsl_vector_get(w->work3, jr) -
creala * gsl_matrix_get(S, jr, j + ja) +
crealb * gsl_matrix_get(T, jr, j + ja));
gsl_vector_set(w->work4, jr,
gsl_vector_get(w->work4, jr) -
cimaga * gsl_matrix_get(S, jr, j + ja) +
cimagb * gsl_matrix_get(T, jr, j + ja));
}
}
else
{
creala = acoef * gsl_vector_get(w->work3, j + ja);
crealb = bcoefr * gsl_vector_get(w->work3, j + ja);
for (jr = 0; jr <= j - 1; ++jr)
{
gsl_vector_set(w->work3, jr,
gsl_vector_get(w->work3, jr) -
creala * gsl_matrix_get(S, jr, j + ja) +
crealb * gsl_matrix_get(T, jr, j + ja));
}
} /* if (!complex_pair) */
} /* for (ja = 0; ja < na; ++ja) */
} /* if (j > 0) */
il2by2 = 0;
} /* for (i = 0; i < je - nw; ++i) */
for (jr = 0; jr < N; ++jr)
{
gsl_vector_set(w->work5, jr,
gsl_vector_get(w->work3, 0) * gsl_matrix_get(Z, jr, 0));
if (nw == 2)
{
gsl_vector_set(w->work6, jr,
gsl_vector_get(w->work4, 0) * gsl_matrix_get(Z, jr, 0));
}
}
for (jc = 1; jc <= je; ++jc)
{
for (jr = 0; jr < N; ++jr)
{
gsl_vector_set(w->work5, jr,
gsl_vector_get(w->work5, jr) +
gsl_vector_get(w->work3, jc) * gsl_matrix_get(Z, jr, jc));
if (nw == 2)
{
gsl_vector_set(w->work6, jr,
gsl_vector_get(w->work6, jr) +
gsl_vector_get(w->work4, jc) * gsl_matrix_get(Z, jr, jc));
}
}
}
/* store the eigenvector */
if (complex_pair)
{
ecol = gsl_matrix_complex_column(evec, je - 1);
re = gsl_vector_complex_real(&ecol.vector);
im = gsl_vector_complex_imag(&ecol.vector);
ecol = gsl_matrix_complex_column(evec, je);
re2 = gsl_vector_complex_real(&ecol.vector);
im2 = gsl_vector_complex_imag(&ecol.vector);
}
else
{
ecol = gsl_matrix_complex_column(evec, je);
re = gsl_vector_complex_real(&ecol.vector);
im = gsl_vector_complex_imag(&ecol.vector);
}
for (jr = 0; jr < N; ++jr)
{
gsl_vector_set(&re.vector, jr, gsl_vector_get(w->work5, jr));
if (complex_pair)
{
gsl_vector_set(&im.vector, jr, gsl_vector_get(w->work6, jr));
gsl_vector_set(&re2.vector, jr, gsl_vector_get(w->work5, jr));
gsl_vector_set(&im2.vector, jr, -gsl_vector_get(w->work6, jr));
}
else
{
gsl_vector_set(&im.vector, jr, 0.0);
}
}
/* scale eigenvector */
xmax = 0.0;
if (complex_pair)
{
for (j = 0; j < N; ++j)
{
xmax = GSL_MAX(xmax,
fabs(gsl_vector_get(&re.vector, j)) +
fabs(gsl_vector_get(&im.vector, j)));
}
}
else
{
for (j = 0; j < N; ++j)
{
xmax = GSL_MAX(xmax, fabs(gsl_vector_get(&re.vector, j)));
}
}
if (xmax > GSL_DBL_MIN)
{
xscale = 1.0 / xmax;
for (j = 0; j < N; ++j)
{
gsl_vector_set(&re.vector, j,
gsl_vector_get(&re.vector, j) * xscale);
if (complex_pair)
{
gsl_vector_set(&im.vector, j,
gsl_vector_get(&im.vector, j) * xscale);
gsl_vector_set(&re2.vector, j,
gsl_vector_get(&re2.vector, j) * xscale);
gsl_vector_set(&im2.vector, j,
gsl_vector_get(&im2.vector, j) * xscale);
}
}
}
} /* for (k = 0; k < N; ++k) */
return GSL_SUCCESS;
} /* genv_get_right_eigenvectors() */
/*
genv_normalize_eigenvectors()
Normalize eigenvectors so that their Euclidean norm is 1
Inputs: alpha - eigenvalue numerators
evec - eigenvectors
*/
static void
genv_normalize_eigenvectors(gsl_vector_complex *alpha,
gsl_matrix_complex *evec)
{
const size_t N = evec->size1;
size_t i; /* looping */
gsl_complex ai;
gsl_vector_complex_view vi;
gsl_vector_view re, im;
double scale; /* scaling factor */
for (i = 0; i < N; ++i)
{
ai = gsl_vector_complex_get(alpha, i);
vi = gsl_matrix_complex_column(evec, i);
re = gsl_vector_complex_real(&vi.vector);
if (GSL_IMAG(ai) == 0.0)
{
scale = 1.0 / gsl_blas_dnrm2(&re.vector);
gsl_blas_dscal(scale, &re.vector);
}
else if (GSL_IMAG(ai) > 0.0)
{
im = gsl_vector_complex_imag(&vi.vector);
scale = 1.0 / gsl_hypot(gsl_blas_dnrm2(&re.vector),
gsl_blas_dnrm2(&im.vector));
gsl_blas_zdscal(scale, &vi.vector);
vi = gsl_matrix_complex_column(evec, i + 1);
gsl_blas_zdscal(scale, &vi.vector);
}
}
} /* genv_normalize_eigenvectors() */