BonHeuristicFPump.cpp
Go to the documentation of this file.
1 // Copyright (C) 2007, International Business Machines Corporation and others.
2 // All Rights Reserved.
3 // This code is published under the Eclipse Public License.
4 //
5 // Authors :
6 // Joao P. Goncalves, International Business Machines Corporation
7 //
8 // Date : November 12, 2007
9 
10 #include "BonHeuristicFPump.hpp"
11 #include "CoinHelperFunctions.hpp"
12 #include "CbcModel.hpp"
13 
14 #include "OsiAuxInfo.hpp"
15 
16 #include "CoinTime.hpp"
17 
18 #include <fstream>
19 
20 #include <iomanip>
21 
22 using namespace std;
23 
24 //#define DEBUG_BON_HEURISTIC_FPUMP
25 
26 namespace Bonmin
27 {
28  class score_sorter {
29  public:
32  score_(score) {}
33 
34  bool operator() (const int x, const int y) const {
35  return score_[x]>score_[y];
36  }
37 
38  private:
40  };
41 
42 
43  HeuristicFPump::HeuristicFPump()
44  :
45  CbcHeuristic(),
46  setup_(NULL),
47  objective_norm_(1),
48  enableAdvanced_(false)
49  {}
50 
52  :
53  CbcHeuristic(),
54  setup_(setup),
55  objective_norm_(1),
56  enableAdvanced_(false)
57  {
58  Initialize(setup->options());
59  }
60 
62  :
63  CbcHeuristic(copy),
64  setup_(copy.setup_),
65  objective_norm_(copy.objective_norm_),
66  enableAdvanced_(copy.enableAdvanced_)
67  {}
68 
71  {
72  if(this != &rhs) {
73  CbcHeuristic::operator=(rhs);
74  setup_ = rhs.setup_;
77  }
78  return *this;
79  }
80 
81  int
82  HeuristicFPump::solution(double &solutionValue, double *betterSolution)
83  {
84  if(model_->getNodeCount() || model_->getCurrentPassNumber() > 1) return 0;
85 
86  bool integerSolutionAlreadyExists = false;
87  if(model_->getSolutionCount()) {
88  // bestSolutionValue = model_->getObjValue();
89  integerSolutionAlreadyExists = true;
90  if(!enableAdvanced_)
91  return 0;
92  assert(solutionValue < 1.0e50);
93  }
94 
95  const int maxNumberIterations = 200;
96  const double toleranceObjectiveFP = 1.0e-5;
97 
98  int returnCode = 0; // 0 means it didn't find a feasible solution
99 
100  OsiTMINLPInterface * nlp = NULL;
101  if(setup_->getAlgorithm() == B_BB)
102  nlp = dynamic_cast<OsiTMINLPInterface *>(model_->solver()->clone());
103  else
104  nlp = dynamic_cast<OsiTMINLPInterface *>(setup_->nonlinearSolver()->clone());
105 
106  TMINLP2TNLP* minlp = nlp->problem();
107 
108  // set tolerances
109  double integerTolerance = model_->getDblParam(CbcModel::CbcIntegerTolerance);
110  double primalTolerance;
111 #if 0
112  OsiSolverInterface * solver = model_->solver();
113  solver->getDblParam(OsiPrimalTolerance,primalTolerance);
114 #endif
115  primalTolerance=1.0e-6;
116 
117  int numberColumns;
118  int numberRows;
119  int nnz_jac_g;
120  int nnz_h_lag;
121  Ipopt::TNLP::IndexStyleEnum index_style;
122  minlp->get_nlp_info(numberColumns, numberRows, nnz_jac_g,
123  nnz_h_lag, index_style);
124 
125  const Bonmin::TMINLP::VariableType* variableType = minlp->var_types();
126  const double* x_sol = minlp->x_sol();
127  const double* x_l = minlp->x_l();
128  const double* x_u = minlp->x_u();
129 
130 #ifdef DEBUG_BON_HEURISTIC_FPUMP
131  const double* g_sol = minlp->g_sol();
132  const double* g_l = minlp->g_l();
133  const double* g_u = minlp->g_u();
134  // print bounds_info
135  for(int i=0; i<numberColumns; i++)
136  cout<<"x_l["<<i<<"]= "<<x_l[i]<<" "
137  <<"x_sol["<<i<<"]= "<<x_sol[i]<<" "
138  <<"x_u["<<i<<"]= "<<x_u[i]<<" "
139  <<"variableType = "<<variableType[i]<<endl;
140  for(int i=0; i<numberRows; i++)
141  cout<<"g_l["<<i<<"]= "<<g_l[i]<<" "
142  <<"g_sol["<<i<<"]= "<<g_sol[i]<<" "
143  <<"g_u["<<i<<"]= "<<g_u[i]<<endl;
144 
145  cout<<"obj_value = "<<minlp->obj_value()<<endl;
146 
147  cout<<"optimization_status = "<<minlp->optimization_status()<<endl;
148 #endif
149 
150  // exit if the current NLP solution is infeasible
151  // infeasibility is determined by the NLP solver
152  if(minlp->optimization_status() != Ipopt::SUCCESS){
153  delete nlp;
154  return returnCode;
155  }
156 
157  // Get solution array for heuristic solution
158  double* newSolution = new double [numberColumns];
159  memcpy(newSolution,x_sol,numberColumns*sizeof(double));
160  double* new_g_sol = new double [numberRows];
161 
162  // create a set with the indices of the fractional variables
163  vector<int> integerColumns; // stores the integer variables
164  int numberFractionalVariables = 0;
165  for (int iColumn=0;iColumn<numberColumns;iColumn++) {
166  if (variableType[iColumn] != Bonmin::TMINLP::CONTINUOUS) {
167  integerColumns.push_back(iColumn);
168  double value=newSolution[iColumn];
169  if (fabs(floor(value+0.5)-value)>integerTolerance) {
170  numberFractionalVariables++;
171  }
172  }
173  }
174  int numberIntegerColumns = (int) integerColumns.size();
175 
176  // create space to store old solutions in order to prevent cycling
177  const int numberOldSolutionsStored = 4;
178  double ** oldSolution = new double * [numberOldSolutionsStored];
179  for (int j=0;j<numberOldSolutionsStored;j++) {
180  oldSolution[j]= new double[numberIntegerColumns];
181  for (int i=0;i<numberIntegerColumns;i++)
182  oldSolution[j][i]=-COIN_DBL_MAX;
183  }
184 
185  RoundingFPump roundObj(minlp);
186 
187  //bool stopDueToAlmostZeroObjective = false;
188  double* x_bar = new double[numberIntegerColumns];
189  int* indexes_x_bar = new int[numberIntegerColumns];
190  double* copy_newSolution = new double[numberColumns];
191  int iteration = 0;
192  while(numberFractionalVariables) {
193  iteration++;
194  if(iteration > maxNumberIterations) {
195  break;
196  }
197  memcpy(copy_newSolution, newSolution, numberColumns*sizeof(double));
198  roundObj.round(integerTolerance, primalTolerance, copy_newSolution);
199  bool flip = true;
200  for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
201  int iColumn = integerColumns[iIntCol];
202  double value=copy_newSolution[iColumn];
203 #if 0
204  double value=newSolution[iColumn];
205  if (fabs(floor(value+0.5)-value)>integerTolerance) {
206  value = floor(value+0.5);
207  // make sure that the new value is within bounds
208  if(value < x_l[iColumn]-primalTolerance)
209  value++;
210  else if(value > x_u[iColumn]+primalTolerance)
211  value--;
212  }
213 #endif
214  x_bar[iIntCol]=value;
215  indexes_x_bar[iIntCol]=iColumn;
216  if(flip &&
217  fabs(x_bar[iIntCol]-oldSolution[0][iIntCol])>integerTolerance)
218  flip = false;
219  }
220 
221 #ifdef DEBUG_BON_HEURISTIC_FPUMP
222  cout<<"iteration= "<<iteration<<", flip= "<<flip<<endl;
223 #endif
224 
225  // flip some of the integer variables if the rounded solution is the
226  // same as the previous one
227  if(flip) {
228  vector<int> sortedIntegerColumns(numberIntegerColumns);
229  vector<double> score(numberIntegerColumns);
230  for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
231  int iColumn = integerColumns[iIntCol];
232  sortedIntegerColumns[iIntCol] = iIntCol;
233  double value=newSolution[iColumn];
234  score[iIntCol] = fabs(value-oldSolution[0][iIntCol]);
235  }
236  sort(sortedIntegerColumns.begin(),sortedIntegerColumns.end(),
237  score_sorter(score));
238 
239  int maxNumberToMove = 1;
240  int numberMoved = 0;
241  for(int i=0; i<numberIntegerColumns; i++) {
242  int iIntCol = sortedIntegerColumns[i];
243  if(score[iIntCol] > 0.00) {
244  int iColumn = integerColumns[iIntCol];
245  double value=newSolution[iColumn];
246  if(value-oldSolution[0][iIntCol]>0.0)
247  value = oldSolution[0][iIntCol]+1.0;
248  else
249  value = oldSolution[0][iIntCol]-1.0;
250  // make sure that the new value is within bounds
251  if(value < x_l[iColumn]-primalTolerance)
252  value++;
253  else if(value > x_u[iColumn]+primalTolerance)
254  value--;
255  assert(fabs(floor(value+0.5)-value)<=integerTolerance);
256  x_bar[iIntCol]=value;
257  numberMoved++;
258  } else
259  break;
260  if(numberMoved >= maxNumberToMove)
261  break;
262  }
263 
264  // check for loop.
265  // see if the new rounded solution is equal to an old solution.
266  // If yes, then perturb the new rounded solution
267  bool matched;
268  for (int k = numberOldSolutionsStored-1; k > 0; k--) {
269  double * b = oldSolution[k];
270  matched = true;
271  for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
272  if (fabs(x_bar[iIntCol]-b[iIntCol])>integerTolerance) {
273  matched=false;
274  break;
275  }
276  }
277  if (matched) break;
278  }
279 
280 #ifdef DEBUG_BON_HEURISTIC_FPUMP
281  cout<<"matched= "<<matched<<endl;
282 #endif
283 
284  if (matched) {
285  // perturbation
286  for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
287  int iColumn = integerColumns[iIntCol];
288  double value=newSolution[iColumn];
289  double random = max(0.0,CoinDrand48()-0.3);
290  double difference = fabs(value-oldSolution[0][iIntCol]);
291  if(difference+random>0.5) {
292  if(value-oldSolution[0][iIntCol]>0.0)
293  value = oldSolution[0][iIntCol]+1.0;
294  else
295  value = oldSolution[0][iIntCol]-1.0;
296  // make sure that the new value is within bounds
297  if(value < x_l[iColumn]-primalTolerance)
298  value++;
299  else if(value > x_u[iColumn]+primalTolerance)
300  value--;
301  assert(fabs(floor(value+0.5)-value)<=integerTolerance);
302  } else {
303  // this variable is not going to be perturbed
304  value = oldSolution[0][iIntCol];
305  }
306  x_bar[iIntCol]=value;
307  }
308  }
309  }
310  // store the new solution and remove the oldest one
311  for (int j=numberOldSolutionsStored-1;j>0;j--) {
312  for (int i = 0; i < numberIntegerColumns; i++)
313  oldSolution[j][i]=oldSolution[j-1][i];
314  }
315  for (int j = 0; j < numberIntegerColumns; j++)
316  oldSolution[0][j] = x_bar[j];
317 
318 
319  // solve the NLP problem
320  double obj_nlp;
321  if(integerSolutionAlreadyExists)
322  // use cutoff constraint
323  obj_nlp = nlp->solveFeasibilityProblem(numberIntegerColumns,
324  x_bar,indexes_x_bar,
325  objective_norm_, solutionValue);
326  else
327  obj_nlp = nlp->solveFeasibilityProblem(numberIntegerColumns,
328  x_bar,indexes_x_bar,
329  1,0,objective_norm_);
330 
331 
332 #ifdef DEBUG_BON_HEURISTIC_FPUMP
333  cout<<"obj_nlp= "<<obj_nlp<<endl;
334 #endif
335 
336  memcpy(newSolution,x_sol,numberColumns*sizeof(double));
337 
338  if(obj_nlp < toleranceObjectiveFP) {
339  //stopDueToAlmostZeroObjective = true;
340  break;
341  }
342 
343  // compute number of fractional variables
344  numberFractionalVariables = 0;
345  for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
346  int iColumn = integerColumns[iIntCol];
347  double value=newSolution[iColumn];
348  if (fabs(floor(value+0.5)-value)>integerTolerance)
349  numberFractionalVariables++;
350  }
351 
352  }
353 
354  for (int j=0;j<numberOldSolutionsStored;j++)
355  delete [] oldSolution[j];
356  delete [] oldSolution;
357  delete [] x_bar;
358  delete [] indexes_x_bar;
359 
360 
361  // fix the integer variables and solve the NLP
362  for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
363  int iColumn = integerColumns[iIntCol];
364  double value=floor(newSolution[iColumn]+0.5);
365  minlp->SetVariableUpperBound(iColumn, floor(value));
366  minlp->SetVariableLowerBound(iColumn, ceil(value));
367  }
368  nlp->initialSolve();
369  bool feasible = true;
370  if(minlp->optimization_status() != Ipopt::SUCCESS) {
371  feasible = false;
372  // if(stopDueToAlmostZeroObjective)
373  // returnCode = 8;
374  }
375  memcpy(newSolution,x_sol,numberColumns*sizeof(double));
376 
377  if(feasible) {
378  double newSolutionValue;
379  minlp->eval_f(numberColumns, newSolution, true, newSolutionValue);
380  if(newSolutionValue < solutionValue) {
381  memcpy(betterSolution,newSolution,numberColumns*sizeof(double));
382  solutionValue = newSolutionValue;
383  returnCode = 1;
384  }
385  }
386 
387 #ifdef DEBUG_BON_HEURISTIC_FPUMP
388  cout<<"returnCode= "<<returnCode<<endl;
389 #endif
390 
391 #if 0
392  delete [] indexRow;
393  delete [] indexCol;
394  delete [] row;
395  delete [] columnStart;
396  delete [] columnLength;
397 #endif
398  delete [] newSolution;
399  delete [] new_g_sol;
400  delete [] copy_newSolution;
401  delete nlp;
402 
403  return returnCode;
404  }
405 
406  void
408  roptions->SetRegisteringCategory("Primal Heuristics", RegisteredOptions::BonminCategory);
409  roptions->AddBoundedIntegerOption("feasibility_pump_objective_norm","Norm of feasibility pump objective function",
410  1, 2, 1,"");
411  roptions->setOptionExtraInfo("feasibility_pump_objective_norm", 63);
412  roptions->AddStringOption2("heuristic_feasibility_pump", "whether the heuristic feasibility pump should be used",
413  "no", "no", "", "yes", "", "");
414  roptions->setOptionExtraInfo("heuristic_feasibility_pump", 63);
415 
416  roptions->SetRegisteringCategory("Primal Heuristics (undocumented)", RegisteredOptions::UndocumentedCategory);
417  roptions->AddStringOption2("unstable_fp","use at your own risks",
418  "no",
419  "no", "",
420  "yes", "","");
421  roptions->setOptionExtraInfo("unstable_fp", 63);
422  }
423 
424  void
426  options->GetIntegerValue("feasibility_pump_objective_norm", objective_norm_, "bonmin.");
427  options->GetEnumValue("unstable_fp", enableAdvanced_, "bonmin.");
428  }
429 
431  :
432  minlp_(minlp)
433  {
435  }
436 
438  {
439  delete [] col_and_jac_g_;
440  }
441 
442  void
444  {
445 
446  int nnz_jac_g;
447  int nnz_h_lag;
448  Ipopt::TNLP::IndexStyleEnum index_style;
450  nnz_h_lag, index_style);
451 
452  const double* x_sol = minlp_->x_sol();
453 
454  // Get the indicies of the jacobian
455  // This is also a way of knowing which variables are
456  // used in each row
457  int* indexRow = new int[nnz_jac_g];
458  int* indexCol = new int[nnz_jac_g];
459  minlp_->eval_jac_g(numberColumns_, x_sol, false,
460  numberRows_, nnz_jac_g,
461  indexRow, indexCol, 0);
462 
463  // get the jacobian for the solution with zeros
464  double* jac_g = new double [nnz_jac_g];
465  double* zero_sol = new double [numberColumns_];
466  minlp_->get_starting_point(numberColumns_, 1, zero_sol, 0, NULL, NULL, numberRows_, 0, NULL);
467  //memset(zero_sol, 0, numberColumns_ * sizeof(double));
468  minlp_->eval_jac_g(numberColumns_, zero_sol, true,
469  numberRows_, nnz_jac_g,
470  0, 0, jac_g);
471 
473 
474  int indexCorrection = (index_style == Ipopt::TNLP::C_STYLE) ? 0 : 1;
475  for(int i=0; i<nnz_jac_g; i++) {
476  int thisIndexRow = indexRow[i]-indexCorrection;
477  int thisIndexCol = indexCol[i]-indexCorrection;
478  pair<int, int> value(thisIndexCol, static_cast<int>(jac_g[i]));
479  col_and_jac_g_[thisIndexRow].push_back(value);
480  }
481 
482  delete [] indexRow;
483  delete [] indexCol;
484  delete [] jac_g;
485  delete [] zero_sol;
486  }
487 
488  void
489  RoundingFPump::round(const double integerTolerance,
490  const double primalTolerance,
491  double* solution)
492  {
493  const Bonmin::TMINLP::VariableType* variableType = minlp_->var_types();
494  const double* x_l = minlp_->x_l();
495  const double* x_u = minlp_->x_u();
496  const double* g_l = minlp_->g_l();
497  const double* g_u = minlp_->g_u();
498 
499 
500  for(int iRow=0; iRow<numberRows_; iRow++) {
501  if(g_l[iRow] == 1.0 && g_u[iRow] == 1.0) {
502  bool sosConstraint = true;
503  double weightedSum = 0.0;
504  int counter = 1;
505  vector<pair<int, int> > jac_g = col_and_jac_g_[iRow];
506  for (unsigned int j=0; j<jac_g.size(); j++) {
507  int iColumn = jac_g[j].first;
508  if (solution[iColumn]>=1.0-integerTolerance ||
509  jac_g[j].second != 1.0 ||
510  variableType[iColumn] == Bonmin::TMINLP::CONTINUOUS) {
511  sosConstraint = false;
512  break;
513  }
514  else {
515  weightedSum += counter * solution[iColumn];
516  counter++;
517  }
518  }
519 #ifdef DEBUG_BON_HEURISTIC_FPUMP
520  if(sosConstraint) {
521  cout<<"weightedSum= "<<weightedSum
522  <<", numberColumns_= "<<numberColumns_<<endl;
523  }
524 #endif
525 
526  if(sosConstraint) {
527  double fl = floor(weightedSum + 0.5);
528  int indexColumnSelected = static_cast<int>(fl) - 1;
529  if(indexColumnSelected < 0){//Looks like all variables have been fixed to 0
530  continue;
531  }
532  assert(indexColumnSelected < jac_g.size());
533  for (size_t j=0; j<jac_g.size(); j++) {
534  int iColumn = jac_g[j].first;
535  if((int)j == indexColumnSelected)
536  solution[iColumn] = 1.0;
537  else
538  solution[iColumn] = 0.0;
539  }
540  }
541  }
542  }
543 
544  for(int iColumn=0; iColumn<numberColumns_; iColumn++) {
545  if(variableType[iColumn] != Bonmin::TMINLP::CONTINUOUS) {
546  double value=solution[iColumn];
547  if (fabs(floor(value+0.5)-value)>integerTolerance) {
548  value = floor(value+0.5);
549  // make sure that the new value is within bounds
550  if(value < x_l[iColumn]-primalTolerance)
551  value++;
552  else if(value > x_u[iColumn]+primalTolerance)
553  value--;
554  solution[iColumn] = value;
555  }
556  }
557  }
558  }
559 }
int numberRows_
Number of rows.
std::vector< std::pair< int, int > > * col_and_jac_g_
Jacobian of g.
score_sorter(const vector< double > &score)
Constructor.
const TMINLP2TNLP * problem() const
get pointer to the TMINLP2TNLP adapter
This is class provides an Osi interface for a Mixed Integer Linear Program expressed as a TMINLP (so ...
OsiSolverInterface * clone(bool copyData=true) const
Virtual copy constructor.
void SetVariableLowerBound(Ipopt::Index var_no, Ipopt::Number x_l)
Change the lower bound on the variable.
const vector< double > & score_
Ipopt::SolverReturn optimization_status() const
Get Optimization status.
int enableAdvanced_
To enable advanced unstable stuff.
void SetVariableUpperBound(Ipopt::Index var_no, Ipopt::Number x_u)
Change the upper bound on the variable.
virtual void initialSolve()
Solve initial continuous relaxation.
HeuristicFPump()
Default constructor.
static char * j
Definition: OSdtoa.cpp:3622
int objective_norm_
Norm of the objective function - either 1 or 2.
Ipopt::Number obj_value() const
Get the objective value.
double solveFeasibilityProblem(size_t n, const double *x_bar, const int *ind, double a, double s, int L)
Given a point x_bar this solves the problem of finding the point which minimize a convex combination ...
Bonmin::Algorithm getAlgorithm()
Get the algorithm used.
const Ipopt::Number * x_l()
Get the current values for the lower bounds.
int numberColumns_
Number of columns.
const Ipopt::Number * g_sol() const
get the g solution (activities)
void round(const double integerTolerance, const double primalTolerance, double *solution)
Rounds the solution.
TMINLP2TNLP * minlp_
Pointer to problem.
HeuristicFPump & operator=(const HeuristicFPump &rhs)
Assignment operator.
const Ipopt::Number * x_u()
Get the current values for the upper bounds.
void fint fint * k
static void registerOptions(Ipopt::SmartPtr< Bonmin::RegisteredOptions > roptions)
Register the options for this heuristic.
static int
Definition: OSdtoa.cpp:2173
const Ipopt::Number * g_l()
Get the current values for constraints lower bounds.
void Initialize(Ipopt::SmartPtr< Ipopt::OptionsList > options)
Initiaize using passed options.
void gutsOfConstructor()
gutsOfConstructor
const Ipopt::Number * x_sol() const
get the solution values
OsiTMINLPInterface * nonlinearSolver()
Pointer to the non-linear solver used.
Ipopt::SmartPtr< Ipopt::OptionsList > options()
Acces list of Options.
virtual int solution(double &solutionValue, double *betterSolution)
Performs heuristic.
virtual bool eval_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number &obj_value)
Returns the value of the objective function in x.
This is an adapter class that converts a TMINLP to a TNLP to be solved by Ipopt.
virtual bool get_nlp_info(Ipopt::Index &n, Ipopt::Index &m, Ipopt::Index &nnz_jac_g, Ipopt::Index &nnz_h_lag, TNLP::IndexStyleEnum &index_style)
This call is just passed onto the TMINLP object.
return b
Definition: OSdtoa.cpp:1719
const Ipopt::Number * g_u()
Get the current values for constraints upper bounds.
VariableType
Type of the variables.
Definition: BonTMINLP.hpp:192
BonminSetup * setup_
Setup to use for local searches (will make copies).
const TMINLP::VariableType * var_types()
Get the variable types.
virtual bool eval_jac_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values)
Returns the jacobian of the constraints.
virtual bool get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number *x, bool init_z, Ipopt::Number *z_L, Ipopt::Number *z_U, Ipopt::Index m, bool init_lambda, Ipopt::Number *lambda)
Method called by Ipopt to get the starting point.
void fint fint fint real fint real * x
RoundingFPump(TMINLP2TNLP *minlp)
Default constructor.