00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <string>
00012
00013 #include "CouenneConfig.h"
00014 #include "CouenneFeasPump.hpp"
00015 #include "CouenneFPpool.hpp"
00016 #include "CouenneMINLPInterface.hpp"
00017 #include "CouenneObject.hpp"
00018 #include "CouenneProblemElem.hpp"
00019 #include "CouenneProblem.hpp"
00020 #include "CouenneExprClone.hpp"
00021 #include "CouenneExprSub.hpp"
00022 #include "CouenneExprPow.hpp"
00023 #include "CouenneExprSum.hpp"
00024 #include "CouenneTNLP.hpp"
00025 #include "CouenneSparseMatrix.hpp"
00026
00027 using namespace Ipopt;
00028 using namespace Couenne;
00029
00030
00031 void CouenneFeasPump::initIpoptApp () {
00032
00033
00034
00035
00036
00037 if (!app_)
00038 app_ = IpoptApplicationFactory ();
00039
00040 ApplicationReturnStatus status = app_ -> Initialize ();
00041
00042 app_ -> Options () -> SetIntegerValue ("max_iter", 1000);
00043 app_ -> Options () -> SetIntegerValue
00044 ("print_level", (problem_ -> Jnlst () -> ProduceOutput (J_ERROR, J_NLPHEURISTIC) ? 4 :
00045 problem_ -> Jnlst () -> ProduceOutput (J_STRONGWARNING, J_NLPHEURISTIC) ? 5 : 0));
00046
00047 app_ -> Options () -> SetStringValue ("fixed_variable_treatment", "make_parameter");
00048
00049 if (status != Solve_Succeeded)
00050 printf ("FP: Error in initialization\n");
00051 }
00052
00053
00054
00055 CouenneFeasPump::CouenneFeasPump (CouenneProblem *couenne,
00056 CouenneCutGenerator *cg,
00057 Ipopt::SmartPtr<Ipopt::OptionsList> options):
00058 CbcHeuristic (),
00059
00060 problem_ (couenne),
00061 couenneCG_ (cg),
00062 nlp_ (NULL),
00063 app_ (NULL),
00064 milp_ (NULL),
00065 postlp_ (NULL),
00066 pool_ (NULL),
00067
00068 numberSolvePerLevel_ (5),
00069
00070 multDistNLP_ (1.),
00071 multHessNLP_ (0.),
00072 multObjFNLP_ (0.),
00073
00074 multDistMILP_ (1.),
00075 multHessMILP_ (0.),
00076 multObjFMILP_ (0.),
00077
00078 compDistInt_ (FP_DIST_INT),
00079 milpCuttingPlane_ (FP_CUT_NONE),
00080 nSepRounds_ (0),
00081 maxIter_ (COIN_INT_MAX),
00082 useSCIP_ (false),
00083 milpMethod_ (0),
00084 tabuMgt_ (FP_TABU_NONE) {
00085
00086 if (IsValid (options)) {
00087
00088 std::string s;
00089
00090 int compareTerm;
00091
00092 options -> GetIntegerValue ("feas_pump_iter", maxIter_, "couenne.");
00093 options -> GetIntegerValue ("feas_pump_level", numberSolvePerLevel_, "couenne.");
00094 options -> GetIntegerValue ("feas_pump_milpmethod", milpMethod_, "couenne.");
00095
00096 options -> GetNumericValue ("feas_pump_mult_dist_nlp", multDistNLP_, "couenne.");
00097 options -> GetNumericValue ("feas_pump_mult_hess_nlp", multHessNLP_, "couenne.");
00098 options -> GetNumericValue ("feas_pump_mult_objf_nlp", multObjFNLP_, "couenne.");
00099
00100 options -> GetNumericValue ("feas_pump_mult_dist_milp", multDistMILP_, "couenne.");
00101 options -> GetNumericValue ("feas_pump_mult_hess_milp", multHessMILP_, "couenne.");
00102 options -> GetNumericValue ("feas_pump_mult_objf_milp", multObjFMILP_, "couenne.");
00103
00104 options -> GetStringValue ("feas_pump_convcuts", s, "couenne.");
00105
00106 milpCuttingPlane_ =
00107 (s == "none") ? FP_CUT_NONE :
00108 (s == "integrated") ? FP_CUT_INTEGRATED :
00109 (s == "postcut") ? FP_CUT_POST : FP_CUT_EXTERNAL;
00110
00111 options -> GetIntegerValue ("feas_pump_nseprounds", nSepRounds_, "couenne.");
00112
00113 options -> GetStringValue ("feas_pump_vardist", s, "couenne.");
00114
00115 compDistInt_ =
00116 (s == "integer") ? FP_DIST_INT :
00117 (s == "all") ? FP_DIST_ALL : FP_DIST_POST;
00118
00119 options -> GetIntegerValue ("feas_pump_milpmethod", milpMethod_, "couenne.");
00120 options -> GetIntegerValue ("feas_pump_poolcomp", compareTerm, "couenne.");
00121
00122 pool_ = new CouenneFPpool ((enum what_to_compare) compareTerm);
00123
00124 options -> GetStringValue ("feas_pump_tabumgt", s, "couenne.");
00125
00126 tabuMgt_ =
00127 (s == "pool") ? FP_TABU_POOL :
00128 (s == "perturb") ? FP_TABU_PERTURB :
00129 (s == "cut") ? FP_TABU_CUT : FP_TABU_NONE;
00130
00131 options -> GetStringValue ("feas_pump_usescip", s, "couenne.");
00132
00133 #ifdef COIN_HAS_SCIP
00134 useSCIP_ = (s == "yes");
00135 if (milpMethod_ < 0)
00136 milpMethod_ = 0;
00137 #else
00138 if (s == "yes")
00139 problem_ -> Jnlst () -> Printf (J_ERROR, J_COUENNE, "Warning: you have set feas_pump_usescip to true, but SCIP is not installed.\n");
00140 #endif
00141
00142 } else
00143 pool_ = new CouenneFPpool (SUM_NINF);
00144
00145 setHeuristicName ("Couenne Feasibility Pump");
00146
00147 initIpoptApp ();
00148 }
00149
00150
00151
00152 CouenneFeasPump::CouenneFeasPump (const CouenneFeasPump &other) {
00153
00154 operator= (other);
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194 }
00195
00196
00197
00198 CbcHeuristic *CouenneFeasPump::clone () const
00199 {return new CouenneFeasPump (*this);}
00200
00201
00202
00203 CouenneFeasPump &CouenneFeasPump::operator= (const CouenneFeasPump & rhs) {
00204
00205 if (this != &rhs) {
00206
00207 CbcHeuristic::operator= (rhs);
00208
00209 problem_ = rhs. problem_;
00210 couenneCG_ = rhs. couenneCG_;
00211 nlp_ = rhs. nlp_;
00212 app_ = NULL;
00213 milp_ = rhs. milp_ ? rhs. milp_ -> clone () : NULL;
00214 postlp_ = rhs. postlp_ ? rhs. postlp_ -> clone () : NULL;
00215 pool_ = NULL;
00216
00217 numberSolvePerLevel_ = rhs. numberSolvePerLevel_;
00218
00219 multDistNLP_ = rhs. multDistNLP_;
00220 multHessNLP_ = rhs. multHessNLP_;
00221 multObjFNLP_ = rhs. multObjFNLP_;
00222
00223 multDistMILP_ = rhs. multDistMILP_;
00224 multHessMILP_ = rhs. multHessMILP_;
00225 multObjFMILP_ = rhs. multObjFMILP_;
00226
00227 compDistInt_ = rhs. compDistInt_;
00228 milpCuttingPlane_ = rhs. milpCuttingPlane_;
00229 nSepRounds_ = rhs. nSepRounds_;
00230 maxIter_ = rhs. maxIter_;
00231 useSCIP_ = rhs. useSCIP_;
00232 milpMethod_ = rhs. milpMethod_;
00233 tabuMgt_ = rhs. tabuMgt_;
00234
00235 if (rhs. pool_)
00236 pool_ = new CouenneFPpool (*(rhs. pool_));
00237
00238 for (std::set <CouenneFPsolution, compareSol>::const_iterator i = rhs.tabuPool_.begin ();
00239 i != rhs.tabuPool_.end ();
00240 ++i)
00241 tabuPool_. insert (CouenneFPsolution (*i));
00242
00243 initIpoptApp ();
00244 }
00245
00246 return *this;
00247 }
00248
00249
00250
00251 CouenneFeasPump::~CouenneFeasPump () {
00252
00253 if (pool_) delete pool_;
00254 if (app_) delete app_;
00255 if (milp_) delete milp_;
00256 if (postlp_) delete postlp_;
00257
00258
00259 }
00260
00261
00265 expression *CouenneFeasPump::updateNLPObj (const double *iSol) {
00266
00267 expression **list = NULL;
00268
00269 int nTerms = 0;
00270
00271 const double *iS = iSol;
00272
00273 if ((multHessNLP_ == 0.) ||
00274 (nlp_ -> optHessian () == NULL)) {
00275
00276 list = new expression * [1 + problem_ -> nVars ()];
00277
00278
00279
00280
00281 for (int i=0; i<problem_ -> nVars (); i++) {
00282
00283 if (compDistInt_ == FP_DIST_INT &&
00284 !(problem_ -> Var (i) -> isInteger ()))
00285 continue;
00286
00287 expression *base;
00288
00289 if (*iS == 0.) base = new exprClone (problem_ -> Var (i));
00290 else if (*iS < 0.) base = new exprSum (new exprClone (problem_ -> Var (i)), new exprConst (-*iS));
00291 else base = new exprSub (new exprClone (problem_ -> Var (i)), new exprConst (*iS));
00292
00293 ++iS;
00294
00295 list [nTerms++] = new exprPow (base, new exprConst (2.));
00296 }
00297
00298 } else {
00299
00300
00301
00302 list = new expression * [problem_ -> nVars () *
00303 problem_ -> nVars ()];
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314 bool *diag = NULL;
00315
00316 if (multDistNLP_ > 0.) {
00317
00318 diag = new bool [problem_ -> nVars ()];
00319 CoinFillN (diag, problem_ -> nVars (), false);
00320 }
00321
00322 int *row = nlp_ -> optHessian () -> row ();
00323 int *col = nlp_ -> optHessian () -> col ();
00324 double *val = nlp_ -> optHessian () -> val ();
00325
00326 int num = nlp_ -> optHessian () -> num ();
00327
00328
00329 for (int i=0; i<num; ++i, ++row, ++col, ++val) {
00330
00331
00332
00333 if (compDistInt_ == FP_DIST_INT &&
00334 !(problem_ -> Var (*row) -> isInteger () &&
00335 problem_ -> Var (*col) -> isInteger ()))
00336 continue;
00337
00338
00339
00340 if (*col < *row) {
00341
00342 if (2. * *val == 1.)
00343
00344 list [nTerms++] = new exprMul (new exprSub (new exprClone (problem_ -> Var (*row)), new exprConst (iSol [*row])),
00345 new exprSub (new exprClone (problem_ -> Var (*col)), new exprConst (iSol [*col])));
00346
00347 else if (fabs (*val) > COUENNE_EPS) {
00348
00349 expression **mlist = new expression * [3];
00350
00351 mlist [0] = new exprConst (2. * *val);
00352 mlist [1] = new exprSub (new exprClone (problem_ -> Var (*row)), new exprConst (iSol [*row]));
00353 mlist [2] = new exprSub (new exprClone (problem_ -> Var (*col)), new exprConst (iSol [*col]));
00354
00355 list [nTerms++] = new exprMul (mlist, 3);
00356 }
00357
00358 } else if (*col == *row) {
00359
00360 if (multDistNLP_ > 0.)
00361 diag [*col] = true;
00362
00363 if (*val + multDistNLP_ == 1.)
00364
00365 list [nTerms++] = new exprPow (new exprSub (new exprClone (problem_ -> Var (*row)),
00366 new exprConst (iSol [*row])), new exprConst (2.));
00367
00368 else if (fabs (*val + multDistNLP_) > COUENNE_EPS)
00369
00370 list [nTerms++] = new exprMul (new exprConst (*val + multDistNLP_),
00371 new exprPow (new exprSub (new exprClone (problem_ -> Var (*row)),
00372 new exprConst (iSol [*row])), new exprConst (2.)));
00373 }
00374 }
00375
00376
00377
00378 if (multDistNLP_ > 0.) {
00379
00380
00381 for (int i=0; i<problem_ -> nVars (); i++) {
00382
00383 if ((compDistInt_ == FP_DIST_INT &&
00384 !(problem_ -> Var (i) -> isInteger ())) ||
00385 diag [i])
00386 continue;
00387
00388 expression *base;
00389
00390 if (*iS == 0.) base = new exprClone (problem_ -> Var (i));
00391 else if (*iS < 0.) base = new exprSum (new exprClone (problem_ -> Var (i)), new exprConst (-*iS));
00392 else base = new exprSub (new exprClone (problem_ -> Var (i)), new exprConst (*iS));
00393
00394 ++iS;
00395
00396 list [nTerms++] = new exprPow (base, new exprConst (2.));
00397 }
00398
00399 delete [] diag;
00400 }
00401 }
00402
00403 if (multObjFNLP_ != 0.)
00404 list [nTerms++] = new exprMul (new exprConst (multObjFNLP_),
00405 new exprClone (problem_ -> Obj (0) -> Body ()));
00406
00407
00408
00409 expression **tmp = list;
00410 list = CoinCopyOfArray (tmp, nTerms);
00411 delete [] tmp;
00412
00413 return new exprSum (list, nTerms);
00414 }
00415
00416
00419 void CouenneFeasPump::fixIntVariables (double *sol) {
00420
00421 assert (sol);
00422
00423 t_chg_bounds *chg_bds = new t_chg_bounds [problem_ -> nVars ()];
00424
00425 for (int i = problem_ -> nVars (); i--;)
00426
00427 if (problem_ -> Var (i) -> isInteger ()) {
00428
00429 double
00430 value = sol [i],
00431 rUp = ceil (value - COUENNE_EPS),
00432 rDn = floor (value + COUENNE_EPS);
00433
00434
00435
00436 value =
00437 (rUp < rDn + 0.5) ? rUp :
00438 (rUp - value < value - rDn) ? rUp : rDn;
00439
00440 #define INT_NLP_BRACKET 1e-6
00441
00442 problem_ -> Lb (i) = value - INT_NLP_BRACKET;
00443 problem_ -> Ub (i) = value + INT_NLP_BRACKET;
00444
00445 chg_bds [i].setLower (t_chg_bounds::CHANGED);
00446 chg_bds [i].setUpper (t_chg_bounds::CHANGED);
00447 }
00448
00449
00450
00451
00452 problem_ -> btCore (chg_bds);
00453
00454 delete [] chg_bds;
00455 }
00456
00457
00459 void CouenneFeasPump::registerOptions (Ipopt::SmartPtr <Bonmin::RegisteredOptions> roptions) {
00460
00461 roptions -> AddStringOption2
00462 ("feas_pump_heuristic",
00463 "Apply the nonconvex Feasibility Pump",
00464 "no",
00465 "no","",
00466 "yes","",
00467 "An implementation of the Feasibility Pump for nonconvex MINLPs");
00468
00469 roptions -> AddLowerBoundedIntegerOption
00470 ("feas_pump_level",
00471 "Specify the logarithm of the number of feasibility pumps to perform"
00472 " on average for each level of given depth of the tree.",
00473 -1,
00474 3, "Solve as many nlp's at the nodes for each level of the tree. "
00475 "Nodes are randomly selected. If for a "
00476 "given level there are less nodes than this number nlp are solved for every nodes. "
00477 "For example if parameter is 8, nlp's are solved for all node until level 8, "
00478 "then for half the node at level 9, 1/4 at level 10.... "
00479 "Set to -1 to perform at all nodes.");
00480
00481 roptions -> AddLowerBoundedIntegerOption
00482 ("feas_pump_iter",
00483 "Number of iterations in the main Feasibility Pump loop",
00484 -1,
00485 10, "-1 means no limit");
00486
00487
00488
00489 char option [40];
00490 char help [250];
00491
00492 std::string terms [] = {"dist", "hess", "objf"};
00493 std::string types [] = {"nlp", "milp"};
00494
00495 for (int j=0; j<3; j++)
00496 for (int i=0; i<2; i++) {
00497
00498 sprintf (option, "feas_pump_mult_%s_%s", terms [j].c_str (), types [i].c_str ());
00499 sprintf (help, "Weight of the %s in the distance function of the %s problem",
00500 !(strcmp ("dist", terms [j].c_str ())) ? "distance" :
00501 !(strcmp ("hess", terms [j].c_str ())) ? "Hessian" : "original objective function", types [i].c_str ());
00502
00503 roptions -> AddBoundedNumberOption
00504 (option, help,
00505 0., false,
00506 1., false,
00507 0., "0: no weight, 1: full weight");
00508 }
00509
00510 roptions -> AddStringOption3
00511 ("feas_pump_vardist",
00512 "Distance computed on integer-only or on both types of variables, in different flavors.",
00513 "integer",
00514 "integer", "Only compute the distance based on integer coordinates (use post-processing if numerical errors occur)",
00515 "all", "Compute the distance using continuous and integer variables",
00516 "int-postprocess", "Use a post-processing fixed-IP LP to determine a closest-point solution");
00517
00518 roptions -> AddStringOption4
00519 ("feas_pump_convcuts",
00520 "Separate MILP-feasible, MINLP-infeasible solution during or after MILP solver.",
00521 "none",
00522 "integrated", "Done within the MILP solver in a branch-and-cut fashion",
00523 "external", "Done after the MILP solver, in a Benders-like fashion",
00524 "postcut", "Do one round of cuts and proceed with NLP",
00525 "none", "Just proceed to the NLP");
00526
00527 roptions -> AddBoundedIntegerOption
00528 ("feas_pump_nseprounds",
00529 "Number of rounds that separate convexification cuts. Must be at least 1",
00530 1, 1e5, 4,
00531 "");
00532
00533 roptions -> AddStringOption4
00534 ("feas_pump_tabumgt",
00535 "Retrieval of MILP solutions when the one returned is unsatisfactory",
00536 "pool",
00537 "pool", "Use a solution pool and replace unsatisfactory solution with Euclidean-closest in pool",
00538 "perturb", "Randomly perturb unsatisfactory solution",
00539 "cut", "Separate convexification cuts",
00540 "none", "Bail out of feasibility pump");
00541
00542 roptions -> AddStringOption2
00543 ("feas_pump_usescip",
00544 "Should SCIP be used to solve the MILPs?",
00545 #ifdef COIN_HAS_SCIP
00546 "yes",
00547 #else
00548 "no",
00549 #endif
00550 "no", "Use Cbc's branch-and-cut to solve the MILP",
00551 "yes", "Use SCIP's branch-and-cut or heuristics (see feas_pump_milpmethod option) to solve the MILP",
00552 "");
00553
00554 roptions -> AddBoundedIntegerOption
00555 ("feas_pump_milpmethod",
00556 "How should the integral solution be constructed?",
00557 -1, 4, -1,
00558 "0: automatic, 1: aggressive heuristics, large node limit, 2: default, node limit, 3: RENS, 4: Objective Feasibility Pump, -1: solve MILP completely");
00559
00560 roptions -> AddBoundedIntegerOption
00561 ("feas_pump_poolcomp",
00562 "Priority field to compare solutions in FP pool",
00563 0, 2, 0,
00564 "0: total number of infeasible objects (integer and nonlinear), 1: maximum infeasibility (integer or nonlinear), 2: objective value.");
00565 }