|
Packit |
67cb25 |
/* normal.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 2015, 2016 Patrick Alken
|
|
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_math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_vector.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_matrix.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_linalg.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_errno.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_blas.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_eigen.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_multifit.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_multilarge.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_permutation.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef struct
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t p; /* number of columns of LS matrix */
|
|
Packit |
67cb25 |
gsl_matrix *ATA; /* A^T A, p-by-p */
|
|
Packit |
67cb25 |
gsl_vector *ATb; /* A^T b, p-by-1 */
|
|
Packit |
67cb25 |
double normb; /* || b || */
|
|
Packit |
67cb25 |
gsl_matrix *work_ATA; /* workspace for chol(ATA), p-by-p */
|
|
Packit |
67cb25 |
gsl_permutation *perm; /* permutation vector */
|
|
Packit |
67cb25 |
gsl_vector *workp; /* workspace size p */
|
|
Packit |
67cb25 |
gsl_vector *work3p; /* workspace size 3*p */
|
|
Packit |
67cb25 |
gsl_vector *D; /* scale factors for ATA, size p */
|
|
Packit |
67cb25 |
gsl_vector *c; /* solution vector for L-curve */
|
|
Packit |
67cb25 |
int eigen; /* 1 if eigenvalues computed */
|
|
Packit |
67cb25 |
double eval_min; /* minimum eigenvalue */
|
|
Packit |
67cb25 |
double eval_max; /* maximum eigenvalue */
|
|
Packit |
67cb25 |
gsl_eigen_symm_workspace *eigen_p;
|
|
Packit |
67cb25 |
} normal_state_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void *normal_alloc(const size_t p);
|
|
Packit |
67cb25 |
static void normal_free(void *vstate);
|
|
Packit |
67cb25 |
static int normal_reset(void *vstate);
|
|
Packit |
67cb25 |
static int normal_accumulate(gsl_matrix * A, gsl_vector * b,
|
|
Packit |
67cb25 |
void * vstate);
|
|
Packit |
67cb25 |
static int normal_solve(const double lambda, gsl_vector * x,
|
|
Packit |
67cb25 |
double * rnorm, double * snorm,
|
|
Packit |
67cb25 |
void * vstate);
|
|
Packit |
67cb25 |
static int normal_rcond(double * rcond, void * vstate);
|
|
Packit |
67cb25 |
static int normal_lcurve(gsl_vector * reg_param, gsl_vector * rho,
|
|
Packit |
67cb25 |
gsl_vector * eta, void * vstate);
|
|
Packit |
67cb25 |
static int normal_solve_system(const double lambda, gsl_vector * x,
|
|
Packit |
67cb25 |
normal_state_t *state);
|
|
Packit |
67cb25 |
static int normal_solve_cholesky(gsl_matrix * ATA, const gsl_vector * ATb,
|
|
Packit |
67cb25 |
gsl_vector * x, normal_state_t *state);
|
|
Packit |
67cb25 |
static int normal_calc_norms(const gsl_vector *x, double *rnorm,
|
|
Packit |
67cb25 |
double *snorm, normal_state_t *state);
|
|
Packit |
67cb25 |
static int normal_eigen(normal_state_t *state);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
normal_alloc()
|
|
Packit |
67cb25 |
Allocate workspace for solving large linear least squares
|
|
Packit |
67cb25 |
problems using the normal equations approach
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: p - number of columns of LS matrix
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: pointer to workspace
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void *
|
|
Packit |
67cb25 |
normal_alloc(const size_t p)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_state_t *state;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (p == 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("p must be a positive integer",
|
|
Packit |
67cb25 |
GSL_EINVAL);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state = calloc(1, sizeof(normal_state_t));
|
|
Packit |
67cb25 |
if (!state)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate normal state", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->p = p;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->ATA = gsl_matrix_alloc(p, p);
|
|
Packit |
67cb25 |
if (state->ATA == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate ATA matrix", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->work_ATA = gsl_matrix_alloc(p, p);
|
|
Packit |
67cb25 |
if (state->work_ATA == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate temporary ATA matrix", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->ATb = gsl_vector_alloc(p);
|
|
Packit |
67cb25 |
if (state->ATb == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate ATb vector", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->perm = gsl_permutation_alloc(p);
|
|
Packit |
67cb25 |
if (state->perm == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate perm", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->D = gsl_vector_alloc(p);
|
|
Packit |
67cb25 |
if (state->D == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate D vector", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->workp = gsl_vector_alloc(p);
|
|
Packit |
67cb25 |
if (state->workp == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate temporary ATb vector", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->work3p = gsl_vector_alloc(3 * p);
|
|
Packit |
67cb25 |
if (state->work3p == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate work3p", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->c = gsl_vector_alloc(p);
|
|
Packit |
67cb25 |
if (state->c == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate c vector", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->eigen_p = gsl_eigen_symm_alloc(p);
|
|
Packit |
67cb25 |
if (state->eigen_p == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate eigen workspace", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
normal_reset(state);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return state;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
normal_free(void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_state_t *state = (normal_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->ATA)
|
|
Packit |
67cb25 |
gsl_matrix_free(state->ATA);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->work_ATA)
|
|
Packit |
67cb25 |
gsl_matrix_free(state->work_ATA);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->ATb)
|
|
Packit |
67cb25 |
gsl_vector_free(state->ATb);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->perm)
|
|
Packit |
67cb25 |
gsl_permutation_free(state->perm);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->D)
|
|
Packit |
67cb25 |
gsl_vector_free(state->D);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->workp)
|
|
Packit |
67cb25 |
gsl_vector_free(state->workp);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->work3p)
|
|
Packit |
67cb25 |
gsl_vector_free(state->work3p);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->c)
|
|
Packit |
67cb25 |
gsl_vector_free(state->c);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->eigen_p)
|
|
Packit |
67cb25 |
gsl_eigen_symm_free(state->eigen_p);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
free(state);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_reset(void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_state_t *state = (normal_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_matrix_set_zero(state->ATA);
|
|
Packit |
67cb25 |
gsl_vector_set_zero(state->ATb);
|
|
Packit |
67cb25 |
state->normb = 0.0;
|
|
Packit |
67cb25 |
state->eigen = 0;
|
|
Packit |
67cb25 |
state->eval_min = 0.0;
|
|
Packit |
67cb25 |
state->eval_max = 0.0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
normal_accumulate()
|
|
Packit |
67cb25 |
Add a new block of rows to the normal equations system
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: A - new block of rows, n-by-p
|
|
Packit |
67cb25 |
b - new rhs vector n-by-1
|
|
Packit |
67cb25 |
vstate - workspace
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: success/error
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_accumulate(gsl_matrix * A, gsl_vector * b, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_state_t *state = (normal_state_t *) vstate;
|
|
Packit |
67cb25 |
const size_t n = A->size1;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (A->size2 != state->p)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("columns of A do not match workspace", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (n != b->size)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("A and b have different numbers of rows", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int s;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* ATA += A^T A, using only the lower half of the matrix */
|
|
Packit |
67cb25 |
s = gsl_blas_dsyrk(CblasLower, CblasTrans, 1.0, A, 1.0, state->ATA);
|
|
Packit |
67cb25 |
if (s)
|
|
Packit |
67cb25 |
return s;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* ATb += A^T b */
|
|
Packit |
67cb25 |
s = gsl_blas_dgemv(CblasTrans, 1.0, A, b, 1.0, state->ATb);
|
|
Packit |
67cb25 |
if (s)
|
|
Packit |
67cb25 |
return s;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* update || b || */
|
|
Packit |
67cb25 |
state->normb = gsl_hypot(state->normb, gsl_blas_dnrm2(b));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
normal_solve()
|
|
Packit |
67cb25 |
Solve normal equations system:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
(A^T A + \lambda^2 I) x = A^T b
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
using Cholesky decomposition
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: lambda - regularization parameter
|
|
Packit |
67cb25 |
x - (output) solution vector p-by-1
|
|
Packit |
67cb25 |
rnorm - (output) residual norm ||b - A x||
|
|
Packit |
67cb25 |
snorm - (output) solution norm ||x||
|
|
Packit |
67cb25 |
vstate - workspace
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: success/error
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_solve(const double lambda, gsl_vector * x,
|
|
Packit |
67cb25 |
double * rnorm, double * snorm,
|
|
Packit |
67cb25 |
void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_state_t *state = (normal_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (x->size != state->p)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("solution vector does not match workspace", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* solve system (A^T A) x = A^T b */
|
|
Packit |
67cb25 |
status = normal_solve_system(lambda, x, state);
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("failed to solve normal equations", status);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute residual norm ||y - X c|| and solution norm ||x|| */
|
|
Packit |
67cb25 |
normal_calc_norms(x, rnorm, snorm, state);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_rcond(double * rcond, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_state_t *state = (normal_state_t *) vstate;
|
|
Packit |
67cb25 |
int status = GSL_SUCCESS;
|
|
Packit |
67cb25 |
double rcond_ATA;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = gsl_linalg_pcholesky_rcond(state->work_ATA, state->perm, &rcond_ATA, state->work3p);
|
|
Packit |
67cb25 |
if (status == GSL_SUCCESS)
|
|
Packit |
67cb25 |
*rcond = sqrt(rcond_ATA);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
normal_lcurve()
|
|
Packit |
67cb25 |
Compute L-curve of least squares system
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: reg_param - (output) vector of regularization parameters
|
|
Packit |
67cb25 |
rho - (output) vector of residual norms
|
|
Packit |
67cb25 |
eta - (output) vector of solution norms
|
|
Packit |
67cb25 |
vstate - workspace
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: success/error
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_lcurve(gsl_vector * reg_param, gsl_vector * rho,
|
|
Packit |
67cb25 |
gsl_vector * eta, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
normal_state_t *state = (normal_state_t *) vstate;
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
double smin, smax; /* minimum/maximum singular values */
|
|
Packit |
67cb25 |
size_t i;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->eigen == 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
status = normal_eigen(state);
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->eval_max < 0.0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("matrix is not positive definite", GSL_EDOM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute singular values which are sqrts of eigenvalues */
|
|
Packit |
67cb25 |
smax = sqrt(state->eval_max);
|
|
Packit |
67cb25 |
if (state->eval_min > 0.0)
|
|
Packit |
67cb25 |
smin = sqrt(state->eval_min);
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
smin = 0.0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute vector of regularization parameters */
|
|
Packit |
67cb25 |
gsl_multifit_linear_lreg(smin, smax, reg_param);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* solve normal equations for each regularization parameter */
|
|
Packit |
67cb25 |
for (i = 0; i < reg_param->size; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double lambda = gsl_vector_get(reg_param, i);
|
|
Packit |
67cb25 |
double rnorm, snorm;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = normal_solve_system(lambda, state->c, state);
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute ||y - X c|| and ||c|| */
|
|
Packit |
67cb25 |
normal_calc_norms(state->c, &rnorm, &snorm, state);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_vector_set(rho, i, rnorm);
|
|
Packit |
67cb25 |
gsl_vector_set(eta, i, snorm);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
normal_solve_system()
|
|
Packit |
67cb25 |
Compute solution to normal equations:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
(A^T A + lambda^2*I) x = A^T b
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
using LDL decomposition.
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: x - (output) solution vector
|
|
Packit |
67cb25 |
state - workspace
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: success/error
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_solve_system(const double lambda, gsl_vector * x, normal_state_t *state)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
const double lambda_sq = lambda * lambda;
|
|
Packit |
67cb25 |
gsl_vector_view d = gsl_matrix_diagonal(state->work_ATA);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* copy ATA matrix to temporary workspace and regularize */
|
|
Packit |
67cb25 |
gsl_matrix_tricpy('L', 1, state->work_ATA, state->ATA);
|
|
Packit |
67cb25 |
gsl_vector_add_constant(&d.vector, lambda_sq);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* solve with LDL decomposition */
|
|
Packit |
67cb25 |
status = normal_solve_cholesky(state->work_ATA, state->ATb, x, state);
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_solve_cholesky(gsl_matrix * ATA, const gsl_vector * ATb,
|
|
Packit |
67cb25 |
gsl_vector * x, normal_state_t *state)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = gsl_linalg_pcholesky_decomp2(ATA, state->perm, state->D);
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = gsl_linalg_pcholesky_solve2(ATA, state->perm, state->D, ATb, x);
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
normal_calc_norms()
|
|
Packit |
67cb25 |
Compute residual norm ||y - X c|| and solution
|
|
Packit |
67cb25 |
norm ||c||
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: x - solution vector
|
|
Packit |
67cb25 |
rnorm - (output) residual norm ||y - X c||
|
|
Packit |
67cb25 |
snorm - (output) solution norm ||c||
|
|
Packit |
67cb25 |
state - workspace
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_calc_norms(const gsl_vector *x, double *rnorm,
|
|
Packit |
67cb25 |
double *snorm, normal_state_t *state)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double r2;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute solution norm ||x|| */
|
|
Packit |
67cb25 |
*snorm = gsl_blas_dnrm2(x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute residual norm ||b - Ax|| */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute: A^T A x - 2 A^T b */
|
|
Packit |
67cb25 |
gsl_vector_memcpy(state->workp, state->ATb);
|
|
Packit |
67cb25 |
gsl_blas_dsymv(CblasLower, 1.0, state->ATA, x, -2.0, state->workp);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute: x^T A^T A x - 2 x^T A^T b */
|
|
Packit |
67cb25 |
gsl_blas_ddot(x, state->workp, &r2;;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* add b^T b */
|
|
Packit |
67cb25 |
r2 += state->normb * state->normb;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
*rnorm = sqrt(r2);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
normal_eigen()
|
|
Packit |
67cb25 |
Compute eigenvalues of A^T A matrix, which
|
|
Packit |
67cb25 |
are stored in state->workp on output. Also,
|
|
Packit |
67cb25 |
state->eval_min and state->eval_max are set
|
|
Packit |
67cb25 |
to the minimum/maximum eigenvalues
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
normal_eigen(normal_state_t *state)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* copy lower triangle of ATA to temporary workspace */
|
|
Packit |
67cb25 |
gsl_matrix_tricpy('L', 1, state->work_ATA, state->ATA);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute eigenvalues of ATA */
|
|
Packit |
67cb25 |
status = gsl_eigen_symm(state->work_ATA, state->workp, state->eigen_p);
|
|
Packit |
67cb25 |
if (status)
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_vector_minmax(state->workp, &state->eval_min, &state->eval_max);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->eigen = 1;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_multilarge_linear_type normal_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
"normal",
|
|
Packit |
67cb25 |
normal_alloc,
|
|
Packit |
67cb25 |
normal_reset,
|
|
Packit |
67cb25 |
normal_accumulate,
|
|
Packit |
67cb25 |
normal_solve,
|
|
Packit |
67cb25 |
normal_rcond,
|
|
Packit |
67cb25 |
normal_lcurve,
|
|
Packit |
67cb25 |
normal_free
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_multilarge_linear_type * gsl_multilarge_linear_normal =
|
|
Packit |
67cb25 |
&normal_type;
|