| /* Cholesky Decomposition |
| * |
| * Copyright (C) 2000 Thomas Walter |
| * |
| * 03 May 2000: Modified for GSL by Brian Gough |
| * 29 Jul 2005: Additions by Gerard Jungman |
| * Copyright (C) 2000,2001, 2002, 2003, 2005 Brian Gough, Gerard Jungman |
| * |
| * This 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, or (at your option) any |
| * later version. |
| * |
| * This source 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. |
| */ |
| |
| /* |
| * Cholesky decomposition of a symmetrix positive definite matrix. |
| * This is useful to solve the matrix arising in |
| * periodic cubic splines |
| * approximating splines |
| * |
| * This algorithm does: |
| * A = L * L' |
| * with |
| * L := lower left triangle matrix |
| * L' := the transposed form of L. |
| * |
| */ |
| |
| #include <config.h> |
| |
| #include <gsl/gsl_math.h> |
| #include <gsl/gsl_errno.h> |
| #include <gsl/gsl_vector.h> |
| #include <gsl/gsl_matrix.h> |
| #include <gsl/gsl_blas.h> |
| #include <gsl/gsl_linalg.h> |
| |
| static inline |
| double |
| quiet_sqrt (double x) |
| /* avoids runtime error, for checking matrix for positive definiteness */ |
| { |
| return (x >= 0) ? sqrt(x) : GSL_NAN; |
| } |
| |
| int |
| gsl_linalg_cholesky_decomp (gsl_matrix * A) |
| { |
| const size_t M = A->size1; |
| const size_t N = A->size2; |
| |
| if (M != N) |
| { |
| GSL_ERROR("cholesky decomposition requires square matrix", GSL_ENOTSQR); |
| } |
| else |
| { |
| size_t i,j,k; |
| int status = 0; |
| |
| /* Do the first 2 rows explicitly. It is simple, and faster. And |
| * one can return if the matrix has only 1 or 2 rows. |
| */ |
| |
| double A_00 = gsl_matrix_get (A, 0, 0); |
| |
| double L_00 = quiet_sqrt(A_00); |
| |
| if (A_00 <= 0) |
| { |
| status = GSL_EDOM ; |
| } |
| |
| gsl_matrix_set (A, 0, 0, L_00); |
| |
| if (M > 1) |
| { |
| double A_10 = gsl_matrix_get (A, 1, 0); |
| double A_11 = gsl_matrix_get (A, 1, 1); |
| |
| double L_10 = A_10 / L_00; |
| double diag = A_11 - L_10 * L_10; |
| double L_11 = quiet_sqrt(diag); |
| |
| if (diag <= 0) |
| { |
| status = GSL_EDOM; |
| } |
| |
| gsl_matrix_set (A, 1, 0, L_10); |
| gsl_matrix_set (A, 1, 1, L_11); |
| } |
| |
| for (k = 2; k < M; k++) |
| { |
| double A_kk = gsl_matrix_get (A, k, k); |
| |
| for (i = 0; i < k; i++) |
| { |
| double sum = 0; |
| |
| double A_ki = gsl_matrix_get (A, k, i); |
| double A_ii = gsl_matrix_get (A, i, i); |
| |
| gsl_vector_view ci = gsl_matrix_row (A, i); |
| gsl_vector_view ck = gsl_matrix_row (A, k); |
| |
| if (i > 0) { |
| gsl_vector_view di = gsl_vector_subvector(&ci.vector, 0, i); |
| gsl_vector_view dk = gsl_vector_subvector(&ck.vector, 0, i); |
| |
| gsl_blas_ddot (&di.vector, &dk.vector, &sum); |
| } |
| |
| A_ki = (A_ki - sum) / A_ii; |
| gsl_matrix_set (A, k, i, A_ki); |
| } |
| |
| { |
| gsl_vector_view ck = gsl_matrix_row (A, k); |
| gsl_vector_view dk = gsl_vector_subvector (&ck.vector, 0, k); |
| |
| double sum = gsl_blas_dnrm2 (&dk.vector); |
| double diag = A_kk - sum * sum; |
| |
| double L_kk = quiet_sqrt(diag); |
| |
| if (diag <= 0) |
| { |
| status = GSL_EDOM; |
| } |
| |
| gsl_matrix_set (A, k, k, L_kk); |
| } |
| } |
| |
| /* Now copy the transposed lower triangle to the upper triangle, |
| * the diagonal is common. |
| */ |
| |
| for (i = 1; i < M; i++) |
| { |
| for (j = 0; j < i; j++) |
| { |
| double A_ij = gsl_matrix_get (A, i, j); |
| gsl_matrix_set (A, j, i, A_ij); |
| } |
| } |
| |
| if (status == GSL_EDOM) |
| { |
| GSL_ERROR ("matrix must be positive definite", GSL_EDOM); |
| } |
| |
| return GSL_SUCCESS; |
| } |
| } |
| |
| |
| int |
| gsl_linalg_cholesky_solve (const gsl_matrix * LLT, |
| const gsl_vector * b, |
| gsl_vector * x) |
| { |
| if (LLT->size1 != LLT->size2) |
| { |
| GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR); |
| } |
| else if (LLT->size1 != b->size) |
| { |
| GSL_ERROR ("matrix size must match b size", GSL_EBADLEN); |
| } |
| else if (LLT->size2 != x->size) |
| { |
| GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN); |
| } |
| else |
| { |
| /* Copy x <- b */ |
| |
| gsl_vector_memcpy (x, b); |
| |
| /* Solve for c using forward-substitution, L c = b */ |
| |
| gsl_blas_dtrsv (CblasLower, CblasNoTrans, CblasNonUnit, LLT, x); |
| |
| /* Perform back-substitution, U x = c */ |
| |
| gsl_blas_dtrsv (CblasUpper, CblasNoTrans, CblasNonUnit, LLT, x); |
| |
| |
| return GSL_SUCCESS; |
| } |
| } |
| |
| int |
| gsl_linalg_cholesky_svx (const gsl_matrix * LLT, |
| gsl_vector * x) |
| { |
| if (LLT->size1 != LLT->size2) |
| { |
| GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR); |
| } |
| else if (LLT->size2 != x->size) |
| { |
| GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN); |
| } |
| else |
| { |
| /* Solve for c using forward-substitution, L c = b */ |
| |
| gsl_blas_dtrsv (CblasLower, CblasNoTrans, CblasNonUnit, LLT, x); |
| |
| /* Perform back-substitution, U x = c */ |
| |
| gsl_blas_dtrsv (CblasUpper, CblasNoTrans, CblasNonUnit, LLT, x); |
| |
| return GSL_SUCCESS; |
| } |
| } |
| |
| |
| int |
| gsl_linalg_cholesky_decomp_unit(gsl_matrix * A, gsl_vector * D) |
| { |
| const size_t N = A->size1; |
| size_t i, j; |
| |
| /* initial Cholesky */ |
| int stat_chol = gsl_linalg_cholesky_decomp(A); |
| |
| if(stat_chol == GSL_SUCCESS) |
| { |
| /* calculate D from diagonal part of initial Cholesky */ |
| for(i = 0; i < N; ++i) |
| { |
| const double C_ii = gsl_matrix_get(A, i, i); |
| gsl_vector_set(D, i, C_ii*C_ii); |
| } |
| |
| /* multiply initial Cholesky by 1/sqrt(D) on the right */ |
| for(i = 0; i < N; ++i) |
| { |
| for(j = 0; j < N; ++j) |
| { |
| gsl_matrix_set(A, i, j, gsl_matrix_get(A, i, j) / sqrt(gsl_vector_get(D, j))); |
| } |
| } |
| |
| /* Because the initial Cholesky contained both L and transpose(L), |
| the result of the multiplication is not symmetric anymore; |
| but the lower triangle _is_ correct. Therefore we reflect |
| it to the upper triangle and declare victory. |
| */ |
| for(i = 0; i < N; ++i) |
| for(j = i + 1; j < N; ++j) |
| gsl_matrix_set(A, i, j, gsl_matrix_get(A, j, i)); |
| } |
| |
| return stat_chol; |
| } |