Blame linalg/pcholesky.c

Packit 67cb25
/* L D L^T Decomposition
Packit 67cb25
 *
Packit 67cb25
 * Copyright (C) 2016 Patrick Alken
Packit 67cb25
 *
Packit 67cb25
 * This is free software; you can redistribute it and/or modify it
Packit 67cb25
 * under the terms of the GNU General Public License as published by the
Packit 67cb25
 * Free Software Foundation; either version 3, or (at your option) any
Packit 67cb25
 * later version.
Packit 67cb25
 *
Packit 67cb25
 * This source is distributed in the hope that it will be useful, but WITHOUT
Packit 67cb25
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
Packit 67cb25
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 67cb25
 * for more details.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
 * L D L^T decomposition of a symmetrix positive definite matrix.
Packit 67cb25
 *
Packit 67cb25
 * This algorithm does:
Packit 67cb25
 *   P A P' = L D L'
Packit 67cb25
 * with
Packit 67cb25
 *   L  := unit lower left triangle matrix
Packit 67cb25
 *   D  := diagonal matrix
Packit 67cb25
 *   L' := the transposed form of L.
Packit 67cb25
 *   P  := permutation matrix
Packit 67cb25
 *
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
Packit 67cb25
#include <gsl/gsl_math.h>
Packit 67cb25
#include <gsl/gsl_errno.h>
Packit 67cb25
#include <gsl/gsl_vector.h>
Packit 67cb25
#include <gsl/gsl_matrix.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
#include <gsl/gsl_linalg.h>
Packit 67cb25
#include <gsl/gsl_permutation.h>
Packit 67cb25
#include <gsl/gsl_permute_vector.h>
Packit 67cb25
Packit 67cb25
#include "cholesky_common.c"
Packit 67cb25
Packit 67cb25
static double cholesky_LDLT_norm1(const gsl_matrix * LDLT, const gsl_permutation * p,
Packit 67cb25
                                  gsl_vector * work);
Packit 67cb25
static int cholesky_LDLT_Ainv(CBLAS_TRANSPOSE_t TransA, gsl_vector * x, void * params);
Packit 67cb25
Packit 67cb25
typedef struct
Packit 67cb25
{
Packit 67cb25
  const gsl_matrix * LDLT;
Packit 67cb25
  const gsl_permutation * perm;
Packit 67cb25
} pcholesky_params;
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
pcholesky_decomp()
Packit 67cb25
  Perform Pivoted Cholesky LDLT decomposition of a symmetric positive
Packit 67cb25
semidefinite matrix
Packit 67cb25
Packit 67cb25
Inputs: copy_uplo - copy lower triangle to upper to save original matrix
Packit 67cb25
                    for rcond calculation later
Packit 67cb25
        A         - (input) symmetric, positive semidefinite matrix,
Packit 67cb25
                    stored in lower triangle
Packit 67cb25
                    (output) lower triangle contains L; diagonal contains D
Packit 67cb25
        p         - permutation vector
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) Based on algorithm 4.2.2 (Outer Product LDLT with Pivoting) of
Packit 67cb25
Golub and Van Loan, Matrix Computations (4th ed).
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
pcholesky_decomp (const int copy_uplo, gsl_matrix * A, gsl_permutation * p)
Packit 67cb25
{
Packit 67cb25
  const size_t N = A->size1;
Packit 67cb25
Packit 67cb25
  if (N != A->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("LDLT decomposition requires square matrix", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (p->size != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("permutation length must match matrix size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_view diag = gsl_matrix_diagonal(A);
Packit 67cb25
      size_t k;
Packit 67cb25
Packit 67cb25
      if (copy_uplo)
Packit 67cb25
        {
Packit 67cb25
          /* save a copy of A in upper triangle (for later rcond calculation) */
Packit 67cb25
          gsl_matrix_transpose_tricpy('L', 0, A, A);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_permutation_init(p);
Packit 67cb25
Packit 67cb25
      for (k = 0; k < N; ++k)
Packit 67cb25
        {
Packit 67cb25
          gsl_vector_view w;
Packit 67cb25
          size_t j;
Packit 67cb25
Packit 67cb25
          /* compute j = max_idx { A_kk, ..., A_nn } */
Packit 67cb25
          w = gsl_vector_subvector(&diag.vector, k, N - k);
Packit 67cb25
          j = gsl_vector_max_index(&w.vector) + k;
Packit 67cb25
          gsl_permutation_swap(p, k, j);
Packit 67cb25
Packit 67cb25
          cholesky_swap_rowcol(A, k, j);
Packit 67cb25
Packit 67cb25
          if (k < N - 1)
Packit 67cb25
            {
Packit 67cb25
              double alpha = gsl_matrix_get(A, k, k);
Packit 67cb25
              double alphainv = 1.0 / alpha;
Packit 67cb25
Packit 67cb25
              /* v = A(k+1:n, k) */
Packit 67cb25
              gsl_vector_view v = gsl_matrix_subcolumn(A, k, k + 1, N - k - 1);
Packit 67cb25
Packit 67cb25
              /* m = A(k+1:n, k+1:n) */
Packit 67cb25
              gsl_matrix_view m = gsl_matrix_submatrix(A, k + 1, k + 1, N - k - 1, N - k - 1);
Packit 67cb25
Packit 67cb25
              /* m = m - v v^T / alpha */
Packit 67cb25
              gsl_blas_dsyr(CblasLower, -alphainv, &v.vector, &m.matrix);
Packit 67cb25
Packit 67cb25
              /* v = v / alpha */
Packit 67cb25
              gsl_vector_scale(&v.vector, alphainv);
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_linalg_pcholesky_decomp()
Packit 67cb25
  Perform Pivoted Cholesky LDLT decomposition of a symmetric positive
Packit 67cb25
semidefinite matrix
Packit 67cb25
Packit 67cb25
Inputs: A - (input) symmetric, positive semidefinite matrix,
Packit 67cb25
                    stored in lower triangle
Packit 67cb25
            (output) lower triangle contains L; diagonal contains D
Packit 67cb25
        p - permutation vector
Packit 67cb25
Packit 67cb25
Return: success/error
Packit 67cb25
Packit 67cb25
Notes:
Packit 67cb25
1) Based on algorithm 4.2.2 (Outer Product LDLT with Pivoting) of
Packit 67cb25
Golub and Van Loan, Matrix Computations (4th ed).
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_decomp (gsl_matrix * A, gsl_permutation * p)
Packit 67cb25
{
Packit 67cb25
  int status = pcholesky_decomp(1, A, p);
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_solve(const gsl_matrix * LDLT,
Packit 67cb25
                           const gsl_permutation * p,
Packit 67cb25
                           const gsl_vector * b,
Packit 67cb25
                           gsl_vector * x)
Packit 67cb25
{
Packit 67cb25
  if (LDLT->size1 != LDLT->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("LDLT matrix must be square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != p->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match permutation size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != b->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match b size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->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
      int status;
Packit 67cb25
Packit 67cb25
      gsl_vector_memcpy (x, b);
Packit 67cb25
Packit 67cb25
      status = gsl_linalg_pcholesky_svx (LDLT, p, x);
Packit 67cb25
      
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_svx(const gsl_matrix * LDLT,
Packit 67cb25
                         const gsl_permutation * p,
Packit 67cb25
                         gsl_vector * x)
Packit 67cb25
{
Packit 67cb25
  if (LDLT->size1 != LDLT->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("LDLT matrix must be square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != p->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match permutation size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->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_const_view D = gsl_matrix_const_diagonal(LDLT);
Packit 67cb25
Packit 67cb25
      /* x := P b */
Packit 67cb25
      gsl_permute_vector(p, x);
Packit 67cb25
Packit 67cb25
      /* solve: L w = P b */
Packit 67cb25
      gsl_blas_dtrsv(CblasLower, CblasNoTrans, CblasUnit, LDLT, x);
Packit 67cb25
Packit 67cb25
      /* solve: D y = w */
Packit 67cb25
      gsl_vector_div(x, &D.vector);
Packit 67cb25
Packit 67cb25
      /* solve: L^T z = y */
Packit 67cb25
      gsl_blas_dtrsv(CblasLower, CblasTrans, CblasUnit, LDLT, x);
Packit 67cb25
Packit 67cb25
      /* compute: x = P^T z */
Packit 67cb25
      gsl_permute_vector_inverse(p, x);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_decomp2(gsl_matrix * A, gsl_permutation * p,
Packit 67cb25
                             gsl_vector * S)
Packit 67cb25
{
Packit 67cb25
  const size_t M = A->size1;
Packit 67cb25
  const size_t N = A->size2;
Packit 67cb25
Packit 67cb25
  if (M != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("cholesky decomposition requires square matrix", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (N != p->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match permutation size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (N != S->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR("S must have length N", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status;
Packit 67cb25
Packit 67cb25
      /* save a copy of A in upper triangle (for later rcond calculation) */
Packit 67cb25
      gsl_matrix_transpose_tricpy('L', 0, A, A);
Packit 67cb25
Packit 67cb25
      /* compute scaling factors to reduce cond(A) */
Packit 67cb25
      status = gsl_linalg_cholesky_scale(A, S);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      /* apply scaling factors */
Packit 67cb25
      status = gsl_linalg_cholesky_scale_apply(A, S);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      /* compute Cholesky decomposition of diag(S) A diag(S) */
Packit 67cb25
      status = pcholesky_decomp(0, A, p);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_solve2(const gsl_matrix * LDLT,
Packit 67cb25
                            const gsl_permutation * p,
Packit 67cb25
                            const gsl_vector * S,
Packit 67cb25
                            const gsl_vector * b,
Packit 67cb25
                            gsl_vector * x)
Packit 67cb25
{
Packit 67cb25
  if (LDLT->size1 != LDLT->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("LDLT matrix must be square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != p->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match permutation size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != S->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match S", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != b->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match b size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->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
      int status;
Packit 67cb25
Packit 67cb25
      gsl_vector_memcpy (x, b);
Packit 67cb25
Packit 67cb25
      status = gsl_linalg_pcholesky_svx2 (LDLT, p, S, x);
Packit 67cb25
      
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_svx2(const gsl_matrix * LDLT,
Packit 67cb25
                          const gsl_permutation * p,
Packit 67cb25
                          const gsl_vector * S,
Packit 67cb25
                          gsl_vector * x)
Packit 67cb25
{
Packit 67cb25
  if (LDLT->size1 != LDLT->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("LDLT matrix must be square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != p->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match permutation size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != S->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match S", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->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
      int status;
Packit 67cb25
Packit 67cb25
      /* x := S b */
Packit 67cb25
      gsl_vector_mul(x, S);
Packit 67cb25
Packit 67cb25
      /* solve: A~ x~ = b~, with A~ = S A S, b~ = S b */
Packit 67cb25
      status = gsl_linalg_pcholesky_svx(LDLT, p, x);
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      /* compute: x = S x~ */
Packit 67cb25
      gsl_vector_mul(x, S);
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_linalg_pcholesky_invert()
Packit 67cb25
  Compute the inverse of a symmetric positive definite matrix in
Packit 67cb25
Cholesky form.
Packit 67cb25
Packit 67cb25
Inputs: LDLT - matrix in cholesky form
Packit 67cb25
        p    - permutation
Packit 67cb25
        Ainv - (output) A^{-1}
Packit 67cb25
Packit 67cb25
Return: success or error
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_invert(const gsl_matrix * LDLT, const gsl_permutation * p,
Packit 67cb25
                            gsl_matrix * Ainv)
Packit 67cb25
{
Packit 67cb25
  const size_t M = LDLT->size1;
Packit 67cb25
  const size_t N = LDLT->size2;
Packit 67cb25
Packit 67cb25
  if (M != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("LDLT matrix must be square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (LDLT->size1 != p->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("matrix size must match permutation size", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (Ainv->size1 != Ainv->size2)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("Ainv matrix must be square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (Ainv->size1 != M)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("Ainv matrix has wrong dimensions", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      size_t i, j;
Packit 67cb25
      gsl_vector_view v1, v2;
Packit 67cb25
Packit 67cb25
      /* invert the lower triangle of LDLT */
Packit 67cb25
      gsl_matrix_memcpy(Ainv, LDLT);
Packit 67cb25
      gsl_linalg_tri_lower_unit_invert(Ainv);
Packit 67cb25
Packit 67cb25
      /* compute sqrt(D^{-1}) L^{-1} in the lower triangle of Ainv */
Packit 67cb25
      for (i = 0; i < N; ++i)
Packit 67cb25
        {
Packit 67cb25
          double di = gsl_matrix_get(LDLT, i, i);
Packit 67cb25
          double sqrt_di = sqrt(di);
Packit 67cb25
Packit 67cb25
          for (j = 0; j < i; ++j)
Packit 67cb25
            {
Packit 67cb25
              double *Lij = gsl_matrix_ptr(Ainv, i, j);
Packit 67cb25
              *Lij /= sqrt_di;
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          gsl_matrix_set(Ainv, i, i, 1.0 / sqrt_di);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /*
Packit 67cb25
       * The lower triangle of Ainv now contains D^{-1/2} L^{-1}. Now compute
Packit 67cb25
       * A^{-1} = L^{-T} D^{-1} L^{-1}
Packit 67cb25
       */
Packit 67cb25
Packit 67cb25
      for (i = 0; i < N; ++i)
Packit 67cb25
        {
Packit 67cb25
          double aii = gsl_matrix_get(Ainv, i, i);
Packit 67cb25
Packit 67cb25
          if (i < N - 1)
Packit 67cb25
            {
Packit 67cb25
              double tmp;
Packit 67cb25
Packit 67cb25
              v1 = gsl_matrix_subcolumn(Ainv, i, i, N - i);
Packit 67cb25
              gsl_blas_ddot(&v1.vector, &v1.vector, &tmp);
Packit 67cb25
              gsl_matrix_set(Ainv, i, i, tmp);
Packit 67cb25
Packit 67cb25
              if (i > 0)
Packit 67cb25
                {
Packit 67cb25
                  gsl_matrix_view m = gsl_matrix_submatrix(Ainv, i + 1, 0, N - i - 1, i);
Packit 67cb25
Packit 67cb25
                  v1 = gsl_matrix_subcolumn(Ainv, i, i + 1, N - i - 1);
Packit 67cb25
                  v2 = gsl_matrix_subrow(Ainv, i, 0, i);
Packit 67cb25
Packit 67cb25
                  gsl_blas_dgemv(CblasTrans, 1.0, &m.matrix, &v1.vector, aii, &v2.vector);
Packit 67cb25
                }
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              v1 = gsl_matrix_row(Ainv, N - 1);
Packit 67cb25
              gsl_blas_dscal(aii, &v1.vector);
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* copy lower triangle to upper */
Packit 67cb25
      gsl_matrix_transpose_tricpy('L', 0, Ainv, Ainv);
Packit 67cb25
Packit 67cb25
      /* now apply permutation p to the matrix */
Packit 67cb25
Packit 67cb25
      /* compute L^{-T} D^{-1} L^{-1} P^T */
Packit 67cb25
      for (i = 0; i < N; ++i)
Packit 67cb25
        {
Packit 67cb25
          v1 = gsl_matrix_row(Ainv, i);
Packit 67cb25
          gsl_permute_vector_inverse(p, &v1.vector);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* compute P L^{-T} D^{-1} L^{-1} P^T */
Packit 67cb25
      for (i = 0; i < N; ++i)
Packit 67cb25
        {
Packit 67cb25
          v1 = gsl_matrix_column(Ainv, i);
Packit 67cb25
          gsl_permute_vector_inverse(p, &v1.vector);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_pcholesky_rcond (const gsl_matrix * LDLT, const gsl_permutation * p,
Packit 67cb25
                            double * rcond, gsl_vector * work)
Packit 67cb25
{
Packit 67cb25
  const size_t M = LDLT->size1;
Packit 67cb25
  const size_t N = LDLT->size2;
Packit 67cb25
Packit 67cb25
  if (M != N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("cholesky matrix must be square", GSL_ENOTSQR);
Packit 67cb25
    }
Packit 67cb25
  else if (work->size != 3 * N)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("work vector must have length 3*N", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status;
Packit 67cb25
      double Anorm = cholesky_LDLT_norm1(LDLT, p, work); /* ||A||_1 */
Packit 67cb25
      double Ainvnorm;                                   /* ||A^{-1}||_1 */
Packit 67cb25
      pcholesky_params params;
Packit 67cb25
Packit 67cb25
      *rcond = 0.0;
Packit 67cb25
Packit 67cb25
      /* don't continue if matrix is singular */
Packit 67cb25
      if (Anorm == 0.0)
Packit 67cb25
        return GSL_SUCCESS;
Packit 67cb25
Packit 67cb25
      params.LDLT = LDLT;
Packit 67cb25
      params.perm = p;
Packit 67cb25
Packit 67cb25
      /* estimate ||A^{-1}||_1 */
Packit 67cb25
      status = gsl_linalg_invnorm1(N, cholesky_LDLT_Ainv, &params, &Ainvnorm, work);
Packit 67cb25
Packit 67cb25
      if (status)
Packit 67cb25
        return status;
Packit 67cb25
Packit 67cb25
      if (Ainvnorm != 0.0)
Packit 67cb25
        *rcond = (1.0 / Anorm) / Ainvnorm;
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
cholesky_LDLT_norm1
Packit 67cb25
  Compute 1-norm of original matrix A, stored in upper triangle of LDLT;
Packit 67cb25
diagonal entries have to be reconstructed
Packit 67cb25
Packit 67cb25
Inputs: LDLT - Cholesky L D L^T decomposition (lower triangle) with
Packit 67cb25
               original matrix in upper triangle
Packit 67cb25
        p    - permutation vector
Packit 67cb25
        work - workspace, length 2*N
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static double
Packit 67cb25
cholesky_LDLT_norm1(const gsl_matrix * LDLT, const gsl_permutation * p, gsl_vector * work)
Packit 67cb25
{
Packit 67cb25
  const size_t N = LDLT->size1;
Packit 67cb25
  gsl_vector_const_view D = gsl_matrix_const_diagonal(LDLT);
Packit 67cb25
  gsl_vector_view diagA = gsl_vector_subvector(work, N, N);
Packit 67cb25
  double max = 0.0;
Packit 67cb25
  size_t i, j;
Packit 67cb25
Packit 67cb25
  /* reconstruct diagonal entries of original matrix A */
Packit 67cb25
  for (j = 0; j < N; ++j)
Packit 67cb25
    {
Packit 67cb25
      double Ajj;
Packit 67cb25
Packit 67cb25
      /* compute diagonal (j,j) entry of A */
Packit 67cb25
      Ajj = gsl_vector_get(&D.vector, j);
Packit 67cb25
      for (i = 0; i < j; ++i)
Packit 67cb25
        {
Packit 67cb25
          double Di = gsl_vector_get(&D.vector, i);
Packit 67cb25
          double Lji = gsl_matrix_get(LDLT, j, i);
Packit 67cb25
Packit 67cb25
          Ajj += Di * Lji * Lji;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_vector_set(&diagA.vector, j, Ajj);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  gsl_permute_vector_inverse(p, &diagA.vector);
Packit 67cb25
Packit 67cb25
  for (j = 0; j < N; ++j)
Packit 67cb25
    {
Packit 67cb25
      double sum = 0.0;
Packit 67cb25
      double Ajj = gsl_vector_get(&diagA.vector, j);
Packit 67cb25
Packit 67cb25
      for (i = 0; i < j; ++i)
Packit 67cb25
        {
Packit 67cb25
          double *wi = gsl_vector_ptr(work, i);
Packit 67cb25
          double Aij = gsl_matrix_get(LDLT, i, j);
Packit 67cb25
          double absAij = fabs(Aij);
Packit 67cb25
Packit 67cb25
          sum += absAij;
Packit 67cb25
          *wi += absAij;
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      gsl_vector_set(work, j, sum + fabs(Ajj));
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  for (i = 0; i < N; ++i)
Packit 67cb25
    {
Packit 67cb25
      double wi = gsl_vector_get(work, i);
Packit 67cb25
      max = GSL_MAX(max, wi);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return max;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/* x := A^{-1} x = A^{-t} x, A = L D L^T */
Packit 67cb25
static int
Packit 67cb25
cholesky_LDLT_Ainv(CBLAS_TRANSPOSE_t TransA, gsl_vector * x, void * params)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
  pcholesky_params *par = (pcholesky_params *) params;
Packit 67cb25
Packit 67cb25
  (void) TransA; /* unused parameter warning */
Packit 67cb25
Packit 67cb25
  status = gsl_linalg_pcholesky_svx(par->LDLT, par->perm, x);
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}