|
Packit |
67cb25 |
/* siman/siman.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 2007 Brian Gough
|
|
Packit |
67cb25 |
* Copyright (C) 1996, 1997, 1998, 1999, 2000 Mark Galassi
|
|
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 <stdio.h>
|
|
Packit |
67cb25 |
#include <math.h>
|
|
Packit |
67cb25 |
#include <stdlib.h>
|
|
Packit |
67cb25 |
#include <string.h>
|
|
Packit |
67cb25 |
#include <assert.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <gsl/gsl_machine.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_rng.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_siman.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static inline double
|
|
Packit |
67cb25 |
boltzmann(double E, double new_E, double T, gsl_siman_params_t *params)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double x = -(new_E - E) / (params->k * T);
|
|
Packit |
67cb25 |
/* avoid underflow errors for large uphill steps */
|
|
Packit |
67cb25 |
return (x < GSL_LOG_DBL_MIN) ? 0.0 : exp(x);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static inline void
|
|
Packit |
67cb25 |
copy_state(void *src, void *dst, size_t size, gsl_siman_copy_t copyfunc)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (copyfunc) {
|
|
Packit |
67cb25 |
copyfunc(src, dst);
|
|
Packit |
67cb25 |
} else {
|
|
Packit |
67cb25 |
memcpy(dst, src, size);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* implementation of a basic simulated annealing algorithm */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
void
|
|
Packit |
67cb25 |
gsl_siman_solve (const gsl_rng * r, void *x0_p, gsl_siman_Efunc_t Ef,
|
|
Packit |
67cb25 |
gsl_siman_step_t take_step,
|
|
Packit |
67cb25 |
gsl_siman_metric_t distance,
|
|
Packit |
67cb25 |
gsl_siman_print_t print_position,
|
|
Packit |
67cb25 |
gsl_siman_copy_t copyfunc,
|
|
Packit |
67cb25 |
gsl_siman_copy_construct_t copy_constructor,
|
|
Packit |
67cb25 |
gsl_siman_destroy_t destructor,
|
|
Packit |
67cb25 |
size_t element_size,
|
|
Packit |
67cb25 |
gsl_siman_params_t params)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
void *x, *new_x, *best_x;
|
|
Packit |
67cb25 |
double E, new_E, best_E;
|
|
Packit |
67cb25 |
int i;
|
|
Packit |
67cb25 |
double T, T_factor;
|
|
Packit |
67cb25 |
int n_evals = 1, n_iter = 0, n_accepts, n_rejects, n_eless;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* this function requires that either the dynamic functions (copy,
|
|
Packit |
67cb25 |
copy_constructor and destrcutor) are passed, or that an element
|
|
Packit |
67cb25 |
size is given */
|
|
Packit |
67cb25 |
assert((copyfunc != NULL && copy_constructor != NULL && destructor != NULL)
|
|
Packit |
67cb25 |
|| (element_size != 0));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
distance = 0 ; /* This parameter is not currently used */
|
|
Packit |
67cb25 |
E = Ef(x0_p);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (copyfunc) {
|
|
Packit |
67cb25 |
x = copy_constructor(x0_p);
|
|
Packit |
67cb25 |
new_x = copy_constructor(x0_p);
|
|
Packit |
67cb25 |
best_x = copy_constructor(x0_p);
|
|
Packit |
67cb25 |
} else {
|
|
Packit |
67cb25 |
x = (void *) malloc (element_size);
|
|
Packit |
67cb25 |
memcpy (x, x0_p, element_size);
|
|
Packit |
67cb25 |
new_x = (void *) malloc (element_size);
|
|
Packit |
67cb25 |
best_x = (void *) malloc (element_size);
|
|
Packit |
67cb25 |
memcpy (best_x, x0_p, element_size);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
best_E = E;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
T = params.t_initial;
|
|
Packit |
67cb25 |
T_factor = 1.0 / params.mu_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (print_position) {
|
|
Packit |
67cb25 |
printf ("#-iter #-evals temperature position energy\n");
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
while (1) {
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
n_accepts = 0;
|
|
Packit |
67cb25 |
n_rejects = 0;
|
|
Packit |
67cb25 |
n_eless = 0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (i = 0; i < params.iters_fixed_T; ++i) {
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
copy_state(x, new_x, element_size, copyfunc);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
take_step (r, new_x, params.step_size);
|
|
Packit |
67cb25 |
new_E = Ef (new_x);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if(new_E <= best_E){
|
|
Packit |
67cb25 |
if (copyfunc) {
|
|
Packit |
67cb25 |
copyfunc(new_x,best_x);
|
|
Packit |
67cb25 |
} else {
|
|
Packit |
67cb25 |
memcpy (best_x, new_x, element_size);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
best_E=new_E;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
++n_evals; /* keep track of Ef() evaluations */
|
|
Packit |
67cb25 |
/* now take the crucial step: see if the new point is accepted
|
|
Packit |
67cb25 |
or not, as determined by the boltzmann probability */
|
|
Packit |
67cb25 |
if (new_E < E) {
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (new_E < best_E) {
|
|
Packit |
67cb25 |
copy_state(new_x, best_x, element_size, copyfunc);
|
|
Packit |
67cb25 |
best_E = new_E;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* yay! take a step */
|
|
Packit |
67cb25 |
copy_state(new_x, x, element_size, copyfunc);
|
|
Packit |
67cb25 |
E = new_E;
|
|
Packit |
67cb25 |
++n_eless;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
} else if (gsl_rng_uniform(r) < boltzmann(E, new_E, T, ¶ms)) {
|
|
Packit |
67cb25 |
/* yay! take a step */
|
|
Packit |
67cb25 |
copy_state(new_x, x, element_size, copyfunc);
|
|
Packit |
67cb25 |
E = new_E;
|
|
Packit |
67cb25 |
++n_accepts;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
} else {
|
|
Packit |
67cb25 |
++n_rejects;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (print_position) {
|
|
Packit |
67cb25 |
/* see if we need to print stuff as we go */
|
|
Packit |
67cb25 |
/* printf("%5d %12g %5d %3d %3d %3d", n_iter, T, n_evals, */
|
|
Packit |
67cb25 |
/* 100*n_eless/n_steps, 100*n_accepts/n_steps, */
|
|
Packit |
67cb25 |
/* 100*n_rejects/n_steps); */
|
|
Packit |
67cb25 |
printf ("%5d %7d %12g", n_iter, n_evals, T);
|
|
Packit |
67cb25 |
print_position (x);
|
|
Packit |
67cb25 |
printf (" %12g %12g\n", E, best_E);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* apply the cooling schedule to the temperature */
|
|
Packit |
67cb25 |
/* FIXME: I should also introduce a cooling schedule for the iters */
|
|
Packit |
67cb25 |
T *= T_factor;
|
|
Packit |
67cb25 |
++n_iter;
|
|
Packit |
67cb25 |
if (T < params.t_min) {
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* at the end, copy the result onto the initial point, so we pass it
|
|
Packit |
67cb25 |
back to the caller */
|
|
Packit |
67cb25 |
copy_state(best_x, x0_p, element_size, copyfunc);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (copyfunc) {
|
|
Packit |
67cb25 |
destructor(x);
|
|
Packit |
67cb25 |
destructor(new_x);
|
|
Packit |
67cb25 |
destructor(best_x);
|
|
Packit |
67cb25 |
} else {
|
|
Packit |
67cb25 |
free (x);
|
|
Packit |
67cb25 |
free (new_x);
|
|
Packit |
67cb25 |
free (best_x);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* implementation of a simulated annealing algorithm with many tries */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
void
|
|
Packit |
67cb25 |
gsl_siman_solve_many (const gsl_rng * r, void *x0_p, gsl_siman_Efunc_t Ef,
|
|
Packit |
67cb25 |
gsl_siman_step_t take_step,
|
|
Packit |
67cb25 |
gsl_siman_metric_t distance,
|
|
Packit |
67cb25 |
gsl_siman_print_t print_position,
|
|
Packit |
67cb25 |
size_t element_size,
|
|
Packit |
67cb25 |
gsl_siman_params_t params)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* the new set of trial points, and their energies and probabilities */
|
|
Packit |
67cb25 |
void *x, *new_x;
|
|
Packit |
67cb25 |
double *energies, *probs, *sum_probs;
|
|
Packit |
67cb25 |
double Ex; /* energy of the chosen point */
|
|
Packit |
67cb25 |
double T, T_factor; /* the temperature and a step multiplier */
|
|
Packit |
67cb25 |
int i;
|
|
Packit |
67cb25 |
double u; /* throw the die to choose a new "x" */
|
|
Packit |
67cb25 |
int n_iter;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (print_position) {
|
|
Packit |
67cb25 |
printf ("#-iter temperature position");
|
|
Packit |
67cb25 |
printf (" delta_pos energy\n");
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
x = (void *) malloc (params.n_tries * element_size);
|
|
Packit |
67cb25 |
new_x = (void *) malloc (params.n_tries * element_size);
|
|
Packit |
67cb25 |
energies = (double *) malloc (params.n_tries * sizeof (double));
|
|
Packit |
67cb25 |
probs = (double *) malloc (params.n_tries * sizeof (double));
|
|
Packit |
67cb25 |
sum_probs = (double *) malloc (params.n_tries * sizeof (double));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
T = params.t_initial;
|
|
Packit |
67cb25 |
T_factor = 1.0 / params.mu_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
memcpy (x, x0_p, element_size);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
n_iter = 0;
|
|
Packit |
67cb25 |
while (1)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
Ex = Ef (x);
|
|
Packit |
67cb25 |
for (i = 0; i < params.n_tries - 1; ++i)
|
|
Packit |
67cb25 |
{ /* only go to N_TRIES-2 */
|
|
Packit |
67cb25 |
/* center the new_x[] around x, then pass it to take_step() */
|
|
Packit |
67cb25 |
sum_probs[i] = 0;
|
|
Packit |
67cb25 |
memcpy ((char *)new_x + i * element_size, x, element_size);
|
|
Packit |
67cb25 |
take_step (r, (char *)new_x + i * element_size, params.step_size);
|
|
Packit |
67cb25 |
energies[i] = Ef ((char *)new_x + i * element_size);
|
|
Packit |
67cb25 |
probs[i] = boltzmann(Ex, energies[i], T, ¶ms);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
/* now add in the old value of "x", so it is a contendor */
|
|
Packit |
67cb25 |
memcpy ((char *)new_x + (params.n_tries - 1) * element_size, x, element_size);
|
|
Packit |
67cb25 |
energies[params.n_tries - 1] = Ex;
|
|
Packit |
67cb25 |
probs[params.n_tries - 1] = boltzmann(Ex, energies[i], T, ¶ms);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* now throw biased die to see which new_x[i] we choose */
|
|
Packit |
67cb25 |
sum_probs[0] = probs[0];
|
|
Packit |
67cb25 |
for (i = 1; i < params.n_tries; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
sum_probs[i] = sum_probs[i - 1] + probs[i];
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
u = gsl_rng_uniform (r) * sum_probs[params.n_tries - 1];
|
|
Packit |
67cb25 |
for (i = 0; i < params.n_tries; ++i)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
if (u < sum_probs[i])
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
memcpy (x, (char *) new_x + i * element_size, element_size);
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
if (print_position)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
printf ("%5d\t%12g\t", n_iter, T);
|
|
Packit |
67cb25 |
print_position (x);
|
|
Packit |
67cb25 |
printf ("\t%12g\t%12g\n", distance (x, x0_p), Ex);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
T *= T_factor;
|
|
Packit |
67cb25 |
++n_iter;
|
|
Packit |
67cb25 |
if (T < params.t_min)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* now return the value via x0_p */
|
|
Packit |
67cb25 |
memcpy (x0_p, x, element_size);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* printf("the result is: %g (E=%g)\n", x, Ex); */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
free (x);
|
|
Packit |
67cb25 |
free (new_x);
|
|
Packit |
67cb25 |
free (energies);
|
|
Packit |
67cb25 |
free (probs);
|
|
Packit |
67cb25 |
free (sum_probs);
|
|
Packit |
67cb25 |
}
|