Blame linalg/choleskyc.c

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() */