BonNWayChoose.cpp
Go to the documentation of this file.
1 // Copyright (C) 2006, 2008 International Business Machines
2 // Corporation and others. All Rights Reserved.
3 
4 #include <climits>
5 #include "CoinPragma.hpp"
6 #include "BonNWayChoose.hpp"
7 #include "BonNWayObject.hpp"
8 #include "CoinTime.hpp"
9 
10 #ifndef NDEBUG
11 #define ASSERTED_CAST static_cast
12 #else
13 #define ASSERTED_CAST dynamic_cast
14 #endif
15 namespace Bonmin
16 {
17 
18 
19  BonNWayChoose::BonNWayChoose(BabSetupBase &b, const OsiSolverInterface* solver):
20  OsiChooseVariable(solver),
21  br_depth_(0),
22  bounds_(),
23  unit_changes_(),
24  num_ps_costs_(),
25  num_eval_(),
26  geo_means_(0)
27  {
29  options->GetNumericValue("time_limit", time_limit_, b.prefix());
30  options->GetNumericValue("cutoff_multiplier", cutoff_multiplier_, b.prefix());
31  options->GetNumericValue("pseudocost_trust_value", pseudocost_trust_value_, b.prefix());
32  options->GetIntegerValue("strong_branch_depth", br_depth_, b.prefix());
33  options->GetIntegerValue("nway_branch_log_level", log_, b.prefix());
34  options->GetEnumValue("do_fixings", do_fixings_, b.prefix());
35  options->GetEnumValue("use_geo_means", geo_means_, b.prefix());
37  int numberObjects = solver_->numberObjects();
38  std::cout<<"Number objects "<<numberObjects<<std::endl;
39  start_time_ = CoinCpuTime();
40  OsiObject ** object = solver->objects();
41  for (int i=0;i<numberObjects;i++) {
42  BonNWayObject * nway = dynamic_cast<BonNWayObject *>(object[i]);
43  if(!nway) continue;
44  start_nway_ = i;
45  break;
46  }
47  numberObjects -= start_nway_;
48  }
49 
51  OsiChooseVariable(rhs),
52  br_depth_(rhs.br_depth_),
53  do_fixings_(rhs.do_fixings_),
54  cutoff_multiplier_(rhs.cutoff_multiplier_),
55  pseudocost_trust_value_(rhs.pseudocost_trust_value_),
56  time_limit_(rhs.time_limit_),
57  start_time_(rhs.start_time_),
58  start_nway_(rhs.start_nway_),
59  log_(rhs.log_),
60  bounds_(rhs.bounds_),
61  unit_changes_(rhs.unit_changes_),
62  num_ps_costs_(rhs.num_ps_costs_),
63  num_eval_(rhs.num_eval_),
64  geo_means_(rhs.geo_means_)
65  {
66  }
67 
70  {
71  if (this != &rhs) {
72  br_depth_ = rhs.br_depth_;
78  log_ = rhs.log_;
80  OsiChooseVariable::operator=(rhs);
81  bounds_ = rhs.bounds_;
84  num_eval_ = rhs.num_eval_;
85  geo_means_ = rhs.geo_means_;
86  }
87  return *this;
88  }
89 
90  OsiChooseVariable *
92  {
93  return new BonNWayChoose(*this);
94  }
95 
97  {
98  }
99 
100  void
102  {
103  roptions->SetRegisteringCategory("NWay Strong branching setup", RegisteredOptions::BonminCategory);
104  roptions->AddLowerBoundedIntegerOption("nway_branch_log_level",
105  "Log level for the branching on nways",
106  0,1,
107  "");
108 
109  roptions->AddLowerBoundedIntegerOption("strong_branch_depth",
110  "To which level do we perform strong-branching",
111  0,0,
112  "");
113 
114  roptions->AddLowerBoundedNumberOption("cutoff_multiplier",
115  "multiplier applied to cutoff_ for computing pseudo-cost of infeasible sub-problems",
116  1.,0,3.,
117  "");
118 
119  roptions->AddLowerBoundedNumberOption("pseudocost_trust_value",
120  "Trust pseudo cost of best nway object if it is above this value",
121  0.,0,0,
122  "");
123 
124  roptions->AddStringOption2("use_geo_means", "Use geometrical means to average pseudo-costs",
125  "yes",
126  "no", "Use artihmetical means",
127  "yes", "Use geometrical means","");
128 
129  roptions->AddStringOption4("do_fixings",
130  "Do we fix variables in strong branching?",
131  "all",
132  "none", "Don't do any.",
133  "in-tree", "Fix only variables in the tree",
134  "strong-branching", "Fix variable in strong branching only",
135  "all", "Fix whenever possible",
136  "");
137 
138 
139  }
140 
141  double
142  BonNWayChoose::compute_usefulness(int objectIndex, const OsiBranchingInformation * info) const
143  {
144  int nwayIndex = objectIndex - start_nway_;
145 
146  BonNWayObject * nway = ASSERTED_CAST<BonNWayObject *>(info->solver_->objects()[objectIndex]);
147  assert(nway);
148  size_t n = nway->numberMembers();
149  const int * vars = nway->members();
150 
151  std::vector<double> unit_changes(unit_changes_[nwayIndex]);
152  if(geo_means_){
153  for(size_t k = 0 ; k < unit_changes.size() ; k++) unit_changes[k] = (num_ps_costs_[nwayIndex][k]) ? pow(unit_changes[k], 1./(double) num_ps_costs_[nwayIndex][k]): unit_changes[k];
154  }
155  else {
156  for(size_t k = 0 ; k < unit_changes.size() ; k++) unit_changes[k] = (num_ps_costs_[nwayIndex][k]) ? unit_changes[k]/(double) num_ps_costs_[nwayIndex][k]: unit_changes[k];
157  }
158 
159 
160  double r_val = compute_usefulness(info, n, vars, bounds_[nwayIndex], unit_changes);
161  return r_val;
162  }
163 
164  double
165  BonNWayChoose::compute_usefulness(const OsiBranchingInformation * info,
166  size_t n, const int * vars,const std::vector<double> &bounds, const std::vector<double> &unit_changes) const
167  {
168  const double * solution = info->solution_;
169  double obj_val = info->objectiveValue_;
170  const double * lower = info->lower_;
171  const double * upper = info->upper_;
172  double integerTolerance = info->integerTolerance_;
173  double cutoff = info->cutoff_*cutoff_multiplier_;
174  double r_val = (geo_means_) ? 1 : 0;
175 
176  for(size_t i = 0 ; i < n ; i++){
177  int iCol = vars[i];
178  if(fabs(lower[iCol] - upper[iCol]) < integerTolerance) {
179  //r_val *= (cutoff - obj_val);
180  continue; //Variable is fixed
181  }
182  assert(lower[iCol] < upper[iCol]);
183  double residual = upper[iCol] - solution[iCol];
184  double score = std::min(cutoff - obj_val, std::max(residual*unit_changes[i],bounds[i] - obj_val));
185  if(geo_means_)
186  r_val*=score;
187  else
188  r_val += score;
189  }
190  return r_val;
191  }
192 
193  int
194  BonNWayChoose::setupList ( OsiBranchingInformation *info, bool initialize)
195  {
196  assert(initialize);
197  if (initialize) {
198  status_=-2;
199  delete [] goodSolution_;
200  bestObjectIndex_=-1;
201  goodSolution_ = NULL;
202  goodObjectiveValue_ = COIN_DBL_MAX;
203  }
204 
205 
206  numberOnList_=0;
207  numberUnsatisfied_=0;
208  int numberObjects = info->solver_->numberObjects() - start_nway_;
209  double cutoff = info->cutoff_;
210  if(info->depth_ == 0){
211  bounds_.resize(0);
212  unit_changes_.resize(0);
213  bounds_.resize(numberObjects, std::vector<double>(numberObjects,cutoff));
214  unit_changes_.resize(numberObjects, std::vector<double>(numberObjects,0));
215  num_eval_.resize(numberObjects, 0);
216  num_ps_costs_.resize(numberObjects, std::vector<int>(numberObjects,0));
217  }
218  else {
219  assert(unit_changes_.size() == numberObjects);
220  assert(bounds_.size() == unit_changes_.size());
221  }
222  //Always allow all objects on the list
223  int maximumStrong = numberObjects;
224 
225  double check = -COIN_DBL_MAX;
226  int checkIndex=0;
227  int bestPriority=COIN_INT_MAX;
228  int putOther = numberObjects;
229  int i;
230  for (i=0;i<numberObjects;i++) {
231  list_[i]=-1;
232  useful_[i]=0.0;
233  }
234 
235  OsiObject ** object = info->solver_->objects();
236  object += start_nway_;
237 
238  // Say feasible
239  bool feasible = true;
240  for ( i=0;i<numberObjects;i++) {
241  int way;
242  double value = object[i]->infeasibility(info,way);
243  if (value>0.0) {
244  numberUnsatisfied_++;
245  int priorityLevel = object[i]->priority();
246  // Better priority? Flush choices.
247  if (priorityLevel<bestPriority) {
248  for (int j=maximumStrong-1;j>=0;j--) {
249  if (list_[j]>=0) {
250  int iObject = list_[j];
251  list_[j]=-1;
252  useful_[j]=0.0;
253  list_[--putOther]=iObject;
254  }
255  }
256  bestPriority = priorityLevel;
257  check=-COIN_DBL_MAX;
258  checkIndex=0;
259  }
260  if (priorityLevel==bestPriority) {
261  // Modify value
262  //Compute usefullness
263  if(info->depth_ != 0){
264  value = compute_usefulness(i + start_nway_, info);
265  }
266 
267  if (value>check) {
268  //add to list
269  int iObject = list_[checkIndex];
270  if (iObject>=0) {
271  assert (list_[putOther-1]<0);
272  list_[--putOther]=iObject; // to end
273  }
274  list_[checkIndex]= i + start_nway_;
275  assert (checkIndex<putOther);
276  useful_[checkIndex]=value;
277  // find worst
278  check=COIN_DBL_MAX;
279  maximumStrong = CoinMin(maximumStrong,putOther);
280  for (int j=0;j<maximumStrong;j++) {
281  if (list_[j]>=0) {
282  if (useful_[j]<check) {
283  check=useful_[j];
284  checkIndex=j;
285  }
286  }
287  else {
288  check=0.0;
289  checkIndex = j;
290  break;
291  }
292  }
293  }
294  else {
295  // to end
296  assert (list_[putOther-1]<0);
297  list_[--putOther]=i + start_nway_;
298  maximumStrong = CoinMin(maximumStrong,putOther);
299  }
300  }
301  else {
302  // worse priority
303  // to end
304  assert (list_[putOther-1]<0);
305  list_[--putOther]=i + start_nway_;
306  maximumStrong = CoinMin(maximumStrong,putOther);
307  }
308  }
309  }
310 
311  // Get list
312  numberOnList_=0;
313  if (feasible) {
314  maximumStrong = CoinMin(maximumStrong,putOther);
315  for (i=0;i<maximumStrong;i++) {
316  if (list_[i]>=0) {
317  list_[numberOnList_]=list_[i];
318  useful_[numberOnList_++]=-useful_[i];
319  }
320  }
321  if (numberOnList_) {
322  // Sort
323  CoinSort_2(useful_,useful_+numberOnList_,list_);
324  // move others
325  i = numberOnList_;
326  for (;putOther<numberObjects;putOther++)
327  list_[i++]=list_[putOther];
328  assert (i==numberUnsatisfied_);
329  if (!numberStrong_)
330  numberOnList_=0;
331  }
332  }
333  else {
334  // not feasible
335  numberUnsatisfied_=-1;
336  }
337  // Get rid of any shadow prices info
338  info->defaultDual_ = -1.0; // switch off
339  delete [] info->usefulRegion_;
340  delete [] info->indexRegion_;
341 
342  return numberUnsatisfied_;
343  }
344 
345  /* Choose a variable
346  Returns -
347  -1 Node is infeasible
348  0 Normal termination - we have a candidate
349  1 All looks satisfied - no candidate
350  2 We can change the bound on a variable - but we also have a strong branching candidate
351  3 We can change the bound on a variable - but we have a non-strong branching candidate
352  4 We can change the bound on a variable - no other candidates
353  We can pick up branch from whichObject() and whichWay()
354  We can pick up a forced branch (can change bound) from whichForcedObject() and whichForcedWay()
355  If we have a solution then we can pick up from goodObjectiveValue() and goodSolution()
356  */
357  int
359  OsiSolverInterface * solver,
360  OsiBranchingInformation *info,
361  bool fixVariables)
362  {
363  if(!numberUnsatisfied_) return 1;//Node is feasible
364 
365 
366  double cutoff = info->cutoff_;
367  double obj_val = info->objectiveValue_;
368  if(info->depth_ > br_depth_ && ( (- useful_[0] > pseudocost_trust_value_ )|| num_eval_[list_[0] - start_nway_] >= 18) ){
369  const double * lower = info->lower_;
370  const double * upper = info->upper_;
371 
372 
373  // See if quick variable fixing can be done
374  int n_fixed = 0;
375  if(do_fixings_ > 1){
376  for(int i = 0 ; i < numberUnsatisfied_ ; i++){
377  int iObject = list_[i];
378  int nwayIndex = iObject - start_nway_;
379  const BonNWayObject * nway = ASSERTED_CAST<const BonNWayObject *>(solver->object(iObject));
380 
381  size_t n = nway->numberMembers();
382  const int * vars = nway->members();
383  for(size_t j = 0 ; j < n ; j++){
384  int iCol = vars[j];
385  if(upper[iCol] < lower[iCol] + 0.5) continue;//variable already fixed to lower buund
386  if(bounds_[nwayIndex][j] > cutoff){//It can be fixed
387  solver->setColUpper(iCol, lower[iCol]);
388  n_fixed ++;
389  }
390  }
391  }
392  if(n_fixed && log_ > 1)
393  printf("NWAY: Fixed %i variables\n", n_fixed);
394  }
395 
396  assert(bounds_.size() == unit_changes_.size());
397  assert(unit_changes_.size() == info->solver_->numberObjects() - start_nway_);
398  //Just take the most usefull
399  bestObjectIndex_ = list_[0];
400  bestWhichWay_ = 1;
401  OsiObject * obj = solver->objects()[bestObjectIndex_];
402  obj->setWhichWay(bestWhichWay_);
403 
404  if(log_ > 1){
405  printf("level %i branch on %i bound %g usefullness %g.\n",
406  info->depth_, bestObjectIndex_ - start_nway_, obj_val, - useful_[0]);
407  }
408  if(n_fixed) return 2;
409  return 0;
410  }
411 
412 
413  if(log_ > 0)
414  printf("Restarting strong branching loop....\n\n");
415 
416  numberStrongIterations_ = 0;
417  numberStrongDone_ = 0;
418  int numberLeft = numberOnList_;//Always do full strong at root
419  int returnCode=0;
420  bestObjectIndex_ = -1;
421  bestWhichWay_ = -1;
422  firstForcedObjectIndex_ = -1;
423  firstForcedWhichWay_ =-1;
424  double best_score = -COIN_DBL_MAX;
425  int bestPriority=0;
426 
427  int n = solver->getNumCols();
428  std::vector<double> saveLower(n);
429  std::vector<double> saveUpper(n);
430  std::copy(info->lower_, info->lower_ + n, saveLower.begin());
431  std::copy(info->upper_, info->upper_ + n, saveUpper.begin());
432 
433 
434  solver->markHotStart();
435  for (int i=0;i<numberLeft;i++) {
436  int iObject = list_[i];
437  const int objectPriority = solver->object(iObject)->priority();
438  if (objectPriority >= bestPriority){
439  bestPriority = objectPriority;
440  }
441  else break;
442  double score;
443  int r_val = doStrongBranching(solver, info, iObject, saveLower.data(),
444  saveUpper.data(), score);
445  if(r_val == -1) {
446  if(log_ > 0)
447  std::cout<<"This is Infeasible"<<std::endl;
448  returnCode = -1;
449  break;
450  }
451 
452  if(do_fixings_ > 1 && r_val == 1 && info->depth_ == 0) returnCode=2;
453  //Compute Score
454  if(log_ > 0)
455  printf("Usefullness from strong branching on %i : %g\n", iObject - start_nway_, score);
456  if(score > best_score){//We have a winner
457  best_score = score;
458  bestObjectIndex_ = iObject;
459  bestWhichWay_ = 0;
460  }
461  if (r_val==3) {
462  returnCode = 3;
463  // max time - just choose one
464  if(bestObjectIndex_ < 0){
465  bestObjectIndex_ = list_[0];
466  bestWhichWay_ = 0;
467  }
468  break;
469  }
470  }
471  solver->unmarkHotStart();
472  return returnCode;
473  }
474 
475  /* This is a utility function which does strong branching on
476  one nway object and stores the results in appropriate arrays of the class
477  and maybe more.
478  It returns -
479  -1 - branch was infeasible both ways
480  0 - nothing can be fixed
481  1 - object can be fixed (returnCriterion==0)
482  3 - time limit
483  */
484  int
485  BonNWayChoose::doStrongBranching( OsiSolverInterface * solver,
486  OsiBranchingInformation *info,
487  int objectIndex,
488  double * saveLower,
489  double * saveUpper, double & score)
490  {
491  int nwayIndex = objectIndex - start_nway_;
492  const double * lower = info->lower_;
493  const double * upper = info->upper_;
494 
495  int numberColumns = solver->getNumCols();
496  double timeStart = CoinCpuTime();
497 
498  int numberObjects = info->solver_->numberObjects();
499  const BonNWayObject * nway = ASSERTED_CAST<const BonNWayObject *>(solver->object(objectIndex));
500  assert(nway);
501  BonNWayBranchingObject * branch = ASSERTED_CAST<BonNWayBranchingObject *>(nway->createBranch(solver, info, 1));
502 
503  int branches_left = branch->numberBranchesLeft();
504  int number_branches = branch->numberBranchesLeft();
505  int n_can_be_fixed = 0;
506 
507  double big_val = cutoff_multiplier_*info->cutoff_;// For infeasibles
508  if(big_val > 1e10){ big_val = 10*info->objectiveValue_;}
509  big_val += fabs(big_val)*1e-5;
510  std::vector<double> unit_changes(numberObjects - start_nway_, -DBL_MAX);
511  //std::vector<double> unit_changes(numberObjects - start_nway_, 0);
512  while(branches_left){
513 
514  branch->branch(solver);
515  int v_br = branch->var_branched_on();
516  int s_br = branch->seq_branched_on();
517  double residual = upper[v_br] - info->solution_[v_br];
518  solver->solveFromHotStart() ;
519  numberStrongIterations_ += solver->getIterationCount();
520  numberStrongDone_++;
521 
522  double obj_val = solver->getObjValue();
523 
524  if(solver->isProvenPrimalInfeasible() ||
525  (solver->isProvenOptimal() && obj_val > info->cutoff_)){//infeasible
526  if(info->depth_ == 0){
527  bounds_[nwayIndex][s_br] = big_val;
528  }
529  unit_changes[s_br] = (big_val - info->objectiveValue_)/residual;
530  if(do_fixings_ > 1){
531  n_can_be_fixed++;
532  if(log_ > 0)
533  printf("Fixing variable %i to 0 the cutoff is %g\n", v_br, big_val);
534  saveUpper[v_br] = saveLower[v_br];
535  }
536  }
537  else{
538  if(info->depth_ == 0){
539  bounds_[nwayIndex][s_br] = obj_val;
540  }
541  unit_changes[s_br] = (obj_val - info->objectiveValue_)/residual;
542  }
543 
544  // Restore bounds
545  for (int j=0;j<numberColumns;j++) {
546  if (saveLower[j] != lower[j])
547  solver->setColLower(j,saveLower[j]);
548  if (saveUpper[j] != upper[j])
549  solver->setColUpper(j,saveUpper[j]);
550  }
551  branches_left = branch->numberBranchesLeft();
552  }
553 
554  score = compute_usefulness(info, nway->numberMembers(), nway->members(), bounds_[nwayIndex], unit_changes);
555  if(info->depth_ == 0){//At root bounds contains valid bound on obj after branching, remember
556  if(do_fixings_ == 1 || do_fixings_ == 3)
557  nway->set_bounds(bounds_[nwayIndex]);
558  for(size_t k = 0 ; k < unit_changes.size() ; k++){
559  num_ps_costs_[nwayIndex][k]=1;
560  }
561  unit_changes_[nwayIndex] = unit_changes;
562  num_eval_[nwayIndex] = 1;
563  }
564  else if (n_can_be_fixed < number_branches -1){
565  num_eval_[nwayIndex]++;
566  for(size_t k = 0 ; k < unit_changes.size() ; k++){
567  if(unit_changes[k] > 0.){
568  if(geo_means_)
569  unit_changes_[nwayIndex][k] *= unit_changes[k];
570  else
571  unit_changes_[nwayIndex][k] += unit_changes[k];
572  num_ps_costs_[nwayIndex][k]++;
573  }
574  }
575  }
576  if(n_can_be_fixed == number_branches){
577  return -1;
578  }
579  if(n_can_be_fixed){
580  return 1;
581  }
582  bool hitMaxTime = ( CoinCpuTime()-timeStart > info->timeRemaining_)
583  || ( CoinCpuTime() - start_time_ > time_limit_);
584  if (hitMaxTime) {
585  return 3;
586  }
587  return 0;
588 }
589 
590 }/* Ends Bonmin's namespace.*/
591 
double start_time_
Starting time of algorithm.
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
double time_limit_
Global time limit for algorithm.
virtual ~BonNWayChoose()
Destructor.
virtual OsiBranchingObject * createBranch(OsiSolverInterface *solver, const OsiBranchingInformation *info, int way) const
Creates a branching object.
This class chooses a variable to branch on.
Ipopt::Index do_fixings_
Do we fix?*.
static void registerOptions(Ipopt::SmartPtr< Bonmin::RegisteredOptions > roptions)
Register class options.
std::vector< std::vector< int > > num_ps_costs_
static char * j
Definition: OSdtoa.cpp:3622
virtual int doStrongBranching(OsiSolverInterface *solver, OsiBranchingInformation *info, int iObject, double *saveLow, double *saveUp, double &score)
This is a utility function which does strong branching on a list of objects and stores the results in...
void fint fint fint real fint real real real real real real real real real * e
A class to have all elements necessary to setup a branch-and-bound.
virtual int chooseVariable(OsiSolverInterface *solver, OsiBranchingInformation *info, bool fixVariables)
Choose a variable Returns - -1 Node is infeasible 0 Normal termination - we have a candidate 1 All lo...
virtual OsiChooseVariable * clone() const
Clone.
size_t numberMembers() const
Number of members.
int start_nway_
Start of nway objects in array.
virtual double branch(OsiSolverInterface *solver)
Does next branch and updates state.
double pseudocost_trust_value_
Trust value for the pseudo costs.
virtual int setupList(OsiBranchingInformation *info, bool initialize)
Sets up strong list and clears all if initialize is true.
void fint fint * k
const int * members() const
Members (indices in range 0 ... numberColumns-1)
double compute_usefulness(int objectIndex, const OsiBranchingInformation *info) const
Helper function for setupList and chooseVariable, compute usefullness of an nway object.
N way branching Object class.
void set_bounds(std::vector< double > &bounds) const
BonNWayChoose & operator=(const BonNWayChoose &rhs)
Assignment operator.
double cutoff_multiplier_
Cutoff multiplier for infeasibles.
Ipopt::SmartPtr< Ipopt::OptionsList > options()
Acces list of Options.
std::vector< int > num_eval_
BonNWayChoose()
Default Constructor, forbiden for some reason.
void fint * n
return b
Definition: OSdtoa.cpp:1719
const char * prefix() const
Get prefix to use for options.
int br_depth_
depth of strong-branching.