queso-0.55.0
GslOptimizer.C
Go to the documentation of this file.
1 //-----------------------------------------------------------------------bl-
2 //--------------------------------------------------------------------------
3 //
4 // QUESO - a library to support the Quantification of Uncertainty
5 // for Estimation, Simulation and Optimization
6 //
7 // Copyright (C) 2008-2015 The PECOS Development Team
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the Version 2.1 GNU Lesser General
11 // Public License as published by the Free Software Foundation.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc. 51 Franklin Street, Fifth Floor,
21 // Boston, MA 02110-1301 USA
22 //
23 //-----------------------------------------------------------------------el-
24 
25 #include <iostream>
26 
27 #include <queso/Defines.h>
28 #include <queso/GslVector.h>
29 #include <queso/VectorSpace.h>
30 #include <queso/ScalarFunction.h>
31 #include <queso/GslOptimizer.h>
32 #include <queso/OptimizerMonitor.h>
33 
34 #include <gsl/gsl_multimin.h>
35 #include <gsl/gsl_blas.h>
36 
37 namespace QUESO {
38 
39 // We need to extern "C" because gsl needs a pointer to a C function to
40 // minimize
41 extern "C" {
42  // This evaluate -log posterior
43  double c_evaluate(const gsl_vector * x, void * context) {
44 
45  GslOptimizer * optimizer = static_cast<GslOptimizer * >(context);
46 
47  GslVector state(
48  optimizer->objectiveFunction().domainSet().vectorSpace().zeroVector());
49 
50  // DM: Doing this copy sucks, but whatever. It'll do for now.
51  for (unsigned int i = 0; i < state.sizeLocal(); i++) {
52  state[i] = gsl_vector_get(x, i);
53  }
54 
55  // Bail early if GSL tries to evaluate outside of the domain
56  if (!optimizer->objectiveFunction().domainSet().contains(state)) {
57  return GSL_NAN;
58  }
59 
60  // Should cache derivative here so we don't a) segfault in the user's code
61  // and b) so we don't recompute stuff
62  double result = -optimizer->objectiveFunction().lnValue(state, NULL, NULL,
63  NULL, NULL);
64 
65  return result;
66  }
67 
68  // This evaluates the derivative of -log posterior
69  void c_evaluate_derivative(const gsl_vector * x, void * context,
70  gsl_vector * derivative) {
71  GslOptimizer * optimizer = static_cast<GslOptimizer * >(context);
72 
73  GslVector state(
74  optimizer->objectiveFunction().domainSet().vectorSpace().zeroVector());
75  GslVector deriv(
76  optimizer->objectiveFunction().domainSet().vectorSpace().zeroVector());
77 
78  // DM: Doing this copy sucks, but whatever. It'll do for now.
79  for (unsigned int i = 0; i < state.sizeLocal(); i++) {
80  state[i] = gsl_vector_get(x, i);
81 
82  // We fill with GSL_NAN and use it as a flag to check later that the user
83  // actually fills the derivative vector with stuff
84  deriv[i] = GSL_NAN;
85  }
86 
87  if (!optimizer->objectiveFunction().domainSet().contains(state)) {
88  // Fill derivative with error codes if the point is outside of the
89  // domain
90  for (unsigned int i = 0; i < deriv.sizeLocal(); i++) {
91  gsl_vector_set(derivative, i, GSL_NAN);
92  }
93  }
94  else {
95  // We should cache the return value here so we don't recompute stuff
96  double fx = -optimizer->objectiveFunction().lnValue(state, NULL, &deriv,
97  NULL, NULL);
98 
99  // Decide whether or not we need to do a finite difference based on
100  // whether the user actually filled deriv with values that are not
101  // GSL_NAN
102  //
103  // We're currently doing this check every time this function gets called.
104  // We could probably pull this logic out of here and put it somewhere
105  // where it only happens once
106  bool userComputedDerivative = true;
107  for (unsigned int i = 0; i < deriv.sizeLocal(); i++) {
108  // If the user missed out a derivative in any direction, fall back to
109  // a finite difference
110  if (gsl_isnan(deriv[i])) {
111  userComputedDerivative = false;
112  break;
113  }
114  }
115 
116  if (userComputedDerivative) {
117  for (unsigned int i = 0; i < deriv.sizeLocal(); i++) {
118  gsl_vector_set(derivative, i, -deriv[i]); // We need the minus sign
119  }
120  }
121  else {
122  // Finite difference step-size
123  double h = optimizer->getFiniteDifferenceStepSize();
124 
125  // User did not provide a derivative, so do a finite difference
126  for (unsigned int i = 0; i < deriv.sizeLocal(); i++) {
127  double tempState = state[i];
128  state[i] += h;
129 
130  // User didn't provide a derivative, so we don't bother passing in
131  // the derivative vector again
132  double fxph = -optimizer->objectiveFunction().lnValue(state, NULL,
133  NULL, NULL, NULL);
134 
135  // Reset the state back to what it was before
136  state[i] = tempState;
137 
138  // Make sure we didn't do anything dumb and tell gsl if we did
139  if (!gsl_isnan(fx) && !gsl_isnan(fxph)) {
140  gsl_vector_set(derivative, i, (fxph - fx) / h);
141  }
142  else {
143  gsl_vector_set(derivative, i, GSL_NAN);
144  }
145  }
146  }
147  }
148  }
149 
150  // This evaluates -log posterior and the derivative of -log posterior
151  void c_evaluate_with_derivative(const gsl_vector * x, void * context,
152  double * f, gsl_vector * derivative) {
153  // We don't need to call both of these
154  *f = c_evaluate(x, context);
155  c_evaluate_derivative(x, context, derivative);
156  }
157 } // End extern "C"
158 
160  const BaseScalarFunction<GslVector, GslMatrix> & objectiveFunction)
161  : BaseOptimizer(),
162  m_objectiveFunction(objectiveFunction),
163  m_initialPoint(new GslVector(objectiveFunction.domainSet().
164  vectorSpace().zeroVector())),
165  m_minimizer(new GslVector(this->m_objectiveFunction.domainSet().
166  vectorSpace().zeroVector())),
167  m_solver_type(BFGS2),
168  m_fstep_size(this->m_objectiveFunction.domainSet().vectorSpace().zeroVector()),
169  m_fdfstep_size(1.0),
170  m_line_tol(0.1)
171 {
172  // We initialize the minimizer to GSL_NAN just in case the optimization fails
173  m_minimizer->cwSet(GSL_NAN);
174 
175  // Set to documented default value.
176  m_fstep_size.cwSet(0.1);
177 }
178 
180 {
181  delete this->m_initialPoint;
182 }
183 
185 
186  // First check that initial guess is reasonable
187  if (!this->m_objectiveFunction.domainSet().contains(*(this->m_initialPoint)))
188  {
189  if( m_objectiveFunction.domainSet().env().fullRank() == 0 )
190  {
191  std::cerr << "Minimization was given initial point outside of domain"
192  << std::endl;
193  }
194  queso_error();
195  }
196 
197  unsigned int dim = this->m_objectiveFunction.domainSet().vectorSpace().
198  zeroVector().sizeLocal();
199 
201  {
202  this->minimize_with_gradient( dim, monitor );
203  }
204  else
205  {
206  this->minimize_no_gradient( dim, monitor );
207  }
208 
209  return;
210 }
211 
214 {
215  return this->m_objectiveFunction;
216 }
217 
218 void
220 {
221  for (unsigned int i = 0; i < initialPoint.sizeLocal(); i++) {
222  (*(this->m_initialPoint))[i] = initialPoint[i];
223  }
224 }
225 
226 const GslVector &
228 {
229  return *(this->m_minimizer);
230 }
231 
233  {
234  m_solver_type = solver;
235  }
236 
238  {
239  bool gradient_needed = false;
240 
241  switch(solver)
242  {
243  case(FLETCHER_REEVES_CG):
244  case(POLAK_RIBIERE_CG):
245  case(BFGS):
246  case(BFGS2):
247  case(STEEPEST_DESCENT):
248  {
249  gradient_needed = true;
250  break;
251  }
252  case(NELDER_MEAD):
253  case(NELDER_MEAD2):
254  case(NELDER_MEAD2_RAND):
255  {
256  break;
257  }
258  default:
259  {
260  // Wat?!
261  queso_error();
262  }
263  } // switch(solver)
264 
265  return gradient_needed;
266  }
267 
269  {
270  // Set initial point
271  gsl_vector * x = gsl_vector_alloc(dim);
272  for (unsigned int i = 0; i < dim; i++) {
273  gsl_vector_set(x, i, (*m_initialPoint)[i]);
274  }
275 
276  // Tell GSL which solver we're using
277  const gsl_multimin_fdfminimizer_type* type = NULL;
278 
279  switch(m_solver_type)
280  {
281  case(FLETCHER_REEVES_CG):
282  type = gsl_multimin_fdfminimizer_conjugate_fr;
283  break;
284  case(POLAK_RIBIERE_CG):
285  type = gsl_multimin_fdfminimizer_conjugate_pr;
286  break;
287  case(BFGS):
288  type = gsl_multimin_fdfminimizer_vector_bfgs;
289  break;
290  case(BFGS2):
291  type = gsl_multimin_fdfminimizer_vector_bfgs2;
292  break;
293  case(STEEPEST_DESCENT):
294  type = gsl_multimin_fdfminimizer_steepest_descent;
295  break;
296  case(NELDER_MEAD):
297  case(NELDER_MEAD2):
298  case(NELDER_MEAD2_RAND):
299  default:
300  // Wat?!
301  queso_error();
302  }
303 
304  // Init solver
305  gsl_multimin_fdfminimizer * solver =
306  gsl_multimin_fdfminimizer_alloc(type, dim);
307 
308  // Point GSL to the right functions
309  gsl_multimin_function_fdf minusLogPosterior;
310  minusLogPosterior.n = dim;
311  minusLogPosterior.f = &c_evaluate;
312  minusLogPosterior.df = &c_evaluate_derivative;
313  minusLogPosterior.fdf = &c_evaluate_with_derivative;
314  minusLogPosterior.params = (void *)(this);
315 
316  gsl_multimin_fdfminimizer_set(solver, &minusLogPosterior, x, m_fdfstep_size, m_line_tol);
317 
318  int status;
319  size_t iter = 0;
320 
321  do {
322  iter++;
323  status = gsl_multimin_fdfminimizer_iterate(solver);
324 
325  if (status) {
326  if( m_objectiveFunction.domainSet().env().fullRank() == 0 )
327  {
328  std::cerr << "Error while GSL does optimisation. "
329  << "See below for GSL error type." << std::endl;
330  std::cerr << "Gsl error: " << gsl_strerror(status) << std::endl;
331  }
332  break;
333  }
334 
335  status = gsl_multimin_test_gradient(solver->gradient, this->getTolerance());
336 
337  if(monitor)
338  {
339  gsl_vector* x = gsl_multimin_fdfminimizer_x(solver);
340  std::vector<double> x_min(dim);
341  for( unsigned int i = 0; i < dim; i++)
342  x_min[i] = gsl_vector_get(x,i);
343 
344  double f = gsl_multimin_fdfminimizer_minimum(solver);
345 
346  gsl_vector* grad = gsl_multimin_fdfminimizer_gradient(solver);
347  double grad_norm = gsl_blas_dnrm2(grad);
348 
349  monitor->append( x_min, f, grad_norm );
350  }
351 
352  } while ((status == GSL_CONTINUE) && (iter < this->getMaxIterations()));
353 
354  for (unsigned int i = 0; i < dim; i++) {
355  (*m_minimizer)[i] = gsl_vector_get(solver->x, i);
356  }
357 
358  // We're being good human beings and cleaning up the memory we allocated
359  gsl_multimin_fdfminimizer_free(solver);
360  gsl_vector_free(x);
361 
362  return;
363  }
364 
366  {
367  // Set initial point
368  gsl_vector* x = gsl_vector_alloc(dim);
369  for (unsigned int i = 0; i < dim; i++) {
370  gsl_vector_set(x, i, (*m_initialPoint)[i]);
371  }
372 
373  // Tell GSL which solver we're using
374  const gsl_multimin_fminimizer_type* type = NULL;
375 
376  switch(m_solver_type)
377  {
378  case(NELDER_MEAD):
379  type = gsl_multimin_fminimizer_nmsimplex;
380  break;
381  case(NELDER_MEAD2):
382  type = gsl_multimin_fminimizer_nmsimplex2;
383  break;
384  case(NELDER_MEAD2_RAND):
385  type = gsl_multimin_fminimizer_nmsimplex2rand;
386  break;
387  case(FLETCHER_REEVES_CG):
388  case(POLAK_RIBIERE_CG):
389  case(BFGS):
390  case(BFGS2):
391  case(STEEPEST_DESCENT):
392  default:
393  // Wat?!
394  queso_error();
395  }
396 
397  // Init solver
398  gsl_multimin_fminimizer* solver =
399  gsl_multimin_fminimizer_alloc(type, dim);
400 
401  // Point GSL at the right functions
402  gsl_multimin_function minusLogPosterior;
403  minusLogPosterior.n = dim;
404  minusLogPosterior.f = &c_evaluate;
405  minusLogPosterior.params = (void *)(this);
406 
407  // Needed for these gradient free algorithms.
408  gsl_vector* step_size = gsl_vector_alloc(dim);
409 
410  for(unsigned int i = 0; i < dim; i++) {
411  gsl_vector_set(step_size, i, m_fstep_size[i]);
412  }
413 
414  gsl_multimin_fminimizer_set(solver, &minusLogPosterior, x, step_size);
415 
416  int status;
417  size_t iter = 0;
418  double size = 0.0;
419 
420  do
421  {
422  iter++;
423  status = gsl_multimin_fminimizer_iterate(solver);
424 
425  if (status) {
426  if( m_objectiveFunction.domainSet().env().fullRank() == 0 )
427  {
428  std::cerr << "Error while GSL does optimisation. "
429  << "See below for GSL error type." << std::endl;
430  std::cerr << "Gsl error: " << gsl_strerror(status) << std::endl;
431  }
432  break;
433  }
434 
435  size = gsl_multimin_fminimizer_size(solver);
436 
437  status = gsl_multimin_test_size (size, this->getTolerance());
438 
439  if(monitor)
440  {
441  gsl_vector* x = gsl_multimin_fminimizer_x(solver);
442  std::vector<double> x_min(dim);
443  for( unsigned int i = 0; i < dim; i++)
444  x_min[i] = gsl_vector_get(x,i);
445 
446  double f = gsl_multimin_fminimizer_minimum(solver);
447 
448  monitor->append( x_min, f, size );
449  }
450 
451  }
452 
453  while ((status == GSL_CONTINUE) && (iter < this->getMaxIterations()));
454 
455  for (unsigned int i = 0; i < dim; i++) {
456  (*m_minimizer)[i] = gsl_vector_get(solver->x, i);
457  }
458 
459  // We're being good human beings and cleaning up the memory we allocated
460  gsl_vector_free(step_size);
461  gsl_multimin_fminimizer_free(solver);
462  gsl_vector_free(x);
463 
464  return;
465  }
466 
467  void GslOptimizer::set_step_size( const GslVector& step_size )
468  {
469  m_fstep_size = step_size;
470  }
471 
472  void GslOptimizer::set_step_size( double step_size )
473  {
474  m_fdfstep_size = step_size;
475  }
476 
478  {
479  SolverType solver_type;
480 
481  if( solver == std::string("fletcher_reeves_cg") )
482  solver_type = FLETCHER_REEVES_CG;
483  else if( solver == std::string("polak_ribiere_cg") )
484  solver_type = POLAK_RIBIERE_CG;
485  else if( solver == std::string("bfgs") )
486  solver_type = BFGS;
487  else if( solver == std::string("bfgs2") )
488  solver_type = BFGS2;
489  else if( solver == std::string("steepest_decent") )
490  solver_type = STEEPEST_DESCENT;
491  else if( solver == std::string("nelder_mead") )
492  solver_type = NELDER_MEAD;
493  else if( solver == std::string("nelder_mead2") )
494  solver_type = NELDER_MEAD2;
495  else if( solver == std::string("nelder_mead2_rand") )
496  solver_type = NELDER_MEAD2_RAND;
497  else
498  {
499  if( m_objectiveFunction.domainSet().env().fullRank() == 0 )
500  {
501  std::cerr << "Error: Invalid GslOptimizer solver name: " << solver << std::endl
502  << " Valids choices are: fletcher_reeves_cg" << std::endl
503  << " polak_ribiere_cg" << std::endl
504  << " bfgs" << std::endl
505  << " bfgs2" << std::endl
506  << " steepest_decent" << std::endl
507  << " nelder_mead" << std::endl
508  << " nelder_mead2" << std::endl
509  << " nelder_mead2_rand" << std::endl;
510  }
511  queso_error();
512  }
513 
514  return solver_type;
515  }
516 
517  void GslOptimizer::set_solver_type( std::string& solver )
518  {
519  this->set_solver_type( this->string_to_enum(solver) );
520  }
521 
522 } // End namespace QUESO
GslOptimizer(const BaseScalarFunction< GslVector, GslMatrix > &objectiveFunction)
Constructs an object that will maximize a scalar function.
Definition: GslOptimizer.C:159
void append(std::vector< double > &x_min, double objective, double norm)
Add current value of minimizer, objective, and error norm.
double m_line_tol
Line minimization tolerance in gradient-based algorithms.
Definition: GslOptimizer.h:134
void set_step_size(const GslVector &step_size)
Sets step size used in gradient-free solvers.
Definition: GslOptimizer.C:467
virtual ~GslOptimizer()
Destructor.
Definition: GslOptimizer.C:179
SolverType m_solver_type
Definition: GslOptimizer.h:125
A base class for handling optimisation of scalar functions.
Definition: GslOptimizer.h:50
int dim
Definition: ann2fig.cpp:81
void setInitialPoint(const GslVector &intialPoint)
Set the point at which the optimization starts.
Definition: GslOptimizer.C:219
const BaseScalarFunction< GslVector, GslMatrix > & m_objectiveFunction
Definition: GslOptimizer.h:120
const BaseScalarFunction< GslVector, GslMatrix > & objectiveFunction() const
Returns the objective function.
Definition: GslOptimizer.C:213
A templated (base) class for handling scalar functions.
virtual void minimize(OptimizerMonitor *monitor=NULL)
Minimize the objective function, starting at m_initialPoint.
Definition: GslOptimizer.C:184
bool solver_needs_gradient(SolverType solver)
Helper function.
Definition: GslOptimizer.C:237
void minimize_with_gradient(unsigned int dim, OptimizerMonitor *monitor)
Definition: GslOptimizer.C:268
void cwSet(double value)
Component-wise sets all values to this with value.
Definition: GslVector.C:326
A base class for handling optimisation of scalar functions.
Definition: Optimizer.h:44
double m_fdfstep_size
For use in gradient-based algorithms.
Definition: GslOptimizer.h:131
GslVector * m_initialPoint
Definition: GslOptimizer.h:122
unsigned int getMaxIterations() const
Returns the maximum number of iterations the optimizer will do.
Definition: Optimizer.C:41
GslVector * m_minimizer
Definition: GslOptimizer.h:123
#define queso_error()
Definition: asserts.h:53
double getTolerance() const
Returns the tolerance used to test for an extremum in the optimizer.
Definition: Optimizer.C:47
void set_solver_type(SolverType solver)
Definition: GslOptimizer.C:232
unsigned int sizeLocal() const
Returns the length of this vector.
Definition: GslVector.C:240
void minimize_no_gradient(unsigned int dim, OptimizerMonitor *monitor)
Definition: GslOptimizer.C:365
const GslVector & minimizer() const
Return the point that minimizes the objective function.
Definition: GslOptimizer.C:227
void c_evaluate_derivative(const gsl_vector *x, void *context, gsl_vector *derivative)
Definition: GslOptimizer.C:69
void c_evaluate_with_derivative(const gsl_vector *x, void *context, double *f, gsl_vector *derivative)
Definition: GslOptimizer.C:151
double getFiniteDifferenceStepSize() const
Returns the step size used in the finite difference formula.
Definition: Optimizer.C:53
Class for vector operations using GSL library.
Definition: GslVector.h:49
GslVector m_fstep_size
For use in gradient-free algorithms.
Definition: GslOptimizer.h:128
Object to monitor convergence of optimizers.
double c_evaluate(const gsl_vector *x, void *context)
Definition: GslOptimizer.C:43
SolverType string_to_enum(std::string &solver)
Definition: GslOptimizer.C:477

Generated on Fri Jun 17 2016 14:17:40 for queso-0.55.0 by  doxygen 1.8.5