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