/home/coin/SVN-release/OS-2.4.0/Bonmin/src/CbcBonmin/Heuristics/BonHeuristicFPump.cpp

Go to the documentation of this file.
00001 // Copyright (C) 2007, International Business Machines Corporation and others. 
00002 // All Rights Reserved.
00003 // This code is published under the Common Public License.
00004 //
00005 // Authors :
00006 // Joao P. Goncalves, International Business Machines Corporation
00007 //
00008 // Date : November 12, 2007
00009 
00010 #include "BonHeuristicFPump.hpp"
00011 #include "CoinHelperFunctions.hpp"
00012 #include "CbcModel.hpp"
00013 
00014 #include "OsiAuxInfo.hpp"
00015 
00016 #include "CoinTime.hpp"
00017 
00018 #include <fstream>
00019 
00020 #include <iomanip>
00021 
00022 using namespace std;
00023 
00024 //#define DEBUG_BON_HEURISTIC_FPUMP
00025 
00026 namespace Bonmin
00027 {
00028   class score_sorter {
00029   public:
00031     score_sorter(const vector<double>& score):
00032       score_(score) {}
00033     
00034     bool operator() (const int x, const int y) const {
00035       return score_[x]>score_[y];
00036     }
00037     
00038   private:
00039     const vector<double>& score_;
00040   };
00041 
00042 
00043   HeuristicFPump::HeuristicFPump()
00044     :
00045     CbcHeuristic(),
00046     setup_(NULL),
00047     objective_norm_(1),
00048     enableAdvanced_(false)
00049   {}
00050 
00051   HeuristicFPump::HeuristicFPump(BonminSetup * setup)
00052     :
00053     CbcHeuristic(),
00054     setup_(setup),
00055     objective_norm_(1),
00056     enableAdvanced_(false)
00057   {
00058     Initialize(setup->options());
00059   }
00060 
00061   HeuristicFPump::HeuristicFPump(const HeuristicFPump &copy)
00062     :
00063     CbcHeuristic(copy),
00064     setup_(copy.setup_),
00065     objective_norm_(copy.objective_norm_),
00066     enableAdvanced_(copy.enableAdvanced_)
00067   {}
00068 
00069   HeuristicFPump &
00070   HeuristicFPump::operator=(const HeuristicFPump & rhs)
00071   {
00072     if(this != &rhs) {
00073       CbcHeuristic::operator=(rhs);
00074       setup_ = rhs.setup_;
00075       objective_norm_ = rhs.objective_norm_;
00076       enableAdvanced_ = rhs.enableAdvanced_;
00077     }
00078     return *this;
00079   }
00080 
00081   int
00082   HeuristicFPump::solution(double &solutionValue, double *betterSolution)
00083   {
00084     if(model_->getNodeCount() || model_->getCurrentPassNumber() > 1) return 0;
00085 
00086     bool integerSolutionAlreadyExists = false;
00087     if(model_->getSolutionCount()) {
00088       //      bestSolutionValue = model_->getObjValue();
00089       integerSolutionAlreadyExists = true;
00090       if(!enableAdvanced_)
00091         return 0;
00092       assert(solutionValue < 1.0e50);
00093     }
00094 
00095     const int maxNumberIterations = 200;
00096     const double toleranceObjectiveFP = 1.0e-5;
00097 
00098     int returnCode = 0; // 0 means it didn't find a feasible solution
00099 
00100     OsiTMINLPInterface * nlp = NULL;
00101     if(setup_->getAlgorithm() == B_BB)
00102       nlp = dynamic_cast<OsiTMINLPInterface *>(model_->solver()->clone());
00103     else
00104       nlp = dynamic_cast<OsiTMINLPInterface *>(setup_->nonlinearSolver()->clone());
00105 
00106     TMINLP2TNLP* minlp = nlp->problem();
00107 
00108     // set tolerances
00109     double integerTolerance = model_->getDblParam(CbcModel::CbcIntegerTolerance);
00110     double primalTolerance;
00111 #if 0
00112     OsiSolverInterface * solver = model_->solver();
00113     solver->getDblParam(OsiPrimalTolerance,primalTolerance);
00114 #endif
00115     primalTolerance=1.0e-6;
00116 
00117     int numberColumns;
00118     int numberRows;
00119     int nnz_jac_g;
00120     int nnz_h_lag;
00121     Ipopt::TNLP::IndexStyleEnum index_style;
00122     minlp->get_nlp_info(numberColumns, numberRows, nnz_jac_g,
00123                         nnz_h_lag, index_style);
00124 
00125     const Bonmin::TMINLP::VariableType* variableType = minlp->var_types();
00126     const double* x_sol = minlp->x_sol();
00127     const double* x_l = minlp->x_l();
00128     const double* x_u = minlp->x_u();
00129 
00130 #ifdef DEBUG_BON_HEURISTIC_FPUMP
00131     const double* g_sol = minlp->g_sol();
00132     const double* g_l = minlp->g_l();
00133     const double* g_u = minlp->g_u();
00134     // print bounds_info
00135     for(int i=0; i<numberColumns; i++)
00136       cout<<"x_l["<<i<<"]= "<<x_l[i]<<" "
00137           <<"x_sol["<<i<<"]= "<<x_sol[i]<<" "
00138           <<"x_u["<<i<<"]= "<<x_u[i]<<" "
00139           <<"variableType = "<<variableType[i]<<endl;
00140     for(int i=0; i<numberRows; i++)
00141       cout<<"g_l["<<i<<"]= "<<g_l[i]<<" "
00142           <<"g_sol["<<i<<"]= "<<g_sol[i]<<" "
00143           <<"g_u["<<i<<"]= "<<g_u[i]<<endl;
00144 
00145     cout<<"obj_value = "<<minlp->obj_value()<<endl;
00146   
00147     cout<<"optimization_status = "<<minlp->optimization_status()<<endl;
00148 #endif
00149 
00150     // exit if the current NLP solution is infeasible
00151     // infeasibility is determined by the NLP solver
00152     if(minlp->optimization_status() != Ipopt::SUCCESS){
00153       delete nlp;
00154       return returnCode;
00155     }
00156 
00157     // Get solution array for heuristic solution
00158     double* newSolution = new double [numberColumns];
00159     memcpy(newSolution,x_sol,numberColumns*sizeof(double));
00160     double* new_g_sol = new double [numberRows];
00161 
00162     // create a set with the indices of the fractional variables
00163     vector<int> integerColumns; // stores the integer variables
00164     int numberFractionalVariables = 0;
00165     for (int iColumn=0;iColumn<numberColumns;iColumn++) {
00166       if (variableType[iColumn] != Bonmin::TMINLP::CONTINUOUS) {
00167         integerColumns.push_back(iColumn);
00168         double value=newSolution[iColumn];
00169         if (fabs(floor(value+0.5)-value)>integerTolerance) {
00170           numberFractionalVariables++;
00171         }
00172       }
00173     }
00174     int numberIntegerColumns = (int) integerColumns.size();
00175 
00176     // create space to store old solutions in order to prevent cycling
00177     const int numberOldSolutionsStored = 4;
00178     double ** oldSolution = new double * [numberOldSolutionsStored];
00179     for (int j=0;j<numberOldSolutionsStored;j++) {
00180       oldSolution[j]= new double[numberIntegerColumns];
00181       for (int i=0;i<numberIntegerColumns;i++)
00182         oldSolution[j][i]=-COIN_DBL_MAX;
00183     }
00184 
00185     RoundingFPump roundObj(minlp);
00186 
00187     bool stopDueToAlmostZeroObjective = false;
00188     double* x_bar = new double[numberIntegerColumns];
00189     int* indexes_x_bar = new int[numberIntegerColumns];
00190     double* copy_newSolution = new double[numberColumns];
00191     int iteration = 0;
00192     while(numberFractionalVariables) {
00193       iteration++;
00194       if(iteration > maxNumberIterations) {
00195         break;
00196       }
00197       memcpy(copy_newSolution, newSolution, numberColumns*sizeof(double));
00198       roundObj.round(integerTolerance, primalTolerance, copy_newSolution);
00199       bool flip = true;
00200       for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
00201         int iColumn = integerColumns[iIntCol];
00202         double value=copy_newSolution[iColumn];
00203 #if 0
00204         double value=newSolution[iColumn];
00205         if (fabs(floor(value+0.5)-value)>integerTolerance) {
00206           value = floor(value+0.5);
00207           // make sure that the new value is within bounds
00208           if(value < x_l[iColumn]-primalTolerance)
00209             value++;
00210           else if(value > x_u[iColumn]+primalTolerance)
00211             value--;
00212         }
00213 #endif
00214         x_bar[iIntCol]=value;
00215         indexes_x_bar[iIntCol]=iColumn;
00216         if(flip && 
00217            fabs(x_bar[iIntCol]-oldSolution[0][iIntCol])>integerTolerance)
00218           flip = false;
00219       }
00220 
00221 #ifdef DEBUG_BON_HEURISTIC_FPUMP
00222       cout<<"iteration= "<<iteration<<", flip= "<<flip<<endl;
00223 #endif
00224 
00225       // flip some of the integer variables if the rounded solution is the
00226       // same as the previous one
00227       if(flip) {
00228         vector<int> sortedIntegerColumns(numberIntegerColumns);
00229         vector<double> score(numberIntegerColumns);
00230         for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
00231           int iColumn = integerColumns[iIntCol];
00232           sortedIntegerColumns[iIntCol] = iIntCol;
00233           double value=newSolution[iColumn];
00234           score[iIntCol] = fabs(value-oldSolution[0][iIntCol]);
00235         }
00236         sort(sortedIntegerColumns.begin(),sortedIntegerColumns.end(),
00237              score_sorter(score));
00238 
00239         int maxNumberToMove = 1;
00240         int numberMoved = 0;
00241         for(int i=0; i<numberIntegerColumns; i++) {
00242           int iIntCol = sortedIntegerColumns[i];
00243           if(score[iIntCol] > 0.00) {
00244             int iColumn = integerColumns[iIntCol];
00245             double value=newSolution[iColumn];
00246             if(value-oldSolution[0][iIntCol]>0.0)
00247               value = oldSolution[0][iIntCol]+1.0;
00248             else
00249               value = oldSolution[0][iIntCol]-1.0;
00250             // make sure that the new value is within bounds
00251             if(value < x_l[iColumn]-primalTolerance)
00252               value++;
00253             else if(value > x_u[iColumn]+primalTolerance)
00254               value--;
00255             assert(fabs(floor(value+0.5)-value)<=integerTolerance);
00256             x_bar[iIntCol]=value;
00257             numberMoved++;
00258           } else
00259             break;
00260           if(numberMoved >= maxNumberToMove)
00261             break;
00262         }
00263 
00264         // check for loop.
00265         // see if the new rounded solution is equal to an old solution.
00266         // If yes, then perturb the new rounded solution
00267         bool matched;
00268         for (int k = numberOldSolutionsStored-1; k > 0; k--) {
00269           double * b = oldSolution[k];
00270           matched = true;
00271           for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
00272             if (fabs(x_bar[iIntCol]-b[iIntCol])>integerTolerance) {
00273               matched=false;
00274               break;
00275             } 
00276           }
00277           if (matched) break;
00278         }
00279 
00280 #ifdef DEBUG_BON_HEURISTIC_FPUMP
00281         cout<<"matched= "<<matched<<endl;
00282 #endif
00283 
00284         if (matched) {
00285           // perturbation
00286           for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
00287             int iColumn = integerColumns[iIntCol];
00288             double value=newSolution[iColumn];
00289             double random = max(0.0,CoinDrand48()-0.3);
00290             double difference = fabs(value-oldSolution[0][iIntCol]);
00291             if(difference+random>0.5) {
00292               if(value-oldSolution[0][iIntCol]>0.0)
00293                 value = oldSolution[0][iIntCol]+1.0;
00294               else
00295                 value = oldSolution[0][iIntCol]-1.0;
00296               // make sure that the new value is within bounds
00297               if(value < x_l[iColumn]-primalTolerance)
00298                 value++;
00299               else if(value > x_u[iColumn]+primalTolerance)
00300                 value--;
00301               assert(fabs(floor(value+0.5)-value)<=integerTolerance);
00302             } else {
00303               // this variable is not going to be perturbed
00304               value = oldSolution[0][iIntCol];
00305             }
00306             x_bar[iIntCol]=value;
00307           }
00308         }
00309       }
00310       // store the new solution and remove the oldest one
00311       for (int j=numberOldSolutionsStored-1;j>0;j--) {
00312         for (int i = 0; i < numberIntegerColumns; i++) 
00313           oldSolution[j][i]=oldSolution[j-1][i];
00314       }
00315       for (int j = 0; j < numberIntegerColumns; j++) 
00316         oldSolution[0][j] = x_bar[j];
00317 
00318 
00319       // solve the NLP problem
00320       double obj_nlp;
00321       if(integerSolutionAlreadyExists)
00322         // use cutoff constraint
00323         obj_nlp = nlp->solveFeasibilityProblem(numberIntegerColumns,
00324                                                x_bar,indexes_x_bar,
00325                                                objective_norm_, solutionValue);
00326       else
00327         obj_nlp = nlp->solveFeasibilityProblem(numberIntegerColumns,
00328                                                x_bar,indexes_x_bar,
00329                                                1,0,objective_norm_);
00330 
00331 
00332 #ifdef DEBUG_BON_HEURISTIC_FPUMP
00333       cout<<"obj_nlp= "<<obj_nlp<<endl;
00334 #endif
00335 
00336       memcpy(newSolution,x_sol,numberColumns*sizeof(double));
00337 
00338       if(obj_nlp < toleranceObjectiveFP) {
00339         stopDueToAlmostZeroObjective = true;
00340         break;
00341       }
00342 
00343       // compute number of fractional variables
00344       numberFractionalVariables = 0;
00345       for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
00346         int iColumn = integerColumns[iIntCol];
00347         double value=newSolution[iColumn];
00348         if (fabs(floor(value+0.5)-value)>integerTolerance)
00349           numberFractionalVariables++;
00350       }
00351 
00352     }
00353 
00354     for (int j=0;j<numberOldSolutionsStored;j++) 
00355       delete [] oldSolution[j];
00356     delete [] oldSolution;
00357     delete [] x_bar;
00358     delete [] indexes_x_bar;
00359 
00360 
00361     // fix the integer variables and solve the NLP
00362     for(int iIntCol=0; iIntCol<numberIntegerColumns; iIntCol++) {
00363       int iColumn = integerColumns[iIntCol];
00364       double value=floor(newSolution[iColumn]+0.5);
00365       minlp->SetVariableUpperBound(iColumn, floor(value));
00366       minlp->SetVariableLowerBound(iColumn, ceil(value));
00367     }
00368     nlp->initialSolve();
00369     bool feasible = true;
00370     if(minlp->optimization_status() != Ipopt::SUCCESS) {
00371       feasible = false;
00372       //      if(stopDueToAlmostZeroObjective)
00373         //      returnCode = 8;
00374     }
00375     memcpy(newSolution,x_sol,numberColumns*sizeof(double));
00376 
00377     if(feasible) {
00378       double newSolutionValue;
00379       minlp->eval_f(numberColumns, newSolution, true, newSolutionValue);
00380       if(newSolutionValue < solutionValue) {
00381         memcpy(betterSolution,newSolution,numberColumns*sizeof(double));
00382         solutionValue = newSolutionValue;
00383         returnCode = 1;
00384       }
00385     }
00386 
00387 #ifdef DEBUG_BON_HEURISTIC_FPUMP
00388     cout<<"returnCode= "<<returnCode<<endl;
00389 #endif
00390 
00391 #if 0
00392     delete [] indexRow;
00393     delete [] indexCol;
00394     delete [] row;
00395     delete [] columnStart;
00396     delete [] columnLength;
00397 #endif
00398     delete [] newSolution;
00399     delete [] new_g_sol;
00400     delete [] copy_newSolution;
00401     delete nlp;
00402 
00403     return returnCode;
00404   }
00405 
00406   void
00407   HeuristicFPump::registerOptions(Ipopt::SmartPtr<Bonmin::RegisteredOptions> roptions){
00408     roptions->SetRegisteringCategory("MINLP Heuristics", RegisteredOptions::BonminCategory);
00409     roptions->AddBoundedIntegerOption("feasibility_pump_objective_norm","Norm of feasibility pump objective function",
00410                                       1, 2, 1,"");
00411     roptions->setOptionExtraInfo("feasibility_pump_objective_norm", 63);
00412     roptions->AddStringOption2("heuristic_feasibility_pump", "whether the heuristic feasibility pump should be used",
00413       "no", "no", "don't use it", "yes", "use it", "");
00414     roptions->setOptionExtraInfo("heuristic_feasibility_pump", 63);
00415 
00416     roptions->SetRegisteringCategory("Test Heuristics", RegisteredOptions::UndocumentedCategory);
00417     roptions->AddStringOption2("unstable_fp","use at your own risks",
00418                                "no",
00419                                "no", "",
00420                                "yes", "","");
00421     roptions->setOptionExtraInfo("unstable_fp", 63);
00422   }
00423 
00424   void 
00425   HeuristicFPump::Initialize(Ipopt::SmartPtr<Ipopt::OptionsList> options){
00426     options->GetIntegerValue("feasibility_pump_objective_norm", objective_norm_, "bonmin.");
00427     options->GetEnumValue("unstable_fp", enableAdvanced_, "bonmin.");
00428   }
00429 
00430   RoundingFPump::RoundingFPump(TMINLP2TNLP* minlp)
00431     :
00432     minlp_(minlp)
00433   {
00434     gutsOfConstructor();
00435   }
00436 
00437   RoundingFPump::~RoundingFPump()
00438   {
00439     delete [] col_and_jac_g_;
00440   }
00441 
00442   void
00443   RoundingFPump::gutsOfConstructor()
00444   {
00445 
00446     int nnz_jac_g;
00447     int nnz_h_lag;
00448     Ipopt::TNLP::IndexStyleEnum index_style;
00449     minlp_->get_nlp_info(numberColumns_, numberRows_, nnz_jac_g,
00450                         nnz_h_lag, index_style);
00451     
00452     const double* x_sol = minlp_->x_sol();
00453 
00454     // Get the indicies of the jacobian
00455     // This is also a way of knowing which variables are
00456     // used in each row
00457     int* indexRow = new int[nnz_jac_g];
00458     int* indexCol = new int[nnz_jac_g];
00459     minlp_->eval_jac_g(numberColumns_, x_sol, false,
00460                        numberRows_, nnz_jac_g,
00461                        indexRow, indexCol, 0);
00462 
00463     // get the jacobian for the solution with zeros
00464     double* jac_g = new double [nnz_jac_g];
00465     double* zero_sol = new double [numberColumns_];
00466     minlp_->get_starting_point(numberColumns_, 1, zero_sol, 0, NULL, NULL, numberRows_, 0, NULL);
00467     //memset(zero_sol, 0, numberColumns_ * sizeof(double));
00468     minlp_->eval_jac_g(numberColumns_, zero_sol, true,
00469                        numberRows_, nnz_jac_g,
00470                        0, 0, jac_g);
00471 
00472     col_and_jac_g_ = new vector<pair<int, int> >[numberRows_];
00473 
00474     int indexCorrection = (index_style == Ipopt::TNLP::C_STYLE) ? 0 : 1;
00475     for(int i=0; i<nnz_jac_g; i++) {
00476       int thisIndexRow = indexRow[i]-indexCorrection;      
00477       int thisIndexCol = indexCol[i]-indexCorrection;
00478       pair<int, int> value(thisIndexCol, static_cast<int>(jac_g[i]));
00479       col_and_jac_g_[thisIndexRow].push_back(value);
00480     }    
00481 
00482     delete [] indexRow;
00483     delete [] indexCol;
00484     delete [] jac_g;
00485     delete [] zero_sol;
00486   }
00487 
00488   void
00489   RoundingFPump::round(const double integerTolerance, 
00490                        const double primalTolerance,
00491                        double* solution)
00492   {
00493     const Bonmin::TMINLP::VariableType* variableType = minlp_->var_types();
00494     const double* x_l = minlp_->x_l();
00495     const double* x_u = minlp_->x_u();
00496     const double* g_l = minlp_->g_l();
00497     const double* g_u = minlp_->g_u();
00498 
00499 
00500     for(int iRow=0; iRow<numberRows_; iRow++) {
00501       if(g_l[iRow] == 1.0 && g_u[iRow] == 1.0) {
00502         bool sosConstraint = true;
00503         double weightedSum = 0.0;
00504         int counter = 1;
00505         vector<pair<int, int> > jac_g = col_and_jac_g_[iRow];
00506         for (unsigned int j=0; j<jac_g.size(); j++) {
00507           int iColumn = jac_g[j].first;
00508           if (solution[iColumn]>=1.0-integerTolerance ||
00509               jac_g[j].second != 1.0 ||
00510               variableType[iColumn] == Bonmin::TMINLP::CONTINUOUS) {
00511             sosConstraint = false;
00512             break;
00513           }
00514           else {
00515             weightedSum += counter * solution[iColumn];
00516             counter++;
00517           }
00518         }
00519 #ifdef DEBUG_BON_HEURISTIC_FPUMP
00520         if(sosConstraint) {
00521           cout<<"weightedSum= "<<weightedSum
00522               <<", numberColumns_= "<<numberColumns_<<endl;
00523         }
00524 #endif
00525         
00526         if(sosConstraint) {
00527           double fl = floor(weightedSum + 0.5); 
00528           int indexColumnSelected = static_cast<int>(fl) - 1;
00529           if(indexColumnSelected < 0){//Looks like all variables have been fixed to 0
00530             continue;
00531           }
00532           assert(indexColumnSelected < jac_g.size());
00533           for (unsigned int j=0; j<jac_g.size(); j++) {
00534             int iColumn = jac_g[j].first;
00535             if(j == indexColumnSelected)
00536               solution[iColumn] = 1.0;
00537             else
00538               solution[iColumn] = 0.0;
00539           }
00540         }
00541       }
00542     }
00543 
00544     for(int iColumn=0; iColumn<numberColumns_; iColumn++) {
00545       if(variableType[iColumn] != Bonmin::TMINLP::CONTINUOUS) {
00546         double value=solution[iColumn];
00547         if (fabs(floor(value+0.5)-value)>integerTolerance) {
00548           value = floor(value+0.5);
00549           // make sure that the new value is within bounds
00550           if(value < x_l[iColumn]-primalTolerance)
00551             value++;
00552           else if(value > x_u[iColumn]+primalTolerance)
00553             value--;
00554           solution[iColumn] = value;
00555         }
00556       }
00557     }
00558   }
00559 }

Generated on Thu Sep 22 03:05:54 2011 by  doxygen 1.4.7