Blame multimin/vector_bfgs2.c

Packit 67cb25
/* multimin/vector_bfgs2.c
Packit 67cb25
 * 
Packit 67cb25
 * Copyright (C) 2007, 2009 Brian Gough
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
Packit 67cb25
 * 02110-1301, USA.
Packit 67cb25
 */
Packit 67cb25
Packit 67cb25
/* vector_bfgs2.c -- Fletcher's implementation of the BFGS method,
Packit 67cb25
   using the line minimisation algorithm from from R.Fletcher,
Packit 67cb25
   "Practical Methods of Optimization", Second Edition, ISBN
Packit 67cb25
   0471915475.  Algorithms 2.6.2 and 2.6.4. */
Packit 67cb25
Packit 67cb25
/* Thanks to Alan Irwin irwin@beluga.phys.uvic.ca. for suggesting this
Packit 67cb25
   algorithm and providing sample fortran benchmarks */
Packit 67cb25
Packit 67cb25
#include <config.h>
Packit 67cb25
#include <gsl/gsl_multimin.h>
Packit 67cb25
#include <gsl/gsl_blas.h>
Packit 67cb25
Packit 67cb25
#include "linear_minimize.c"
Packit 67cb25
#include "linear_wrapper.c"
Packit 67cb25
Packit 67cb25
typedef struct
Packit 67cb25
{
Packit 67cb25
  int iter;
Packit 67cb25
  double step;
Packit 67cb25
  double g0norm;
Packit 67cb25
  double pnorm;
Packit 67cb25
  double delta_f;
Packit 67cb25
  double fp0;                   /* f'(0) for f(x-alpha*p) */
Packit 67cb25
  gsl_vector *x0;
Packit 67cb25
  gsl_vector *g0;
Packit 67cb25
  gsl_vector *p;
Packit 67cb25
  /* work space */
Packit 67cb25
  gsl_vector *dx0;
Packit 67cb25
  gsl_vector *dg0;
Packit 67cb25
  gsl_vector *x_alpha;
Packit 67cb25
  gsl_vector *g_alpha;
Packit 67cb25
  /* wrapper function */
Packit 67cb25
  wrapper_t wrap;
Packit 67cb25
  /* minimization parameters */
Packit 67cb25
  double rho;
Packit 67cb25
  double sigma;
Packit 67cb25
  double tau1;
Packit 67cb25
  double tau2;
Packit 67cb25
  double tau3;
Packit 67cb25
  int order;
Packit 67cb25
}
Packit 67cb25
vector_bfgs2_state_t;
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
vector_bfgs2_alloc (void *vstate, size_t n)
Packit 67cb25
{
Packit 67cb25
  vector_bfgs2_state_t *state = (vector_bfgs2_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  state->p = gsl_vector_calloc (n);
Packit 67cb25
Packit 67cb25
  if (state->p == 0)
Packit 67cb25
    {
Packit 67cb25
      GSL_ERROR ("failed to allocate space for p", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->x0 = gsl_vector_calloc (n);
Packit 67cb25
Packit 67cb25
  if (state->x0 == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_free (state->p);
Packit 67cb25
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->g0 = gsl_vector_calloc (n);
Packit 67cb25
Packit 67cb25
  if (state->g0 == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_free (state->x0);
Packit 67cb25
      gsl_vector_free (state->p);
Packit 67cb25
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->dx0 = gsl_vector_calloc (n);
Packit 67cb25
Packit 67cb25
  if (state->dx0 == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_free (state->g0);
Packit 67cb25
      gsl_vector_free (state->x0);
Packit 67cb25
      gsl_vector_free (state->p);
Packit 67cb25
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->dg0 = gsl_vector_calloc (n);
Packit 67cb25
Packit 67cb25
  if (state->dg0 == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_free (state->dx0);
Packit 67cb25
      gsl_vector_free (state->g0);
Packit 67cb25
      gsl_vector_free (state->x0);
Packit 67cb25
      gsl_vector_free (state->p);
Packit 67cb25
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->x_alpha = gsl_vector_calloc (n);
Packit 67cb25
Packit 67cb25
  if (state->x_alpha == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_free (state->dg0);
Packit 67cb25
      gsl_vector_free (state->dx0);
Packit 67cb25
      gsl_vector_free (state->g0);
Packit 67cb25
      gsl_vector_free (state->x0);
Packit 67cb25
      gsl_vector_free (state->p);
Packit 67cb25
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  state->g_alpha = gsl_vector_calloc (n);
Packit 67cb25
Packit 67cb25
  if (state->g_alpha == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_free (state->x_alpha);
Packit 67cb25
      gsl_vector_free (state->dg0);
Packit 67cb25
      gsl_vector_free (state->dx0);
Packit 67cb25
      gsl_vector_free (state->g0);
Packit 67cb25
      gsl_vector_free (state->x0);
Packit 67cb25
      gsl_vector_free (state->p);
Packit 67cb25
      GSL_ERROR ("failed to allocate space for g0", GSL_ENOMEM);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
vector_bfgs2_set (void *vstate, gsl_multimin_function_fdf * fdf,
Packit 67cb25
                  const gsl_vector * x, double *f, gsl_vector * gradient,
Packit 67cb25
                  double step_size, double tol)
Packit 67cb25
{
Packit 67cb25
  vector_bfgs2_state_t *state = (vector_bfgs2_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  state->iter = 0;
Packit 67cb25
  state->step = step_size;
Packit 67cb25
  state->delta_f = 0;
Packit 67cb25
Packit 67cb25
  GSL_MULTIMIN_FN_EVAL_F_DF (fdf, x, f, gradient);
Packit 67cb25
Packit 67cb25
  /* Use the gradient as the initial direction */
Packit 67cb25
Packit 67cb25
  gsl_vector_memcpy (state->x0, x);
Packit 67cb25
  gsl_vector_memcpy (state->g0, gradient);
Packit 67cb25
  state->g0norm = gsl_blas_dnrm2 (state->g0);
Packit 67cb25
Packit 67cb25
  gsl_vector_memcpy (state->p, gradient);
Packit 67cb25
  gsl_blas_dscal (-1 / state->g0norm, state->p);
Packit 67cb25
  state->pnorm = gsl_blas_dnrm2 (state->p);     /* should be 1 */
Packit 67cb25
  state->fp0 = -state->g0norm;
Packit 67cb25
Packit 67cb25
  /* Prepare the wrapper */
Packit 67cb25
Packit 67cb25
  prepare_wrapper (&state->wrap, fdf,
Packit 67cb25
                   state->x0, *f, state->g0,
Packit 67cb25
                   state->p, state->x_alpha, state->g_alpha);
Packit 67cb25
Packit 67cb25
  /* Prepare 1d minimisation parameters */
Packit 67cb25
Packit 67cb25
  state->rho = 0.01;
Packit 67cb25
  state->sigma = tol;
Packit 67cb25
  state->tau1 = 9;
Packit 67cb25
  state->tau2 = 0.05;
Packit 67cb25
  state->tau3 = 0.5;
Packit 67cb25
  state->order = 3;  /* use cubic interpolation where possible */
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static void
Packit 67cb25
vector_bfgs2_free (void *vstate)
Packit 67cb25
{
Packit 67cb25
  vector_bfgs2_state_t *state = (vector_bfgs2_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  gsl_vector_free (state->x_alpha);
Packit 67cb25
  gsl_vector_free (state->g_alpha);
Packit 67cb25
  gsl_vector_free (state->dg0);
Packit 67cb25
  gsl_vector_free (state->dx0);
Packit 67cb25
  gsl_vector_free (state->g0);
Packit 67cb25
  gsl_vector_free (state->x0);
Packit 67cb25
  gsl_vector_free (state->p);
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
vector_bfgs2_restart (void *vstate)
Packit 67cb25
{
Packit 67cb25
  vector_bfgs2_state_t *state = (vector_bfgs2_state_t *) vstate;
Packit 67cb25
Packit 67cb25
  state->iter = 0;
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static int
Packit 67cb25
vector_bfgs2_iterate (void *vstate, gsl_multimin_function_fdf * fdf,
Packit 67cb25
                      gsl_vector * x, double *f,
Packit 67cb25
                      gsl_vector * gradient, gsl_vector * dx)
Packit 67cb25
{
Packit 67cb25
  vector_bfgs2_state_t *state = (vector_bfgs2_state_t *) vstate;
Packit 67cb25
  double alpha = 0.0, alpha1;
Packit 67cb25
  gsl_vector *x0 = state->x0;
Packit 67cb25
  gsl_vector *g0 = state->g0;
Packit 67cb25
  gsl_vector *p = state->p;
Packit 67cb25
Packit 67cb25
  double g0norm = state->g0norm;
Packit 67cb25
  double pnorm = state->pnorm;
Packit 67cb25
  double delta_f = state->delta_f;
Packit 67cb25
  double pg, dir;
Packit 67cb25
  int status;
Packit 67cb25
Packit 67cb25
  double f0 = *f;
Packit 67cb25
Packit 67cb25
  if (pnorm == 0.0 || g0norm == 0.0 || state->fp0 == 0)
Packit 67cb25
    {
Packit 67cb25
      gsl_vector_set_zero (dx);
Packit 67cb25
      return GSL_ENOPROG;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  if (delta_f < 0)
Packit 67cb25
    {
Packit 67cb25
      double del = GSL_MAX_DBL (-delta_f, 10 * GSL_DBL_EPSILON * fabs(f0));
Packit 67cb25
      alpha1 = GSL_MIN_DBL (1.0, 2.0 * del / (-state->fp0));
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    {
Packit 67cb25
      alpha1 = fabs(state->step);
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* line minimisation, with cubic interpolation (order = 3) */
Packit 67cb25
Packit 67cb25
  status = minimize (&state->wrap.fdf_linear, state->rho, state->sigma, 
Packit 67cb25
                     state->tau1, state->tau2, state->tau3, state->order,
Packit 67cb25
                     alpha1,  &alpha);
Packit 67cb25
Packit 67cb25
  if (status != GSL_SUCCESS)
Packit 67cb25
    {
Packit 67cb25
      return status;
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  update_position (&(state->wrap), alpha, x, f, gradient);
Packit 67cb25
  
Packit 67cb25
  state->delta_f = *f - f0;
Packit 67cb25
Packit 67cb25
  /* Choose a new direction for the next step */
Packit 67cb25
Packit 67cb25
  {
Packit 67cb25
    /* This is the BFGS update: */
Packit 67cb25
    /* p' = g1 - A dx - B dg */
Packit 67cb25
    /* A = - (1+ dg.dg/dx.dg) B + dg.g/dx.dg */
Packit 67cb25
    /* B = dx.g/dx.dg */
Packit 67cb25
Packit 67cb25
    gsl_vector *dx0 = state->dx0;
Packit 67cb25
    gsl_vector *dg0 = state->dg0;
Packit 67cb25
Packit 67cb25
    double dxg, dgg, dxdg, dgnorm, A, B;
Packit 67cb25
Packit 67cb25
    /* dx0 = x - x0 */
Packit 67cb25
    gsl_vector_memcpy (dx0, x);
Packit 67cb25
    gsl_blas_daxpy (-1.0, x0, dx0);
Packit 67cb25
Packit 67cb25
    gsl_vector_memcpy (dx, dx0);  /* keep a copy */
Packit 67cb25
Packit 67cb25
    /* dg0 = g - g0 */
Packit 67cb25
    gsl_vector_memcpy (dg0, gradient);
Packit 67cb25
    gsl_blas_daxpy (-1.0, g0, dg0);
Packit 67cb25
Packit 67cb25
    gsl_blas_ddot (dx0, gradient, &dxg);
Packit 67cb25
    gsl_blas_ddot (dg0, gradient, &dgg);
Packit 67cb25
    gsl_blas_ddot (dx0, dg0, &dxdg);
Packit 67cb25
Packit 67cb25
    dgnorm = gsl_blas_dnrm2 (dg0);
Packit 67cb25
Packit 67cb25
    if (dxdg != 0)
Packit 67cb25
      {
Packit 67cb25
        B = dxg / dxdg;
Packit 67cb25
        A = -(1.0 + dgnorm * dgnorm / dxdg) * B + dgg / dxdg;
Packit 67cb25
      }
Packit 67cb25
    else
Packit 67cb25
      {
Packit 67cb25
        B = 0;
Packit 67cb25
        A = 0;
Packit 67cb25
      }
Packit 67cb25
Packit 67cb25
    gsl_vector_memcpy (p, gradient);
Packit 67cb25
    gsl_blas_daxpy (-A, dx0, p);
Packit 67cb25
    gsl_blas_daxpy (-B, dg0, p);
Packit 67cb25
  }
Packit 67cb25
Packit 67cb25
  gsl_vector_memcpy (g0, gradient);
Packit 67cb25
  gsl_vector_memcpy (x0, x);
Packit 67cb25
  state->g0norm = gsl_blas_dnrm2 (g0);
Packit 67cb25
  state->pnorm = gsl_blas_dnrm2 (p);
Packit 67cb25
Packit 67cb25
  /* update direction and fp0 */
Packit 67cb25
Packit 67cb25
  gsl_blas_ddot (p, gradient, &pg;;
Packit 67cb25
  dir = (pg >= 0.0) ? -1.0 : +1.0;
Packit 67cb25
  gsl_blas_dscal (dir / state->pnorm, p);
Packit 67cb25
  state->pnorm = gsl_blas_dnrm2 (p);
Packit 67cb25
  gsl_blas_ddot (p, g0, &state->fp0);
Packit 67cb25
Packit 67cb25
  change_direction (&state->wrap);
Packit 67cb25
Packit 67cb25
  return GSL_SUCCESS;
Packit 67cb25
}
Packit 67cb25
Packit 67cb25
static const gsl_multimin_fdfminimizer_type vector_bfgs2_type = {
Packit 67cb25
  "vector_bfgs2",               /* name */
Packit 67cb25
  sizeof (vector_bfgs2_state_t),
Packit 67cb25
  &vector_bfgs2_alloc,
Packit 67cb25
  &vector_bfgs2_set,
Packit 67cb25
  &vector_bfgs2_iterate,
Packit 67cb25
  &vector_bfgs2_restart,
Packit 67cb25
  &vector_bfgs2_free
Packit 67cb25
};
Packit 67cb25
Packit 67cb25
const gsl_multimin_fdfminimizer_type
Packit 67cb25
  * gsl_multimin_fdfminimizer_vector_bfgs2 = &vector_bfgs2_type;