|
Packit |
67cb25 |
/* linalg/choleskyc.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 2007 Patrick Alken
|
|
Packit |
67cb25 |
* Copyright (C) 2010 Huan Wu (gsl_linalg_complex_cholesky_invert)
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
67cb25 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
67cb25 |
* the Free Software Foundation; either version 3 of the License, or (at
|
|
Packit |
67cb25 |
* your option) any later version.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
67cb25 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
67cb25 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
67cb25 |
* General Public License for more details.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
67cb25 |
* along with this program; if not, write to the Free Software
|
|
Packit |
67cb25 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <config.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_matrix.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_vector.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_linalg.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_complex.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_complex_math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_blas.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_errno.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* This module contains routines related to the Cholesky decomposition
|
|
Packit |
67cb25 |
* of a complex Hermitian positive definite matrix.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void cholesky_complex_conj_vector(gsl_vector_complex *v);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_decomp()
|
|
Packit |
67cb25 |
Perform the Cholesky decomposition on a Hermitian positive definite
|
|
Packit |
67cb25 |
matrix. See Golub & Van Loan, "Matrix Computations" (3rd ed),
|
|
Packit |
67cb25 |
algorithm 4.2.2.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: A - (input/output) complex postive definite matrix
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: success or error
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
The lower triangle of A is overwritten with the Cholesky decomposition
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_decomp(gsl_matrix_complex *A)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const size_t N = A->size1;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (N != A->size2)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("cholesky decomposition requires square matrix", GSL_ENOTSQR);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t i, j;
|
|
Packit |
67cb25 |
gsl_complex z;
|
|
Packit |
67cb25 |
double ajj;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (j = 0; j < N; ++j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
z = gsl_matrix_complex_get(A, j, j);
|
|
Packit |
67cb25 |
ajj = GSL_REAL(z);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (j > 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_complex_const_view aj =
|
|
Packit |
67cb25 |
gsl_matrix_complex_const_subrow(A, j, 0, j);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_zdotc(&aj.vector, &aj.vector, &z);
|
|
Packit |
67cb25 |
ajj -= GSL_REAL(z);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (ajj <= 0.0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("matrix is not positive definite", GSL_EDOM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
ajj = sqrt(ajj);
|
|
Packit |
67cb25 |
GSL_SET_COMPLEX(&z, ajj, 0.0);
|
|
Packit |
67cb25 |
gsl_matrix_complex_set(A, j, j, z);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (j < N - 1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_complex_view av =
|
|
Packit |
67cb25 |
gsl_matrix_complex_subcolumn(A, j, j + 1, N - j - 1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (j > 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_complex_view aj =
|
|
Packit |
67cb25 |
gsl_matrix_complex_subrow(A, j, 0, j);
|
|
Packit |
67cb25 |
gsl_matrix_complex_view am =
|
|
Packit |
67cb25 |
gsl_matrix_complex_submatrix(A, j + 1, 0, N - j - 1, j);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
cholesky_complex_conj_vector(&aj.vector);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_zgemv(CblasNoTrans,
|
|
Packit |
67cb25 |
GSL_COMPLEX_NEGONE,
|
|
Packit |
67cb25 |
&am.matrix,
|
|
Packit |
67cb25 |
&aj.vector,
|
|
Packit |
67cb25 |
GSL_COMPLEX_ONE,
|
|
Packit |
67cb25 |
&av.vector);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
cholesky_complex_conj_vector(&aj.vector);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_zdscal(1.0 / ajj, &av.vector);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Now store L^H in upper triangle */
|
|
Packit |
67cb25 |
for (i = 1; i < N; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
for (j = 0; j < i; ++j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
z = gsl_matrix_complex_get(A, i, j);
|
|
Packit |
67cb25 |
gsl_matrix_complex_set(A, j, i, gsl_complex_conjugate(z));
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
} /* gsl_linalg_complex_cholesky_decomp() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_solve()
|
|
Packit |
67cb25 |
Solve A x = b where A is in cholesky form
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_solve (const gsl_matrix_complex * cholesky,
|
|
Packit |
67cb25 |
const gsl_vector_complex * b,
|
|
Packit |
67cb25 |
gsl_vector_complex * x)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (cholesky->size1 != cholesky->size2)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (cholesky->size1 != b->size)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR ("matrix size must match b size", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (cholesky->size2 != x->size)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_complex_memcpy (x, b);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* solve for y using forward-substitution, L y = b */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_ztrsv (CblasLower, CblasNoTrans, CblasNonUnit, cholesky, x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* perform back-substitution, L^H x = y */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_ztrsv (CblasLower, CblasConjTrans, CblasNonUnit, cholesky, x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
} /* gsl_linalg_complex_cholesky_solve() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_svx()
|
|
Packit |
67cb25 |
Solve A x = b in place where A is in cholesky form
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_svx (const gsl_matrix_complex * cholesky,
|
|
Packit |
67cb25 |
gsl_vector_complex * x)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (cholesky->size1 != cholesky->size2)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (cholesky->size2 != x->size)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* solve for y using forward-substitution, L y = b */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_ztrsv (CblasLower, CblasNoTrans, CblasNonUnit, cholesky, x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* perform back-substitution, L^H x = y */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_ztrsv (CblasLower, CblasConjTrans, CblasNonUnit, cholesky, x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
} /* gsl_linalg_complex_cholesky_svx() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/******************************************************************************
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_invert()
|
|
Packit |
67cb25 |
Compute the inverse of an Hermitian positive definite matrix in
|
|
Packit |
67cb25 |
Cholesky form.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: LLT - matrix in cholesky form on input
|
|
Packit |
67cb25 |
A^{-1} = L^{-H} L^{-1} on output
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: success or error
|
|
Packit |
67cb25 |
******************************************************************************/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
int
|
|
Packit |
67cb25 |
gsl_linalg_complex_cholesky_invert(gsl_matrix_complex * LLT)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (LLT->size1 != LLT->size2)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t N = LLT->size1;
|
|
Packit |
67cb25 |
size_t i, j;
|
|
Packit |
67cb25 |
gsl_vector_complex_view v1;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* invert the lower triangle of LLT */
|
|
Packit |
67cb25 |
for (i = 0; i < N; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double ajj;
|
|
Packit |
67cb25 |
gsl_complex z;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
j = N - i - 1;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_complex z0 = gsl_matrix_complex_get(LLT, j, j);
|
|
Packit |
67cb25 |
ajj = 1.0 / GSL_REAL(z0);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
GSL_SET_COMPLEX(&z, ajj, 0.0);
|
|
Packit |
67cb25 |
gsl_matrix_complex_set(LLT, j, j, z);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_complex z1 = gsl_matrix_complex_get(LLT, j, j);
|
|
Packit |
67cb25 |
ajj = -GSL_REAL(z1);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (j < N - 1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_matrix_complex_view m;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
m = gsl_matrix_complex_submatrix(LLT, j + 1, j + 1,
|
|
Packit |
67cb25 |
N - j - 1, N - j - 1);
|
|
Packit |
67cb25 |
v1 = gsl_matrix_complex_subcolumn(LLT, j, j + 1, N - j - 1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_ztrmv(CblasLower, CblasNoTrans, CblasNonUnit,
|
|
Packit |
67cb25 |
&m.matrix, &v1.vector);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_zdscal(ajj, &v1.vector);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
} /* for (i = 0; i < N; ++i) */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* The lower triangle of LLT now contains L^{-1}. Now compute
|
|
Packit |
67cb25 |
* A^{-1} = L^{-H} L^{-1}
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* The (ij) element of A^{-1} is column i of conj(L^{-1}) dotted into
|
|
Packit |
67cb25 |
* column j of L^{-1}
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (i = 0; i < N; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_complex sum;
|
|
Packit |
67cb25 |
for (j = i + 1; j < N; ++j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_complex_view v2;
|
|
Packit |
67cb25 |
v1 = gsl_matrix_complex_subcolumn(LLT, i, j, N - j);
|
|
Packit |
67cb25 |
v2 = gsl_matrix_complex_subcolumn(LLT, j, j, N - j);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute Ainv[i,j] = sum_k{conj(Linv[k,i]) * Linv[k,j]} */
|
|
Packit |
67cb25 |
gsl_blas_zdotc(&v1.vector, &v2.vector, &sum);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* store in upper triangle */
|
|
Packit |
67cb25 |
gsl_matrix_complex_set(LLT, i, j, sum);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* now compute the diagonal element */
|
|
Packit |
67cb25 |
v1 = gsl_matrix_complex_subcolumn(LLT, i, i, N - i);
|
|
Packit |
67cb25 |
gsl_blas_zdotc(&v1.vector, &v1.vector, &sum);
|
|
Packit |
67cb25 |
gsl_matrix_complex_set(LLT, i, i, sum);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* copy the Hermitian upper triangle to the lower triangle */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (j = 1; j < N; j++)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
for (i = 0; i < j; i++)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_complex z = gsl_matrix_complex_get(LLT, i, j);
|
|
Packit |
67cb25 |
gsl_matrix_complex_set(LLT, j, i, gsl_complex_conjugate(z));
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
} /* gsl_linalg_complex_cholesky_invert() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/********************************************
|
|
Packit |
67cb25 |
* INTERNAL ROUTINES *
|
|
Packit |
67cb25 |
********************************************/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
cholesky_complex_conj_vector(gsl_vector_complex *v)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t i;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (i = 0; i < v->size; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_complex z = gsl_vector_complex_get(v, i);
|
|
Packit |
67cb25 |
gsl_vector_complex_set(v, i, gsl_complex_conjugate(z));
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
} /* cholesky_complex_conj_vector() */
|