optimize_gsl.cpp
Go to the documentation of this file.
1 // Copyright 2019, University of Maryland and the MANGO development team.
2 //
3 // This file is part of MANGO.
4 //
5 // MANGO is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License as
7 // published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // MANGO is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with MANGO. If not, see
17 // <https://www.gnu.org/licenses/>.
18 
19 #include <iostream>
20 #include <cassert>
21 #include <stdexcept>
22 #include "mango.hpp"
23 #include "Solver.hpp"
24 #include "Package_gsl.hpp"
25 
26 #ifdef MANGO_GSL_AVAILABLE
27 #include <gsl/gsl_vector.h>
28 #include <gsl/gsl_matrix.h>
29 #include <gsl/gsl_multimin.h>
30 #endif
31 
33 #ifdef MANGO_GSL_AVAILABLE
34  if (solver->verbose>0) std::cout << "Hello from optimize_gsl" << std::endl;
35 
36  // Set initial condition
37  gsl_vector *gsl_state_vector = gsl_vector_alloc(solver->N_parameters);
38  for (int j=0; j<solver->N_parameters; j++) gsl_vector_set(gsl_state_vector, j, solver->state_vector[j]); // Could be faster
39 
40  int iterations = 0;
41 
42  if (algorithms[solver->algorithm].uses_derivatives) {
43  ///////////////////////////////////////////////////////////////////////////////
44  // Derivative-based algorithms:
45  ///////////////////////////////////////////////////////////////////////////////
46 
47  const gsl_multimin_fdfminimizer_type *Tfdf;
48  switch (solver->algorithm) {
49  case GSL_CONJUGATE_FR:
50  Tfdf = gsl_multimin_fdfminimizer_conjugate_fr;
51  break;
52  case GSL_CONJUGATE_PR:
53  Tfdf = gsl_multimin_fdfminimizer_conjugate_pr;
54  break;
55  case GSL_BFGS:
56  // The GSL documentation indicates that gsl_multimin_fdfminimizer_bfgs2 supercedes gsl_multimin_fdfminimizer_bfgs.
57  Tfdf = gsl_multimin_fdfminimizer_vector_bfgs2;
58  break;
59  default:
60  throw std::runtime_error("Error in optimize_gsl.cpp switch 1! Should not get here!");
61  }
62 
63  gsl_multimin_fdfminimizer* fdfminimizer = gsl_multimin_fdfminimizer_alloc(Tfdf, solver->N_parameters);
64  gsl_multimin_function_fdf fdf_parameters;
65  fdf_parameters.f = &mango::Package_gsl::gsl_objective_function;
66  fdf_parameters.df = &mango::Package_gsl::gsl_gradient;
67  fdf_parameters.fdf = &mango::Package_gsl::gsl_objective_function_and_gradient;
68  fdf_parameters.n = solver->N_parameters;
69  fdf_parameters.params = (void*)solver;
70 
71  double step_size = 0.01;
72  double line_search_tolerance = 0.1;
73  gsl_multimin_fdfminimizer_set(fdfminimizer, &fdf_parameters, gsl_state_vector, step_size, line_search_tolerance);
74 
75  int status;
76  do {
77  iterations++;
78  status = gsl_multimin_fdfminimizer_iterate(fdfminimizer); // Take a step.
79  if (status) break;
80  status = gsl_multimin_test_gradient (fdfminimizer->gradient, 1e-5); // Need to make this tolerance a variable
81  } while (status == GSL_CONTINUE && solver->function_evaluations < solver->max_function_evaluations);
82 
83  gsl_multimin_fdfminimizer_free(fdfminimizer);
84 
85  } else {
86  ///////////////////////////////////////////////////////////////////////////////
87  // Derivative-free algorithms
88  ///////////////////////////////////////////////////////////////////////////////
89 
90  const gsl_multimin_fminimizer_type *Tf;
91  switch (solver->algorithm) {
92  case GSL_NM:
93  Tf = gsl_multimin_fminimizer_nmsimplex2;
94  break;
95  default:
96  throw std::runtime_error("Error in optimize_gsl.cpp switch 2! Should not get here!");
97  }
98  gsl_multimin_fminimizer* fminimizer = gsl_multimin_fminimizer_alloc(Tf, solver->N_parameters);
99  gsl_multimin_function f_parameters;
100  f_parameters.f = &gsl_objective_function;
101  f_parameters.n = solver->N_parameters;
102  f_parameters.params = (void*)solver;
103 
104  // Set initial step sizes
105  gsl_vector* step_sizes;
106  step_sizes = gsl_vector_alloc(solver->N_parameters);
107  gsl_vector_set_all(step_sizes, 0.1); // I should make a smarter choice for the value here.
108 
109  gsl_multimin_fminimizer_set(fminimizer, &f_parameters, gsl_state_vector, step_sizes);
110 
111  int status;
112  double size;
113  do {
114  iterations++;
115  status = gsl_multimin_fminimizer_iterate(fminimizer);
116  if (status) break;
117  size = gsl_multimin_fminimizer_size (fminimizer);
118  status = gsl_multimin_test_size (size, 1e-6); // This tolerance should be changed into a variable.
119  } while (status == GSL_CONTINUE && solver->function_evaluations < solver->max_function_evaluations);
120 
121  gsl_vector_free(step_sizes);
122  gsl_multimin_fminimizer_free(fminimizer);
123  }
124 
125  ///////////////////////////////////////////////////////////////////////////////
126  // End of separate blocks for derivative-based vs derivative-free algorithms.
127 
128  gsl_vector_free(gsl_state_vector);
129  if (solver->verbose>0) std::cout << "Goodbye from optimize_gsl" << std::endl;
130 #else
131  throw std::runtime_error("Error! A GSL algorithm was requested, but Mango was compiled without GSL support.");
132 #endif
133 }
134 
135 //////////////////////////////////////////////////////////////////////////////
136 //////////////////////////////////////////////////////////////////////////////
137 #ifdef MANGO_GSL_AVAILABLE
138 
139 double mango::Package_gsl::gsl_objective_function(const gsl_vector * x, void *params) {
140  mango::Solver* solver = (mango::Solver*) params;
141 
142  if (solver->verbose > 0) std::cout << "Hello from gsl_objective_function." << std::endl << std::flush;
143 
144  // gsl vectors have a 'stride'. Only if the stride is 1 does the layout of a gsl vector correspond to a standard double array.
145  // Curran pointed out that the stride for f may not be 1!
146  // See https://github.com/PrincetonUniversity/STELLOPT/commit/5820c453283785ffd97e40aec261ca97f76e9071
147  assert(x->stride == 1);
148 
149  double f;
150  bool failed;
151  solver->objective_function_wrapper(x->data, &f, &failed);
152 
153  if (solver->verbose > 0) std::cout << "Goodbye from gsl_objective_function" << std::endl << std::flush;
154  if (failed) {
155  return GSL_NAN;
156  } else {
157  return f;
158  }
159 }
160 
161 //////////////////////////////////////////////////////////////////////////////
162 //////////////////////////////////////////////////////////////////////////////
163 
164 void mango::Package_gsl::gsl_objective_function_and_gradient(const gsl_vector * x, void *params, double* f, gsl_vector* gradient) {
165  mango::Solver* solver = (mango::Solver*) params;
166  if (solver->verbose > 0) std::cout << "Hello from gsl_objective_function_and_gradient" << std::endl << std::flush;
167 
168  // gsl vectors have a 'stride'. Only if the stride is 1 does the layout of a gsl vector correspond to a standard double array.
169  // Curran pointed out that the stride may not be 1!
170  // See https://github.com/PrincetonUniversity/STELLOPT/commit/5820c453283785ffd97e40aec261ca97f76e9071
171  assert(x->stride == 1);
172  assert(gradient->stride == 1);
173 
174  solver->finite_difference_gradient(x->data, f, gradient->data);
175 
176  if (solver->verbose > 0) std::cout << "Goodbye from gsl_objective_function_and_gradient" << std::endl << std::flush;
177 }
178 
179 //////////////////////////////////////////////////////////////////////////////
180 //////////////////////////////////////////////////////////////////////////////
181 
182 void mango::Package_gsl::gsl_gradient(const gsl_vector * x, void *params, gsl_vector* gradient) {
183  mango::Solver* solver = (mango::Solver*) params;
184  if (solver->verbose > 0) std::cout << "Hello from gsl_gradient" << std::endl << std::flush;
185  double f;
186  // Just call objective_function_and_gradient and throw away the objective function.
187  mango::Package_gsl::gsl_objective_function_and_gradient(x, params, &f, gradient);
188 }
189 
190 #endif
mango::GSL_CONJUGATE_PR
@ GSL_CONJUGATE_PR
Definition: mango.hpp:113
mango::Solver::finite_difference_gradient
virtual void finite_difference_gradient(const double *, double *, double *)
Definition: Solver.cpp:82
mango::Solver::verbose
int verbose
Definition: Solver.hpp:63
mango::Solver::objective_function_wrapper
virtual void objective_function_wrapper(const double *, double *, bool *)
Definition: objective_function_wrapper.cpp:27
Package_gsl.hpp
Solver.hpp
mango::Solver::state_vector
double * state_vector
Definition: Solver.hpp:58
mango::algorithms
const algorithm_properties algorithms[NUM_ALGORITHMS]
A database of the algorithms that MANGO is aware of, including various properties of each algorithm.
Definition: mango.hpp:124
mango.hpp
mango::Solver::max_function_evaluations
int max_function_evaluations
Definition: Solver.hpp:62
mango::Solver::N_parameters
int N_parameters
Definition: Solver.hpp:43
mango::Solver::algorithm
algorithm_type algorithm
Definition: Solver.hpp:42
mango::GSL_NM
@ GSL_NM
Definition: mango.hpp:115
mango::GSL_BFGS
@ GSL_BFGS
Definition: mango.hpp:114
mango::Solver
Definition: Solver.hpp:31
mango::algorithm_properties::uses_derivatives
bool uses_derivatives
Whether the algorithm requires derivatives of the objective function or residuals to be supplied.
Definition: mango.hpp:62
mango::GSL_CONJUGATE_FR
@ GSL_CONJUGATE_FR
Definition: mango.hpp:112
mango::Solver::function_evaluations
int function_evaluations
Definition: Solver.hpp:45
mango::Package_gsl::optimize
void optimize(Solver *)
Definition: optimize_gsl.cpp:32