Blame multifit/fdfridge.c

Packit 67cb25
/* multifit/fdfridge.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2014 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 <stdlib.h>
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_multifit_nlin.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
Packit 67cb25
static int fdfridge_f(const gsl_vector * x, void * params, gsl_vector * f);
Packit 67cb25
static int fdfridge_df(const gsl_vector * x, void * params, gsl_matrix * J);
Packit 67cb25
Packit 67cb25
gsl_multifit_fdfridge *
Packit 67cb25
gsl_multifit_fdfridge_alloc (const gsl_multifit_fdfsolver_type * T,
Packit 67cb25
                             const size_t n, const size_t p)
Packit 67cb25
{
Packit 67cb25
  gsl_multifit_fdfridge * work;
Packit 67cb25
Packit 67cb25
  work = calloc(1, sizeof(gsl_multifit_fdfridge));
Packit 67cb25
  if (work == NULL)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR_VAL("failed to allocate workspace",
Packit 67cb25
                    GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  work->s = gsl_multifit_fdfsolver_alloc (T, n + p, p);
Packit 67cb25
  if (work->s == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_multifit_fdfridge_free(work);
Packit 67cb25
      GSL_ERROR_VAL("failed to allocate space for fdfsolver",
Packit 67cb25
                    GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  work->wts = gsl_vector_alloc(n + p);
Packit 67cb25
  if (work->wts == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_multifit_fdfridge_free(work);
Packit 67cb25
      GSL_ERROR_VAL("failed to allocate space for weight vector",
Packit 67cb25
                    GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  work->f = gsl_vector_alloc(n);
Packit 67cb25
  if (work->f == NULL)
Packit 67cb25
    {
Packit 67cb25
      gsl_multifit_fdfridge_free(work);
Packit 67cb25
      GSL_ERROR_VAL("failed to allocate space for f vector",
Packit 67cb25
                    GSL_ENOMEM, 0);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  work->n = n;
Packit 67cb25
  work->p = p;
Packit 67cb25
  work->lambda = 0.0;
Packit 67cb25
Packit 67cb25
  /* initialize weights to 1 (for augmented portion of vector) */
Packit 67cb25
  gsl_vector_set_all(work->wts, 1.0);
Packit 67cb25
Packit 67cb25
  return work;
Packit 67cb25
} /* gsl_multifit_fdfridge_alloc() */
Packit 67cb25
Packit 67cb25
void
Packit 67cb25
gsl_multifit_fdfridge_free(gsl_multifit_fdfridge *work)
Packit 67cb25
{
Packit 67cb25
  if (work->s)
Packit 67cb25
    gsl_multifit_fdfsolver_free(work->s);
Packit 67cb25
Packit 67cb25
  if (work->wts)
Packit 67cb25
    gsl_vector_free(work->wts);
Packit 67cb25
    
Packit 67cb25
  if (work->f)
Packit 67cb25
    gsl_vector_free(work->f);
Packit 67cb25
Packit 67cb25
  free(work);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
const char *
Packit 67cb25
gsl_multifit_fdfridge_name(const gsl_multifit_fdfridge * w)
Packit 67cb25
{
Packit 67cb25
  return gsl_multifit_fdfsolver_name(w->s);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
gsl_vector *
Packit 67cb25
gsl_multifit_fdfridge_position (const gsl_multifit_fdfridge * w)
Packit 67cb25
{
Packit 67cb25
  return gsl_multifit_fdfsolver_position(w->s);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
gsl_vector *
Packit 67cb25
gsl_multifit_fdfridge_residual (const gsl_multifit_fdfridge * w)
Packit 67cb25
{
Packit 67cb25
  return gsl_multifit_fdfsolver_residual(w->s);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
size_t
Packit 67cb25
gsl_multifit_fdfridge_niter (const gsl_multifit_fdfridge * w)
Packit 67cb25
{
Packit 67cb25
  return w->s->niter;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_set (gsl_multifit_fdfridge * w,
Packit 67cb25
                           gsl_multifit_function_fdf * f,
Packit 67cb25
                           const gsl_vector * x,
Packit 67cb25
                           const double lambda)
Packit 67cb25
{
Packit 67cb25
  return gsl_multifit_fdfridge_wset(w, f, x, lambda, NULL);
Packit 67cb25
} /* gsl_multifit_fdfridge_set() */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_wset (gsl_multifit_fdfridge * w,
Packit 67cb25
                            gsl_multifit_function_fdf * f,
Packit 67cb25
                            const gsl_vector * x,
Packit 67cb25
                            const double lambda,
Packit 67cb25
                            const gsl_vector * wts)
Packit 67cb25
{
Packit 67cb25
  const size_t n = w->n;
Packit 67cb25
  const size_t p = w->p;
Packit 67cb25
Packit 67cb25
  if (n != f->n || p != f->p)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("function size does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p != x->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("vector length does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (wts != NULL && n != wts->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("weight vector length does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status;
Packit 67cb25
      gsl_vector_view wv = gsl_vector_subvector(w->wts, 0, n);
Packit 67cb25
Packit 67cb25
      /* save user defined fdf */
Packit 67cb25
      w->fdf = f;
Packit 67cb25
Packit 67cb25
      /* build modified fdf for Tikhonov terms */
Packit 67cb25
      w->fdftik.f = &fdfridge_f;
Packit 67cb25
      w->fdftik.df = &fdfridge_df;
Packit 67cb25
      w->fdftik.n = n + p; /* add p for Tikhonov terms */
Packit 67cb25
      w->fdftik.p = p;
Packit 67cb25
      w->fdftik.params = (void *) w;
Packit 67cb25
Packit 67cb25
      /* store damping parameter */
Packit 67cb25
      w->lambda = lambda;
Packit 67cb25
      w->L = NULL;
Packit 67cb25
Packit 67cb25
      if (wts)
Packit 67cb25
        {
Packit 67cb25
          /* copy weight vector into user portion of w->wts */
Packit 67cb25
          gsl_vector_memcpy(&wv.vector, wts);
Packit 67cb25
          status = gsl_multifit_fdfsolver_wset(w->s, &(w->fdftik), x, w->wts);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          status = gsl_multifit_fdfsolver_wset(w->s, &(w->fdftik), x, NULL);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* update function/Jacobian evaluations */
Packit 67cb25
      f->nevalf = w->fdftik.nevalf;
Packit 67cb25
      f->nevaldf = w->fdftik.nevaldf;
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_fdfridge_wset() */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_set2 (gsl_multifit_fdfridge * w,
Packit 67cb25
                            gsl_multifit_function_fdf * f,
Packit 67cb25
                            const gsl_vector * x,
Packit 67cb25
                            const gsl_vector * lambda)
Packit 67cb25
{
Packit 67cb25
  return gsl_multifit_fdfridge_wset2(w, f, x, lambda, NULL);
Packit 67cb25
} /* gsl_multifit_fdfridge_set2() */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_wset2 (gsl_multifit_fdfridge * w,
Packit 67cb25
                             gsl_multifit_function_fdf * f,
Packit 67cb25
                             const gsl_vector * x,
Packit 67cb25
                             const gsl_vector * lambda,
Packit 67cb25
                             const gsl_vector * wts)
Packit 67cb25
{
Packit 67cb25
  const size_t n = w->n;
Packit 67cb25
  const size_t p = w->p;
Packit 67cb25
Packit 67cb25
  if (n != f->n || p != f->p)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("function size does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p != x->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("vector length does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (lambda->size != p)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("lambda vector size does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (wts != NULL && n != wts->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("weight vector length does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status;
Packit 67cb25
      gsl_vector_view wv = gsl_vector_subvector(w->wts, 0, n);
Packit 67cb25
Packit 67cb25
      /* save user defined fdf */
Packit 67cb25
      w->fdf = f;
Packit 67cb25
      w->fdf->nevalf = 0;
Packit 67cb25
      w->fdf->nevaldf = 0;
Packit 67cb25
Packit 67cb25
      /* build modified fdf for Tikhonov terms */
Packit 67cb25
      w->fdftik.f = &fdfridge_f;
Packit 67cb25
      w->fdftik.df = &fdfridge_df;
Packit 67cb25
      w->fdftik.n = n + p; /* add p for Tikhonov terms */
Packit 67cb25
      w->fdftik.p = p;
Packit 67cb25
      w->fdftik.params = (void *) w;
Packit 67cb25
Packit 67cb25
      /* store damping matrix */
Packit 67cb25
      w->lambda = 0.0;
Packit 67cb25
      w->L_diag = lambda;
Packit 67cb25
      w->L = NULL;
Packit 67cb25
Packit 67cb25
      if (wts)
Packit 67cb25
        {
Packit 67cb25
          /* copy weight vector into user portion */
Packit 67cb25
          gsl_vector_memcpy(&wv.vector, wts);
Packit 67cb25
          status = gsl_multifit_fdfsolver_wset(w->s, &(w->fdftik), x, w->wts);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          status = gsl_multifit_fdfsolver_wset(w->s, &(w->fdftik), x, NULL);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* update function/Jacobian evaluations */
Packit 67cb25
      f->nevalf = w->fdftik.nevalf;
Packit 67cb25
      f->nevaldf = w->fdftik.nevaldf;
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_fdfridge_wset2() */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_set3 (gsl_multifit_fdfridge * w,
Packit 67cb25
                            gsl_multifit_function_fdf * f,
Packit 67cb25
                            const gsl_vector * x,
Packit 67cb25
                            const gsl_matrix * L)
Packit 67cb25
{
Packit 67cb25
  return gsl_multifit_fdfridge_wset3(w, f, x, L, NULL);
Packit 67cb25
} /* gsl_multifit_fdfridge_set3() */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_wset3 (gsl_multifit_fdfridge * w,
Packit 67cb25
                             gsl_multifit_function_fdf * f,
Packit 67cb25
                             const gsl_vector * x,
Packit 67cb25
                             const gsl_matrix * L,
Packit 67cb25
                             const gsl_vector * wts)
Packit 67cb25
{
Packit 67cb25
  const size_t n = w->n;
Packit 67cb25
  const size_t p = w->p;
Packit 67cb25
Packit 67cb25
  if (n != f->n || p != f->p)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("function size does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (p != x->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("vector length does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (L->size2 != p)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("L matrix size2 does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else if (wts != NULL && n != wts->size)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("weight vector length does not match solver", GSL_EBADLEN);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      int status;
Packit 67cb25
      gsl_vector_view wv = gsl_vector_subvector(w->wts, 0, n);
Packit 67cb25
Packit 67cb25
      /* save user defined fdf */
Packit 67cb25
      w->fdf = f;
Packit 67cb25
      w->fdf->nevalf = 0;
Packit 67cb25
      w->fdf->nevaldf = 0;
Packit 67cb25
Packit 67cb25
      /* build modified fdf for Tikhonov terms */
Packit 67cb25
      w->fdftik.f = &fdfridge_f;
Packit 67cb25
      w->fdftik.df = &fdfridge_df;
Packit 67cb25
      w->fdftik.n = n + p; /* add p for Tikhonov terms */
Packit 67cb25
      w->fdftik.p = p;
Packit 67cb25
      w->fdftik.params = (void *) w;
Packit 67cb25
Packit 67cb25
      /* store damping matrix */
Packit 67cb25
      w->lambda = 0.0;
Packit 67cb25
      w->L_diag = NULL;
Packit 67cb25
      w->L = L;
Packit 67cb25
Packit 67cb25
      if (wts)
Packit 67cb25
        {
Packit 67cb25
          /* copy weight vector into user portion */
Packit 67cb25
          gsl_vector_memcpy(&wv.vector, wts);
Packit 67cb25
          status = gsl_multifit_fdfsolver_wset(w->s, &(w->fdftik), x, w->wts);
Packit 67cb25
        }
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          status = gsl_multifit_fdfsolver_wset(w->s, &(w->fdftik), x, NULL);
Packit 67cb25
        }
Packit 67cb25
Packit 67cb25
      /* update function/Jacobian evaluations */
Packit 67cb25
      f->nevalf = w->fdftik.nevalf;
Packit 67cb25
      f->nevaldf = w->fdftik.nevaldf;
Packit 67cb25
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
} /* gsl_multifit_fdfridge_wset3() */
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_iterate (gsl_multifit_fdfridge * w)
Packit 67cb25
{
Packit 67cb25
  int status = gsl_multifit_fdfsolver_iterate(w->s);
Packit 67cb25
Packit 67cb25
  /* update function/Jacobian evaluations */
Packit 67cb25
  w->fdf->nevalf = w->fdftik.nevalf;
Packit 67cb25
  w->fdf->nevaldf = w->fdftik.nevaldf;
Packit 67cb25
Packit 67cb25
  return status;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
int
Packit 67cb25
gsl_multifit_fdfridge_driver (gsl_multifit_fdfridge * w,
Packit 67cb25
                              const size_t maxiter,
Packit 67cb25
                              const double xtol,
Packit 67cb25
                              const double gtol,
Packit 67cb25
                              const double ftol,
Packit 67cb25
                              int *info)
Packit 67cb25
{
Packit 67cb25
  int status = gsl_multifit_fdfsolver_driver(w->s, maxiter, xtol,
Packit 67cb25
                                             gtol, ftol, info);
Packit 67cb25
  return status;
Packit 67cb25
} /* gsl_multifit_fdfridge_driver() */
Packit 67cb25
Packit 67cb25
/*
Packit 67cb25
fdfridge_f()
Packit 67cb25
  Callback function to provide residuals, including extra p
Packit 67cb25
Tikhonov terms. The residual vector will have the form:
Packit 67cb25
Packit 67cb25
f~ = [     f     ]
Packit 67cb25
     [ \lambda x ]
Packit 67cb25
Packit 67cb25
where f is the user supplied residuals, x are the model
Packit 67cb25
parameters, and \lambda is the Tikhonov damping parameter
Packit 67cb25
Packit 67cb25
Inputs: x      - model parameters (size p)
Packit 67cb25
        params - pointer to fdfridge workspace
Packit 67cb25
        f      - (output) (n+p) vector to store f~
Packit 67cb25
*/
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
fdfridge_f(const gsl_vector * x, void * params, gsl_vector * f)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
  gsl_multifit_fdfridge *w = (gsl_multifit_fdfridge *) params;
Packit 67cb25
  const size_t n = w->n;
Packit 67cb25
  const size_t p = w->p;
Packit 67cb25
  gsl_vector_view f_user = gsl_vector_subvector(f, 0, n);
Packit 67cb25
  gsl_vector_view f_tik = gsl_vector_subvector(f, n, p);
Packit 67cb25
Packit 67cb25
  /* call user callback function to get residual vector f */
Packit 67cb25
  status = gsl_multifit_eval_wf(w->fdf, x, NULL, &f_user.vector);
Packit 67cb25
  if (status)
Packit 67cb25
    return status;
Packit 67cb25
Packit 67cb25
  if (w->L_diag)
Packit 67cb25
    {
Packit 67cb25
      /* store diag(L_diag) x in Tikhonov portion of f~ */
Packit 67cb25
      gsl_vector_memcpy(&f_tik.vector, x);
Packit 67cb25
      gsl_vector_mul(&f_tik.vector, w->L_diag);
Packit 67cb25
    }
Packit 67cb25
  else if (w->L)
Packit 67cb25
    {
Packit 67cb25
      /* store Lx in Tikhonov portion of f~ */
Packit 67cb25
      gsl_blas_dgemv(CblasNoTrans, 1.0, w->L, x, 0.0, &f_tik.vector);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /* store \lambda x in Tikhonov portion of f~ */
Packit 67cb25
      gsl_vector_memcpy(&f_tik.vector, x);
Packit 67cb25
      gsl_vector_scale(&f_tik.vector, w->lambda);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
} /* fdfridge_f() */
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
fdfridge_df(const gsl_vector * x, void * params, gsl_matrix * J)
Packit 67cb25
{
Packit 67cb25
  int status;
Packit 67cb25
  gsl_multifit_fdfridge *w = (gsl_multifit_fdfridge *) params;
Packit 67cb25
  const size_t n = w->n;
Packit 67cb25
  const size_t p = w->p;
Packit 67cb25
  gsl_matrix_view J_user = gsl_matrix_submatrix(J, 0, 0, n, p);
Packit 67cb25
  gsl_matrix_view J_tik = gsl_matrix_submatrix(J, n, 0, p, p);
Packit 67cb25
  gsl_vector_view diag = gsl_matrix_diagonal(&J_tik.matrix);
Packit 67cb25
Packit 67cb25
  /* compute Jacobian */
Packit 67cb25
  if (w->fdf->df)
Packit 67cb25
    status = gsl_multifit_eval_wdf(w->fdf, x, NULL, &J_user.matrix);
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /* compute f(x) and then finite difference Jacobian */
Packit 67cb25
      status = gsl_multifit_eval_wf(w->fdf, x, NULL, w->f);
Packit 67cb25
      status += gsl_multifit_fdfsolver_dif_df(x, NULL, w->fdf, w->f,
Packit 67cb25
                                              &J_user.matrix);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (status)
Packit 67cb25
    return status;
Packit 67cb25
Packit 67cb25
  if (w->L_diag)
Packit 67cb25
    {
Packit 67cb25
      /* store diag(L_diag) in Tikhonov portion of J */
Packit 67cb25
      gsl_matrix_set_zero(&J_tik.matrix);
Packit 67cb25
      gsl_vector_memcpy(&diag.vector, w->L_diag);
Packit 67cb25
    }
Packit 67cb25
  else if (w->L)
Packit 67cb25
    {
Packit 67cb25
      /* store L in Tikhonov portion of J */
Packit 67cb25
      gsl_matrix_memcpy(&J_tik.matrix, w->L);
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      /* store \lambda I_p in Tikhonov portion of J */
Packit 67cb25
      gsl_matrix_set_zero(&J_tik.matrix);
Packit 67cb25
      gsl_vector_set_all(&diag.vector, w->lambda);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
} /* fdfridge_df() */