Blame multifit_nlinear/qr.c

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;