|
Packit |
67cb25 |
/* multifit_nlinear/qr.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 |
/*
|
|
Packit |
67cb25 |
* This module handles the solution of the linear least squares
|
|
Packit |
67cb25 |
* system:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* [ J~ ] p~ = - [ f ]
|
|
Packit |
67cb25 |
* [ sqrt(mu)*D~ ] [ 0 ]
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* using a QR approach. Quantities are scaled according to:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* J~ = J S
|
|
Packit |
67cb25 |
* D~ = D S
|
|
Packit |
67cb25 |
* p~ = S^{-1} p
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* where S is a diagonal matrix and S_jj = || J_j || and J_j is column
|
|
Packit |
67cb25 |
* j of the Jacobian. This balancing transformation seems to be more
|
|
Packit |
67cb25 |
* numerically stable for some Jacobians.
|
|
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 |
#include "common.c"
|
|
Packit |
67cb25 |
#include "qrsolv.c"
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef struct
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t p;
|
|
Packit |
67cb25 |
gsl_matrix *QR; /* QR factorization of J */
|
|
Packit |
67cb25 |
gsl_vector *tau_Q; /* Householder scalars for Q */
|
|
Packit |
67cb25 |
gsl_matrix *T; /* workspace matrix for qrsolv, p-by-p */
|
|
Packit |
67cb25 |
gsl_permutation *perm; /* permutation matrix */
|
|
Packit |
67cb25 |
size_t rank; /* rank of J */
|
|
Packit |
67cb25 |
gsl_vector *residual; /* residual of LS problem [ J; sqrt(mu) D ] p = - [ f; 0 ] */
|
|
Packit |
67cb25 |
gsl_vector *qtf; /* Q^T f */
|
|
Packit |
67cb25 |
gsl_vector *workn; /* workspace, length n */
|
|
Packit |
67cb25 |
gsl_vector *workp; /* workspace, length p */
|
|
Packit |
67cb25 |
gsl_vector *work3p; /* workspace, length 3*p */
|
|
Packit |
67cb25 |
double mu; /* LM parameter */
|
|
Packit |
67cb25 |
} qr_state_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int qr_init(const void * vtrust_state, void * vstate);
|
|
Packit |
67cb25 |
static int qr_presolve(const double mu, const void * vtrust_state, void * vstate);
|
|
Packit |
67cb25 |
static int qr_solve(const gsl_vector * f, gsl_vector *x,
|
|
Packit |
67cb25 |
const void * vtrust_state, void *vstate);
|
|
Packit |
67cb25 |
static int qr_rcond(double * rcond, void * vstate);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void *
|
|
Packit |
67cb25 |
qr_alloc (const size_t n, const size_t p)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
qr_state_t *state;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
(void)n;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state = calloc(1, sizeof(qr_state_t));
|
|
Packit |
67cb25 |
if (state == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate qr state", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->QR = gsl_matrix_alloc(n, p);
|
|
Packit |
67cb25 |
if (state->QR == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for QR", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->tau_Q = gsl_vector_alloc(p);
|
|
Packit |
67cb25 |
if (state->tau_Q == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for tau_Q",
|
|
Packit |
67cb25 |
GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->T = gsl_matrix_alloc(p, p);
|
|
Packit |
67cb25 |
if (state->T == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for T", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->qtf = gsl_vector_alloc(n);
|
|
Packit |
67cb25 |
if (state->qtf == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for qtf",
|
|
Packit |
67cb25 |
GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->residual = gsl_vector_alloc(n);
|
|
Packit |
67cb25 |
if (state->residual == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for residual",
|
|
Packit |
67cb25 |
GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->perm = gsl_permutation_calloc(p);
|
|
Packit |
67cb25 |
if (state->perm == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for perm",
|
|
Packit |
67cb25 |
GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->workn = gsl_vector_alloc(n);
|
|
Packit |
67cb25 |
if (state->workn == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for workn",
|
|
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->work3p = gsl_vector_alloc(3 * p);
|
|
Packit |
67cb25 |
if (state->work3p == NULL)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL ("failed to allocate space for work3p",
|
|
Packit |
67cb25 |
GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->p = p;
|
|
Packit |
67cb25 |
state->mu = 0.0;
|
|
Packit |
67cb25 |
state->rank = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return state;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
qr_free(void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
qr_state_t *state = (qr_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->QR)
|
|
Packit |
67cb25 |
gsl_matrix_free(state->QR);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->tau_Q)
|
|
Packit |
67cb25 |
gsl_vector_free(state->tau_Q);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->T)
|
|
Packit |
67cb25 |
gsl_matrix_free(state->T);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->qtf)
|
|
Packit |
67cb25 |
gsl_vector_free(state->qtf);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->residual)
|
|
Packit |
67cb25 |
gsl_vector_free(state->residual);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->perm)
|
|
Packit |
67cb25 |
gsl_permutation_free(state->perm);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->workn)
|
|
Packit |
67cb25 |
gsl_vector_free(state->workn);
|
|
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 |
free(state);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute J = Q R PT */
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
qr_init(const void * vtrust_state, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const gsl_multifit_nlinear_trust_state *trust_state =
|
|
Packit |
67cb25 |
(const gsl_multifit_nlinear_trust_state *) vtrust_state;
|
|
Packit |
67cb25 |
qr_state_t *state = (qr_state_t *) vstate;
|
|
Packit |
67cb25 |
int signum;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* perform QR decomposition of J */
|
|
Packit |
67cb25 |
gsl_matrix_memcpy(state->QR, trust_state->J);
|
|
Packit |
67cb25 |
gsl_linalg_QRPT_decomp(state->QR, state->tau_Q, state->perm,
|
|
Packit |
67cb25 |
&signum, state->workp);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return GSL_SUCCESS;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
qr_presolve(const double mu, const void * vtrust_state, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
qr_state_t *state = (qr_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 |
qr_solve(const gsl_vector * f, gsl_vector *x,
|
|
Packit |
67cb25 |
const void * vtrust_state, void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
qr_state_t *state = (qr_state_t *) vstate;
|
|
Packit |
67cb25 |
int status;
|
|
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 |
* with an attempt to identify rank deficiency in J
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
size_t rank = gsl_linalg_QRPT_rank(state->QR, -1.0);
|
|
Packit |
67cb25 |
status = gsl_linalg_QRPT_lssolve2(state->QR, state->tau_Q, state->perm,
|
|
Packit |
67cb25 |
f, rank, x, state->residual);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* solve:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* [ J ] x = [ f ]
|
|
Packit |
67cb25 |
* [ sqrt(mu) D ] [ 0 ]
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* using QRPT factorization of J
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_multifit_nlinear_trust_state *trust_state =
|
|
Packit |
67cb25 |
(const gsl_multifit_nlinear_trust_state *) vtrust_state;
|
|
Packit |
67cb25 |
double sqrt_mu = sqrt(state->mu);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute qtf = Q^T f */
|
|
Packit |
67cb25 |
gsl_vector_memcpy(state->qtf, f);
|
|
Packit |
67cb25 |
gsl_linalg_QR_QTvec(state->QR, state->tau_Q, state->qtf);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = qrsolv(state->QR, state->perm, sqrt_mu, trust_state->diag,
|
|
Packit |
67cb25 |
state->qtf, state->T, x, state->workn);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* reverse step to go downhill */
|
|
Packit |
67cb25 |
gsl_vector_scale(x, -1.0);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
qr_rcond(double * rcond, void * vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status;
|
|
Packit |
67cb25 |
qr_state_t *state = (qr_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
status = gsl_linalg_QRPT_rcond(state->QR, rcond, state->work3p);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_multifit_nlinear_solver qr_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
"qr",
|
|
Packit |
67cb25 |
qr_alloc,
|
|
Packit |
67cb25 |
qr_init,
|
|
Packit |
67cb25 |
qr_presolve,
|
|
Packit |
67cb25 |
qr_solve,
|
|
Packit |
67cb25 |
qr_rcond,
|
|
Packit |
67cb25 |
qr_free
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_multifit_nlinear_solver *gsl_multifit_nlinear_solver_qr = &qr_type;
|