PaGMO  1.1.5
gsl_gradient.cpp
1 /*****************************************************************************
2  * Copyright (C) 2004-2015 The PaGMO development team, *
3  * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4  * *
5  * https://github.com/esa/pagmo *
6  * *
7  * act@esa.int *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  *****************************************************************************/
24 
25 #include <algorithm>
26 #include <boost/numeric/conversion/cast.hpp>
27 #include <cstddef>
28 #include <exception>
29 #include <gsl/gsl_deriv.h>
30 #include <gsl/gsl_multimin.h>
31 #include <gsl/gsl_vector.h>
32 #include <new>
33 #include <sstream>
34 #include <stdexcept>
35 #include <string>
36 
37 #include "../exceptions.h"
38 #include "../population.h"
39 #include "../problem/base.h"
40 #include "../types.h"
41 #include "base_gsl.h"
42 #include "gsl_gradient.h"
43 
44 namespace pagmo { namespace algorithm {
45 
47 
57 gsl_gradient::gsl_gradient(int max_iter, const double &grad_tol, const double &numdiff_step_size, const double &step_size, const double &tol):
58  base_gsl(),
59  m_max_iter(boost::numeric_cast<std::size_t>(max_iter)),m_grad_tol(grad_tol),m_numdiff_step_size(numdiff_step_size),
60  m_step_size(step_size),m_tol(tol)
61 {
62  if (step_size <= 0) {
63  pagmo_throw(value_error,"step size must be positive");
64  }
65  if (tol <= 0) {
66  pagmo_throw(value_error,"tolerance must be positive");
67  }
68  if (numdiff_step_size <= 0) {
69  pagmo_throw(value_error,"step size for numerical differentiation must be positive");
70  }
71  if (grad_tol <= 0) {
72  pagmo_throw(value_error,"gradient tolerance must be positive");
73  }
74 }
75 
76 // Wrapper for the numerical differentiation of the objective function of a problem via GSL.
77 double gsl_gradient::objfun_numdiff_wrapper(double x, void *params)
78 {
79  objfun_numdiff_wrapper_params *pars = (objfun_numdiff_wrapper_params *)params;
80  pars->x[pars->coord] = x;
81  pars->prob->objfun(pars->f,pars->x);
82  return pars->f[0];
83 }
84 
85 // Write into retval the gradient of the continuous part of the objective function of prob calculated in input.
86 void gsl_gradient::objfun_numdiff_central(gsl_vector *retval, const problem::base &prob, const decision_vector &input, const double &step_size)
87 {
88  if (input.size() != prob.get_dimension()) {
89  pagmo_throw(value_error,"invalid input vector dimension in numerical differentiation of the objective function");
90  }
91  if (prob.get_f_dimension() != 1) {
92  pagmo_throw(value_error,"numerical differentiation of the objective function cannot work on multi-objective problems");
93  }
94  // Size of the continuous part of the problem.
95  const problem::base::size_type cont_size = prob.get_dimension() - prob.get_i_dimension();
96  // Structure to pass data to the wrapper.
97  objfun_numdiff_wrapper_params pars;
98  pars.x = input;
99  pars.f.resize(1);
100  pars.prob = &prob;
101  // GSL function.
102  gsl_function F;
103  F.function = &objfun_numdiff_wrapper;
104  F.params = (void *)&pars;
105  double result, abserr;
106  // Numerical differentiation component by component.
107  for (problem::base::size_type i = 0; i < cont_size; ++i) {
108  pars.coord = i;
109  gsl_deriv_central(&F,input[i],step_size,&result,&abserr);
110  gsl_vector_set(retval,i,result);
111  }
112 }
113 
114 // Objective function's derivative wrapper.
115 void gsl_gradient::d_objfun_wrapper(const gsl_vector *v, void *params, gsl_vector *df)
116 {
117  objfun_wrapper_params *par = (objfun_wrapper_params *)params;
118  // Size of the continuous part of the problem.
119  const problem::base::size_type cont_size = par->p->get_dimension() - par->p->get_i_dimension();
120  // Fill up the continuous part of temporary storage with the contents of v.
121  for (problem::base::size_type i = 0; i < cont_size; ++i) {
122  par->x[i] = gsl_vector_get(v,i);
123  }
124  // Calculate the gradient.
125  objfun_numdiff_central(df,*par->p,par->x,par->step_size);
126 }
127 
128 // Simmultaneous function/derivative computation wrapper for the objective function.
129 void gsl_gradient::fd_objfun_wrapper(const gsl_vector *v, void *params, double *retval, gsl_vector *df)
130 {
131  *retval = objfun_wrapper(v,params);
132  d_objfun_wrapper(v,params,df);
133 }
134 
136 
146 {
147  // Do nothing if the population is empty.
148  if (!pop.size()) {
149  return;
150  }
151  // Useful variables.
152  const problem::base &problem = pop.problem();
153  if (problem.get_f_dimension() != 1) {
154  pagmo_throw(value_error,"this algorithm does not support multi-objective optimisation");
155  }
156  if (problem.get_c_dimension()) {
157  pagmo_throw(value_error,"this algorithm does not support constrained optimisation");
158  }
159  const problem::base::size_type cont_size = problem.get_dimension() - problem.get_i_dimension();
160  if (!cont_size) {
161  pagmo_throw(value_error,"the problem has no continuous part");
162  }
163  // Extract the best individual.
164  const population::size_type best_ind_idx = pop.get_best_idx();
165  const population::individual_type &best_ind = pop.get_individual(best_ind_idx);
166  // GSL wrapper parameters structure.
167  objfun_wrapper_params params;
168  params.p = &problem;
169  // Integer part of the temporay decision vector must be filled with the integer part of the best individual,
170  // which will not be optimised.
171  params.x.resize(problem.get_dimension());
172  std::copy(best_ind.cur_x.begin() + cont_size, best_ind.cur_x.end(), params.x.begin() + cont_size);
173  params.f.resize(1);
174  params.step_size = m_numdiff_step_size;
175  // GSL function structure.
176  gsl_multimin_function_fdf gsl_func;
177  gsl_func.n = boost::numeric_cast<std::size_t>(cont_size);
178  gsl_func.f = &objfun_wrapper;
179  gsl_func.df = &d_objfun_wrapper;
180  gsl_func.fdf = &fd_objfun_wrapper;
181  gsl_func.params = (void *)&params;
182  // Minimiser.
183  gsl_multimin_fdfminimizer *s = 0;
184  // This will be the starting point.
185  gsl_vector *x = 0;
186  // Here we start the allocations.
187  // Recast as size_t here, in order to avoid potential overflows later.
188  const std::size_t s_cont_size = boost::numeric_cast<std::size_t>(cont_size);
189  // Allocate and check the allocation results.
190  x = gsl_vector_alloc(s_cont_size);
191  const gsl_multimin_fdfminimizer_type *minimiser = get_gsl_minimiser_ptr();
192  pagmo_assert(minimiser);
193  s = gsl_multimin_fdfminimizer_alloc(minimiser,s_cont_size);
194  // Check the allocations.
195  check_allocs(x,s);
196  // Fill in the starting point (from the best individual).
197  for (std::size_t i = 0; i < s_cont_size; ++i) {
198  gsl_vector_set(x,i,best_ind.cur_x[i]);
199  }
200  // Init the solver.
201  gsl_multimin_fdfminimizer_set(s,&gsl_func,x,m_step_size,m_tol);
202  // Iterate.
203  std::size_t iter = 0;
204  int status;
205  try {
206  do
207  {
208  ++iter;
209  status = gsl_multimin_fdfminimizer_iterate(s);
210  if (status) {
211  break;
212  }
213  status = gsl_multimin_test_gradient(s->gradient,m_grad_tol);
214  } while (status == GSL_CONTINUE && iter < m_max_iter);
215  } catch (const std::exception &e) {
216  // Cleanup and re-throw.
217  cleanup(x,s);
218  throw e;
219  } catch (...) {
220  // Cleanup and throw.
221  cleanup(x,s);
222  pagmo_throw(std::runtime_error,"unknown exception caught in gsl_gradient::evolve");
223  }
224  // Free up resources.
225  cleanup(x,s);
226  // Check the generated individual and change it to respect the bounds as necessary.
227  for (problem::base::size_type i = 0; i < cont_size; ++i) {
228  if (params.x[i] < problem.get_lb()[i]) {
229  params.x[i] = problem.get_lb()[i];
230  }
231  if (params.x[i] > problem.get_ub()[i]) {
232  params.x[i] = problem.get_ub()[i];
233  }
234  }
235  // Replace the best individual.
236  pop.set_x(best_ind_idx,params.x);
237 }
238 
239 // Check GSL allocations done during evolve().
240 void gsl_gradient::check_allocs(gsl_vector *x, gsl_multimin_fdfminimizer *s)
241 {
242  // If at least one allocation failed, cleanup and throw a memory error.
243  if (!x || !s) {
244  cleanup(x,s);
245  throw std::bad_alloc();
246  }
247 }
248 
249 // Cleanup allocations done during evolve.
250 void gsl_gradient::cleanup(gsl_vector *x, gsl_multimin_fdfminimizer *s)
251 {
252  if (x) {
253  gsl_vector_free(x);
254  }
255  if (s) {
256  gsl_multimin_fdfminimizer_free(s);
257  }
258 }
259 
261 
265 {
266  std::ostringstream oss;
267  oss << "max_iter: " << m_max_iter << ' ';
268  oss << "step_size: " << m_step_size << ' ';
269  oss << "tol: " << m_tol << ' ';
270  oss << "grad_step_size: " << m_numdiff_step_size << ' ';
271  oss << "grad_tol: " << m_grad_tol << ' ';
272 
273 
274  return oss.str();
275 }
276 
277 }}
Root PaGMO namespace.
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
virtual const gsl_multimin_fdfminimizer_type * get_gsl_minimiser_ptr() const =0
Selected minimiser.
double step_size
Initial step size for the computation of the gradient.
Definition: base_gsl.h:71
const individual_type & get_individual(const size_type &) const
Get constant reference to individual at position n.
Definition: population.cpp:277
Individuals stored in the population.
Definition: population.h:80
STL namespace.
Base problem class.
Definition: problem/base.h:148
Population class.
Definition: population.h:70
void evolve(population &) const
Evolve method.
size_type get_dimension() const
Return global dimension.
Base class for GSL algorithms.
Definition: base_gsl.h:56
gsl_gradient(int, const double &, const double &, const double &, const double &)
Constructor.
std::string human_readable_extra() const
Extra information in human-readable format.
fitness_vector f
Fitness vector.
Definition: base_gsl.h:69
size_type get_i_dimension() const
Return integer dimension.
c_size_type get_c_dimension() const
Return global constraints dimension.
const decision_vector & get_ub() const
Upper bounds getter.
container_type::size_type size_type
Population size type.
Definition: population.h:192
static double objfun_wrapper(const gsl_vector *, void *)
Objective function wrapper.
Definition: base_gsl.cpp:67
decision_vector cur_x
Current decision vector.
Definition: population.h:83
Structure to feed parameters to the wrappers for the objective function and its derivative.
Definition: base_gsl.h:62
f_size_type get_f_dimension() const
Return fitness dimension.
decision_vector x
Decision vector.
Definition: base_gsl.h:67
problem::base const * p
Pointer to the problem.
Definition: base_gsl.h:65
const decision_vector & get_lb() const
Lower bounds getter.
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160