aggressiveBT.cpp
Go to the documentation of this file.
1 /* $Id: aggressiveBT.cpp 997 2013-08-11 23:21:38Z pbelotti $
2  *
3  * Name: aggressiveBT.cpp
4  * Author: Pietro Belotti
5  * Purpose: probing -- fake bounds in variables to exclude parts of
6  * the solution space through fathoming on
7  * bounds/infeasibility
8  *
9  * (C) Carnegie-Mellon University, 2007-11.
10  * This file is licensed under the Eclipse Public License (EPL)
11  */
12 
13 #include "CouenneCutGenerator.hpp"
14 
15 #include "BonTNLPSolver.hpp"
16 #include "BonNlpHeuristic.hpp"
17 #include "CoinHelperFunctions.hpp"
18 #include "BonCouenneInfo.hpp"
19 
20 #include "CouenneProblem.hpp"
21 #include "CouenneExprVar.hpp"
22 #include "CouenneProblemElem.hpp"
23 
24 using namespace Ipopt;
25 using namespace Couenne;
26 
27 namespace Bonmin {
28  class OsiTMINLPInterface;
29  class BabInfo;
30  class TNLPSolver;
31 }
32 
33 #define MAX_ABT_ITER 4 // max # aggressive BT iterations
34 #define THRES_ABT_IMPROVED 0 // only continue ABT if at least these bounds have improved
35 #define THRES_ABT_ORIG 100 // only do ABT on auxiliaries if they are less originals than this
36 
37 static double distanceToBound (register int n,
38  register const double* xOrig,
39  register const double* lower,
40  register const double* upper,
41  register double cutoff_distance) { // stop if distance is above this
42 
43  register double Xdist = 0.;
44 
45  for (; n--; ++upper, ++xOrig) {
46 
47  register CouNumber diff = *lower++ - *xOrig;
48 
49  if ( diff > 0.) {if ((Xdist += diff) > cutoff_distance) break;}
50  else if ((diff = *xOrig - *upper) > 0.) {if ((Xdist += diff) > cutoff_distance) break;}
51  }
52 
53  return Xdist;
54 }
55 
56 
57 // PROBING: for each variable, fake new bounds [l,b] or [b,u], with
58 // given b, and apply bound tightening. If the interval is fathomed on
59 // bounds or on infeasibility, the complementary bound interval is a
60 // valid tightening.
61 
62 bool CouenneProblem::aggressiveBT (Bonmin::OsiTMINLPInterface *nlp,
63  t_chg_bounds *chg_bds,
64  const CglTreeInfo &info,
65  Bonmin::BabInfo * babInfo) const {
66 
67  if (info.level <= 0 && !(info.inTree)) {
68  jnlst_ -> Printf (J_ERROR, J_COUENNE, "Probing: ");
69  fflush (stdout);
70  }
71 
72  CouenneInfo* couInfo =
73  dynamic_cast <CouenneInfo *> (babInfo);
74 
75  int ncols = nVars ();
76  bool retval = false;
77 
78  CouNumber
79  *olb = CoinCopyOfArray (Lb (), ncols),
80  *oub = CoinCopyOfArray (Ub (), ncols);
81 
82  // Find the solution that is closest to the current bounds
83  // TODO: Also check obj value
85  double dist = 1.e50;
86 
87  if (couInfo) {
88 
89  const std::list<SmartPtr<const CouenneInfo::NlpSolution> >& solList =
90  couInfo->NlpSolutions();
91 
92  for (std::list<SmartPtr<const CouenneInfo::NlpSolution> >::const_iterator
93  i = solList.begin();
94  i != solList.end(); ++i) {
95 
96  assert (nOrigVars_ - ndefined_ == (*i)->nVars());
97 
98  const double thisDist = distanceToBound (nOrigVars_ - ndefined_, (*i)->solution(), olb, oub, dist);
99 
100  if (thisDist < dist) {
101  closestSol = *i;
102  dist = thisDist;
103  }
104  }
105  }
106 
107  jnlst_ -> Printf (J_VECTOR, J_BOUNDTIGHTENING, "best dist = %e\n", dist);
108 
109  bool haveNLPsol = false;
110 
111  // If this solution is not sufficiently inside the bounds, we solve the NLP now
112  if (dist > 0.1) { // TODO: Find tolerance
113 
114  // find integer initial point /////////////////////////
115 
116  int nvars = nVars ();
117 
118  double *lower = new double [nvars];
119  double *upper = new double [nvars];
120 
121  CoinFillN (lower, nvars, -COUENNE_INFINITY);
122  CoinFillN (upper, nvars, COUENNE_INFINITY);
123 
124  CoinCopyN (nlp -> getColLower (), nOrigVars_ - ndefined_, lower);
125  CoinCopyN (nlp -> getColUpper (), nOrigVars_ - ndefined_, upper);
126 
127  double *Y = new double [nvars];
128 
129  CoinZeroN (Y, nvars);
130  CoinCopyN (X (), nOrigVars_ - ndefined_, Y);
131 
132  if (getIntegerCandidate (nlp -> getColSolution () ? nlp -> getColSolution () : X (), Y, lower, upper) < 0) {
133 
134  jnlst_ -> Printf (J_ITERSUMMARY, J_BOUNDTIGHTENING, "TODO: find NLP point in ABT failed\n");
135  retval = true;
136 
137  } else {
138 
140 
141  nlp -> setColLower (lower);
142  nlp -> setColUpper (upper);
143  nlp -> setColSolution (Y);
144 
145  try {
146 
147  nlp -> options () -> SetNumericValue ("max_cpu_time", CoinMax (0.1, getMaxCpuTime () - CoinCpuTime ()));
148  nlp -> initialSolve ();
149  }
150 
152 
153  delete [] Y;
154  delete [] lower;
155  delete [] upper;
156 
157  if (nlp -> isProvenOptimal ()) {
158 
159  if (couInfo) {
160  closestSol = new CouenneInfo::NlpSolution
161  (nOrigVars_ - ndefined_, nlp->getColSolution(), nlp->getObjValue());
162  couInfo->addSolution(closestSol);
163  dist = 0.;
164  haveNLPsol = true;
165  }
166  }
167  else {
168  jnlst_ -> Printf(J_ITERSUMMARY, J_BOUNDTIGHTENING, "TODO: NLP solve in ABT failed\n");
169  retval = true;
170  }
171  }
172  }
173 
174  int nTotImproved = 0;
175 
176  // Probing can also run on an LP point.
177 
178  //if (true || (retval && (dist < 1e10))) {
179 
180  {
181  retval = true;
182 
183  // if the NLP solver succeeded, X is now the NLP solution, but in
184  // a low-dimensional space. We have to get the corresponding point
185  // in higher dimensional space through getAuxs()
186 
187  double *X = NULL;
188 
189  if (haveNLPsol) {
190 
191  X = new double [ncols];
192  CoinZeroN (X, nVars ());
193  CoinCopyN (closestSol -> solution(), nOrigVars_ - ndefined_, X);
194  getAuxs (X);
195  } else X = domain () -> x ();
196 
197  // create a new, fictitious, bound bookkeeping structure
198  t_chg_bounds *f_chg = new t_chg_bounds [ncols];
199 
200  if (Jnlst()->ProduceOutput(J_ITERSUMMARY, J_BOUNDTIGHTENING)) {
201  int objind = Obj (0) -> Body () -> Index ();
202  for (int i=0; i<nOrigVars_ - ndefined_; i++) Jnlst()->Printf(J_MOREVECTOR, J_BOUNDTIGHTENING, " %2d %+20g [%+20g %+20g]\n",i, X [i], Lb (i), Ub (i));
203  if (objind >= 0) Jnlst()->Printf(J_ITERSUMMARY, J_BOUNDTIGHTENING, "-------------\nAggressive BT. Current bound = %g, cutoff = %g, %d vars\n", Lb (objind), getCutOff (), ncols);
204  }
205 
206  int improved, second, iter = 0;
207 
208  // Repeatedly fake tightening bounds on both sides of every variable
209  // to concentrate around current NLP point.
210  //
211  // MAX_ABT_ITER is the maximum # of outer cycles. Each call to
212  // fake_tighten in turn has an iterative algorithm for a
213  // derivative-free, uni-dimensional optimization problem on a
214  // monotone function.
215 
216  bool maxTimeReached = false;
217 
218  do {
219 
220  improved = 0;
221 
222  // scan all variables
223  for (int i=0; i<ncols; i++) {
224 
225  if (CoinCpuTime () > maxCpuTime_) {
226  maxTimeReached = true; // avoids getrusage after loop...
227  break;
228  }
229 
230  int index = evalOrder (i);
231 
232  if ((Var (index) -> Multiplicity () <= 0) ||
233  (fabs (Lb (index) - Ub (index)) < COUENNE_EPS) ||
234  ((nOrigVars_ >= THRES_ABT_ORIG) &&
235  (index >= nOrigVars_)))
236  continue;
237 
238  // AW: We only want to do the loop that temporarily changes
239  // bounds around the NLP solution only for those points from the
240  // NLP solution (no auxiliary vars)?
241 
242  // PBe: if we do want that, index should be initialized as i, as
243  // evalOrder gives a variable index out of an array index.
244 
245  // PBe: That makes a lot of sense when problems are really
246  // big. Instances arki000[24].nl spend a lot of time here
247 
248  { //if ((nOrigVars_ < THRES_ABT_ORIG) || (index < nOrigVars_)) {
249 
250  // if (index == objind) continue; // don't do it on objective function
251 
252  improved = 0;
253 
254  if ((variables_ [index] -> sign () != expression::AUX_GEQ) &&
255  (X [index] >= Lb (index) + COUENNE_EPS)) {
256 
257  Jnlst()->Printf(J_DETAILED, J_BOUNDTIGHTENING, "------------- tighten left %d-th = x%d @%g [%g,%g]\n", i, index, X [index], olb [index], oub [index]);
258 
259  // tighten on left
260  if ((improved = fake_tighten (0, index, X, olb, oub, chg_bds, f_chg)) < 0) {
261 
262  retval = false;
263  break;
264  }
265  }
266 
267  second = 0;
268 
269  if (retval && (variables_ [index] -> sign () != expression::AUX_LEQ) &&
270  (X [index] <= Ub (index) - COUENNE_EPS)) {
271 
272  Jnlst()->Printf(J_DETAILED, J_BOUNDTIGHTENING, "------------- tighten right %d-th = x%d @%g [%g,%g]\n", i, index, X [index], olb [index], oub [index]);
273 
274  // tighten on right
275  if ((second = fake_tighten (1, index, X, olb, oub, chg_bds, f_chg) < 0)) {
276 
277  retval = false;
278  break;
279  }
280  }
281 
282  improved += second;
283  nTotImproved += improved;
284  }
285  }
286 
287  } while (!retval && (improved > THRES_ABT_IMPROVED) && (iter++ < MAX_ABT_ITER));
288 
289  // store new valid bounds, or restore old ones if none changed
290  CoinCopyN (olb, ncols, Lb ());
291  CoinCopyN (oub, ncols, Ub ());
292 
293  if (Jnlst()->ProduceOutput(J_ITERSUMMARY, J_BOUNDTIGHTENING)) {
294 
295  Jnlst()->Printf(J_ITERSUMMARY, J_BOUNDTIGHTENING,"------------------\n");
296  if (!retval) Jnlst()->Printf(J_ITERSUMMARY, J_BOUNDTIGHTENING, "Couenne infeasible node from aggressive BT\n");
297 
298  int objind = Obj (0) -> Body () -> Index ();
299 
300  if (objind >= 0)Jnlst()->Printf(J_ITERSUMMARY, J_BOUNDTIGHTENING, "-------------\ndone Aggressive BT. Current bound = %g, cutoff = %g, %d vars\n", Lb (objind), getCutOff (), ncols);
301  if (Jnlst()->ProduceOutput(J_DETAILED, J_BOUNDTIGHTENING)) for (int i=0; i<nOrigVars_; i++) printf (" x%02d [%+20g %+20g] | %+20g\n", i, Lb (i), Ub (i), X [i]);
302  if (Jnlst()->ProduceOutput(J_MOREDETAILED, J_BOUNDTIGHTENING)) for (int i=nOrigVars_; i<ncols; i++) printf (" w%02d [%+20g %+20g] | %+20g\n", i, Lb (i), Ub (i), X [i]);
303  }
304 
305  if (haveNLPsol)
306  delete [] X;
307  delete [] f_chg;
308  }
309  // else
310  // if ((dist > 1e10) && !retval)
311  // jnlst_ -> Printf(J_ITERSUMMARY, J_BOUNDTIGHTENING, "TODO: Don't have point for ABT\n");
312 
313  delete [] olb;
314  delete [] oub;
315 
316  if (info.level <= 0 && !(info.inTree)) {
317  if (!retval) jnlst_ -> Printf (J_ERROR, J_COUENNE, "infeasible\n");
318  else jnlst_ -> Printf (J_ERROR, J_COUENNE, "%d improved bounds\n", nTotImproved);
319  }
320 
321  return retval; // && btCore (psi, cs, chg_bds, babInfo, true); // !!!
322  //return retval && btCore (psi, cs, chg_bds, babInfo, true);
323 }
#define THRES_ABT_ORIG
static Bigint * diff(Bigint *a, Bigint *b)
Definition: OSdtoa.cpp:1120
void fint fint fint real fint real real real real real real real real real fint real fint fint fint real fint fint fint fint * info
Class for storing an Nlp Solution.
status of lower/upper bound of a variable, to be checked/modified in bound tightening ...
This is class provides an Osi interface for a Mixed Integer Linear Program expressed as a TMINLP (so ...
static double distanceToBound(register int n, register const double *xOrig, register const double *lower, register const double *upper, register double cutoff_distance)
#define MAX_ABT_ITER
virtual double getObjValue() const
Get objective function value (can&#39;t use default)
virtual const double * getColSolution() const
Get pointer to array[getNumCols()] of primal solution vector.
Bonmin class for passing info between components of branch-and-cuts.
const std::list< Ipopt::SmartPtr< const NlpSolution > > & NlpSolutions() const
List of all stored NLP solutions.
void addSolution(Ipopt::SmartPtr< const NlpSolution > newSol)
Add a new NLP solution.
const Ipopt::EJournalCategory J_BOUNDTIGHTENING(Ipopt::J_USER2)
#define COUENNE_EPS
double CouNumber
main number type in Couenne
#define THRES_ABT_IMPROVED
#define COUENNE_INFINITY
We will throw this error when a problem is not solved.
const Ipopt::EJournalCategory J_COUENNE(Ipopt::J_USER8)
void fint * n
Bonmin class for passing info between components of branch-and-cuts.
Definition: BonBabInfos.hpp:19
void fint fint fint real fint real * x