00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <sstream>
00011 #include <climits>
00012
00013 #include <algorithm>
00014 #include "BonOaDecBase.hpp"
00015
00016
00017 #include "BonminConfig.h"
00018
00019 #include "OsiClpSolverInterface.hpp"
00020
00021 #include "CbcModel.hpp"
00022 #include "CbcStrategy.hpp"
00023 #include "BonCbcLpStrategy.hpp"
00024 #ifdef COIN_HAS_CPX
00025 #include "OsiCpxSolverInterface.hpp"
00026 #include "cplex.h"
00027 #define CHECK_CPX_STAT(a,b) if(b) throw CoinError("Error in CPLEX call",__FILE__,a);
00028
00029 #endif
00030 #include "BonCbc.hpp"
00031 #include "BonSolverHelp.hpp"
00032
00033 extern CbcModel * OAModel;
00034
00035 namespace Bonmin {
00036
00037
00038 OaDecompositionBase::OaDecompositionBase(BabSetupBase &b, bool leaveSiUnchanged,
00039 bool reassignLpsolver):
00040 CglCutGenerator(),
00041 nlp_(b.nonlinearSolver()),
00042 s_(&b),
00043 lp_(NULL),
00044 objects_(NULL),
00045 nObjects_(0),
00046 nLocalSearch_(0),
00047 handler_(NULL),
00048 leaveSiUnchanged_(leaveSiUnchanged),
00049 reassignLpsolver_(reassignLpsolver),
00050 timeBegin_(0),
00051 numSols_(0),
00052 parameters_(),
00053 currentNodeNumber_(-1)
00054 {
00055 handler_ = new CoinMessageHandler();
00056 int logLevel;
00057 b.options()->GetIntegerValue("oa_log_level",logLevel,b.prefix());
00058 b.options()->GetNumericValue("oa_log_frequency",parameters_.logFrequency_,b.prefix());
00059 b.options()->GetNumericValue("allowable_fraction_gap", parameters_.gap_tol_, b.prefix());
00060 handler_ -> setLogLevel(logLevel);
00061 b.options()->GetIntegerValue("solution_limit", parameters_.maxSols_,b.prefix());
00062
00063 messages_ = OaMessages();
00064 timeBegin_ = CoinCpuTime();
00065 b.options()->GetIntegerValue("milp_log_level",parameters_.subMilpLogLevel_,b.prefix());
00066 b.options()->GetNumericValue("cutoff_decr",parameters_.cbcCutoffIncrement_,b.prefix());
00067 b.options()->GetNumericValue("integer_tolerance",parameters_.cbcIntegerTolerance_,b.prefix());
00068 int ivalue;
00069 b.options()->GetEnumValue("add_only_violated_oa", ivalue,b.prefix());
00070 parameters_.addOnlyViolated_ = ivalue;
00071 b.options()->GetEnumValue("oa_cuts_scope", ivalue,b.prefix());
00072 parameters_.global_ = ivalue;
00073 }
00074
00075 OaDecompositionBase::OaDecompositionBase
00076 (const OaDecompositionBase & other)
00077 :
00078 CglCutGenerator(other),
00079 nlp_(other.nlp_),
00080 s_(other.s_),
00081 lp_(other.lp_),
00082 objects_(other.objects_),
00083 nObjects_(other.nObjects_),
00084 nLocalSearch_(0),
00085 messages_(other.messages_),
00086 leaveSiUnchanged_(other.leaveSiUnchanged_),
00087 reassignLpsolver_(other.reassignLpsolver_),
00088 timeBegin_(0),
00089 numSols_(other.numSols_),
00090 parameters_(other.parameters_),
00091 currentNodeNumber_(other.currentNodeNumber_)
00092 {
00093 timeBegin_ = CoinCpuTime();
00094 handler_ = other.handler_->clone();
00095 }
00097 OaDecompositionBase::Parameters::Parameters():
00098 global_(true),
00099 addOnlyViolated_(false),
00100 cbcCutoffIncrement_(1e-06),
00101 cbcIntegerTolerance_(1e-05),
00102 gap_tol_(1e-05),
00103 maxLocalSearch_(0),
00104 maxLocalSearchTime_(3600),
00105 subMilpLogLevel_(0),
00106 maxSols_(INT_MAX),
00107 logFrequency_(1000.),
00108 strategy_(NULL)
00109 {}
00110
00112 OaDecompositionBase::~OaDecompositionBase()
00113 {
00114 delete handler_;
00115 }
00116
00117
00119 OaDecompositionBase::Parameters::Parameters(const Parameters & other):
00120 global_(other.global_),
00121 addOnlyViolated_(other.addOnlyViolated_),
00122 cbcCutoffIncrement_(other.cbcCutoffIncrement_),
00123 cbcIntegerTolerance_(other.cbcIntegerTolerance_),
00124 gap_tol_(other.gap_tol_),
00125 maxLocalSearch_(other.maxLocalSearch_),
00126 maxLocalSearchTime_(other.maxLocalSearchTime_),
00127 subMilpLogLevel_(other.subMilpLogLevel_),
00128 maxSols_(other.maxSols_),
00129 logFrequency_(other.logFrequency_),
00130 strategy_(NULL)
00131 {
00132 if (other.strategy_)
00133 strategy_ = other.strategy_->clone();
00134 }
00135
00136
00137
00138 OaDecompositionBase::solverManip::solverManip
00139 (OsiSolverInterface * si,
00140 bool saveNumRows,
00141 bool saveBasis,
00142 bool saveBounds,
00143 bool saveCutoff,
00144 bool resolve):
00145 si_(si),
00146 initialNumberRows_(-1),
00147 colLower_(NULL),
00148 colUpper_(NULL),
00149 warm_(NULL),
00150 cutoff_(DBL_MAX),
00151 deleteSolver_(false),
00152 objects_(NULL),
00153 nObjects_(0)
00154 {
00155 getCached();
00156 if (saveNumRows)
00157 initialNumberRows_ = numrows_;
00158 if (saveBasis)
00159 warm_ = si->getWarmStart();
00160 if (saveBounds) {
00161 colLower_ = new double[numcols_];
00162 colUpper_ = new double[numcols_];
00163 CoinCopyN(si->getColLower(), numcols_ , colLower_);
00164 CoinCopyN(si->getColUpper(), numcols_ , colUpper_);
00165 }
00166 if (saveCutoff)
00167 si->getDblParam(OsiDualObjectiveLimit, cutoff_);
00168 si->messageHandler()->setLogLevel(0);
00169 if (resolve) si->resolve();
00170 }
00171
00172
00173 OaDecompositionBase::solverManip::solverManip
00174 (const OsiSolverInterface & si):
00175 si_(NULL),
00176 initialNumberRows_(-1),
00177 colLower_(NULL),
00178 colUpper_(NULL),
00179 warm_(NULL),
00180 cutoff_(DBL_MAX),
00181 deleteSolver_(true),
00182 objects_(NULL),
00183 nObjects_(0)
00184 {
00185 si_ = si.clone();
00186 getCached();
00187 }
00188
00189 OaDecompositionBase::solverManip::~solverManip()
00190 {
00191 if (warm_) delete warm_;
00192 if (colLower_) delete [] colLower_;
00193 if (colUpper_) delete [] colUpper_;
00194 if (deleteSolver_) delete si_;
00195 }
00196
00197 void
00198 OaDecompositionBase::solverManip::restore()
00199 {
00200 if (initialNumberRows_ >= 0) {
00201 int nRowsToDelete = si_->getNumRows() - initialNumberRows_;
00202 int * rowsToDelete = new int[nRowsToDelete];
00203 for (int i = 0 ; i < nRowsToDelete ; i++) {
00204 rowsToDelete[i] = i + initialNumberRows_;
00205 }
00206 si_->deleteRows(nRowsToDelete, rowsToDelete);
00207 delete [] rowsToDelete;
00208 numrows_ = si_->getNumRows() ;
00209 }
00210
00211 if (colLower_) {
00212 si_->setColLower(colLower_);
00213 }
00214
00215 if (colUpper_) {
00216 si_->setColUpper(colUpper_);
00217 }
00218
00219 if (cutoff_<COIN_DBL_MAX) {
00220 si_->setDblParam(OsiDualObjectiveLimit, cutoff_);
00221 }
00222
00223 if (warm_) {
00224 if (si_->setWarmStart(warm_)==false) {
00225 throw CoinError("Fail restoring the warm start at the end of procedure",
00226 "restore","OaDecompositionBase::SaveSolverState") ;
00227 }
00228 }
00229 getCached();
00230 }
00231
00232 void
00233 OaDecompositionBase::passInMessageHandler(CoinMessageHandler * handler)
00234 {
00235 int logLevel = handler_->logLevel();
00236 delete handler_;
00237 handler_=handler->clone();
00238 handler_->setLogLevel(logLevel);
00239 }
00240
00242 void
00243 OaDecompositionBase::generateCuts(const OsiSolverInterface &si, OsiCuts & cs,
00244 const CglTreeInfo info) {
00245 if (nlp_ == NULL) {
00246 throw CoinError("Error in cut generator for outer approximation no NLP ipopt assigned", "generateCuts", "OaDecompositionBase");
00247 }
00248
00249
00250 BabInfo * babInfo = dynamic_cast<BabInfo *> (si.getAuxiliaryInfo());
00251 assert(babInfo);
00252 assert(babInfo->babPtr());
00253 numSols_ = babInfo->babPtr()->model().getSolutionCount ();
00254 CglTreeInfo info_copy = info;
00255 const CbcNode * node = babInfo->babPtr()->model().currentNode();
00256 info_copy.level = (node == NULL) ? 0 : babInfo->babPtr()->model().currentNode()->depth();
00257 if(babInfo->hasSolution()) numSols_ ++;
00258 if (babInfo)
00259 if (!babInfo->mipFeasible())
00260 return;
00261
00262
00263 const double *colsol = si.getColSolution();
00264
00265
00266 vector<double> savedColLower(nlp_->getNumCols());
00267 CoinCopyN(nlp_->getColLower(), nlp_->getNumCols(), savedColLower());
00268 vector<double> savedColUpper(nlp_->getNumCols());
00269 CoinCopyN(nlp_->getColUpper(), nlp_->getNumCols(), savedColUpper());
00270
00271
00272 OsiBranchingInformation brInfo(nlp_, false);
00273 brInfo.solution_ = colsol;
00274
00275 bool isInteger = integerFeasible(*nlp_, brInfo, parameters_.cbcIntegerTolerance_,
00276 objects_, nObjects_);
00277
00278
00279
00280 int nodeNumber = babInfo->babPtr()->model().getNodeCount();
00281 if(nodeNumber == currentNodeNumber_){
00282 #ifdef OA_DEBUG
00283 printf("OA decomposition recalled from the same node!\n");
00284 #endif
00285 int numCuts = savedCuts_.sizeRowCuts();
00286 for(int i = 0 ; i < numCuts ; i++){
00287
00288 if(savedCuts_.rowCut(i).violated(colsol) > 0.){
00289 #ifdef OA_DEBUG
00290 printf("A violated saved cut has been found\n");
00291 #endif
00292 savedCuts_.rowCut(i).setEffectiveness(9.99e99);
00293 cs.insert(savedCuts_.rowCut(i));
00294 savedCuts_.eraseRowCut(i);
00295 return;
00296 i--; numCuts--;
00297 }
00298 }
00299 }
00300 else {
00301 currentNodeNumber_ = nodeNumber;
00302 savedCuts_.dumpCuts();
00303 }
00304
00305 if (!isInteger) {
00306 if (!doLocalSearch(babInfo))
00307 return;
00308 }
00309
00310
00311 double cutoff;
00312 si.getDblParam(OsiDualObjectiveLimit, cutoff);
00313
00314
00315
00316 solverManip * lpManip = NULL;
00317 if (lp_ != NULL) {
00318 assert(lp_ == &si);
00319 lpManip = new solverManip(lp_, true, leaveSiUnchanged_, true, true);
00320 }
00321 else {
00322 lpManip = new solverManip(si);
00323 }
00324 lpManip->setObjects(objects_, nObjects_);
00325
00326 double milpBound = performOa(cs, *lpManip, babInfo, cutoff, info_copy);
00327
00328 if(babInfo->hasSolution()){
00329 babInfo->babPtr()->model().setSolutionCount (numSols_ - 1);
00330 }
00331
00332
00333 {
00334 if (milpBound>-1e100)
00335 {
00336
00337 if (babInfo)
00338 babInfo->setMipBound(milpBound);
00339 }
00340 }
00341
00342
00343 if (leaveSiUnchanged_)
00344 lpManip->restore();
00345 delete lpManip;
00346
00347 nlp_->setColLower(savedColLower());
00348 nlp_->setColUpper(savedColUpper());
00349
00350 return;
00351 }
00352
00353 void
00354 OaDecompositionBase::solverManip::getCached(){
00355 numrows_ = si_->getNumRows();
00356 numcols_ = si_->getNumCols();
00357 siColLower_ = si_->getColLower();
00358 siColUpper_ = si_->getColUpper();
00359 }
00360
00361
00363 bool
00364 OaDecompositionBase::post_nlp_solve(BabInfo * babInfo, double cutoff) const{
00365 nSolve_++;
00366 bool return_value = false;
00367 if (nlp_->isProvenOptimal()) {
00368 handler_->message(FEASIBLE_NLP, messages_)
00369 <<nlp_->getIterationCount()
00370 <<nlp_->getObjValue()<<CoinMessageEol;
00371
00372 #ifdef OA_DEBUG
00373 const double * colsol2 = nlp_->getColSolution();
00374 debug_.checkInteger(*nlp_,std::cerr);
00375 #endif
00376
00377 if ((nlp_->getObjValue() < cutoff) ) {
00378 handler_->message(UPDATE_UB, messages_)
00379 <<nlp_->getObjValue()
00380 <<CoinCpuTime()-timeBegin_
00381 <<CoinMessageEol;
00382
00383 return_value = true;
00384
00385 assert(babInfo);
00386 if (babInfo) {
00387 int numcols = nlp_->getNumCols();
00388 double * lpSolution = new double[numcols + 1];
00389 CoinCopyN(nlp_->getColSolution(), numcols, lpSolution);
00390 lpSolution[numcols] = nlp_->getObjValue();
00391 babInfo->setSolution(lpSolution,
00392 numcols + 1, lpSolution[numcols]);
00393 delete [] lpSolution;
00394 }
00395 }
00396 }
00397 else if (nlp_->isAbandoned() || nlp_->isIterationLimitReached()) {
00398 (*handler_)<<"Unsolved NLP... exit"<<CoinMessageEol;
00399 }
00400 else {
00401 handler_->message(INFEASIBLE_NLP, messages_)
00402 <<nlp_->getIterationCount()
00403 <<CoinMessageEol;
00404 }
00405 return return_value;
00406 }
00407
00408 void
00409 OaDecompositionBase::setupMipSolver(BabSetupBase &b, const std::string & prefix){
00410
00411
00412 }
00413
00414 #ifdef OA_DEBUG
00415 bool
00416 OaDecompositionBase::OaDebug::checkInteger(const OsiSolverInterface &nlp,
00417 std::ostream & os) const {
00418 const double * colsol = nlp.getColSolution();
00419 int numcols = nlp.getNumCols();
00420 for (int i = 0 ; i < numcols ; i++) {
00421 if (nlp.isInteger(i)) {
00422 if (fabs(colsol[i]) - floor(colsol[i] + 0.5) >
00423 1e-07) {
00424 std::cerr<<"Integer infeasible point (should not be), integer infeasibility for variable "<<i
00425 <<" is, "<<fabs(colsol[i] - floor(colsol[i] + 0.5))<<std::endl;
00426 }
00427 }
00428 return true;
00429 }
00430
00431 }
00432
00433 void
00434 OaDecompositionBase::OaDebug::printEndOfProcedureDebugMessage(const OsiCuts &cs,
00435 bool foundSolution,
00436 double solValue,
00437 double milpBound,
00438 bool isInteger,
00439 bool feasible,
00440 std::ostream & os) const{
00441 std::cout<<"------------------------------------------------------------------"
00442 <<std::endl;
00443 std::cout<<"OA procedure finished"<<std::endl;
00444 std::cout<<"Generated "<<cs.sizeRowCuts()<<std::endl;
00445 if (foundSolution)
00446 std::cout <<"Found NLP-integer feasible solution of value : "<<solValue<<std::endl;
00447 std::cout<<"Current MILP lower bound is : "<<milpBound<<std::endl;
00448 std::cout<<"-------------------------------------------------------------------"<<std::endl;
00449 std::cout<<"Stopped because : isInteger "<<isInteger<<", feasible "<<feasible<<std::endl<<std::endl;
00450
00451 }
00452
00453
00454
00455 #endif
00456 }
00457