diff options
Diffstat (limited to 'gsl-1.9/multimin/simplex.c')
-rw-r--r-- | gsl-1.9/multimin/simplex.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/gsl-1.9/multimin/simplex.c b/gsl-1.9/multimin/simplex.c new file mode 100644 index 0000000..ffe55da --- /dev/null +++ b/gsl-1.9/multimin/simplex.c @@ -0,0 +1,415 @@ +/* multimin/simplex.c + * + * Copyright (C) 2002 Tuomo Keskitalo, Ivo Alxneit + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + - Originally written by Tuomo Keskitalo <tuomo.keskitalo@iki.fi> + - Corrections to nmsimplex_iterate and other functions + by Ivo Alxneit <ivo.alxneit@psi.ch> + - Additional help by Brian Gough <bjg@network-theory.co.uk> +*/ + +/* The Simplex method of Nelder and Mead, + also known as the polytope search alogorithm. Ref: + Nelder, J.A., Mead, R., Computer Journal 7 (1965) pp. 308-313. + + This implementation uses n+1 corner points in the simplex. +*/ + +#include <config.h> +#include <stdlib.h> +#include <gsl/gsl_blas.h> +#include <gsl/gsl_multimin.h> + +typedef struct +{ + gsl_matrix *x1; /* simplex corner points */ + gsl_vector *y1; /* function value at corner points */ + gsl_vector *ws1; /* workspace 1 for algorithm */ + gsl_vector *ws2; /* workspace 2 for algorithm */ +} +nmsimplex_state_t; + +static double +nmsimplex_move_corner (const double coeff, const nmsimplex_state_t * state, + size_t corner, gsl_vector * xc, + const gsl_multimin_function * f) +{ + /* moves a simplex corner scaled by coeff (negative value represents + mirroring by the middle point of the "other" corner points) + and gives new corner in xc and function value at xc as a + return value + */ + + gsl_matrix *x1 = state->x1; + + size_t i, j; + double newval, mp; + + if (x1->size1 < 2) + { + GSL_ERROR ("simplex cannot have less than two corners!", GSL_EFAILED); + } + + for (j = 0; j < x1->size2; j++) + { + mp = 0.0; + for (i = 0; i < x1->size1; i++) + { + if (i != corner) + { + mp += (gsl_matrix_get (x1, i, j)); + } + } + mp /= (double) (x1->size1 - 1); + newval = mp - coeff * (mp - gsl_matrix_get (x1, corner, j)); + gsl_vector_set (xc, j, newval); + } + + newval = GSL_MULTIMIN_FN_EVAL (f, xc); + + return newval; +} + +static int +nmsimplex_contract_by_best (nmsimplex_state_t * state, size_t best, + gsl_vector * xc, gsl_multimin_function * f) +{ + + /* Function contracts the simplex in respect to + best valued corner. That is, all corners besides the + best corner are moved. */ + + /* the xc vector is simply work space here */ + + gsl_matrix *x1 = state->x1; + gsl_vector *y1 = state->y1; + + size_t i, j; + double newval; + + for (i = 0; i < x1->size1; i++) + { + if (i != best) + { + for (j = 0; j < x1->size2; j++) + { + newval = 0.5 * (gsl_matrix_get (x1, i, j) + + gsl_matrix_get (x1, best, j)); + gsl_matrix_set (x1, i, j, newval); + } + + /* evaluate function in the new point */ + + gsl_matrix_get_row (xc, x1, i); + newval = GSL_MULTIMIN_FN_EVAL (f, xc); + gsl_vector_set (y1, i, newval); + } + } + + return GSL_SUCCESS; +} + +static int +nmsimplex_calc_center (const nmsimplex_state_t * state, gsl_vector * mp) +{ + /* calculates the center of the simplex to mp */ + + gsl_matrix *x1 = state->x1; + + size_t i, j; + double val; + + for (j = 0; j < x1->size2; j++) + { + val = 0.0; + for (i = 0; i < x1->size1; i++) + { + val += gsl_matrix_get (x1, i, j); + } + val /= x1->size1; + gsl_vector_set (mp, j, val); + } + + return GSL_SUCCESS; +} + +static double +nmsimplex_size (nmsimplex_state_t * state) +{ + /* calculates simplex size as average sum of length of vectors + from simplex center to corner points: + + ( sum ( || y - y_middlepoint || ) ) / n + */ + + gsl_vector *s = state->ws1; + gsl_vector *mp = state->ws2; + + gsl_matrix *x1 = state->x1; + size_t i; + + double ss = 0.0; + + /* Calculate middle point */ + nmsimplex_calc_center (state, mp); + + for (i = 0; i < x1->size1; i++) + { + gsl_matrix_get_row (s, x1, i); + gsl_blas_daxpy (-1.0, mp, s); + ss += gsl_blas_dnrm2 (s); + } + + return ss / (double) (x1->size1); +} + +static int +nmsimplex_alloc (void *vstate, size_t n) +{ + nmsimplex_state_t *state = (nmsimplex_state_t *) vstate; + + state->x1 = gsl_matrix_alloc (n + 1, n); + + if (state->x1 == NULL) + { + GSL_ERROR ("failed to allocate space for x1", GSL_ENOMEM); + } + + state->y1 = gsl_vector_alloc (n + 1); + + if (state->y1 == NULL) + { + GSL_ERROR ("failed to allocate space for y", GSL_ENOMEM); + } + + state->ws1 = gsl_vector_alloc (n); + + if (state->ws1 == NULL) + { + GSL_ERROR ("failed to allocate space for ws1", GSL_ENOMEM); + } + + state->ws2 = gsl_vector_alloc (n); + + if (state->ws2 == NULL) + { + GSL_ERROR ("failed to allocate space for ws2", GSL_ENOMEM); + } + + return GSL_SUCCESS; +} + +static int +nmsimplex_set (void *vstate, gsl_multimin_function * f, + const gsl_vector * x, + double *size, const gsl_vector * step_size) +{ + int status; + size_t i; + double val; + + nmsimplex_state_t *state = (nmsimplex_state_t *) vstate; + + gsl_vector *xtemp = state->ws1; + + /* first point is the original x0 */ + + val = GSL_MULTIMIN_FN_EVAL (f, x); + gsl_matrix_set_row (state->x1, 0, x); + gsl_vector_set (state->y1, 0, val); + + /* following points are initialized to x0 + step_size */ + + for (i = 0; i < x->size; i++) + { + status = gsl_vector_memcpy (xtemp, x); + + if (status != 0) + { + GSL_ERROR ("vector memcopy failed", GSL_EFAILED); + } + + val = gsl_vector_get (xtemp, i) + gsl_vector_get (step_size, i); + gsl_vector_set (xtemp, i, val); + val = GSL_MULTIMIN_FN_EVAL (f, xtemp); + gsl_matrix_set_row (state->x1, i + 1, xtemp); + gsl_vector_set (state->y1, i + 1, val); + } + + /* Initialize simplex size */ + + *size = nmsimplex_size (state); + + return GSL_SUCCESS; +} + +static void +nmsimplex_free (void *vstate) +{ + nmsimplex_state_t *state = (nmsimplex_state_t *) vstate; + + gsl_matrix_free (state->x1); + gsl_vector_free (state->y1); + gsl_vector_free (state->ws1); + gsl_vector_free (state->ws2); +} + +static int +nmsimplex_iterate (void *vstate, gsl_multimin_function * f, + gsl_vector * x, double *size, double *fval) +{ + + /* Simplex iteration tries to minimize function f value */ + /* Includes corrections from Ivo Alxneit <ivo.alxneit@psi.ch> */ + + nmsimplex_state_t *state = (nmsimplex_state_t *) vstate; + + /* xc and xc2 vectors store tried corner point coordinates */ + + gsl_vector *xc = state->ws1; + gsl_vector *xc2 = state->ws2; + gsl_vector *y1 = state->y1; + gsl_matrix *x1 = state->x1; + + size_t n = y1->size; + size_t i; + size_t hi = 0, s_hi = 0, lo = 0; + double dhi, ds_hi, dlo; + int status; + double val, val2; + + /* get index of highest, second highest and lowest point */ + + dhi = ds_hi = dlo = gsl_vector_get (y1, 0); + + for (i = 1; i < n; i++) + { + val = (gsl_vector_get (y1, i)); + if (val < dlo) + { + dlo = val; + lo = i; + } + else if (val > dhi) + { + ds_hi = dhi; + s_hi = hi; + dhi = val; + hi = i; + } + else if (val > ds_hi) + { + ds_hi = val; + s_hi = i; + } + } + + /* reflect the highest value */ + + val = nmsimplex_move_corner (-1.0, state, hi, xc, f); + + if (val < gsl_vector_get (y1, lo)) + { + + /* reflected point becomes lowest point, try expansion */ + + val2 = nmsimplex_move_corner (-2.0, state, hi, xc2, f); + + if (val2 < gsl_vector_get (y1, lo)) + { + gsl_matrix_set_row (x1, hi, xc2); + gsl_vector_set (y1, hi, val2); + } + else + { + gsl_matrix_set_row (x1, hi, xc); + gsl_vector_set (y1, hi, val); + } + } + + /* reflection does not improve things enough */ + + else if (val > gsl_vector_get (y1, s_hi)) + { + if (val <= gsl_vector_get (y1, hi)) + { + + /* if trial point is better than highest point, replace + highest point */ + + gsl_matrix_set_row (x1, hi, xc); + gsl_vector_set (y1, hi, val); + } + + /* try one dimensional contraction */ + + val2 = nmsimplex_move_corner (0.5, state, hi, xc2, f); + + if (val2 <= gsl_vector_get (y1, hi)) + { + gsl_matrix_set_row (state->x1, hi, xc2); + gsl_vector_set (y1, hi, val2); + } + + else + { + + /* contract the whole simplex in respect to the best point */ + + status = nmsimplex_contract_by_best (state, lo, xc, f); + if (status != 0) + { + GSL_ERROR ("nmsimplex_contract_by_best failed", GSL_EFAILED); + } + } + } + else + { + + /* trial point is better than second highest point. + Replace highest point by it */ + + gsl_matrix_set_row (x1, hi, xc); + gsl_vector_set (y1, hi, val); + } + + /* return lowest point of simplex as x */ + + lo = gsl_vector_min_index (y1); + gsl_matrix_get_row (x, x1, lo); + *fval = gsl_vector_get (y1, lo); + + /* Update simplex size */ + + *size = nmsimplex_size (state); + + return GSL_SUCCESS; +} + +static const gsl_multimin_fminimizer_type nmsimplex_type = +{ "nmsimplex", /* name */ + sizeof (nmsimplex_state_t), + &nmsimplex_alloc, + &nmsimplex_set, + &nmsimplex_iterate, + &nmsimplex_free +}; + +const gsl_multimin_fminimizer_type + * gsl_multimin_fminimizer_nmsimplex = &nmsimplex_type; |