|
Packit |
67cb25 |
/* multifit_nlinear/svd.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 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 |
/*
|
|
Packit |
67cb25 |
* This module handles the solution of the linear least squares
|
|
Packit |
67cb25 |
* system:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* [ J ] dx = - [ f ]
|
|
Packit |
67cb25 |
* [ sqrt(mu)*D ] [ 0 ]
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* using an SVD approach. The system above is transformed to "standard form"
|
|
Packit |
67cb25 |
* via:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* J~ = J D^{-1}
|
|
Packit |
67cb25 |
* dx~ = D dx
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* so that
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* [ J~ ] dx~ = - [ f ]
|
|
Packit |
67cb25 |
* [ sqrt(mu)*I ] [ 0 ]
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* can be solved with a standard SVD method, and then dx is recovered
|
|
Packit |
67cb25 |
* from dx~ via: dx = D^{-1} dx~
|
|
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_multifit_nlinear.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_errno.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_blas.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef struct
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t n; /* number of residuals */
|
|
Packit |
67cb25 |
size_t p; /* number of parameters */
|
|
Packit |
67cb25 |
gsl_matrix *U; /* U factor of J, n-by-p */
|
|
Packit |
67cb25 |
gsl_matrix *V; /* V factor of J, p-by-p */
|
|
Packit |
67cb25 |
gsl_vector *S; /* singular values, size p */
|
|
Packit |
67cb25 |
gsl_vector *workp; /* workspace, length p */
|
|
Packit |
67cb25 |
double mu; /* LM parameter */
|
|
Packit |
67cb25 |
} svd_state_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int svd_init(const void * vtrust_state, void * vstate);
|
|
Packit |
67cb25 |
static int svd_presolve(const double mu, const void * vtrust_state, void * vstate);
|
|
Packit |
67cb25 |
static int svd_solve(const gsl_vector * f, gsl_vector *x,
|
|
Packit |
67cb25 |
const void * vtrust_state, void *vstate);
|
|
Packit |
67cb25 |
static int svd_rcond(double * rcond, void * vstate);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void *
|
|
Packit |
67cb25 |
svd_alloc (const size_t n, const size_t p)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
svd_state_t *state;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
(void)n;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state = calloc(1, sizeof(svd_state_t));
|
|
Packit |
67cb25 |
if (state == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate svd state", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->U = gsl_matrix_alloc(n, p);
|
|
Packit |
67cb25 |
if (state->U == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for U", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->V = gsl_matrix_alloc(p, p);
|
|
Packit |
67cb25 |
if (state->V == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for V", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->S = gsl_vector_alloc(p);
|
|
Packit |
67cb25 |
if (state->S == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for S",
|
|
Packit |
67cb25 |
GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->workp = gsl_vector_alloc(p);
|
|
Packit |
67cb25 |
if (state->workp == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for workp",
|
|
Packit |
67cb25 |
GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->mu = 0.0;
|
|
Packit |
67cb25 |
state->n = n;
|
|
Packit |
67cb25 |
state->p = p;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return state;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
svd_free(void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
svd_state_t *state = (svd_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->U)
|
|
Packit |
67cb25 |
gsl_matrix_free(state->U);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->V)
|
|
Packit |
67cb25 |
gsl_matrix_free(state->V);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->S)
|
|
Packit |
67cb25 |
gsl_vector_free(state->S);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->workp)
|
|
Packit |
67cb25 |
gsl_vector_free(state->workp);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
free(state);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute svd of J */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
svd_init(const void * vtrust_state, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
const gsl_multifit_nlinear_trust_state *trust_state =
|
|
Packit |
67cb25 |
(const gsl_multifit_nlinear_trust_state *) vtrust_state;
|
|
Packit |
67cb25 |
svd_state_t *state = (svd_state_t *) vstate;
|
|
Packit |
67cb25 |
size_t i;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_matrix_set_zero(state->U);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute U = J D^{-1} */
|
|
Packit |
67cb25 |
for (i = 0; i < state->p; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_const_view Ji = gsl_matrix_const_column(trust_state->J, i);
|
|
Packit |
67cb25 |
gsl_vector_view ui = gsl_matrix_column(state->U, i);
|
|
Packit |
67cb25 |
double di = gsl_vector_get(trust_state->diag, i);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
gsl_blas_daxpy(1.0 / di, &Ji.vector, &ui.vector);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = gsl_linalg_SV_decomp(state->U, state->V, state->S, state->workp);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
svd_presolve(const double mu, const void * vtrust_state, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
svd_state_t *state = (svd_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->mu = mu;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
(void)vtrust_state;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
svd_solve(const gsl_vector * f, gsl_vector *x,
|
|
Packit |
67cb25 |
const void * vtrust_state, void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status = GSL_SUCCESS;
|
|
Packit |
67cb25 |
const gsl_multifit_nlinear_trust_state *trust_state =
|
|
Packit |
67cb25 |
(const gsl_multifit_nlinear_trust_state *) vtrust_state;
|
|
Packit |
67cb25 |
svd_state_t *state = (svd_state_t *) vstate;
|
|
Packit |
67cb25 |
const size_t p = state->p;
|
|
Packit |
67cb25 |
const double tol = GSL_DBL_EPSILON;
|
|
Packit |
67cb25 |
const double s0 = gsl_vector_get(state->S, 0);
|
|
Packit |
67cb25 |
size_t j;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute workp = - U^T f */
|
|
Packit |
67cb25 |
gsl_blas_dgemv(CblasTrans, -1.0, state->U, f, 0.0, state->workp);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* compute:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* workp = sum_i s_i / (s_i^2 + mu) (-u_i^T f)
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->mu == 0.0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* compute Gauss-Newton direction by solving
|
|
Packit |
67cb25 |
* J x = -f
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (j = 0; j < p; ++j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double sj = gsl_vector_get(state->S, j);
|
|
Packit |
67cb25 |
double *ptr = gsl_vector_ptr(state->workp, j);
|
|
Packit |
67cb25 |
double alpha;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (sj <= tol * s0)
|
|
Packit |
67cb25 |
alpha = 0.0;
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
alpha = 1.0 / sj;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
*ptr *= alpha;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* solve:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* [ J D^{-1} ] (D x) = -[ f ]
|
|
Packit |
67cb25 |
* [ sqrt(mu) I ] [ 0 ]
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* using SVD factorization of J D^{-1}
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (j = 0; j < p; ++j)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double sj = gsl_vector_get(state->S, j);
|
|
Packit |
67cb25 |
double *ptr = gsl_vector_ptr(state->workp, j);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
*ptr *= sj / (sj*sj + state->mu);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute: x = V * workp */
|
|
Packit |
67cb25 |
gsl_blas_dgemv(CblasNoTrans, 1.0, state->V, state->workp, 0.0, x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute D^{-1} x */
|
|
Packit |
67cb25 |
gsl_vector_div(x, trust_state->diag);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
svd_rcond(double * rcond, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status = GSL_SUCCESS;
|
|
Packit |
67cb25 |
svd_state_t *state = (svd_state_t *) vstate;
|
|
Packit |
67cb25 |
double smax = gsl_vector_get(state->S, 0);
|
|
Packit |
67cb25 |
double smin = gsl_vector_get(state->S, state->p - 1);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
*rcond = smin / smax;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_multifit_nlinear_solver svd_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
"svd",
|
|
Packit |
67cb25 |
svd_alloc,
|
|
Packit |
67cb25 |
svd_init,
|
|
Packit |
67cb25 |
svd_presolve,
|
|
Packit |
67cb25 |
svd_solve,
|
|
Packit |
67cb25 |
svd_rcond,
|
|
Packit |
67cb25 |
svd_free
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_multifit_nlinear_solver *gsl_multifit_nlinear_solver_svd = &svd_type;
|