problem.cpp
Go to the documentation of this file.
1 /* $Id: problem.cpp 1085 2014-11-20 17:32:59Z pbelotti $
2  *
3  * Name: problem.cpp
4  * Author: Pietro Belotti
5  * Purpose: methods of the class CouenneProblem
6  *
7  * (C) Carnegie-Mellon University, 2006-10.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include <vector>
12 
13 #include "BonRegisteredOptions.hpp"
14 
15 #include "CoinHelperFunctions.hpp"
16 #include "CoinTime.hpp"
17 
18 #include "BonBabSetupBase.hpp"
19 
20 #include "CouenneTypes.hpp"
21 
22 #include "CouenneExpression.hpp"
23 #include "CouenneExprConst.hpp"
24 #include "CouenneExprGroup.hpp"
25 #include "CouenneExprClone.hpp"
26 #include "CouenneExprAux.hpp"
27 #include "CouenneProblem.hpp"
28 #include "CouenneGlobalCutOff.hpp"
29 #include "CouenneProblemElem.hpp"
30 #include "CouenneLQelems.hpp"
31 
32 using namespace Couenne;
33 
34 #define MAX_FBBT_ITER 3
35 
38  bonBase_ = base;
39  jnlst_ = base -> journalist ();
40 }
41 
42 // tricky... smaller values cut the optimum in OS unitTest
43 const CouNumber SafeCutoff = 1e-4;
44 
45 // absolute difference
46 const CouNumber SafeDelta = 1e-2;
47 
50 
51 void CouenneProblem::initAuxs () const {
52 
53  domain_.current () -> resize (nVars ());
54 
55  // initially, auxiliary variables are unbounded, their bounds only
56  // depending on their function
57 
58  int nvars = nVars ();
59 
60  for (int i=0; i < nvars; i++) {
61 
62  int indvar = variables_ [i] -> Index ();
63 
64  if (((variables_ [i] -> Type () == AUX) && // this is an auxiliary
65  (indvar >= nOrigVars_)) || // and not an original, originally
66  (variables_ [i] -> Multiplicity () == 0)) // or a useless one
67  //int index = variables_ [i] -> Index ();
68  Lb (indvar) = - (Ub (indvar) = COIN_DBL_MAX);
69  }
70 
71  // first initialize with values from constraints
72 
73  //Jnlst()->Printf(Ipopt::J_VECTOR, J_PROBLEM, "Initial bounds for aux (initAuxs):\n");
74 
75  for (std::vector <CouenneConstraint *>::const_iterator con = constraints_.begin ();
76  con != constraints_.end (); ++con) {
77 
78  CouNumber
79  lb = (*((*con) -> Lb ())) (),
80  ub = (*((*con) -> Ub ())) ();
81 
82  int index = (*con) -> Body () -> Index ();
83 
84  assert (index >= 0);
85 
86  if ((Lb (index) = CoinMax (Lb (index), lb)) <= -COUENNE_INFINITY) Lb (index) = -COIN_DBL_MAX;
87  if ((Ub (index) = CoinMin (Ub (index), ub)) >= COUENNE_INFINITY) Ub (index) = COIN_DBL_MAX;
88  }
89 
90  // only one loop is sufficient here, since auxiliary variable are
91  // defined in such a way that w_i does NOT depend on w_j if i<j.
92 
93  Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM, "InitAux -- assigning bounds\n");
94 
95  for (int j=0, i=nVars (); i--; j++) {
96 
97  int ord = numbering_ [j];
98 
99  // ignore these variables!
100  if (variables_ [ord] -> Multiplicity () == 0) {
101  Lb (ord) = - (Ub (ord) = COIN_DBL_MAX);
102  X (ord) = 0.;
103  continue;
104  }
105 
106  exprVar *var = variables_ [ord];
107 
108  // and handle only those with nonzero multiplicity
109  if (var -> Type () == AUX) {
110 
111  Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM,
112  "w_%04d [%10g,%10g] ", ord, Lb (ord), Ub (ord));
113 
114  CouNumber l, u;
115 
116  var -> Image () -> getBounds (l, u);
117 
118  /*printf ("printing bounds: [%g %g]\n", Lb (ord), Ub (ord));
119  var -> Lb () -> print (); printf ("\n");
120  var -> Ub () -> print (); printf ("\n");*/
121 
122  Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM,
123  " ( --> w_%04d [%10g,%10g] ) vs [%10g %10g]",
124  ord, l, u, Lb (ord), Ub (ord));
125 
126  // set bounds
127  if ((var -> sign () != expression::AUX_LEQ) && ((Lb (ord) = CoinMax (Lb (ord), l)) <= -COUENNE_INFINITY)) Lb (ord) = -COIN_DBL_MAX;
128  if ((var -> sign () != expression::AUX_GEQ) && ((Ub (ord) = CoinMin (Ub (ord), u)) >= COUENNE_INFINITY)) Ub (ord) = COIN_DBL_MAX;
129 
130  //if ((lb_ [ord] = (*(aux -> Lb ())) ()) <= -COUENNE_INFINITY) lb_ [ord] = -DBL_MAX;
131  //if ((ub_ [ord] = (*(aux -> Ub ())) ()) >= COUENNE_INFINITY) ub_ [ord] = DBL_MAX;
132 
133  bool integer = var -> isDefinedInteger ();
134 
135  if (integer) {
136  if (var -> sign () != expression::AUX_GEQ) Lb (ord) = ceil (Lb (ord) - COUENNE_EPS);
137  if (var -> sign () != expression::AUX_LEQ) Ub (ord) = floor (Ub (ord) + COUENNE_EPS);
138  }
139 
140  double image = (*(var -> Image ())) ();
141 
142  X (ord) = (*var) (); // take value, then only change if violating constraint (</>/=)
143 
144  if ((var -> sign () != expression::AUX_LEQ) && X (ord) < image) X (ord) = image;
145  else if ((var -> sign () != expression::AUX_GEQ) && X (ord) > image) X (ord) = image;
146 
147  // normalize within bounds
148 
149  X (ord) = CoinMax (Lb (ord), CoinMin (Ub (ord), X (ord)));
150 
151  Jnlst () -> Printf (Ipopt::J_MOREMATRIX, J_PROBLEM,
152  " --> [%10g,%10g] (%g)\n", Lb (ord), Ub (ord), X (ord));
153  }
154  }
155 
156  restoreUnusedOriginals (); // no argument as the local x vector will be used
157 }
158 
159 
163 
164  // set point at x, don't copy
165  domain_.push (nVars (), x, domain_.lb (), domain_.ub (), false);
166 
167  // if there are common expressions, set them (they usually don't get
168  // set by the AMPL interface as they are not really variables)
169  if (ndefined_ > 0)
170  for (int i = 0; i < nVars (); ++i) {
171  int ii = numbering_ [i];
172  if (ii >= nOrigVars_ - ndefined_ &&
173  ii < nOrigVars_)
174  X (ii) = (*(commonexprs_ [ii - nOrigVars_ + ndefined_])) ();
175  }
176 
177  // set auxiliary w to f(x). This procedure is exact even though the
178  // auxiliary variables have an incomplete image, i.e. they have been
179  // decomposed previously, since they are updated with increasing
180  // index.
181 
182  for (int j=0, i=nVars (); i--; j++) {
183 
184  int index = numbering_ [j];
185  exprVar *var = variables_ [index];
186 
187  if (var -> Multiplicity () > 0) {
188 
189  CouNumber l, u;
190 
191  if (var -> Type () == AUX)
192  var -> Image () -> getBounds (l,u);
193  else {
194  l = Lb (index);
195  u = Ub (index);
196  }
197 
198  if (var -> Type () == AUX) {
199 
200  CouNumber &x = X (index);
201 
202  bool isInt = var -> isDefinedInteger ();
203 
204  // if (Jnlst () -> ProduceOutput (Ipopt::J_ERROR, J_PROBLEM)) {
205  // printf ("checking "); var -> print ();
206  // printf (" = %g = %g. Sign: %d, int: %d, [%g,%g]",
207  // X (index), (*(var -> Image ())) (),
208  // var -> sign (), isInt, l, u);
209  // }
210 
211  if ((var -> sign () == expression::AUX_EQ) &&
212  ((index >= nOrigVars_) ||
213  (index < nOrigVars_ - ndefined_)))
214  x = (*(var -> Image ())) (); // addresses of x[] and X() are equal
215 
216  x =
217  CoinMax ((var -> sign () != expression::AUX_LEQ) ? (isInt ? ceil (l - COUENNE_EPS) : l) : -COIN_DBL_MAX,
218  CoinMin ((var -> sign () != expression::AUX_GEQ) ? (isInt ? floor (u + COUENNE_EPS) : u) : COIN_DBL_MAX, x));
219 
220  // feasibility heuristic: if semiaux, round value to nearest integer if variable is integer
221 
222  if (isInt) {
223  if (var -> sign () == expression::AUX_GEQ) x = ceil (x - COUENNE_EPS);
224  else if (var -> sign () == expression::AUX_LEQ) x = floor (x + COUENNE_EPS);
225  }
226 
227  //printf (" -> %g\n", X (index));
228  }
229  } else X (index) = 0.;
230  }
231 
233 
234  domain_.pop ();
235 }
236 
237 
240 
241 void CouenneProblem::fillObjCoeff (double *&obj) {
242 
243  // linearized objective can be an exprAux, an exprSub, an exprGroup,
244  // or an exprSum. In the last two cases, the components are
245  // variables or constants
246 
247  expression *body = objectives_ [0] -> Body ();
248  //int sense = objectives_ [0] -> Sense ();
249 
250  switch (body -> code ()) {
251 
252  case COU_EXPRVAR: //
253  obj [body -> Index ()] = 1; //(sense == MINIMIZE) ? 1 : -1;
254  break;
255 
256  case COU_EXPRSUB: { //
257 
258  expression **arglist = body -> ArgList ();
259 
260  obj [arglist [0] -> Index ()] = 1; //(sense == MINIMIZE) ? 1 : -1;
261  obj [arglist [1] -> Index ()] = -1; //(sense == MINIMIZE) ? -1 : 1;
262 
263  } break;
264 
265  case COU_EXPRGROUP: { //
266 
267  exprGroup *eg = dynamic_cast <exprGroup *> (body -> isaCopy () ?
268  body -> Copy () :
269  body);
270 
271  const exprGroup::lincoeff &lcoe = eg -> lcoeff ();
272 
273  // if (sense == MINIMIZE) while (*index >= 0) obj [*index++] = *coeff++;
274  // else while (*index >= 0) obj [*index++] = -*coeff++;
275 
276  for (int n = (int) lcoe.size (), i=0; n--; i++)
277  //exprGroup::lincoeff::iterator el = lcoe.begin (); el != lcoe.end (); ++el)
278  obj [lcoe [i]. first -> Index ()] = lcoe [i]. second;
279  //(sense == MINIMIZE) ?
280  //(lcoe [i]. second) :
281  //-(lcoe [i]. second);
282 
283  } // no break, as exprGroup is derived from exprSum
284 
285  case COU_EXPRSUM: { //
286 
287  expression **arglist = body -> ArgList ();
288 
289  for (int i = body -> nArgs (); i--;)
290  switch ((arglist [i]) -> code ()) {
291 
292  case COU_EXPRCONST:
293  break;
294 
295  case COU_EXPRVAR:
296  obj [arglist [i] -> Index ()] = 1; //(sense == MINIMIZE) ? 1 : -1;
297  break;
298 
299  case COU_EXPRMUL: {
300 
301  expression **mulArgList = arglist [i] -> ArgList ();
302  int index = mulArgList [0] -> Index ();
303 
304  if (index >= 0) obj [index] = mulArgList [1] -> Value ();
305  else obj [mulArgList [1] -> Index ()] = mulArgList [0] -> Value ();
306  } break;
307 
308  default:
309  Jnlst()->Printf(Ipopt::J_ERROR, J_PROBLEM,
310  "Couenne: invalid element of sum\nAborting\n");
311  exit (-1);
312  }
313  } break;
314 
315  case COU_EXPRCONST: break; // a constant objective
316 
317  default:
318  Jnlst()->Printf(Ipopt::J_WARNING, J_PROBLEM,
319  "Couenne: warning, objective function not recognized\n");
320  break;
321  }
322 }
323 
324 
326 void CouenneProblem::setCutOff (CouNumber cutoff, const double *s) const {
327 
328  if (cutoff > COUENNE_INFINITY/2)
329  return;
330 
331  int indobj = objectives_ [0] -> Body () -> Index ();
332 
333  // AW: Should we use the value of the objective variable computed by
334  // Couenne here?
335  if ((indobj >= 0) && (cutoff < pcutoff_ -> getCutOff () - COUENNE_EPS)) {
336 
337  //if (fabs (cutoff - pcutoff_ -> getCutOff ()) > (1 + fabs (cutoff)) * 2 * SafeCutoff) // avoid too many printouts
338  Jnlst () -> Printf (Ipopt::J_ERROR, J_COUENNE, "Couenne: new cutoff value %.10e (%g seconds)\n", cutoff, CoinCpuTime ());
339  //pcutoff_ -> getCutOff ());
340 
341  if (Var (indobj) -> isInteger ())
342  pcutoff_ -> setCutOff (this, floor (cutoff + COUENNE_EPS), s);
343  else pcutoff_ -> setCutOff (this, cutoff, s);
344  }
345 }
346 
349 
350  int indobj = objectives_ [0] -> Body () -> Index ();
351 
352  if ((indobj >= 0)) {
353 
354  if (Var (indobj) -> isInteger ())
355  pcutoff_ -> setCutOff (this, floor (value + COUENNE_EPS), NULL);
356  else pcutoff_ -> setCutOff (this, value, NULL);
357  }
358 }
359 
360 
364 
365  // all problem are assumed to be minimization
366  double cutoff = pcutoff_ -> getCutOff();
367 
368  if (cutoff > COUENNE_INFINITY)
369  return;
370 
371  int indobj = objectives_ [0] -> Body () -> Index ();
372 
373  //assert (indobj >= 0);
374 
375  if (indobj < 0)
376  return;
377 
378  //Jnlst () -> Printf (Ipopt::J_WARNING, J_PROBLEM,
379  //"installing cutoff %.10e vs current ub %.10e\n",
380  //cutoff, Ub (indobj));
381 
382  cutoff = (Var (indobj) -> isInteger ()) ?
383  floor (cutoff + COUENNE_EPS) :
384  (cutoff + CoinMin (SafeDelta, SafeCutoff * (1. + fabs (cutoff)))); // tolerance needed to retain feasibility
385 
386  if (cutoff < Ub (indobj))
387  Ub (indobj) = cutoff;
388 }
389 
390 
391 // clear all spurious variables pointers not referring to the variables_ vector
393 
394  // link variables to problem's domain
395  for (std::vector <exprVar *>::iterator i = variables_.begin ();
396  i != variables_.end (); ++i) {
397 
398  if ((*i) -> Multiplicity () <= 0)
399  continue;
400 
401  (*i) -> linkDomain (&domain_);
402  (*i) -> realign (this);
403  if ((*i) -> Type () == AUX)
404  (*i) -> Image () -> realign (this);
405  }
406 
407  // link variables to problem's domain
408  for (std::vector <CouenneObjective *>::iterator i = objectives_.begin ();
409  i != objectives_.end (); ++i)
410  (*i) -> Body () -> realign (this);
411 
412 
413  // link variables to problem's domain
414  for (std::vector <CouenneConstraint *>::iterator i = constraints_.begin ();
415  i != constraints_.end (); ++i)
416  (*i) -> Body () -> realign (this);
417 }
418 
419 
422 
423  roptions -> SetRegisteringCategory ("Couenne options", Bonmin::RegisteredOptions::CouenneCategory);
424 
425  roptions -> AddNumberOption
426  ("art_cutoff",
427  "Artificial cutoff",
428  COIN_DBL_MAX,
429  "Default value is infinity.");
430 
431  roptions -> AddNumberOption
432  ("opt_window",
433  "Window around known optimum",
434  COIN_DBL_MAX,
435  "Default value is infinity.");
436 
437  roptions -> AddStringOption2
438  ("use_semiaux",
439  "Use semiauxiliaries, i.e. auxiliaries defined as w >= f(x) rather than w := f(x))",
440  "yes",
441  "no", "Only use auxiliaries assigned with \"=\" ",
442  "yes", "Use auxiliaries defined by w <= f(x), w >= f(x), and w = f(x)"
443  );
444 
445  roptions -> AddStringOption2
446  ("use_auxcons",
447  "Use constraints-defined auxiliaries, i.e. auxiliaries w = f(x) defined by original constraints f(x) - w = 0",
448  "yes",
449  "no", "",
450  "yes", ""
451  );
452 
453  roptions -> AddStringOption2
454  ("redcost_bt",
455  "Reduced cost bound tightening",
456  "yes",
457  "no","",
458  "yes","",
459  "This bound reduction technique uses the reduced costs of the LP in order to infer better variable bounds.");
460 
461  roptions -> AddStringOption2
462  ("use_quadratic",
463  "Use quadratic expressions and related exprQuad class",
464  "no",
465  "no","Use an auxiliary for each bilinear term",
466  "yes","Create only one auxiliary for a quadratic expression",
467  "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. "
468  "Envelopes for these expressions are generated through alpha-convexification."
469  );
470 
471  roptions -> AddStringOption2
472  ("optimality_bt",
473  "Optimality-based (expensive) bound tightening (OBBT)",
474  "yes",
475  "no","",
476  "yes","",
477  "This is another bound reduction technique aiming at reducing the solution set by looking at the initial LP relaxation. "
478  "This technique is computationally expensive, and should be used only when necessary."
479  );
480 
481  roptions -> AddLowerBoundedIntegerOption
482  ("log_num_obbt_per_level",
483  "Specify the frequency (in terms of nodes) for optimality-based bound tightening.",
484  -1,1,
485  "\
486 If -1, apply at every node (expensive!). \
487 If 0, apply at root node only. \
488 If k>=0, apply with probability 2^(k - level), level being the current depth of the B&B tree.");
489 
490  roptions -> AddLowerBoundedIntegerOption
491  ("max_fbbt_iter",
492  "Number of FBBT iterations before stopping even with tightened bounds.",
493  -1, MAX_FBBT_ITER,
494  "Set to -1 to impose no upper limit");
495 
496  roptions -> AddStringOption2
497  ("aggressive_fbbt",
498  "Aggressive feasibility-based bound tightening (to use with NLP points)",
499  "yes",
500  "no","",
501  "yes","",
502  "Aggressive FBBT is a version of probing that also allows to reduce the solution set, although it is not as quick as FBBT. "
503  "It can be applied up to a certain depth of the B&B tree -- see ``log_num_abt_per_level''. "
504  "In general, this option is useful but can be switched off if a problem is too large and seems not to benefit from it."
505  );
506 
507  roptions -> AddLowerBoundedIntegerOption
508  ("log_num_abt_per_level",
509  "Specify the frequency (in terms of nodes) for aggressive bound tightening.",
510  -1,2,
511  "\
512 If -1, apply at every node (expensive!). \
513 If 0, apply at root node only. \
514 If k>=0, apply with probability 2^(k - level), level being the current depth of the B&B tree.");
515 
516  roptions -> AddNumberOption
517  ("art_lower",
518  "Artificial lower bound",
519  -COIN_DBL_MAX,
520  "Default value is -COIN_DBL_MAX.");
521 
522  roptions -> AddStringOption3
523  ("branching_object",
524  "type of branching object for variable selection",
525  "var_obj",
526  "vt_obj", "use Violation Transfer from Tawarmalani and Sahinidis",
527  "var_obj", "use one object for each variable",
528  "expr_obj", "use one object for each nonlinear expression");
529 
530  roptions -> AddStringOption2
531  ("delete_redundant",
532  "Eliminate redundant variables, which appear in the problem as x_k = x_h",
533  "yes",
534  "no","Keep redundant variables, making the problem a bit larger",
535  "yes","Eliminate redundant variables (the problem will be equivalent, only smaller)");
536 
537  roptions -> AddStringOption4
538  ("quadrilinear_decomp",
539  "type of decomposition for quadrilinear terms (see work by Cafieri, Lee, Liberti)",
540  "rAI",
541  "rAI", "Recursive decomposition in bilinear terms (as in Ryoo and Sahinidis): x5 = ((x1 x2) x3) x4)",
542  "tri+bi", "Trilinear and bilinear term: x5 = (x1 (x2 x3 x4))",
543  "bi+tri", "Bilinear, THEN trilinear term: x5 = ((x1 x2) x3 x4))",
544  "hier-bi", "Hierarchical decomposition: x5 = ((x1 x2) (x3 x4))");
545 }
void realign()
clear all spurious variables pointers not referring to the variables_ vector
Definition: problem.cpp:392
GlobalCutOff * pcutoff_
Pointer to a global cutoff object.
int nVars() const
Total number of variables.
class Group, with constant, linear and nonlinear terms:
const CouNumber SafeDelta
Definition: problem.cpp:46
CouNumber & ub(register int index)
current upper bound
std::vector< CouenneObjective * > objectives_
Objectives.
CouNumber & lb(register int index)
current lower bound
void setCutOff(CouNumber cutoff, const CouNumber *sol=NULL) const
Set cutoff.
Definition: problem.cpp:326
std::vector< expression * > commonexprs_
AMPL&#39;s common expressions (read from AMPL through structures cexps and cexps1)
static char * j
Definition: OSdtoa.cpp:3622
CouNumber getCutOff() const
Get cutoff.
void setBase(Bonmin::BabSetupBase *base)
save CouenneBase
Definition: problem.cpp:37
void fint fint fint real fint real real real real real real real real real * e
A class to have all elements necessary to setup a branch-and-bound.
void restoreUnusedOriginals(CouNumber *=NULL) const
Some originals may be unused due to their zero multiplicity (that happens when they are duplicates)...
void getAuxs(CouNumber *) const
Get auxiliary variables from original variables.
Definition: problem.cpp:162
int ndefined_
Number of &quot;defined variables&quot; (aka &quot;common expressions&quot;)
ConstJnlstPtr Jnlst() const
Provide Journalist.
Bonmin::BabSetupBase * bonBase_
options
std::vector< CouenneConstraint * > constraints_
Constraints.
const CouNumber SafeCutoff
Definition: problem.cpp:43
void pop()
restore previous point
Definition: domain.cpp:255
void fint fint fint fint fint fint fint fint fint fint real real real real real real real real * s
#define COUENNE_EPS
exprVar * Var(int i) const
Return pointer to i-th variable.
std::vector< exprVar * > variables_
Variables (original, auxiliary, and defined)
Domain domain_
current point and bounds;
int * numbering_
numbering of variables.
CouNumber * Ub() const
Return vector of upper bounds.
void installCutOff() const
Make cutoff known to the problem.
Definition: problem.cpp:363
void initAuxs() const
Initialize auxiliary variables and their bounds from original variables.
Definition: problem.cpp:51
std::vector< std::pair< exprVar *, CouNumber > > lincoeff
double CouNumber
main number type in Couenne
int nOrigVars_
Number of original variables.
void resetCutOff(CouNumber value=COUENNE_INFINITY) const
Reset cutoff.
Definition: problem.cpp:348
#define COUENNE_INFINITY
CouNumber * X() const
Return vector of variables.
#define MAX_FBBT_ITER
Definition: problem.cpp:34
void push(int dim, CouNumber *x, CouNumber *lb, CouNumber *ub, bool copy=true)
save current point and start using another
Definition: domain.cpp:166
variable-type operator
JnlstPtr jnlst_
SmartPointer to the Journalist.
Expression base class.
DomainPoint * current()
return current point
const Ipopt::EJournalCategory J_COUENNE(Ipopt::J_USER8)
static void registerOptions(Ipopt::SmartPtr< Bonmin::RegisteredOptions > roptions)
Add list of options to be read from file.
Definition: problem.cpp:421
const Ipopt::EJournalCategory J_PROBLEM(Ipopt::J_USER4)
void fint * n
void fillObjCoeff(double *&)
Fill vector with coefficients of objective function.
Definition: problem.cpp:241
void fint fint fint real fint real * x
CouNumber * Lb() const
Return vector of lower bounds.
bool isInteger(CouNumber x)
is this number integer?