1 /* Cholesky Decomposition
3 * Copyright (C) 2000 Thomas Walter
5 * 03 May 2000: Modified for GSL by Brian Gough
6 * 29 Jul 2005: Additions by Gerard Jungman
7 * Copyright (C) 2000,2001, 2002, 2003, 2005, 2007 Brian Gough, Gerard Jungman
9 * This is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 3, or (at your option) any
14 * This source is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * Cholesky decomposition of a symmetrix positive definite matrix.
22 * This is useful to solve the matrix arising in
23 * periodic cubic splines
24 * approximating splines
26 * This algorithm does:
29 * L := lower left triangle matrix
30 * L' := the transposed form of L.
36 #include <gsl/gsl_math.h>
37 #include <gsl/gsl_errno.h>
38 #include <gsl/gsl_vector.h>
39 #include <gsl/gsl_matrix.h>
40 #include <gsl/gsl_blas.h>
41 #include <gsl/gsl_linalg.h>
46 /* avoids runtime error, for checking matrix for positive definiteness */
48 return (x >= 0) ? sqrt(x) : GSL_NAN;
52 gsl_linalg_cholesky_decomp (gsl_matrix * A)
54 const size_t M = A->size1;
55 const size_t N = A->size2;
59 GSL_ERROR("cholesky decomposition requires square matrix", GSL_ENOTSQR);
66 /* Do the first 2 rows explicitly. It is simple, and faster. And
67 * one can return if the matrix has only 1 or 2 rows.
70 double A_00 = gsl_matrix_get (A, 0, 0);
72 double L_00 = quiet_sqrt(A_00);
79 gsl_matrix_set (A, 0, 0, L_00);
83 double A_10 = gsl_matrix_get (A, 1, 0);
84 double A_11 = gsl_matrix_get (A, 1, 1);
86 double L_10 = A_10 / L_00;
87 double diag = A_11 - L_10 * L_10;
88 double L_11 = quiet_sqrt(diag);
95 gsl_matrix_set (A, 1, 0, L_10);
96 gsl_matrix_set (A, 1, 1, L_11);
99 for (k = 2; k < M; k++)
101 double A_kk = gsl_matrix_get (A, k, k);
103 for (i = 0; i < k; i++)
107 double A_ki = gsl_matrix_get (A, k, i);
108 double A_ii = gsl_matrix_get (A, i, i);
110 gsl_vector_view ci = gsl_matrix_row (A, i);
111 gsl_vector_view ck = gsl_matrix_row (A, k);
114 gsl_vector_view di = gsl_vector_subvector(&ci.vector, 0, i);
115 gsl_vector_view dk = gsl_vector_subvector(&ck.vector, 0, i);
117 gsl_blas_ddot (&di.vector, &dk.vector, &sum);
120 A_ki = (A_ki - sum) / A_ii;
121 gsl_matrix_set (A, k, i, A_ki);
125 gsl_vector_view ck = gsl_matrix_row (A, k);
126 gsl_vector_view dk = gsl_vector_subvector (&ck.vector, 0, k);
128 double sum = gsl_blas_dnrm2 (&dk.vector);
129 double diag = A_kk - sum * sum;
131 double L_kk = quiet_sqrt(diag);
138 gsl_matrix_set (A, k, k, L_kk);
142 /* Now copy the transposed lower triangle to the upper triangle,
143 * the diagonal is common.
146 for (i = 1; i < M; i++)
148 for (j = 0; j < i; j++)
150 double A_ij = gsl_matrix_get (A, i, j);
151 gsl_matrix_set (A, j, i, A_ij);
155 if (status == GSL_EDOM)
157 GSL_ERROR ("matrix must be positive definite", GSL_EDOM);
166 gsl_linalg_cholesky_solve (const gsl_matrix * LLT,
167 const gsl_vector * b,
170 if (LLT->size1 != LLT->size2)
172 GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR);
174 else if (LLT->size1 != b->size)
176 GSL_ERROR ("matrix size must match b size", GSL_EBADLEN);
178 else if (LLT->size2 != x->size)
180 GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN);
186 gsl_vector_memcpy (x, b);
188 /* Solve for c using forward-substitution, L c = b */
190 gsl_blas_dtrsv (CblasLower, CblasNoTrans, CblasNonUnit, LLT, x);
192 /* Perform back-substitution, U x = c */
194 gsl_blas_dtrsv (CblasUpper, CblasNoTrans, CblasNonUnit, LLT, x);
202 gsl_linalg_cholesky_svx (const gsl_matrix * LLT,
205 if (LLT->size1 != LLT->size2)
207 GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR);
209 else if (LLT->size2 != x->size)
211 GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN);
215 /* Solve for c using forward-substitution, L c = b */
217 gsl_blas_dtrsv (CblasLower, CblasNoTrans, CblasNonUnit, LLT, x);
219 /* Perform back-substitution, U x = c */
221 gsl_blas_dtrsv (CblasUpper, CblasNoTrans, CblasNonUnit, LLT, x);
229 gsl_linalg_cholesky_decomp_unit(gsl_matrix * A, gsl_vector * D)
231 const size_t N = A->size1;
234 /* initial Cholesky */
235 int stat_chol = gsl_linalg_cholesky_decomp(A);
237 if(stat_chol == GSL_SUCCESS)
239 /* calculate D from diagonal part of initial Cholesky */
240 for(i = 0; i < N; ++i)
242 const double C_ii = gsl_matrix_get(A, i, i);
243 gsl_vector_set(D, i, C_ii*C_ii);
246 /* multiply initial Cholesky by 1/sqrt(D) on the right */
247 for(i = 0; i < N; ++i)
249 for(j = 0; j < N; ++j)
251 gsl_matrix_set(A, i, j, gsl_matrix_get(A, i, j) / sqrt(gsl_vector_get(D, j)));
255 /* Because the initial Cholesky contained both L and transpose(L),
256 the result of the multiplication is not symmetric anymore;
257 but the lower triangle _is_ correct. Therefore we reflect
258 it to the upper triangle and declare victory.
260 for(i = 0; i < N; ++i)
261 for(j = i + 1; j < N; ++j)
262 gsl_matrix_set(A, i, j, gsl_matrix_get(A, j, i));