00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <vector>
00012
00013 #include "BonRegisteredOptions.hpp"
00014
00015 #include "CoinHelperFunctions.hpp"
00016 #include "CoinTime.hpp"
00017
00018 #include "BonBabSetupBase.hpp"
00019
00020 #include "CouenneTypes.hpp"
00021
00022 #include "CouenneExpression.hpp"
00023 #include "CouenneExprConst.hpp"
00024 #include "CouenneExprGroup.hpp"
00025 #include "CouenneExprClone.hpp"
00026 #include "CouenneExprAux.hpp"
00027 #include "CouenneProblem.hpp"
00028 #include "CouenneGlobalCutOff.hpp"
00029 #include "CouenneProblemElem.hpp"
00030 #include "CouenneLQelems.hpp"
00031
00032 using namespace Couenne;
00033
00034 #define MAX_FBBT_ITER 3
00035
00037 void CouenneProblem::setBase (Bonmin::BabSetupBase *base) {
00038 bonBase_ = base;
00039 jnlst_ = base -> journalist ();
00040 }
00041
00042
00043 const CouNumber SafeCutoff = 1e-4;
00044
00045
00046 const CouNumber SafeDelta = 1e-2;
00047
00050
00051 void CouenneProblem::initAuxs () const {
00052
00053 domain_.current () -> resize (nVars ());
00054
00055
00056
00057
00058 int nvars = nVars ();
00059
00060 for (int i=0; i < nvars; i++) {
00061
00062 int indvar = variables_ [i] -> Index ();
00063
00064 if (((variables_ [i] -> Type () == AUX) &&
00065 (indvar >= nOrigVars_)) ||
00066 (variables_ [i] -> Multiplicity () == 0))
00067
00068 Lb (indvar) = - (Ub (indvar) = COIN_DBL_MAX);
00069 }
00070
00071
00072
00073
00074
00075 for (std::vector <CouenneConstraint *>::const_iterator con = constraints_.begin ();
00076 con != constraints_.end (); ++con) {
00077
00078 CouNumber
00079 lb = (*((*con) -> Lb ())) (),
00080 ub = (*((*con) -> Ub ())) ();
00081
00082 int index = (*con) -> Body () -> Index ();
00083
00084 assert (index >= 0);
00085
00086 if ((Lb (index) = CoinMax (Lb (index), lb)) <= -COUENNE_INFINITY) Lb (index) = -COIN_DBL_MAX;
00087 if ((Ub (index) = CoinMin (Ub (index), ub)) >= COUENNE_INFINITY) Ub (index) = COIN_DBL_MAX;
00088 }
00089
00090
00091
00092
00093 Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM, "InitAux -- assigning bounds\n");
00094
00095 for (int j=0, i=nVars (); i--; j++) {
00096
00097 int ord = numbering_ [j];
00098
00099
00100 if (variables_ [ord] -> Multiplicity () == 0) {
00101 Lb (ord) = - (Ub (ord) = COIN_DBL_MAX);
00102 X (ord) = 0.;
00103 continue;
00104 }
00105
00106 exprVar *var = variables_ [ord];
00107
00108
00109 if (var -> Type () == AUX) {
00110
00111 Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM,
00112 "w_%04d [%10g,%10g] ", ord, Lb (ord), Ub (ord));
00113
00114 CouNumber l, u;
00115
00116 var -> Image () -> getBounds (l, u);
00117
00118
00119
00120
00121
00122 Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM,
00123 " ( --> w_%04d [%10g,%10g] ) vs [%10g %10g]",
00124 ord, l, u, Lb (ord), Ub (ord));
00125
00126
00127 if ((var -> sign () != expression::AUX_LEQ) && ((Lb (ord) = CoinMax (Lb (ord), l)) <= -COUENNE_INFINITY)) Lb (ord) = -COIN_DBL_MAX;
00128 if ((var -> sign () != expression::AUX_GEQ) && ((Ub (ord) = CoinMin (Ub (ord), u)) >= COUENNE_INFINITY)) Ub (ord) = COIN_DBL_MAX;
00129
00130
00131
00132
00133 bool integer = var -> isDefinedInteger ();
00134
00135 if (integer) {
00136 if (var -> sign () != expression::AUX_GEQ) Lb (ord) = ceil (Lb (ord) - COUENNE_EPS);
00137 if (var -> sign () != expression::AUX_LEQ) Ub (ord) = floor (Ub (ord) + COUENNE_EPS);
00138 }
00139
00140 double image = (*(var -> Image ())) ();
00141
00142 X (ord) = (*var) ();
00143
00144 if ((var -> sign () != expression::AUX_LEQ) && X (ord) < image) X (ord) = image;
00145 else if ((var -> sign () != expression::AUX_GEQ) && X (ord) > image) X (ord) = image;
00146
00147
00148
00149 X (ord) = CoinMax (Lb (ord), CoinMin (Ub (ord), X (ord)));
00150
00151 Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM,
00152 " --> [%10g,%10g] (%g)\n", Lb (ord), Ub (ord), X (ord));
00153 }
00154 }
00155
00156 restoreUnusedOriginals ();
00157 }
00158
00159
00162 void CouenneProblem::getAuxs (CouNumber * x) const {
00163
00164
00165 domain_.push (nVars (), x, domain_.lb (), domain_.ub (), false);
00166
00167
00168
00169 if (ndefined_ > 0)
00170 for (int i = 0; i < nVars (); ++i) {
00171 int ii = numbering_ [i];
00172 if (ii >= nOrigVars_ - ndefined_ &&
00173 ii < nOrigVars_)
00174 X (ii) = (*(commonexprs_ [ii - nOrigVars_ + ndefined_])) ();
00175 }
00176
00177
00178
00179
00180
00181
00182 for (int j=0, i=nVars (); i--; j++) {
00183
00184 int index = numbering_ [j];
00185 exprVar *var = variables_ [index];
00186
00187 if (var -> Multiplicity () > 0) {
00188
00189 CouNumber l, u;
00190
00191 if (var -> Type () == AUX)
00192 var -> Image () -> getBounds (l,u);
00193 else {
00194 l = Lb (index);
00195 u = Ub (index);
00196 }
00197
00198 if (var -> Type () == AUX) {
00199
00200 CouNumber &x = X (index);
00201
00202 bool isInt = var -> isDefinedInteger ();
00203
00204
00205
00206
00207
00208
00209
00210
00211 if ((var -> sign () == expression::AUX_EQ) &&
00212 ((index >= nOrigVars_) ||
00213 (index < nOrigVars_ - ndefined_)))
00214 x = (*(var -> Image ())) ();
00215
00216 x =
00217 CoinMax ((var -> sign () != expression::AUX_LEQ) ? (isInt ? ceil (l - COUENNE_EPS) : l) : -COIN_DBL_MAX,
00218 CoinMin ((var -> sign () != expression::AUX_GEQ) ? (isInt ? floor (u + COUENNE_EPS) : u) : COIN_DBL_MAX, x));
00219
00220
00221
00222 if (isInt) {
00223 if (var -> sign () == expression::AUX_GEQ) x = ceil (x - COUENNE_EPS);
00224 else if (var -> sign () == expression::AUX_LEQ) x = floor (x + COUENNE_EPS);
00225 }
00226
00227
00228 }
00229 } else X (index) = 0.;
00230 }
00231
00232 restoreUnusedOriginals ();
00233
00234 domain_.pop ();
00235 }
00236
00237
00240
00241 void CouenneProblem::fillObjCoeff (double *&obj) {
00242
00243
00244
00245
00246
00247 expression *body = objectives_ [0] -> Body ();
00248
00249
00250 switch (body -> code ()) {
00251
00252 case COU_EXPRVAR:
00253 obj [body -> Index ()] = 1;
00254 break;
00255
00256 case COU_EXPRSUB: {
00257
00258 expression **arglist = body -> ArgList ();
00259
00260 obj [arglist [0] -> Index ()] = 1;
00261 obj [arglist [1] -> Index ()] = -1;
00262
00263 } break;
00264
00265 case COU_EXPRGROUP: {
00266
00267 exprGroup *eg = dynamic_cast <exprGroup *> (body -> isaCopy () ?
00268 body -> Copy () :
00269 body);
00270
00271 const exprGroup::lincoeff &lcoe = eg -> lcoeff ();
00272
00273
00274
00275
00276 for (int n = (int) lcoe.size (), i=0; n--; i++)
00277
00278 obj [lcoe [i]. first -> Index ()] = lcoe [i]. second;
00279
00280
00281
00282
00283 }
00284
00285 case COU_EXPRSUM: {
00286
00287 expression **arglist = body -> ArgList ();
00288
00289 for (int i = body -> nArgs (); i--;)
00290 switch ((arglist [i]) -> code ()) {
00291
00292 case COU_EXPRCONST:
00293 break;
00294
00295 case COU_EXPRVAR:
00296 obj [arglist [i] -> Index ()] = 1;
00297 break;
00298
00299 case COU_EXPRMUL: {
00300
00301 expression **mulArgList = arglist [i] -> ArgList ();
00302 int index = mulArgList [0] -> Index ();
00303
00304 if (index >= 0) obj [index] = mulArgList [1] -> Value ();
00305 else obj [mulArgList [1] -> Index ()] = mulArgList [0] -> Value ();
00306 } break;
00307
00308 default:
00309 Jnlst()->Printf(Ipopt::J_ERROR, J_PROBLEM,
00310 "Couenne: invalid element of sum\nAborting\n");
00311 exit (-1);
00312 }
00313 } break;
00314
00315 case COU_EXPRCONST: break;
00316
00317 default:
00318 Jnlst()->Printf(Ipopt::J_WARNING, J_PROBLEM,
00319 "Couenne: warning, objective function not recognized\n");
00320 break;
00321 }
00322 }
00323
00324
00326 void CouenneProblem::setCutOff (CouNumber cutoff, const double *s) const {
00327
00328 if (cutoff > COUENNE_INFINITY/2)
00329 return;
00330
00331 int indobj = objectives_ [0] -> Body () -> Index ();
00332
00333
00334
00335 if ((indobj >= 0) && (cutoff < pcutoff_ -> getCutOff () - COUENNE_EPS)) {
00336
00337
00338 Jnlst () -> Printf (Ipopt::J_ERROR, J_COUENNE, "Couenne: new cutoff value %.10e (%g seconds)\n", cutoff, CoinCpuTime ());
00339
00340
00341 if (Var (indobj) -> isInteger ())
00342 pcutoff_ -> setCutOff (this, floor (cutoff + COUENNE_EPS), s);
00343 else pcutoff_ -> setCutOff (this, cutoff, s);
00344 }
00345 }
00346
00348 void CouenneProblem::resetCutOff (CouNumber value) const {
00349
00350 int indobj = objectives_ [0] -> Body () -> Index ();
00351
00352 if ((indobj >= 0)) {
00353
00354 if (Var (indobj) -> isInteger ())
00355 pcutoff_ -> setCutOff (this, floor (value + COUENNE_EPS), NULL);
00356 else pcutoff_ -> setCutOff (this, value, NULL);
00357 }
00358 }
00359
00360
00363 void CouenneProblem::installCutOff () const {
00364
00365
00366 double cutoff = pcutoff_ -> getCutOff();
00367
00368 if (cutoff > COUENNE_INFINITY)
00369 return;
00370
00371 int indobj = objectives_ [0] -> Body () -> Index ();
00372
00373
00374
00375 if (indobj < 0)
00376 return;
00377
00378
00379
00380
00381
00382 cutoff = (Var (indobj) -> isInteger ()) ?
00383 floor (cutoff + COUENNE_EPS) :
00384 (cutoff + CoinMin (SafeDelta, SafeCutoff * (1. + fabs (cutoff))));
00385
00386 if (cutoff < Ub (indobj))
00387 Ub (indobj) = cutoff;
00388 }
00389
00390
00391
00392 void CouenneProblem::realign () {
00393
00394
00395 for (std::vector <exprVar *>::iterator i = variables_.begin ();
00396 i != variables_.end (); ++i) {
00397
00398 if ((*i) -> Multiplicity () <= 0)
00399 continue;
00400
00401 (*i) -> linkDomain (&domain_);
00402 (*i) -> realign (this);
00403 if ((*i) -> Type () == AUX)
00404 (*i) -> Image () -> realign (this);
00405 }
00406
00407
00408 for (std::vector <CouenneObjective *>::iterator i = objectives_.begin ();
00409 i != objectives_.end (); ++i)
00410 (*i) -> Body () -> realign (this);
00411
00412
00413
00414 for (std::vector <CouenneConstraint *>::iterator i = constraints_.begin ();
00415 i != constraints_.end (); ++i)
00416 (*i) -> Body () -> realign (this);
00417 }
00418
00419
00421 void CouenneProblem::registerOptions (Ipopt::SmartPtr <Bonmin::RegisteredOptions> roptions) {
00422
00423 roptions -> SetRegisteringCategory ("Couenne options", Bonmin::RegisteredOptions::CouenneCategory);
00424
00425 roptions -> AddNumberOption
00426 ("art_cutoff",
00427 "Artificial cutoff",
00428 COIN_DBL_MAX,
00429 "Default value is infinity.");
00430
00431 roptions -> AddNumberOption
00432 ("opt_window",
00433 "Window around known optimum",
00434 COIN_DBL_MAX,
00435 "Default value is infinity.");
00436
00437 roptions -> AddStringOption2
00438 ("use_semiaux",
00439 "Use semiauxiliaries, i.e. auxiliaries defined as w >= f(x) rather than w := f(x))",
00440 "yes",
00441 "no", "Only use auxiliaries assigned with \"=\" ",
00442 "yes", "Use auxiliaries defined by w <= f(x), w >= f(x), and w = f(x)"
00443 );
00444
00445 roptions -> AddStringOption2
00446 ("use_auxcons",
00447 "Use constraints-defined auxiliaries, i.e. auxiliaries w = f(x) defined by original constraints f(x) - w = 0",
00448 "yes",
00449 "no", "",
00450 "yes", ""
00451 );
00452
00453 roptions -> AddStringOption2
00454 ("redcost_bt",
00455 "Reduced cost bound tightening",
00456 "yes",
00457 "no","",
00458 "yes","",
00459 "This bound reduction technique uses the reduced costs of the LP in order to infer better variable bounds.");
00460
00461 roptions -> AddStringOption2
00462 ("use_quadratic",
00463 "Use quadratic expressions and related exprQuad class",
00464 "no",
00465 "no","Use an auxiliary for each bilinear term",
00466 "yes","Create only one auxiliary for a quadratic expression",
00467 "If enabled, then quadratic forms are not reformulated and therefore decomposed as a sum of auxiliary variables, each associated with a bilinear term, but rather taken as a whole expression. "
00468 "Envelopes for these expressions are generated through alpha-convexification."
00469 );
00470
00471 roptions -> AddStringOption2
00472 ("optimality_bt",
00473 "Optimality-based (expensive) bound tightening (OBBT)",
00474 "yes",
00475 "no","",
00476 "yes","",
00477 "This is another bound reduction technique aiming at reducing the solution set by looking at the initial LP relaxation. "
00478 "This technique is computationally expensive, and should be used only when necessary."
00479 );
00480
00481 roptions -> AddLowerBoundedIntegerOption
00482 ("log_num_obbt_per_level",
00483 "Specify the frequency (in terms of nodes) for optimality-based bound tightening.",
00484 -1,1,
00485 "\
00486 If -1, apply at every node (expensive!). \
00487 If 0, apply at root node only. \
00488 If k>=0, apply with probability 2^(k - level), level being the current depth of the B&B tree.");
00489
00490 roptions -> AddLowerBoundedIntegerOption
00491 ("max_fbbt_iter",
00492 "Number of FBBT iterations before stopping even with tightened bounds.",
00493 -1, MAX_FBBT_ITER,
00494 "Set to -1 to impose no upper limit");
00495
00496 roptions -> AddStringOption2
00497 ("aggressive_fbbt",
00498 "Aggressive feasibility-based bound tightening (to use with NLP points)",
00499 "yes",
00500 "no","",
00501 "yes","",
00502 "Aggressive FBBT is a version of probing that also allows to reduce the solution set, although it is not as quick as FBBT. "
00503 "It can be applied up to a certain depth of the B&B tree -- see ``log_num_abt_per_level''. "
00504 "In general, this option is useful but can be switched off if a problem is too large and seems not to benefit from it."
00505 );
00506
00507 roptions -> AddLowerBoundedIntegerOption
00508 ("log_num_abt_per_level",
00509 "Specify the frequency (in terms of nodes) for aggressive bound tightening.",
00510 -1,2,
00511 "\
00512 If -1, apply at every node (expensive!). \
00513 If 0, apply at root node only. \
00514 If k>=0, apply with probability 2^(k - level), level being the current depth of the B&B tree.");
00515
00516 roptions -> AddNumberOption
00517 ("art_lower",
00518 "Artificial lower bound",
00519 -COIN_DBL_MAX,
00520 "Default value is -COIN_DBL_MAX.");
00521
00522 roptions -> AddStringOption3
00523 ("branching_object",
00524 "type of branching object for variable selection",
00525 "var_obj",
00526 "vt_obj", "use Violation Transfer from Tawarmalani and Sahinidis",
00527 "var_obj", "use one object for each variable",
00528 "expr_obj", "use one object for each nonlinear expression");
00529
00530 roptions -> AddStringOption2
00531 ("delete_redundant",
00532 "Eliminate redundant variables, which appear in the problem as x_k = x_h",
00533 "yes",
00534 "no","Keep redundant variables, making the problem a bit larger",
00535 "yes","Eliminate redundant variables (the problem will be equivalent, only smaller)");
00536
00537 roptions -> AddStringOption4
00538 ("quadrilinear_decomp",
00539 "type of decomposition for quadrilinear terms (see work by Cafieri, Lee, Liberti)",
00540 "rAI",
00541 "rAI", "Recursive decomposition in bilinear terms (as in Ryoo and Sahinidis): x5 = ((x1 x2) x3) x4)",
00542 "tri+bi", "Trilinear and bilinear term: x5 = (x1 (x2 x3 x4))",
00543 "bi+tri", "Bilinear, THEN trilinear term: x5 = ((x1 x2) x3 x4))",
00544 "hier-bi", "Hierarchical decomposition: x5 = ((x1 x2) (x3 x4))");
00545 }