PaGMO  1.1.5
ipopt_problem.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 <boost/numeric/conversion/cast.hpp>
26 
27 #include "ipopt_problem.h"
28 #include "../../exceptions.h"
29 
30 using namespace Ipopt;
31 
32 /* Constructor. */
33 ipopt_problem::ipopt_problem(pagmo::population *pop) : m_pop(pop)
34 {
35  //We size the various members
36  affects_obj.resize(0);
37  dv.resize(m_pop->problem().get_dimension());
38  fit.resize(m_pop->problem().get_f_dimension());
39  con.resize(m_pop->problem().get_c_dimension());
40 
41  //We need to initialize the class members len_jac, iJfun, jJvar. We do this by first
42  //storing the information in duples, ordering using the std::sort algorithm and then assigning
43  //them to iJfun, jJvar. This is done to increase the constraint cache hits
44  ::Ipopt::Index lenG;
45  std::vector< ::Ipopt::Index> iGfun,jGvar;
46  len_jac=0;
47  std::vector< boost::array< ::Ipopt::Index,2> > duples(0);
48  boost::array< ::Ipopt::Index,2> tmp;
49 
50 
51  //If the problem has its set_sparsity implemented, store the values relevant to the constraints
52  try {
53  m_pop->problem().set_sparsity(lenG,iGfun,jGvar);
54  for (::Ipopt::Index i = 0; i<lenG; ++i)
55  {
56  if (iGfun[i]!=0) //objective function gradient sparsity is not used by ipopt
57  {
58  tmp[0] = iGfun[i] - 1; //HERE WAS THE UNDEBUGGABLE!! -1 ffffffkkkkkk
59  tmp[1] = jGvar[i];
60  duples.push_back(tmp);
61  len_jac++;
62  }
63  else
64  {
65  affects_obj.push_back(jGvar[i]);
66  }
67  }
68  //We now reorder the entries so that the cache will be hit avoiding useless
69  //re-evaluations of the constraints in eval_jac_g when performing finite differences
70  std::sort (duples.begin(), duples.end(), cache_efficiency_criterion);
71  }
72  //Otherwise assume no sparsity
73  catch (not_implemented_error)
74  {
75  for (pagmo::problem::base::size_type j=0;j<m_pop->problem().get_dimension();++j)
76  {
77  affects_obj.push_back(j);
78  for (pagmo::problem::base::size_type i=0;i<m_pop->problem().get_c_dimension();++i)
79  {
80  tmp[0] = i;
81  tmp[1] = j;
82  duples.push_back(tmp);
83  len_jac++;
84  }
85  }
86  }
87 
88  //And we finally assign the ordered duples to iJfun, jJvar
89  iJfun.resize(len_jac);
90  jJvar.resize(len_jac);
91  for (::Ipopt::Index i = 0;i<len_jac;++i)
92  {
93  iJfun[i] = duples[i][0];
94  jJvar[i] = duples[i][1];
95  //std::cout << "[" << iJfun[i] << "," << jJvar[i] << "]" << std::endl;
96  }
97 }
98 
99 ipopt_problem::~ipopt_problem()
100 {}
101 
102 //This function is the sort criteria for the elements of the sparse matrix J. First the variables,
103 //then the constraint number. i.e.
104 //Before sorting:
105 //iJfun = [0,3,0,0,2,1]
106 //jJvar = [1,2,0,2,1,0]
107 //After sorting:
108 //iJfun = [0,1,0,2,0,3]
109 //jJvar = [0,0,1,1,2,2]
110 bool ipopt_problem::cache_efficiency_criterion(boost::array<int,2> one,boost::array<int,2> two)
111 {
112  if (one[1] < two[1]) {
113  return true;
114  }
115  else {
116  if (one[1] > two[1]) return false;
117  else{ //they are equal!!! look to the other element
118  if (one[0] < two[0]) return true;
119  else return false;
120  }
121  }
122 }
123 
124 
125 bool ipopt_problem::get_nlp_info(Ipopt::Index& n, Ipopt::Index& m, Ipopt::Index& nnz_jac_g,
126  Ipopt::Index& nnz_h_lag, IndexStyleEnum& index_style)
127 {
128  (void)nnz_h_lag; //hessian is evaluated numerically using BFGS
129  //Problem dimension
130  n = m_pop->problem().get_dimension();
131 
132  //Dimension of the constraint vector (equality and inequality)
133  m = m_pop->problem().get_c_dimension();
134 
135  // Number of non -zero elements in Jacobian
136  nnz_jac_g = len_jac;
137 
138  // We use the standard fortran index style for row/col entries
139  index_style = C_STYLE;
140 
141  return true;
142 }
143 
144 bool ipopt_problem::get_bounds_info(Ipopt::Index n, Ipopt::Number* x_l, Ipopt::Number* x_u,
145  Ipopt::Index m, Ipopt::Number* g_l, Ipopt::Number* g_u)
146 {
147  //Bounds on the decision vector
148  for (::Ipopt::Index i=0; i<n;++i)
149  {
150  x_l[i] = m_pop->problem().get_lb()[i];
151  x_u[i] = m_pop->problem().get_ub()[i];
152  }
153 
154  //Bounds on equality constraints
155  for (::Ipopt::Index i=0; i < boost::numeric_cast< ::Ipopt::Index>(m-m_pop->problem().get_ic_dimension());++i)
156  {
157  g_l[i] = g_u[i] = 0.0;
158  }
159 
160  //Bounds on inequality constraints
161  for (::Ipopt::Index i = ( m - m_pop->problem().get_ic_dimension()); i < m;++i)
162  {
163  g_l[i] = - 1e20;
164  g_u[i] = 0.0;
165  }
166 
167  return true;
168 }
169 
170 
171 bool ipopt_problem::get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number* x,
172  bool init_z, Ipopt::Number* z_L, Ipopt::Number* z_U,
173  Ipopt::Index m, bool init_lambda,
174  Ipopt::Number* lambda)
175 {
176  // Here, we assume we only have starting values for x, if you code
177  // your own NLP, you can provide starting values for the others if
178  // you wish.
179  pagmo_assert(init_x == true);
180  pagmo_assert(init_z == false);
181  pagmo_assert(init_lambda == false);
182  pagmo_assert(n == boost::numeric_cast<Index>(m_pop->problem().get_dimension()));
183  pagmo_assert(m == boost::numeric_cast<Index>(m_pop->problem().get_c_dimension()));
184 
185  // Shut off compiler warnings about unused variables. Cool, eh?
186  (void) z_L;
187  (void) z_U;
188  (void) lambda;
189  (void) n;
190  (void) init_x;
191  (void) init_z;
192  (void) m;
193  (void) init_lambda;
194 
195  // we initialize x as the best individual of the population
196  pagmo::population::size_type bestidx = m_pop->get_best_idx();
197  std::copy(m_pop->get_individual(bestidx).cur_x.begin(),m_pop->get_individual(bestidx).cur_x.end(),x);
198 
199  return true;
200 }
201 
202 
203 bool ipopt_problem::eval_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number& obj_value)
204 {
205  (void) new_x;
206  // return the value of the objective function
207  std::copy(x,x+n,dv.begin());
208  m_pop->problem().objfun(fit,dv);
209  obj_value = fit[0];
210  return true;
211 }
212 
213 bool ipopt_problem::eval_grad_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number* grad_f)
214 {
215  (void) new_x;
216  double central_diff;
217  const double h0=1e-8;
218  double h;
219  std::copy(x,x+n,dv.begin());
220  for (pagmo::decision_vector::size_type i=0; i<dv.size();++i)
221  {
222  grad_f[i] = 0;
223  }
224 
225  double mem;
226  for (size_t i =0;i<affects_obj.size();++i)
227  {
228  h = h0 * std::max(1.,fabs(dv[affects_obj[i]]));
229  mem = dv[affects_obj[i]];
230  dv[affects_obj[i]] += h;
231  m_pop->problem().objfun(fit,dv);
232  central_diff = fit[0];
233  dv[affects_obj[i]] -= 2*h;
234  m_pop->problem().objfun(fit,dv);
235  central_diff = (central_diff-fit[0]) / 2 / h;
236  grad_f[affects_obj[i]] = central_diff;
237  dv[affects_obj[i]] = mem;
238  }
239 
240  return true;
241 }
242 
243 bool ipopt_problem::eval_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Index m, Ipopt::Number* g)
244 {
245  (void) new_x;
246  (void) m;
247  // return the value of the constraints: g(x)
248  std::copy(x,x+n,dv.begin());
249  m_pop->problem().compute_constraints(con,dv);
250  std::copy(con.begin(),con.end(),g);
251  return true;
252 }
253 
254 bool ipopt_problem::eval_jac_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x,
255  Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index* iRow, Ipopt::Index *jCol,
256  Ipopt::Number* values)
257 {
258  (void) new_x;
259  (void) m;
260  pagmo_assert(n == boost::numeric_cast<Index>(m_pop->problem().get_dimension()));
261  pagmo_assert(m == boost::numeric_cast<Index>(m_pop->problem().get_c_dimension()));
262  if (values == NULL) {
263  // return the structure of the jacobian of the constraints
264  for (Ipopt::Index i=0;i<nele_jac;++i)
265  {
266  iRow[i] = iJfun[i];
267  jCol[i] = jJvar[i];
268  }
269  }
270  else {
271  double central_diff;
272  const double h0 = 1e-8;
273  double h;
274  double mem;
275  std::copy(x,x+n,dv.begin());
276  for (Ipopt::Index i=0;i<nele_jac;++i)
277  {
278  h = h0 * std::max(1.,fabs(dv[jJvar[i]]));
279  mem = dv[jJvar[i]];
280  dv[jJvar[i]] += h;
281  m_pop->problem().compute_constraints(con,dv);
282  central_diff = con[iJfun[i]];
283  dv[jJvar[i]] -= 2 * h;
284  m_pop->problem().compute_constraints(con,dv);
285  central_diff = (central_diff - con[iJfun[i]])/2/h;
286  values[i] = central_diff;
287  dv[jJvar[i]] = mem;
288  }
289  }
290 
291  return true;
292 }
293 
294 void ipopt_problem::finalize_solution(Ipopt::SolverReturn status,
295  Ipopt::Index n, const Ipopt::Number* x, const Ipopt::Number* z_L, const Ipopt::Number* z_U,
296  Ipopt::Index m, const Ipopt::Number* g, const Ipopt::Number* lambda,
297  Ipopt::Number obj_value,
298  const Ipopt::IpoptData* ip_data,
299  Ipopt::IpoptCalculatedQuantities* ip_cq)
300 {
301  (void) status;
302  (void) z_L;
303  (void) z_U;
304  (void) m;
305  (void) g;
306  (void) lambda;
307  (void) obj_value;
308  (void) ip_data;
309  (void) ip_cq;
310 
311  for (::Ipopt::Index i=0;i<n;i++) dv[i] = x[i];
312  int bestidx = m_pop->get_best_idx();
313  pagmo::decision_vector newx = dv;
314  std::transform(dv.begin(), dv.end(), m_pop->get_individual(bestidx).cur_x.begin(), dv.begin(),std::minus<double>());
315  for (int i=0;i<n;i++)
316  {
317  newx[i] = std::min(std::max(m_pop->problem().get_lb()[i],newx[i]),m_pop->problem().get_ub()[i]);
318  }
319  m_pop->set_x(bestidx,newx);
320  m_pop->set_v(bestidx,dv);
321 }
std::vector< double > decision_vector
Decision vector type.
Definition: types.h:40
Population class.
Definition: population.h:70
container_type::size_type size_type
Population size type.
Definition: population.h:192
decision_vector::size_type size_type
Problem's size type: the same as pagmo::decision_vector's size type.
Definition: problem/base.h:160