MAST
function_evaluation.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2019 Manav Bhatia
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but 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 this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 // C++ includes
21 #include <sys/stat.h>
22 #include <string>
23 #include <boost/algorithm/string.hpp>
24 
25 // MAST includes
27 
28 // libMesh includes
29 #include "libmesh/parallel_implementation.h"
30 
31 
32 void
34 
35  libmesh_assert(!_optimization_interface);
36 
38 }
39 
40 
41 void
43  const std::vector<Real> &x,
44  Real obj,
45  const std::vector<Real> &fval,
46  bool if_write_to_optim_file) {
47 
48  libmesh_assert_equal_to(x.size(), _n_vars);
49  libmesh_assert_equal_to(fval.size(), _n_eq + _n_ineq);
50 
51 
52  libMesh::out
53  << " *************************** " << std::endl
54  << " *** Optimization Output *** " << std::endl
55  << " *************************** " << std::endl
56  << std::endl
57  << "Iter: " << std::setw(10) << iter << std::endl
58  << "Nvars: " << std::setw(10) << x.size() << std::endl
59  << "Ncons-Equality: " << std::setw(10) << _n_eq << std::endl
60  << "Ncons-Inquality: " << std::setw(10) << _n_ineq << std::endl
61  << std::endl
62  << "Obj = " << std::setw(20) << obj << std::endl
63  << std::endl
64  << "Vars: " << std::endl;
65 
66  for (unsigned int i=0; i<_n_vars; i++)
67  libMesh::out
68  << "x [ " << std::setw(10) << i << " ] = "
69  << std::setw(20) << x[i] << std::endl;
70 
71  if (_n_eq) {
72  libMesh::out << std::endl
73  << "Equality Constraints: " << std::endl;
74 
75  for (unsigned int i=0; i<_n_eq; i++)
76  libMesh::out
77  << "feq [ " << std::setw(10) << i << " ] = "
78  << std::setw(20) << fval[i] << std::endl;
79  }
80 
81  if (_n_ineq) {
82  libMesh::out << std::endl
83  << "Inequality Constraints: " << std::endl;
84  unsigned int
85  n_active = 0,
86  n_violated = 0,
87  max_constr_id = 0;
88  Real
89  max_constr = -1.e20;
90 
91  for (unsigned int i=0; i<_n_ineq; i++) {
92  libMesh::out
93  << "fineq [ " << std::setw(10) << i << " ] = "
94  << std::setw(20) << fval[i+_n_eq];
95  if (fabs(fval[i+_n_eq]) <= _tol) {
96  n_active++;
97  libMesh::out << " ***";
98  }
99  else if (fval[i+_n_eq] > _tol) {
100  n_violated++;
101  libMesh::out << " +++";
102  }
103  libMesh::out << std::endl;
104 
105  if (max_constr < fval[i+_n_eq]) {
106  max_constr_id = i;
107  max_constr = fval[i+_n_eq];
108  }
109  }
110 
111  libMesh::out << std::endl
112  << std::setw(35) << " N Active Constraints: "
113  << std::setw(20) << n_active << std::endl
114  << std::setw(35) << " N Violated Constraints: "
115  << std::setw(20) << n_violated << std::endl
116  << std::setw(35) << " Most critical constraint: "
117  << std::setw(20) << max_constr << std::endl;
118  }
119 
120  libMesh::out << std::endl
121  << " *************************** " << std::endl;
122 
123 
124  // the next section writes to the optimization file.
125  if (!if_write_to_optim_file ||
126  !_output) // or if the output has not been specified
127  return;
128 
129  {
130  // write header for the first iteration
131  if (iter == 0) {
132 
133  // number of desing variables
134  *_output
135  << std::setw(10) << "n_dv" << std::setw(10) << _n_vars << std::endl;
136  *_output
137  << std::setw(10) << "n_eq" << std::setw(10) << _n_eq << std::endl;
138  *_output
139  << std::setw(10) << "n_ineq" << std::setw(10) << _n_ineq << std::endl;
140 
141  *_output << std::setw(10) << "Iter";
142  for (unsigned int i=0; i < x.size(); i++) {
143  std::stringstream x; x << "x_" << i;
144  *_output << std::setw(20) << x.str();
145  }
146  *_output << std::setw(20) << "Obj";
147  for (unsigned int i=0; i<fval.size(); i++) {
148  std::stringstream f; f << "f_" << i;
149  *_output << std::setw(20) << f.str();
150  }
151  *_output << std::endl;
152  }
153 
154  *_output << std::setw(10) << iter;
155  for (unsigned int i=0; i < x.size(); i++)
156  *_output << std::setw(20) << x[i];
157  *_output << std::setw(20) << obj;
158  for (unsigned int i=0; i < fval.size(); i++)
159  *_output << std::setw(20) << fval[i];
160  *_output << std::endl;
161  }
162 }
163 
164 
165 
166 void
168  const unsigned int iter,
169  std::vector<Real> &x) {
170 
171  struct stat stat_info;
172  int stat_result = stat(nm.c_str(), &stat_info);
173 
174  if (stat_result != 0)
175  libmesh_error_msg("File does not exist: " + nm);
176 
177  if (!std::ifstream(nm))
178  libmesh_error_msg("File missing: " + nm);
179 
180  std::ifstream input;
181  input.open(nm, std::ofstream::in);
182 
183 
184  std::string
185  line;
186  unsigned int
187  ndv = 0,
188  nineq = 0,
189  neq = 0,
190  it_num = 0;
191 
192  std::vector<std::string> results;
193 
194  // number of desing variables
195  std::getline(input, line);
196  boost::trim(line);
197  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
198  libmesh_assert_equal_to(results[0], "n_dv");
199  ndv = stod(results[1]);
200  libmesh_assert_equal_to( ndv, x.size());
201 
202 
203  // number of equality constraint
204  std::getline(input, line);
205  boost::trim(line);
206  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
207  libmesh_assert_equal_to(results[0], "n_eq");
208  neq = stod(results[1]);
209  libmesh_assert_equal_to( neq, _n_eq);
210 
211 
212  // number of inequality constriants
213  std::getline(input, line);
214  boost::trim(line);
215  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
216  libmesh_assert_equal_to(results[0], "n_ineq");
217  nineq = stod(results[1]);
218  //libmesh_assert_equal_to( nineq, _n_ineq);
219 
220 
221  // skip all lines before iter.
222  while (!input.eof() && it_num < iter+1) {
223  std::getline(input, line);
224  it_num++;
225  }
226 
227  // make sure that the iteration number is what we are looking for
228  std::getline(input, line);
229  boost::trim(line);
230  boost::split(results, line, boost::is_any_of(" \t"), boost::token_compress_on);
231 
232  libmesh_assert_greater(results.size(), ndv+1);
233 
234  it_num = stoi(results[0]);
235  libmesh_assert_equal_to(it_num, iter);
236 
237  // make sure that the file has data
238  for (unsigned int i=0; i<ndv; i++)
239  x[i] = stod(results[i+1]);
240 }
241 
242 
243 bool
244 MAST::FunctionEvaluation::verify_gradients(const std::vector<Real>& dvars) {
245 
246 
247  // first call theh evaluate method to get the analytical sensitivities
248  Real
249  delta = 1.e-5,
250  tol = 1.e-3,
251  obj = 0.,
252  obj_fd_p = 0., // at x+h
253  obj_fd_m = 0.; // at x-h
254 
255  bool
256  eval_obj_grad = true;
257 
258 
259  std::vector<Real>
260  dvars_fd (dvars),
261  obj_grad (_n_vars),
262  obj_grad_fd(_n_vars),
263  fvals (_n_ineq + _n_eq),
264  fvals_fd_p (_n_ineq + _n_eq), // at x+h
265  fvals_fd_m (_n_ineq + _n_eq), // at x-h
266  grads (_n_vars*(_n_ineq + _n_eq)),
267  grads_fd (_n_vars*(_n_ineq + _n_eq));
268 
269 
270  std::vector<bool>
271  eval_grads (_n_eq+_n_ineq);
272 
273 
274  std::fill( dvars_fd.begin(), dvars_fd.end(), 0.);
275  std::fill( obj_grad.begin(), obj_grad.end(), 0.);
276  std::fill( obj_grad_fd.begin(), obj_grad_fd.end(), 0.);
277  std::fill( fvals.begin(), fvals.end(), 0.);
278  std::fill( fvals_fd_p.begin(), fvals_fd_p.end(), 0.);
279  std::fill( fvals_fd_m.begin(), fvals_fd_m.end(), 0.);
280  std::fill( grads.begin(), grads.end(), 0.);
281  std::fill( grads_fd.begin(), grads_fd.end(), 0.);
282  std::fill( eval_grads.begin(), eval_grads.end(), true);
283 
284 
285  // calculate the analytical sensitivity
286  this->evaluate(dvars,
287  obj,
288  eval_obj_grad,
289  obj_grad,
290  fvals,
291  eval_grads,
292  grads);
293 
294 
295  // now turn off the sensitivity variables
296  eval_obj_grad = false;
297  std::fill( eval_grads.begin(), eval_grads.end(), false);
298 
299  // now iteratve over the design variables, and calculate the finite
300  // difference sensitivity values
301 
302  for (unsigned int i=0; i<_n_vars; i++) {
303 
304  // copy the original vector
305  dvars_fd = dvars;
306 
307  // central difference approx
308  // du/dx = ((u2-u1)/(x2-x1) + (u1-u0)/(x1-x0))/2
309  // = ((u2-u1)/h + (u1-u0)/h)/2
310  // = (u2-u0)/2h
311 
312  // now perturb it: first the positive value
313  dvars_fd[i] += delta;
314 
315  // call the evaluate routine
316  obj_fd_p = 0.;
317  obj_fd_m = 0.;
318  std::fill( fvals_fd_p.begin(), fvals_fd_p.end(), 0.);
319  std::fill( fvals_fd_m.begin(), fvals_fd_m.end(), 0.);
320  this->evaluate(dvars_fd,
321  obj_fd_p,
322  eval_obj_grad,
323  obj_grad_fd,
324  fvals_fd_p,
325  eval_grads,
326  grads_fd);
327 
328  // now perturb it: first the positive value
329  dvars_fd[i] -= 2*delta;
330 
331  this->evaluate(dvars_fd,
332  obj_fd_m,
333  eval_obj_grad,
334  obj_grad_fd,
335  fvals_fd_m,
336  eval_grads,
337  grads_fd);
338 
339  // objective gradient
340  obj_grad_fd[i] = (obj_fd_p-obj_fd_m)/2./delta;
341 
342  // constraint gradient
343  for (unsigned int j=0; j<_n_eq+_n_ineq; j++)
344  grads_fd[i*(_n_eq+_n_ineq)+j] = (fvals_fd_p[j]-fvals_fd_m[j])/2./delta;
345  }
346 
347 
348  // compare the values
349  libMesh::out
350  << " *** Objective function gradients: analytical vs numerical"
351  << std::endl;
352 
353  bool accurate_sens = true;
354 
355  libMesh::out
356  << std::setw(10) << "DV"
357  << std::setw(30) << "Analytical"
358  << std::setw(30) << "Numerical" << std::endl;
359 
360  for (unsigned int i=0; i<_n_vars; i++) {
361  libMesh::out
362  << std::setw(10) << i
363  << std::setw(30) << obj_grad[i]
364  << std::setw(30) << obj_grad_fd[i];
365  if (fabs((obj_grad[i] - obj_grad_fd[i])/obj_grad[i]) > tol) {
366  libMesh::out << " : Mismatched sensitivity";
367  accurate_sens = false;
368  }
369  libMesh::out << std::endl;
370  }
371 
372 
373 
374  libMesh::out
375  << " *** Constraint function gradients: analytical vs numerical"
376  << std::endl;
377 
378  libMesh::out
379  << std::setw(10) << "DV"
380  << std::setw(30) << "Analytical"
381  << std::setw(30) << "Numerical" << std::endl;
382 
383  for (unsigned int j=0; j<_n_eq+_n_ineq; j++) {
384 
385  libMesh::out << " Constraint: " << j << std::endl;
386  for (unsigned int i=0; i<_n_vars; i++) {
387  libMesh::out
388  << std::setw(10) << i
389  << std::setw(30) << grads[i*(_n_eq+_n_ineq)+j]
390  << std::setw(30) << grads_fd[i*(_n_eq+_n_ineq)+j];
391  if (fabs((grads[i*(_n_eq+_n_ineq)+j] - grads_fd[i*(_n_eq+_n_ineq)+j])/grads[i*(_n_eq+_n_ineq)+j]) > tol) {
392  libMesh::out << " : Mismatched sensitivity";
393  accurate_sens = false;
394  }
395  libMesh::out << std::endl;
396  }
397  }
398  // print the message that all sensitivity data satisfied limits.
399  if (accurate_sens)
400  libMesh::out
401  << "Verify gradients: all gradients satisfied relative tol: " << tol
402  << " with delta: " << delta
403  << std::endl;
404 
405  return accurate_sens;
406 }
407 
408 
409 void
411  const unsigned int iter1,
412  const unsigned int iter2,
413  unsigned int divs) {
414 
415  std::vector<Real>
416  dv1(_n_vars, 0.),
417  dv2(_n_vars, 0.),
418  dv (_n_vars, 0.),
419  fval(_n_ineq+_n_eq, 0.);
420 
421  this->initialize_dv_from_output_file(nm, iter1, dv1);
422  this->initialize_dv_from_output_file(nm, iter2, dv2);
423 
424  Real
425  f = 0.,
426  obj = 0.;
427 
428  for (unsigned int i=0; i<=divs; i++) {
429 
430  f = (1.*i)/(1.*divs);
431  for (unsigned int j=0; j<_n_vars; j++) {
432  dv[j] = (1.-f) * dv1[j] + f * dv2[j];
433  }
434 
435  this->_output_wrapper(i, dv, obj, fval, true);
436  }
437 }
438 
439 
440 
441 void
443 
444  unsigned int
445  N = this->n_vars(),
446  N_EQ = this->n_eq(),
447  N_INEQ = this->n_ineq(),
448  n_rel_change_iters = this->n_iters_relative_change();
449 
450  // make sure all processors have the same values
451  libmesh_assert(this->comm().verify(N));
452  libmesh_assert(this->comm().verify(N_EQ));
453  libmesh_assert(this->comm().verify(N_INEQ));
454  libmesh_assert(this->comm().verify(n_rel_change_iters));
455 }
456 
457 
458 
459 void
461  std::vector<Real>& xmin,
462  std::vector<Real>& xmax) {
463 
464  this->init_dvar(x, xmin, xmax);
465 
466  libmesh_assert(this->comm().verify(x));
467  libmesh_assert(this->comm().verify(xmin));
468  libmesh_assert(this->comm().verify(xmax));
469 }
470 
471 
472 
473 void
474 MAST::FunctionEvaluation::_evaluate_wrapper(const std::vector<Real>& dvars,
475  Real& obj,
476  bool eval_obj_grad,
477  std::vector<Real>& obj_grad,
478  std::vector<Real>& fvals,
479  std::vector<bool>& eval_grads,
480  std::vector<Real>& grads) {
481 
482  // verify that all values going into the function are consistent
483  // across all processors
484  libmesh_assert(this->comm().verify(dvars));
485  libmesh_assert(this->comm().verify(eval_obj_grad));
486 
487  this->evaluate(dvars,
488  obj,
489  eval_obj_grad,
490  obj_grad,
491  fvals,
492  eval_grads,
493  grads);
494 
495  // verify that all output values coming out of all functions are
496  // consistent across all processors
497  libmesh_assert(this->comm().verify(obj));
498  libmesh_assert(this->comm().verify(obj_grad));
499  libmesh_assert(this->comm().verify(fvals));
500  libmesh_assert(this->comm().verify(grads));
501 }
502 
503 
504 
505 void
507  const std::vector<Real>& x,
508  Real obj,
509  const std::vector<Real>& fval,
510  bool if_write_to_optim_file) {
511 
512  // verify that all values going into the function are consistent
513  // across all processors
514  libmesh_assert(this->comm().verify(iter));
515  libmesh_assert(this->comm().verify(x));
516 
517  this->output(iter, x, obj, fval, if_write_to_optim_file);
518 }
519 
virtual void output(unsigned int iter, const std::vector< Real > &x, Real obj, const std::vector< Real > &fval, bool if_write_to_optim_file)
outputs the the current iterate to libMesh::out, and to the output file if it was set for this rank...
virtual void parametric_line_study(const std::string &nm, const unsigned int iter1, const unsigned int iter2, unsigned int divs)
computes a parametric evaluation along a line from iter1 to iter2 in file nm with divs runs between t...
void sanitize_parallel()
make sure that the analysis is setup consistently across all parallel processes
virtual void init_dvar(std::vector< Real > &x, std::vector< Real > &xmin, std::vector< Real > &xmax)=0
libMesh::Real Real
MAST::OptimizationInterface * _optimization_interface
virtual void evaluate(const std::vector< Real > &dvars, Real &obj, bool eval_obj_grad, std::vector< Real > &obj_grad, std::vector< Real > &fvals, std::vector< bool > &eval_grads, std::vector< Real > &grads)=0
grads(k): Derivative of f_i(x) with respect to x_j, where k = (j-1)*M + i.
unsigned int n_ineq() const
virtual void _evaluate_wrapper(const std::vector< Real > &dvars, Real &obj, bool eval_obj_grad, std::vector< Real > &obj_grad, std::vector< Real > &fvals, std::vector< bool > &eval_grads, std::vector< Real > &grads)
This serves as a wrapper around evaluate() and makes sure that the derived class&#39;s implementation is ...
void attach_optimization_interface(MAST::OptimizationInterface &opt)
Provides the basic interface API for classes the provide implement optimization problems.
unsigned int n_vars() const
void initialize_dv_from_output_file(const std::string &nm, const unsigned int iter, std::vector< Real > &x)
This reads and initializes the DV vector from a previous optimization history output file...
virtual void _output_wrapper(unsigned int iter, const std::vector< Real > &x, Real obj, const std::vector< Real > &fval, bool if_write_to_optim_file)
This serves as a wrapper around evaluate() and makes sure that the derived class&#39;s implementation is ...
virtual void _init_dvar_wrapper(std::vector< Real > &x, std::vector< Real > &xmin, std::vector< Real > &xmax)
This serves as a wrapper around init_dvar() and makes sure that the derived class&#39;s implementation pr...
unsigned int n_eq() const
unsigned int n_iters_relative_change() const
virtual bool verify_gradients(const std::vector< Real > &dvars)
verifies the gradients at the specified design point