00001
00002
00003
00004
00005
00006
00007
00008
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
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 ©)
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
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;
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
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
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
00151
00152 if(minlp->optimization_status() != Ipopt::SUCCESS){
00153 delete nlp;
00154 return returnCode;
00155 }
00156
00157
00158 double* newSolution = new double [numberColumns];
00159 memcpy(newSolution,x_sol,numberColumns*sizeof(double));
00160 double* new_g_sol = new double [numberRows];
00161
00162
00163 vector<int> integerColumns;
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
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
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
00226
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
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
00265
00266
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
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
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
00304 value = oldSolution[0][iIntCol];
00305 }
00306 x_bar[iIntCol]=value;
00307 }
00308 }
00309 }
00310
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
00320 double obj_nlp;
00321 if(integerSolutionAlreadyExists)
00322
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
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
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
00373
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
00455
00456
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
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
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){
00530 continue;
00531 }
00532 assert(indexColumnSelected < jac_g.size());
00533 for (size_t 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
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 }