3 * Copyright (C) 2001, 2007 Brian Gough
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.
22 #include <gsl/gsl_math.h>
23 #include <gsl/gsl_vector.h>
24 #include <gsl/gsl_matrix.h>
25 #include <gsl/gsl_complex_math.h>
26 #include <gsl/gsl_linalg.h>
27 #include <gsl/gsl_eigen.h>
29 /* Compute eigenvalues/eigenvectors of complex hermitian matrix using
30 reduction to real symmetric tridiagonal form, followed by QR
31 iteration with implicit shifts.
33 See Golub & Van Loan, "Matrix Computations" (3rd ed), Section 8.3 */
37 gsl_eigen_hermv_workspace *
38 gsl_eigen_hermv_alloc (const size_t n)
40 gsl_eigen_hermv_workspace * w ;
44 GSL_ERROR_NULL ("matrix dimension must be positive integer", GSL_EINVAL);
47 w = (gsl_eigen_hermv_workspace *) malloc (sizeof(gsl_eigen_hermv_workspace));
51 GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
54 w->d = (double *) malloc (n * sizeof (double));
59 GSL_ERROR_NULL ("failed to allocate space for diagonal", GSL_ENOMEM);
62 w->sd = (double *) malloc (n * sizeof (double));
68 GSL_ERROR_NULL ("failed to allocate space for subdiagonal", GSL_ENOMEM);
71 w->tau = (double *) malloc (2 * n * sizeof (double));
78 GSL_ERROR_NULL ("failed to allocate space for tau", GSL_ENOMEM);
81 w->gc = (double *) malloc (n * sizeof (double));
89 GSL_ERROR_NULL ("failed to allocate space for cosines", GSL_ENOMEM);
92 w->gs = (double *) malloc (n * sizeof (double));
101 GSL_ERROR_NULL ("failed to allocate space for sines", GSL_ENOMEM);
110 gsl_eigen_hermv_free (gsl_eigen_hermv_workspace * w)
121 gsl_eigen_hermv (gsl_matrix_complex * A, gsl_vector * eval,
122 gsl_matrix_complex * evec,
123 gsl_eigen_hermv_workspace * w)
125 if (A->size1 != A->size2)
127 GSL_ERROR ("matrix must be square to compute eigenvalues", GSL_ENOTSQR);
129 else if (eval->size != A->size1)
131 GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
133 else if (evec->size1 != A->size1 || evec->size2 != A->size1)
135 GSL_ERROR ("eigenvector matrix must match matrix size", GSL_EBADLEN);
139 const size_t N = A->size1;
140 double *const d = w->d;
141 double *const sd = w->sd;
145 /* handle special case */
149 gsl_complex A00 = gsl_matrix_complex_get (A, 0, 0);
150 gsl_vector_set (eval, 0, GSL_REAL(A00));
151 gsl_matrix_complex_set (evec, 0, 0, GSL_COMPLEX_ONE);
155 /* Transform the matrix into a symmetric tridiagonal form */
158 gsl_vector_view d_vec = gsl_vector_view_array (d, N);
159 gsl_vector_view sd_vec = gsl_vector_view_array (sd, N - 1);
160 gsl_vector_complex_view tau_vec = gsl_vector_complex_view_array (w->tau, N-1);
161 gsl_linalg_hermtd_decomp (A, &tau_vec.vector);
162 gsl_linalg_hermtd_unpack (A, &tau_vec.vector, evec, &d_vec.vector, &sd_vec.vector);
165 /* Make an initial pass through the tridiagonal decomposition
166 to remove off-diagonal elements which are effectively zero */
168 chop_small_elements (N, d, sd);
170 /* Progressively reduce the matrix until it is diagonal */
176 if (sd[b - 1] == 0.0 || isnan(sd[b - 1]))
182 /* Find the largest unreduced block (a,b) starting from b
183 and working backwards */
189 if (sd[a - 1] == 0.0)
198 const size_t n_block = b - a + 1;
199 double *d_block = d + a;
200 double *sd_block = sd + a;
201 double * const gc = w->gc;
202 double * const gs = w->gs;
204 /* apply QR reduction with implicit deflation to the
207 qrstep (n_block, d_block, sd_block, gc, gs);
209 /* Apply Givens rotation Gij(c,s) to matrix Q, Q <- Q G */
211 for (i = 0; i < n_block - 1; i++)
213 const double c = gc[i], s = gs[i];
216 for (k = 0; k < N; k++)
218 gsl_complex qki = gsl_matrix_complex_get (evec, k, a + i);
219 gsl_complex qkj = gsl_matrix_complex_get (evec, k, a + i + 1);
220 /* qki <= qki * c - qkj * s */
221 /* qkj <= qki * s + qkj * c */
222 gsl_complex x1 = gsl_complex_mul_real(qki, c);
223 gsl_complex y1 = gsl_complex_mul_real(qkj, -s);
225 gsl_complex x2 = gsl_complex_mul_real(qki, s);
226 gsl_complex y2 = gsl_complex_mul_real(qkj, c);
228 gsl_complex qqki = gsl_complex_add(x1, y1);
229 gsl_complex qqkj = gsl_complex_add(x2, y2);
231 gsl_matrix_complex_set (evec, k, a + i, qqki);
232 gsl_matrix_complex_set (evec, k, a + i + 1, qqkj);
236 /* remove any small off-diagonal elements */
238 chop_small_elements (n_block, d_block, sd_block);
243 gsl_vector_view d_vec = gsl_vector_view_array (d, N);
244 gsl_vector_memcpy (eval, &d_vec.vector);