#define RCSID "$Id: gsl_brent.c,v 1.7 2006/02/25 15:00:24 geuzaine Exp $"
/*
 * Copyright (C) 1997-2006 P. Dular, C. Geuzaine
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to <getdp@geuz.org>.
 */

#include "GetDP.h"

#if defined(HAVE_GSL)

#include "Numeric.h"

#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_min.h>

static double (*nrfunc) (double);

double fn1(double x, void *params)
{
  double val = nrfunc(x);
  return val;
}

#define MAXITER 100

/* Returns the minimum betwen ax and cx to a given tolerance tol using
   brent's method. */

double brent(double ax, double bx, double cx,
             double (*f) (double), double tol, double *xmin)
{
  int status;
  int iter = 0;
  double a, b, c;               /* a < b < c */
  const gsl_min_fminimizer_type *T;
  gsl_min_fminimizer *s;
  gsl_function F;

  /* gsl wants a<b */
  b = bx;
  if(ax < cx) {
    a = ax;
    c = cx;
  }
  else {
    a = ax;
    c = cx;
  }

  /* if a-b < tol, return func(a) */
  if(fabs(c - a) < tol) {
    *xmin = ax;
    return (f(*xmin));
  }

  nrfunc = f;

  F.function = &fn1;
  F.params = 0;

  T = gsl_min_fminimizer_brent;
  s = gsl_min_fminimizer_alloc(T);
  gsl_min_fminimizer_set(s, &F, b, a, c);

  do {
    iter++;
    status = gsl_min_fminimizer_iterate(s);
    if(status)
      break;    /* solver problem */
    b = gsl_min_fminimizer_minimum(s);
    /* this is deprecated: we should use gsl_min_fminimizer_x_minimum(s) instead */
    a = gsl_min_fminimizer_x_lower(s);
    c = gsl_min_fminimizer_x_upper(s);
    /* we should think about the tolerance more carefully... */
    status = gsl_min_test_interval(a, c, tol, tol);
  }
  while(status == GSL_CONTINUE && iter < MAXITER);

  if(status != GSL_SUCCESS)
    Msg(GERROR, "MIN1D not converged: f(%g) = %g", b, fn1(b, NULL));

  *xmin = b;
  gsl_min_fminimizer_free(s);
  return fn1(b, NULL);
}


/* Find an initial bracketting of the minimum of func. Given 2 initial
   points ax and bx, mnbrak checks in which direction func decreases,
   and takes some steps in that direction, until the function
   increases--at cx. mnbrak returns ax and cx (possibly switched),
   bracketting a minimum. */

#define MYGOLD_  1.618034
#define MYLIMIT_ 100.0
#define MYTINY_  1.0e-20

void mnbrak(double *ax, double *bx, double *cx, 
	    double *fa_dummy, double *fb_dummy, double *fc_dummy, 
	    double (*func) (double))
{
  double ulim, u, r, q;
  double tmp;
  volatile double f_a, f_b, f_c, f_u;

  f_a = (*func) (*ax);
  f_b = (*func) (*bx);
  if(f_b > f_a) {
    tmp = *ax;
    *ax = *bx;
    *bx = tmp;
    tmp = f_b;
    f_b = f_a;
    f_a = tmp;
  }

  *cx = *bx + MYGOLD_ * (*bx - *ax);
  f_c = (*func) (*cx);

  while(f_b > f_c) {
    r = (*bx - *ax) * (f_b - f_c);
    q = (*bx - *cx) * (f_b - f_a);
    u = (*bx) - ((*bx - *cx) * q - (*bx - *ax) * r) / 
      (2.0 * SIGN(MAX(fabs(q - r), MYTINY_), q - r));
    ulim = *bx + MYLIMIT_ * (*cx - *bx);

    if((*bx - u) * (u - *cx) > 0.0) {
      f_u = (*func) (u);
      if(f_u < f_c) {
        *ax = *bx;
        *bx = u;
        return;
      }
      else if(f_u > f_b) {
        *cx = u;
        return;
      }
      u = *cx + MYGOLD_ * (*cx - *bx);
      f_u = (*func) (u);
    }
    else if((*cx - u) * (u - ulim) > 0.0) {
      f_u = (*func) (u);
      if(f_u < f_c) {
        *bx = *cx;
        *cx = u;
        u = *cx + MYGOLD_ * (*cx - *bx);
        f_b = f_c;
        f_c = f_u;
        f_u = (*func) (u);
      }
    }
    else if((u - ulim) * (ulim - *cx) >= 0.0) {
      u = ulim;
      f_u = (*func) (u);
    }
    else {
      u = *cx + MYGOLD_ * (*cx - *bx);
      f_u = (*func) (u);
    }
    *ax = *bx;
    *bx = *cx;
    *cx = u;
    f_a = f_b;
    f_b = f_c;
    f_c = f_u;
  }
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1