|
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 |
}
|