BonNlpHeuristic.cpp
Go to the documentation of this file.
1 /* $Id: BonNlpHeuristic.cpp 1145 2015-04-30 09:00:40Z stefan $ */
2 // (C) Copyright International Business Machines Corporation 2007
3 // All Rights Reserved.
4 // This code is published under the Eclipse Public License (EPL).
5 //
6 // Authors :
7 // Pierre Bonami, International Business Machines Corporation
8 // Pietro Belotti, Lehigh University
9 //
10 // Date : 04/09/2007
11 
12 #include "CouenneCutGenerator.hpp"
13 
14 #include "BonCouenneInterface.hpp"
15 #include "CouenneObject.hpp"
16 #include "CouenneProblem.hpp"
17 #include "CbcCutGenerator.hpp"
18 //#include "CbcBranchActual.hpp"
19 #include "BonAuxInfos.hpp"
20 #include "CoinHelperFunctions.hpp"
22 #include "BonNlpHeuristic.hpp"
23 #include "CouenneRecordBestSol.hpp"
24 
25 using namespace Ipopt;
26 using namespace Couenne;
27 
28 NlpSolveHeuristic::NlpSolveHeuristic():
29  CbcHeuristic(),
30  nlp_(NULL),
31  hasCloned_(false),
32  maxNlpInf_(maxNlpInf_0),
33  numberSolvePerLevel_(-1),
34  couenne_(NULL){
35  setHeuristicName("NlpSolveHeuristic");
36 }
37 
38 NlpSolveHeuristic::NlpSolveHeuristic(CbcModel & model, Bonmin::OsiTMINLPInterface &nlp, bool cloneNlp, CouenneProblem * couenne):
39  CbcHeuristic(model), nlp_(&nlp), hasCloned_(cloneNlp),maxNlpInf_(maxNlpInf_0),
40  numberSolvePerLevel_(-1),
41  couenne_(couenne){
42  setHeuristicName("NlpSolveHeuristic");
43  if(cloneNlp)
44  nlp_ = dynamic_cast <Bonmin::OsiTMINLPInterface *> (nlp.clone());
45  }
46 
48  CbcHeuristic(other), nlp_(other.nlp_),
49  hasCloned_(other.hasCloned_),
50  maxNlpInf_(other.maxNlpInf_),
51  numberSolvePerLevel_(other.numberSolvePerLevel_),
52  couenne_(other.couenne_){
53  if(hasCloned_ && nlp_ != NULL)
54  nlp_ = dynamic_cast <Bonmin::OsiTMINLPInterface *> (other.nlp_->clone());
55 }
56 
57 CbcHeuristic *
59  return new NlpSolveHeuristic(*this);
60 }
61 
64  if(this != &rhs){
65  CbcHeuristic::operator=(rhs);
66  if(hasCloned_ && nlp_)
67  delete nlp_;
68 
69  hasCloned_ = rhs.hasCloned_;
70  if(nlp_ != NULL){
71  if(hasCloned_)
72  nlp_ = dynamic_cast <Bonmin::OsiTMINLPInterface *> (rhs.nlp_->clone());
73  else
74  nlp_ = rhs.nlp_;
75  }
76  }
77  maxNlpInf_ = rhs.maxNlpInf_;
79  couenne_ = rhs.couenne_;
80  return *this;
81 }
82 
84  if(hasCloned_)
85  delete nlp_;
86  nlp_ = NULL;
87 }
88 
89 void
91  if(hasCloned_ && nlp_ != NULL)
92  delete nlp_;
93  hasCloned_ = cloneNlp;
94  if(cloneNlp)
95  nlp_ = dynamic_cast <Bonmin::OsiTMINLPInterface *> (nlp.clone());
96  else
97  nlp_ = &nlp;
98 }
99 
100 void
102 {couenne_ = couenne;}
103 
104 
105 int
106 NlpSolveHeuristic::solution (double & objectiveValue, double * newSolution) {
107 
108  int noSolution = 1, maxTime = 2;
109 
110  // do heuristic the usual way, but if for any reason (time is up, no
111  // better solution found) there is no improvement, get the best
112  // solution from the GlobalCutOff object in the pointer to the
113  // CouenneProblem and return it instead.
114  //
115  // Although this should be handled by Cbc, very often this doesn't
116  // happen.
117 
118  // int nodeDepth = -1;
119 
120  const int depth = (model_ -> currentNode ()) ? model_ -> currentNode () -> depth () : 0;
121 
122  if (depth <= 0)
123  couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "NLP Heuristic: "); fflush (stdout);
124 
125  try {
126 
127  if (CoinCpuTime () > couenne_ -> getMaxCpuTime ())
128  throw maxTime;
129 
130  OsiSolverInterface * solver = model_ -> solver();
131 
132  OsiAuxInfo * auxInfo = solver->getAuxiliaryInfo();
133  Bonmin::BabInfo * babInfo = dynamic_cast <Bonmin::BabInfo *> (auxInfo);
134 
135  if(babInfo){
136  babInfo->setHasNlpSolution(false);
137  if(babInfo->infeasibleNode()){
138  throw noSolution;
139  }
140  }
141 
142  // if too deep in the BB tree, only run NLP heuristic if
143  // feasibility is low
144  bool too_deep = false;
145 
146  // check depth
147  if (numberSolvePerLevel_ > -1) {
148 
149  if (numberSolvePerLevel_ == 0)
150  throw maxTime;
151 
152  //if (CoinDrand48 () > pow (2., numberSolvePerLevel_ - depth))
153  if (CoinDrand48 () > 1. / CoinMax
154  (1., (double) ((depth - numberSolvePerLevel_) *
155  (depth - numberSolvePerLevel_))))
156  too_deep = true;
157  }
158 
159  if (too_deep)
160  throw maxTime;
161 
162  double *lower = new double [couenne_ -> nVars ()];
163  double *upper = new double [couenne_ -> nVars ()];
164 
165  CoinFillN (lower, couenne_ -> nVars (), -COUENNE_INFINITY);
166  CoinFillN (upper, couenne_ -> nVars (), COUENNE_INFINITY);
167 
168  CoinCopyN (solver->getColLower(), nlp_ -> getNumCols (), lower);
169  CoinCopyN (solver->getColUpper(), nlp_ -> getNumCols (), upper);
170 
171  /*printf ("-- int candidate, before: ");
172  for (int i=0; i<couenne_ -> nOrig (); i++)
173  printf ("[%g %g] ", lower [i], upper [i]);
174  printf ("\n");*/
175 
176  const double * solution = solver->getColSolution();
177  OsiBranchingInformation info (solver, true);
178  const int & numberObjects = model_->numberObjects();
179  OsiObject ** objects = model_->objects();
180  double maxInfeasibility = 0;
181 
182  bool haveRoundedIntVars = false;
183 
184  for (int i = 0 ; i < numberObjects ; i++) {
185 
186  CouenneObject * couObj = dynamic_cast <CouenneObject *> (objects [i]);
187 
188  if (couObj) {
189  if (too_deep) { // only test infeasibility if BB level is high
190  int dummy;
191  double infeas;
192  maxInfeasibility = CoinMax ( maxInfeasibility, infeas = couObj->infeasibility(&info, dummy));
193 
194  if (maxInfeasibility > maxNlpInf_){
195  delete [] lower;
196  delete [] upper;
197  throw noSolution;
198  }
199  }
200  } else {
201 
202  OsiSimpleInteger * intObj = dynamic_cast<OsiSimpleInteger *>(objects[i]);
203 
204  if (intObj) {
205  const int & i = intObj -> columnNumber ();
206  // Round the variable in the solver
207  double value = solution [i];
208  if (value < lower[i])
209  value = lower[i];
210  else if (value > upper[i])
211  value = upper[i];
212 
213  double rounded = floor (value + 0.5);
214 
215  if (fabs (value - rounded) > COUENNE_EPS) {
216  haveRoundedIntVars = true;
217  //value = rounded;
218  }
219 
220  // fix bounds anyway, if a better candidate is not found
221  // below at least we have an integer point
222  //lower[i] = upper[i] = value;
223  }
224  else{
225 
226  // Probably a SOS object -- do not stop here
227  //throw CoinError("Bonmin::NlpSolveHeuristic","solution",
228  //"Unknown object.");
229  }
230  }
231  }
232 
233  // if here, it means the infeasibility is not too high. Generate a
234  // better integer point as there are rounded integer variables
235 
236  bool skipOnInfeasibility = false;
237 
238  double *Y = new double [couenne_ -> nVars ()];
239  CoinFillN (Y, couenne_ -> nVars (), 0.);
240  CoinCopyN (solution, nlp_ -> getNumCols (), Y);
241 
242  /*printf ("-- int candidate, upon call: ");
243  for (int i=0; i<couenne_ -> nOrig (); i++)
244  if (couenne_ -> Var (i) -> isInteger ())
245  printf ("[%g <%g> %g] ", lower [i], Y [i], upper [i]);
246  else printf ("%g ", Y [i]);
247  printf ("\n");*/
248 
249  if (haveRoundedIntVars) // create "good" integer candidate for Ipopt
250  skipOnInfeasibility = (couenne_ -> getIntegerCandidate (solution, Y, lower, upper) < 0);
251 
252  /*printf ("-- int candidate, after: ");
253  for (int i=0; i<couenne_ -> nOrig (); i++)
254  if (couenne_ -> Var (i) -> isInteger ())
255  printf ("[%g <%g> %g] ", lower [i], Y [i], upper [i]);
256  else printf ("%g ", Y [i]);
257  printf ("\n");*/
258 
259  bool foundSolution = false;
260 
261  if (haveRoundedIntVars && skipOnInfeasibility)
262  // no integer initial point could be found, make up some random rounding
263 
264  for (int i = couenne_ -> nOrigVars (); i--;)
265 
266  if (couenne_ -> Var (i) -> isDefinedInteger ())
267  lower [i] = upper [i] = Y [i] =
268  (CoinDrand48 () < 0.5) ?
269  floor (Y [i] + COUENNE_EPS) :
270  ceil (Y [i] - COUENNE_EPS);
271 
272  else if (lower [i] > upper [i]) {
273 
274  // sanity check (should avoid problems in ex1263 with
275  // couenne.opt.obbt)
276 
277  double swap = lower [i];
278  lower [i] = upper [i];
279  upper [i] = swap;
280  }
281 
282  {
283  // printf ("[%g <%g> %g] ", lower [i], Y [i], upper [i]);
284 
285  /*printf ("int candidate: ");
286  for (int i=0; i<couenne_ -> nOrig (); i++)
287  if (couenne_ -> Var (i) -> isInteger ())
288  printf ("[%g <%g> %g] ", lower [i], Y [i], upper [i]);
289  else printf ("%g ", Y [i]);
290  printf ("\n");*/
291 
292  // Now set column bounds and solve the NLP with starting point
293  double * saveColLower = CoinCopyOfArray (nlp_ -> getColLower (), nlp_ -> getNumCols ());
294  double * saveColUpper = CoinCopyOfArray (nlp_ -> getColUpper (), nlp_ -> getNumCols ());
295 
296  for (int i = nlp_ -> getNumCols (); i--;) {
297 
298  if (lower [i] > upper [i]) {
299  double swap = lower [i];
300  lower [i] = upper [i];
301  upper [i] = swap;
302  }
303 
304  if (Y [i] < lower [i]) Y [i] = lower [i];
305  else if (Y [i] > upper [i]) Y [i] = upper [i];
306  }
307 
308  nlp_ -> setColLower (lower);
309  nlp_ -> setColUpper (upper);
310  nlp_ -> setColSolution (Y);
311 
312  // apply NLP solver /////////////////////////////////
313  try {
314  nlp_ -> options () -> SetNumericValue ("max_cpu_time", CoinMax (0.1, couenne_ -> getMaxCpuTime () - CoinCpuTime ()));
315  nlp_ -> initialSolve ();
316  }
318 
319  double obj = (nlp_ -> isProvenOptimal()) ? nlp_ -> getObjValue (): COIN_DBL_MAX;
320 
321  if (nlp_ -> isProvenOptimal () &&
322  couenne_ -> checkNLP (nlp_ -> getColSolution (), obj, true) && // true for recomputing obj
323  (obj < couenne_ -> getCutOff ())) {
324 
325  // store solution in Aux info
326 
327  const int nVars = solver->getNumCols();
328  double* tmpSolution = new double [nVars];
329  CoinCopyN (nlp_ -> getColSolution(), nlp_ -> getNumCols(), tmpSolution);
330 
331  //Get correct values for all auxiliary variables
332  CouenneInterface * couenne = dynamic_cast <CouenneInterface *> (nlp_);
333 
334  if (couenne)
335  couenne_ -> getAuxs (tmpSolution);
336 
337 #ifdef FM_CHECKNLP2
338  if(!couenne_->checkNLP2(tmpSolution,
339  0, false, // do not care about obj
340  true, // stopAtFirstViol
341  false, // checkAll
342  couenne_->getFeasTol())) {
343 #ifdef FM_USE_REL_VIOL_CONS
344  printf("NlpSolveHeuristic::solution(): ### ERROR: checkNLP(): returns true, checkNLP2() returns false\n");
345  exit(1);
346 #endif
347  }
350 #else
351  couenne_->getRecordBestSol()->update(tmpSolution, nVars,
352  obj, couenne_->getFeasTol());
353 #endif
354 
355  if (babInfo){
356  babInfo->setNlpSolution (tmpSolution, nVars, obj);
357  babInfo->setHasNlpSolution (true);
358  }
359 
360  if (obj < objectiveValue) { // found better solution?
361 
362  const CouNumber
363  *lb = solver -> getColLower (),
364  *ub = solver -> getColUpper ();
365 
366  // check bounds once more after getAux. This avoids false
367  // asserts in CbcModel.cpp:8305 on integerTolerance violated
368  for (int i=0; i < nVars; i++, lb++, ub++) {
369 
370  CouNumber &t = tmpSolution [i];
371  if (t < *lb) t = *lb;
372  else if (t > *ub) t = *ub;
373  }
374 
375  //printf ("new cutoff %g from BonNlpHeuristic\n", obj);
376  couenne_ -> setCutOff (obj);
377  foundSolution = true;
378  objectiveValue = obj;
379  CoinCopyN (tmpSolution, nVars, newSolution);
380  }
381  delete [] tmpSolution;
382  }
383 
384  nlp_ -> setColLower (saveColLower);
385  nlp_ -> setColUpper (saveColUpper);
386 
387  delete [] saveColLower;
388  delete [] saveColUpper;
389  }
390 
391  delete [] Y;
392 
393  delete [] lower;
394  delete [] upper;
395 
396  if (depth <= 0) {
397 
398  if (foundSolution) couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "solution found, obj. %g\n", objectiveValue);
399  else couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "no solution.\n");
400  }
401 
402  return foundSolution;
403 
404  }
405  catch (int &e) {
406 
407  // forget about using the global cutoff. That has to trickle up to
408  // Cbc some other way
409 
410  if (e==noSolution) {if (depth <= 0) couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "no solution.\n"); return 0;}
411  else if (e==maxTime) {if (depth <= 0) couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "time limit reached.\n"); return 0;}
412  else {if (depth <= 0) couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "solution found, obj. %g\n", objectiveValue); return 1;}
413 
414  // // no solution available? Use the one from the global cutoff
415  // if ((couenne_ -> getCutOff () < objectiveValue) &&
416  // couenne_ -> getCutOffSol ()) {
417  // objectiveValue = couenne_ -> getCutOff ();
418  // CoinCopyN (couenne_ -> getCutOffSol (), couenne_ -> nVars (), newSolution);
419  // if (depth <= 0)
420  // couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "solution found, obj. %g\n", objectiveValue);
421  // return 1;
422  // } else {
423  // if (depth <= 0 && e==noSolution)
424  // couenne_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "no solution.\n", objectiveValue);
425  // return 0;
426  // }
427  }
428 }
429 
430 
433 
434  roptions -> AddStringOption2
435  ("local_optimization_heuristic",
436  "Search for local solutions of MINLPs",
437  "yes",
438  "no","",
439  "yes","",
440  "If enabled, a heuristic based on Ipopt is used to find feasible solutions for the problem. "
441  "It is highly recommended that this option is left enabled, as it would be difficult to find feasible solutions otherwise.");
442 
443  roptions -> AddLowerBoundedIntegerOption
444  ("log_num_local_optimization_per_level",
445  "Specify the logarithm of the number of local optimizations to perform"
446  " on average for each level of given depth of the tree.",
447  -1,
448  2, "Solve as many nlp's at the nodes for each level of the tree. "
449  "Nodes are randomly selected. If for a "
450  "given level there are less nodes than this number nlp are solved for every nodes. "
451  "For example if parameter is 8, nlp's are solved for all node until level 8, "
452  "then for half the node at level 9, 1/4 at level 10.... "
453  "Value -1 specify to perform at all nodes.");
454 }
void setCouenneProblem(CouenneProblem *)
set the couenne problem to use.
NlpSolveHeuristic()
Default constructor.
void update(const double *givenSol, const int givenCard, const double givenVal, const double givenMaxViol)
void fint fint fint real fint real real real real real real real real real fint real fint fint fint real fint fint fint fint * info
OsiObject for auxiliary variables $w=f(x)$.
void setNlp(Bonmin::OsiTMINLPInterface &nlp, bool cloneNlp=true)
Set the nlp solver.
This is class provides an Osi interface for a Mixed Integer Linear Program expressed as a TMINLP (so ...
virtual int solution(double &objectiveValue, double *newSolution)
Run heuristic, return 1 if a better solution than the one passed is found and 0 otherwise.
OsiSolverInterface * clone(bool copyData=true) const
Virtual copy constructor.
bool checkNLP2(const double *solution, const double obj, const bool careAboutObj, const bool stopAtFirstViol, const bool checkAll, const double precision) const
Return true if either solution or recomputed_solution obtained using getAuxs() from the original vari...
Definition: checkNLP.cpp:571
const double maxNlpInf_0
A heuristic to call an NlpSolver if all CouenneObjects are close to be satisfied (for other integer o...
virtual int getNumCols() const
Get number of columns.
virtual CbcHeuristic * clone() const
Clone.
NlpSolveHeuristic & operator=(const NlpSolveHeuristic &rhs)
Assignment operator.
Bonmin::OsiTMINLPInterface * nlp_
Pointer to an nlp solver interface.
void fint fint fint real fint real real real real real real real real real * e
CouenneRecordBestSol * getRecordBestSol() const
returns recorded best solution
int numberSolvePerLevel_
Number of nlp&#39;s solved for each given level of the tree.
bool hasCloned_
is nlp_ cloned or just a pointer?
double maxNlpInf_
maximum nlp infeasibility under which try to solve problem with Ipopt.
Class for MINLP problems with symbolic information.
#define COUENNE_EPS
CouenneProblem * couenne_
Pointer to a couenne representation of the problem.
double CouNumber
main number type in Couenne
bool infeasibleNode()
Say if current node is found feasible by cut generators.
Definition: BonAuxInfos.hpp:49
#define COUENNE_INFINITY
We will throw this error when a problem is not solved.
double getFeasTol()
returns feasibility tolerance
void setNlpSolution(const double *sol, int numcols, double objValue)
Pass a solution found by an nlp solver.
Definition: BonAuxInfos.cpp:78
const Ipopt::EJournalCategory J_COUENNE(Ipopt::J_USER8)
virtual double infeasibility(const OsiBranchingInformation *info, int &way) const
compute infeasibility of this variable, |w - f(x)| (where w is the auxiliary variable defined as w = ...
void setHasNlpSolution(bool b)
Say if has an nlp solution.
Definition: BonAuxInfos.hpp:68
static char * t
Definition: OSdtoa.cpp:3645
static void registerOptions(Ipopt::SmartPtr< Bonmin::RegisteredOptions >)
initialize options
Bonmin class for passing info between components of branch-and-cuts.
Definition: BonBabInfos.hpp:19
virtual ~NlpSolveHeuristic()
Destructor.