Blame linalg/mcholesky.c

Packit 67cb25
/* Modified Cholesky 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
#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
/*
Packit 67cb25
 * This module contains routines related to the Modified Cholesky
Packit 67cb25
 * Decomposition, which factors a symmetric indefinite matrix A as
Packit 67cb25
 *
Packit 67cb25
 * P (A + E) P^T = L D L^T
Packit 67cb25
 *
Packit 67cb25
 * where:
Packit 67cb25
 * P: permutation matrix
Packit 67cb25
 * E: small, non-negative diagonal matrix
Packit 67cb25
 * L: unit lower triangular matrix
Packit 67cb25
 * D: strictly positive diagonal matrix
Packit 67cb25
 *
Packit 67cb25
 * These routines follow these works closely:
Packit 67cb25
 *
Packit 67cb25
 * [1] P. E. Gill, W. Murray, M. H. Wright, Practical Optimization,
Packit 67cb25
 *     Academic Press, 1981.
Packit 67cb25
 *
Packit 67cb25
 * [2] Dennis and Schnabel, Numerical Methods for Unconstrained Optimization
Packit 67cb25
 *     and Nonlinear Equations, SIAM, 1996
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
static size_t mcholesky_maxabs(const gsl_vector * v, double *maxabs);
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
gsl_linalg_mcholesky_decomp()
Packit 67cb25
  Perform Pivoted Modified Cholesky LDLT decomposition of a symmetric positive
Packit 67cb25
indefinite matrix:
Packit 67cb25
Packit 67cb25
P (A + E) P^T = L D L^T
Packit 67cb25
Packit 67cb25
Inputs: A - (input) symmetric, positive indefinite matrix,
Packit 67cb25
                    stored in lower triangle
Packit 67cb25
            (output) lower triangle contains L; diagonal contains D
Packit 67cb25
        p - (output) permutation matrix P
Packit 67cb25
        E - (output) perturbation matrix E
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), with modifications
Packit 67cb25
described in [1] and [2]
Packit 67cb25
Packit 67cb25
2) E can be set to NULL if not required
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_mcholesky_decomp (gsl_matrix * A, gsl_permutation * p,
Packit 67cb25
                             gsl_vector * E)
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
      const double delta = GSL_DBL_EPSILON;
Packit 67cb25
      double beta;
Packit 67cb25
      double gamma = 0.0;
Packit 67cb25
      double xi = 0.0;
Packit 67cb25
      gsl_vector_view diag = gsl_matrix_diagonal(A);
Packit 67cb25
      size_t i, j;
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
      gsl_permutation_init(p);
Packit 67cb25
Packit 67cb25
      /* compute:
Packit 67cb25
       * gamma = max | A_{ii} |
Packit 67cb25
       * xi = max_{i \ne j} | A_{ij} |
Packit 67cb25
       */
Packit 67cb25
      for (i = 0; i < N; ++i)
Packit 67cb25
        {
Packit 67cb25
          double aii = gsl_matrix_get(A, i, i);
Packit 67cb25
Packit 67cb25
          gamma = GSL_MAX(gamma, fabs(aii));
Packit 67cb25
Packit 67cb25
          for (j = 0; j < i; ++j)
Packit 67cb25
            {
Packit 67cb25
              double aij = gsl_matrix_get(A, i, j);
Packit 67cb25
              xi = GSL_MAX(xi, fabs(aij));
Packit 67cb25
            }
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* compute:
Packit 67cb25
       * beta = sqrt[ max { gamma, xi/nu, eps } ]
Packit 67cb25
       * with: nu = max{ sqrt(N^2 - 1), 1 }
Packit 67cb25
       */
Packit 67cb25
      if (N == 1)
Packit 67cb25
        {
Packit 67cb25
          beta = GSL_MAX(GSL_MAX(gamma, xi), GSL_DBL_EPSILON);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          double nu = sqrt(N*N - 1.0);
Packit 67cb25
          beta = GSL_MAX(GSL_MAX(gamma, xi / nu), GSL_DBL_EPSILON);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      beta = sqrt(beta);
Packit 67cb25
Packit 67cb25
      for (j = 0; j < N; ++j)
Packit 67cb25
        {
Packit 67cb25
          double ajj, thetaj, u, alpha, alphainv;
Packit 67cb25
          gsl_vector_view w;
Packit 67cb25
          size_t q;
Packit 67cb25
Packit 67cb25
          /* compute q = max_idx { A_jj, ..., A_nn } */
Packit 67cb25
          w = gsl_vector_subvector(&diag.vector, j, N - j);
Packit 67cb25
          q = mcholesky_maxabs(&w.vector, NULL) + j;
Packit 67cb25
Packit 67cb25
          gsl_permutation_swap(p, q, j);
Packit 67cb25
          cholesky_swap_rowcol(A, q, j);
Packit 67cb25
Packit 67cb25
          /* theta_j = max_{j+1 <= i <= n} |A_{ij}| */
Packit 67cb25
          if (j < N - 1)
Packit 67cb25
            {
Packit 67cb25
              w = gsl_matrix_subcolumn(A, j, j + 1, N - j - 1);
Packit 67cb25
              mcholesky_maxabs(&w.vector, &thetaj;;
Packit 67cb25
            }
Packit 67cb25
          else
Packit 67cb25
            {
Packit 67cb25
              thetaj = 0.0;
Packit 67cb25
            }
Packit 67cb25
Packit 67cb25
          u = thetaj / beta;
Packit 67cb25
Packit 67cb25
          /* compute alpha = d_j */
Packit 67cb25
          ajj = gsl_matrix_get(A, j, j);
Packit 67cb25
          alpha = GSL_MAX(GSL_MAX(delta, fabs(ajj)), u * u);
Packit 67cb25
          alphainv = 1.0 / alpha;
Packit 67cb25
Packit 67cb25
          if (j < N - 1)
Packit 67cb25
            {
Packit 67cb25
              /* v = A(j+1:n, j) */
Packit 67cb25
              gsl_vector_view v = gsl_matrix_subcolumn(A, j, j + 1, N - j - 1);
Packit 67cb25
Packit 67cb25
              /* m = A(j+1:n, j+1:n) */
Packit 67cb25
              gsl_matrix_view m = gsl_matrix_submatrix(A, j + 1, j + 1, N - j - 1, N - j - 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
          if (E)
Packit 67cb25
            gsl_vector_set(E, j, alpha - ajj);
Packit 67cb25
Packit 67cb25
          gsl_matrix_set(A, j, j, alpha);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      if (E)
Packit 67cb25
        {
Packit 67cb25
          /* we currently have: P A P^T + E = L D L^T, permute E
Packit 67cb25
           * so that we have: P (A + E) P^T = L D L^T */
Packit 67cb25
          gsl_permute_vector_inverse(p, E);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      return GSL_SUCCESS;
Packit 67cb25
    }
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_mcholesky_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
  int status = gsl_linalg_pcholesky_solve(LDLT, p, b, x);
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_mcholesky_svx(const gsl_matrix * LDLT,
Packit 67cb25
                         const gsl_permutation * p,
Packit 67cb25
                         gsl_vector * x)
Packit 67cb25
{
Packit 67cb25
  int status = gsl_linalg_pcholesky_svx(LDLT, p, x);
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_mcholesky_rcond (const gsl_matrix * LDLT, const gsl_permutation * p,
Packit 67cb25
                            double * rcond, gsl_vector * work)
Packit 67cb25
{
Packit 67cb25
  int status = gsl_linalg_pcholesky_rcond(LDLT, p, rcond, work);
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_linalg_mcholesky_invert(const gsl_matrix * LDLT, const gsl_permutation * p,
Packit 67cb25
                            gsl_matrix * Ainv)
Packit 67cb25
{
Packit 67cb25
  int status = gsl_linalg_pcholesky_invert(LDLT, p, Ainv);
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
mcholesky_maxabs()
Packit 67cb25
  Compute:
Packit 67cb25
Packit 67cb25
val = max_i |v_i|
Packit 67cb25
Packit 67cb25
Inputs: v      - vector
Packit 67cb25
        maxabs - (output) max abs value
Packit 67cb25
Packit 67cb25
Return: index corresponding to max_i |v_i|
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static size_t
Packit 67cb25
mcholesky_maxabs(const gsl_vector * v, double *maxabs)
Packit 67cb25
{
Packit 67cb25
  const size_t n = v->size;
Packit 67cb25
  size_t i;
Packit 67cb25
  size_t idx = 0;
Packit 67cb25
  double max = gsl_vector_get(v, idx);
Packit 67cb25
Packit 67cb25
  for (i = 1; i < n; ++i)
Packit 67cb25
    {
Packit 67cb25
      double vi = gsl_vector_get(v, i);
Packit 67cb25
      double absvi = fabs(vi);
Packit 67cb25
Packit 67cb25
      if (absvi > max)
Packit 67cb25
        {
Packit 67cb25
          max = absvi;
Packit 67cb25
          idx = i;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (maxabs)
Packit 67cb25
    *maxabs = max;
Packit 67cb25
Packit 67cb25
  return idx;
Packit 67cb25
}