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