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.
20 /* Balance a general matrix by scaling the rows and columns, so the
21 * new row and column norms are the same order of magnitude.
25 * where D is a diagonal matrix
27 * This is necessary for the unsymmetric eigenvalue problem since the
28 * calculation can become numerically unstable for unbalanced
31 * See Golub & Van Loan, "Matrix Computations" (3rd ed), Section 7.5.7
32 * and Wilkinson & Reinsch, "Handbook for Automatic Computation", II/11 p320.
37 #include <gsl/gsl_math.h>
38 #include <gsl/gsl_vector.h>
39 #include <gsl/gsl_matrix.h>
40 #include <gsl/gsl_blas.h>
42 #include <gsl/gsl_linalg.h>
44 #define FLOAT_RADIX 2.0
45 #define FLOAT_RADIX_SQ (FLOAT_RADIX * FLOAT_RADIX)
48 gsl_linalg_balance_matrix(gsl_matrix * A, gsl_vector * D)
50 const size_t N = A->size1;
54 GSL_ERROR ("vector must match matrix size", GSL_EBADLEN);
63 /* initialize D to the identity matrix */
64 gsl_vector_set_all(D, 1.0);
75 for (i = 0; i < N; ++i)
80 for (j = 0; j < N; ++j)
84 col_norm += fabs(gsl_matrix_get(A, j, i));
85 row_norm += fabs(gsl_matrix_get(A, i, j));
89 if ((col_norm == 0.0) || (row_norm == 0.0))
94 g = row_norm / FLOAT_RADIX;
96 s = col_norm + row_norm;
99 * find the integer power of the machine radix which
100 * comes closest to balancing the matrix
105 col_norm *= FLOAT_RADIX_SQ;
108 g = row_norm * FLOAT_RADIX;
113 col_norm /= FLOAT_RADIX_SQ;
116 if ((row_norm + col_norm) < 0.95 * s * f)
123 * apply similarity transformation D, where
124 * D_{ij} = f_i * delta_{ij}
127 /* multiply by D^{-1} on the left */
128 v = gsl_matrix_row(A, i);
129 gsl_blas_dscal(g, &v.vector);
131 /* multiply by D on the right */
132 v = gsl_matrix_column(A, i);
133 gsl_blas_dscal(f, &v.vector);
135 /* keep track of transformation */
136 gsl_vector_set(D, i, gsl_vector_get(D, i) * f);
143 } /* gsl_linalg_balance_matrix() */
146 gsl_linalg_balance_accum()
147 Accumulate a balancing transformation into a matrix.
148 This is used during the computation of Schur vectors since the
149 Schur vectors computed are the vectors for the balanced matrix.
150 We must at some point accumulate the balancing transformation into
151 the Schur vector matrix to get the vectors for the original matrix.
155 where D is the diagonal matrix
157 Inputs: A - matrix to transform
158 D - vector containing diagonal elements of D
162 gsl_linalg_balance_accum(gsl_matrix *A, gsl_vector *D)
164 const size_t N = A->size1;
168 GSL_ERROR ("vector must match matrix size", GSL_EBADLEN);
176 for (i = 0; i < N; ++i)
178 s = gsl_vector_get(D, i);
179 r = gsl_matrix_row(A, i);
181 gsl_blas_dscal(s, &r.vector);
186 } /* gsl_linalg_balance_accum() */