summaryrefslogtreecommitdiff
path: root/gsl-1.9/linalg/hessenberg.c
diff options
context:
space:
mode:
Diffstat (limited to 'gsl-1.9/linalg/hessenberg.c')
-rw-r--r--gsl-1.9/linalg/hessenberg.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/gsl-1.9/linalg/hessenberg.c b/gsl-1.9/linalg/hessenberg.c
new file mode 100644
index 0000000..de9a47a
--- /dev/null
+++ b/gsl-1.9/linalg/hessenberg.c
@@ -0,0 +1,430 @@
+/* linalg/hessenberg.c
+ *
+ * Copyright (C) 2006 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 2 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 <config.h>
+#include <gsl/gsl_linalg.h>
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_vector.h>
+
+/*
+gsl_linalg_hessenberg()
+ Compute the Householder reduction to Hessenberg form of a
+square N-by-N matrix A.
+
+H = U^t A U
+
+See Golub & Van Loan, "Matrix Computations" (3rd ed), algorithm
+7.4.2
+
+Inputs: A - matrix to reduce
+ tau - where to store scalar factors in Householder
+ matrices; this vector must be of length N,
+ where N is the order of A
+
+Return: GSL_SUCCESS unless error occurs
+
+Notes: on output, the upper triangular portion of A (including
+the diagaonal and subdiagonal) contains the Hessenberg matrix.
+The lower triangular portion (below the subdiagonal) contains
+the Householder vectors which can be used to construct
+the similarity transform matrix U.
+
+The matrix U is
+
+U = U(1) U(2) ... U(n - 2)
+
+where
+
+U(i) = I - tau(i) * v(i) * v(i)^t
+
+and the vector v(i) is stored in column i of the matrix A
+underneath the subdiagonal. So the first element of v(i)
+is stored in row i + 2, column i, the second element at
+row i + 3, column i, and so on.
+
+Also note that for the purposes of computing U(i),
+v(1:i) = 0, v(i + 1) = 1, and v(i+2:n) is what is stored in
+column i of A beneath the subdiagonal.
+*/
+
+int
+gsl_linalg_hessenberg(gsl_matrix *A, gsl_vector *tau)
+{
+ const size_t N = A->size1;
+
+ if (N != A->size2)
+ {
+ GSL_ERROR ("Hessenberg reduction requires square matrix",
+ GSL_ENOTSQR);
+ }
+ else if (N != tau->size)
+ {
+ GSL_ERROR ("tau vector must match matrix size", GSL_EBADLEN);
+ }
+ else if (N < 3)
+ {
+ /* nothing to do */
+ return GSL_SUCCESS;
+ }
+ else
+ {
+ size_t i; /* looping */
+ gsl_vector_view c, /* matrix column */
+ hv; /* householder vector */
+ gsl_matrix_view m;
+ double tau_i; /* beta in algorithm 7.4.2 */
+
+ for (i = 0; i < N - 2; ++i)
+ {
+ /*
+ * make a copy of A(i + 1:n, i) and store it in the section
+ * of 'tau' that we haven't stored coefficients in yet
+ */
+
+ c = gsl_matrix_column(A, i);
+ c = gsl_vector_subvector(&c.vector, i + 1, N - (i + 1));
+
+ hv = gsl_vector_subvector(tau, i + 1, N - (i + 1));
+ gsl_vector_memcpy(&hv.vector, &c.vector);
+
+ /* compute householder transformation of A(i+1:n,i) */
+ tau_i = gsl_linalg_householder_transform(&hv.vector);
+
+ /* apply left householder matrix (I - tau_i v v') to A */
+ m = gsl_matrix_submatrix(A, i + 1, i, N - (i + 1), N - i);
+ gsl_linalg_householder_hm(tau_i, &hv.vector, &m.matrix);
+
+ /* apply right householder matrix (I - tau_i v v') to A */
+ m = gsl_matrix_submatrix(A, 0, i + 1, N, N - (i + 1));
+ gsl_linalg_householder_mh(tau_i, &hv.vector, &m.matrix);
+
+ /* save Householder coefficient */
+ gsl_vector_set(tau, i, tau_i);
+
+ /*
+ * store Householder vector below the subdiagonal in column
+ * i of the matrix. hv(1) does not need to be stored since
+ * it is always 1.
+ */
+ c = gsl_vector_subvector(&c.vector, 1, c.vector.size - 1);
+ hv = gsl_vector_subvector(&hv.vector, 1, hv.vector.size - 1);
+ gsl_vector_memcpy(&c.vector, &hv.vector);
+ }
+
+ return GSL_SUCCESS;
+ }
+} /* gsl_linalg_hessenberg() */
+
+/*
+gsl_linalg_hessenberg_unpack()
+ Construct the matrix U which transforms a matrix A into
+its upper Hessenberg form:
+
+H = U^t A U
+
+by unpacking the information stored in H from gsl_linalg_hessenberg().
+
+U is a product of Householder matrices:
+
+U = U(1) U(2) ... U(n - 2)
+
+where
+
+U(i) = I - tau(i) * v(i) * v(i)^t
+
+The v(i) are stored in the lower triangular part of H by
+gsl_linalg_hessenberg(). The tau(i) are stored in the vector tau.
+
+Inputs: H - Hessenberg matrix computed from
+ gsl_linalg_hessenberg()
+ tau - tau vector computed from gsl_linalg_hessenberg()
+ U - (output) where to store similarity matrix
+
+Return: success or error
+*/
+
+int
+gsl_linalg_hessenberg_unpack(gsl_matrix * H, gsl_vector * tau,
+ gsl_matrix * U)
+{
+ int s;
+
+ gsl_matrix_set_identity(U);
+
+ s = gsl_linalg_hessenberg_unpack_accum(H, tau, U);
+
+ return s;
+} /* gsl_linalg_hessenberg_unpack() */
+
+/*
+gsl_linalg_hessenberg_unpack_accum()
+ This routine is the same as gsl_linalg_hessenberg_unpack(), except
+instead of storing the similarity matrix in U, it accumulates it,
+so that
+
+U -> U * [ U(1) U(2) ... U(n - 2) ]
+
+instead of:
+
+U -> U(1) U(2) ... U(n - 2)
+
+Inputs: H - Hessenberg matrix computed from
+ gsl_linalg_hessenberg()
+ tau - tau vector computed from gsl_linalg_hessenberg()
+ V - (input/output) where to accumulate similarity matrix
+
+Return: success or error
+
+Notes: 1) On input, V needs to be initialized. The Householder matrices
+ are accumulated into V, so on output,
+
+ V_out = V_in * U(1) * U(2) * ... * U(n - 2)
+
+ so if you just want the product of the Householder matrices,
+ initialize V to the identity matrix before calling this
+ function.
+
+ 2) V does not have to be square, but must have the same
+ number of columns as the order of H
+*/
+
+int
+gsl_linalg_hessenberg_unpack_accum(gsl_matrix * H, gsl_vector * tau,
+ gsl_matrix * V)
+{
+ const size_t N = H->size1;
+
+ if (N != H->size2)
+ {
+ GSL_ERROR ("Hessenberg reduction requires square matrix",
+ GSL_ENOTSQR);
+ }
+ else if (N != tau->size)
+ {
+ GSL_ERROR ("tau vector must match matrix size", GSL_EBADLEN);
+ }
+ else if (N != V->size2)
+ {
+ GSL_ERROR ("V matrix has wrong dimension", GSL_EBADLEN);
+ }
+ else
+ {
+ size_t j; /* looping */
+ double tau_j; /* householder coefficient */
+ gsl_vector_view c, /* matrix column */
+ hv; /* householder vector */
+ gsl_matrix_view m;
+
+ if (N < 3)
+ {
+ /* nothing to do */
+ return GSL_SUCCESS;
+ }
+
+ for (j = 0; j < (N - 2); ++j)
+ {
+ c = gsl_matrix_column(H, j);
+
+ tau_j = gsl_vector_get(tau, j);
+
+ /*
+ * get a view to the householder vector in column j, but
+ * make sure hv(2) starts at the element below the
+ * subdiagonal, since hv(1) was never stored and is always
+ * 1
+ */
+ hv = gsl_vector_subvector(&c.vector, j + 1, N - (j + 1));
+
+ /*
+ * Only operate on part of the matrix since the first
+ * j + 1 entries of the real householder vector are 0
+ *
+ * V -> V * U(j)
+ *
+ * Note here that V->size1 is not necessarily equal to N
+ */
+ m = gsl_matrix_submatrix(V, 0, j + 1, V->size1, N - (j + 1));
+
+ /* apply right Householder matrix to V */
+ gsl_linalg_householder_mh(tau_j, &hv.vector, &m.matrix);
+ }
+
+ return GSL_SUCCESS;
+ }
+} /* gsl_linalg_hessenberg_unpack_accum() */
+
+/*
+gsl_linalg_hessenberg_set_zero()
+ Zero out the lower triangular portion of the Hessenberg matrix H.
+This is useful when Householder vectors may be stored in the lower
+part of H, but eigenvalue solvers need some scratch space with zeros.
+*/
+
+void
+gsl_linalg_hessenberg_set_zero(gsl_matrix * H)
+{
+ const int N = (int) H->size1;
+ int i, j;
+
+ for (j = 0; j < N - 2; ++j)
+ {
+ for (i = j + 2; i < N; ++i)
+ {
+ gsl_matrix_set(H, i, j, 0.0);
+ }
+ }
+} /* gsl_linalg_hessenberg_set_zero() */
+
+/*
+gsl_linalg_hessenberg_submatrix()
+
+ This routine does the same thing as gsl_linalg_hessenberg(),
+except that it operates on a submatrix of a larger matrix, but
+updates the larger matrix with the Householder transformations.
+
+For example, suppose
+
+M = [ M_{11} | M_{12} | M_{13} ]
+ [ 0 | A | M_{23} ]
+ [ 0 | 0 | M_{33} ]
+
+where M_{11} and M_{33} are already in Hessenberg form, and we
+just want to reduce A to Hessenberg form. Applying the transformations
+to A alone will cause the larger matrix M to lose its similarity
+information. So this routine updates M_{12} and M_{23} as A gets
+reduced.
+
+Inputs: M - total matrix
+ A - (sub)matrix to reduce
+ top - row index of top of A in M
+ tau - where to store scalar factors in Householder
+ matrices; this vector must be of length N,
+ where N is the order of A
+
+Return: GSL_SUCCESS unless error occurs
+
+Notes: on output, the upper triangular portion of A (including
+the diagaonal and subdiagonal) contains the Hessenberg matrix.
+The lower triangular portion (below the subdiagonal) contains
+the Householder vectors which can be used to construct
+the similarity transform matrix U.
+
+The matrix U is
+
+U = U(1) U(2) ... U(n - 2)
+
+where
+
+U(i) = I - tau(i) * v(i) * v(i)^t
+
+and the vector v(i) is stored in column i of the matrix A
+underneath the subdiagonal. So the first element of v(i)
+is stored in row i + 2, column i, the second element at
+row i + 3, column i, and so on.
+
+Also note that for the purposes of computing U(i),
+v(1:i) = 0, v(i + 1) = 1, and v(i+2:n) is what is stored in
+column i of A beneath the subdiagonal.
+*/
+
+int
+gsl_linalg_hessenberg_submatrix(gsl_matrix *M, gsl_matrix *A, size_t top,
+ gsl_vector *tau)
+{
+ const size_t N = A->size1;
+ const size_t N_M = M->size1;
+
+ if (N != A->size2)
+ {
+ GSL_ERROR ("Hessenberg reduction requires square matrix",
+ GSL_ENOTSQR);
+ }
+ else if (N != tau->size)
+ {
+ GSL_ERROR ("tau vector must match matrix size", GSL_EBADLEN);
+ }
+ else if (N < 3)
+ {
+ /* nothing to do */
+ return GSL_SUCCESS;
+ }
+ else
+ {
+ size_t i; /* looping */
+ gsl_vector_view c, /* matrix column */
+ hv; /* householder vector */
+ gsl_matrix_view m;
+ double tau_i; /* beta in algorithm 7.4.2 */
+
+ for (i = 0; i < N - 2; ++i)
+ {
+ /*
+ * make a copy of A(i + 1:n, i) and store it in the section
+ * of 'tau' that we haven't stored coefficients in yet
+ */
+
+ c = gsl_matrix_column(A, i);
+ c = gsl_vector_subvector(&c.vector, i + 1, N - (i + 1));
+
+ hv = gsl_vector_subvector(tau, i + 1, N - (i + 1));
+ gsl_vector_memcpy(&hv.vector, &c.vector);
+
+ /* compute householder transformation of A(i+1:n,i) */
+ tau_i = gsl_linalg_householder_transform(&hv.vector);
+
+ /*
+ * apply left householder matrix (I - tau_i v v') to
+ * [ A | M_{23} ]
+ */
+ m = gsl_matrix_submatrix(M,
+ top + i + 1,
+ top + i,
+ N - (i + 1),
+ N_M - top - i);
+ gsl_linalg_householder_hm(tau_i, &hv.vector, &m.matrix);
+
+ /*
+ * apply right householder matrix (I - tau_i v v') to
+ *
+ * [ M_{12} ]
+ * [ A ]
+ */
+ m = gsl_matrix_submatrix(M,
+ 0,
+ top + i + 1,
+ top + N,
+ N - (i + 1));
+ gsl_linalg_householder_mh(tau_i, &hv.vector, &m.matrix);
+
+ /* save Householder coefficient */
+ gsl_vector_set(tau, i, tau_i);
+
+ /*
+ * store Householder vector below the subdiagonal in column
+ * i of the matrix. hv(1) does not need to be stored since
+ * it is always 1.
+ */
+ c = gsl_vector_subvector(&c.vector, 1, c.vector.size - 1);
+ hv = gsl_vector_subvector(&hv.vector, 1, hv.vector.size - 1);
+ gsl_vector_memcpy(&c.vector, &hv.vector);
+ }
+
+ return GSL_SUCCESS;
+ }
+} /* gsl_linalg_hessenberg_submatrix() */