obbt.cpp
Go to the documentation of this file.
1 /* $Id: obbt.cpp 698 2011-06-20 13:36:43Z pbelotti $
2  *
3  * Name: obbt.cpp
4  * Author: Pietro Belotti
5  * Purpose: Optimality-Based Bound Tightening
6  *
7  * (C) Carnegie-Mellon University, 2006-10.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "CoinHelperFunctions.hpp"
12 #include "CglCutGenerator.hpp"
13 #include "OsiClpSolverInterface.hpp"
14 
15 #include "CouenneExpression.hpp"
16 #include "CouenneExprVar.hpp"
17 #include "CouenneCutGenerator.hpp"
18 #include "CouenneProblem.hpp"
19 #include "CouenneInfeasCut.hpp"
20 
21 using namespace Ipopt;
22 using namespace Couenne;
23 
24 #define THRESH_OBBT_AUX 50 // if more than this originals, don't do OBBT on auxs
25 #define OBBT_EPS 1e-3
26 #define MAX_OBBT_LP_ITERATION 100
27 #define MAX_OBBT_ATTEMPTS 1 // number of OBBT iterations at root node
28  // -- fixed at one as for some instance it
29  // doesn't seem to do anything after first run
30 
31 // minimum #bound changed in obbt to generate further cuts
32 #define THRES_NBD_CHANGED 1
33 
34 // maximum number of obbt iterations
35 #define MAX_OBBT_ITER 1
36 
37 // defined in generateCuts.cpp
38 void sparse2dense (int ncols, t_chg_bounds *chg_bds, int *&changed, int &nchanged);
39 
40 
41 // OBBT for one sense (max/min) and one class of variables (orig/aux)
42 int CouenneProblem::call_iter (OsiSolverInterface *csi,
43  t_chg_bounds *chg_bds,
44  const CoinWarmStart *warmstart,
45  Bonmin::BabInfo *babInfo,
46  double *objcoe,
47  enum nodeType type,
48  int sense) const {
49 
50  int ncols = csi -> getNumCols (),
51  nimprov = 0;
52 
53  for (int ii=0; ii<ncols; ii++) {
54 
55  if (CoinCpuTime () > maxCpuTime_)
56  break;
57 
58  int i = evalOrder (ii);
59 
60  enum expression::auxSign aSign = Var (i) -> sign ();
61 
62  if ((Var (i) -> Type () == type) &&
63  (Var (i) -> Multiplicity () > 0) &&
64  ((type == VAR) ||
65  (aSign == expression::AUX_EQ) ||
66  ((aSign == expression::AUX_LEQ) && (sense > 0)) ||
67  ((aSign == expression::AUX_GEQ) && (sense < 0)))) {
68 
69  int ni = obbt_iter (csi, chg_bds, warmstart, babInfo, objcoe, sense, i);
70 
71 // {
72 // // ToDo: Pipe all output through journalist
73 // Jnlst()->Printf(J_DETAILED, J_BOUNDTIGHTENING,
74 // " bounds after obbt step =====================\n ");
75 // int j=0;
76 // for (int i=0; i < nVars (); i++)
77 // if (variables_ [i] -> Multiplicity () > 0) {
78 // Jnlst()->Printf(J_VECTOR, J_BOUNDTIGHTENING,
79 // "x_%03d [%+10g %+10g] ", i,
80 // domain_. lb (i),
81 // domain_. ub (i));
82 // if (!(++j % 6)) Jnlst()->Printf(J_VECTOR, J_BOUNDTIGHTENING,"\n ");
83 // }
84 // if (j % 6) Jnlst()->Printf(J_VECTOR, J_BOUNDTIGHTENING,"\n");
85 // }
86 
87  if (ni < 0) return ni;
88  nimprov += ni;
89  }
90  }
91 
92  return nimprov;
93 }
94 
95 
97 
98 int CouenneProblem::obbtInner (OsiSolverInterface *csi,
99  OsiCuts &cs,
100  t_chg_bounds *chg_bds,
101  Bonmin::BabInfo * babInfo) const {
102 
103  // set large bounds to infinity (as per suggestion by JJF)
104 
105  int ncols = csi -> getNumCols ();
106  const double *lb = csi -> getColLower (),
107  *ub = csi -> getColUpper ();
108 
109  double inf = csi -> getInfinity ();
110 
111  for (int i=ncols; i--;) {
112  if (lb [i] < - COUENNE_INFINITY) csi -> setColLower (i, -inf);
113  if (ub [i] > COUENNE_INFINITY) csi -> setColUpper (i, inf);
114  }
115 
116  // csi -> setHintParam (OsiDoDualInResolve, false);
117 
118  // setup cloned interface for later use
119  csi -> setObjSense (1); // minimization
120  csi -> setIntParam (OsiMaxNumIteration, MAX_OBBT_LP_ITERATION);
121  csi -> applyCuts (cs); // apply all (row+column) cuts to formulation
122  csi -> initialSolve ();
123 
124  const CoinWarmStart *warmstart = csi -> getWarmStart ();
125 
126  // improve each bound
127 
128  double *objcoe = (double *) malloc (ncols * sizeof (double));
129 
130  // set obj function coefficients to zero
131  for (int i=ncols; i--;)
132  *objcoe++ = 0.;
133  objcoe -= ncols;
134 
135  csi -> setObjective (objcoe);
136  csi -> setObjSense (1); // minimization
137 
138  int nimprov = 0;
139 
140  const int Infeasible = 1;
141 
142  try {
143 
144  int ni;
145 
146  if ((ni = call_iter (csi, chg_bds, warmstart, babInfo, objcoe, VAR, 1)) < 0) throw Infeasible;
147  nimprov += ni;
148 
149  if ((ni = call_iter (csi, chg_bds, warmstart, babInfo, objcoe, VAR, -1)) < 0) throw Infeasible;
150  nimprov += ni;
151 
152  if (nVars () < THRESH_OBBT_AUX) {
153 
154  if ((ni = call_iter (csi, chg_bds, warmstart, babInfo, objcoe, AUX, 1)) < 0) throw Infeasible;
155  nimprov += ni;
156 
157  if ((ni = call_iter (csi, chg_bds, warmstart, babInfo, objcoe, AUX, -1)) < 0) throw Infeasible;
158  nimprov += ni;
159  }
160  }
161 
162  catch (int exception) {
163 
164  if (exception == Infeasible)
165  nimprov = -1;
166  }
167 
168  free (objcoe);
169  delete warmstart;
170 
171  return (nimprov);
172 }
173 
174 
175 // Optimality based bound tightening -- main loop
176 int CouenneProblem::obbt (const CouenneCutGenerator *cg,
177  const OsiSolverInterface &si,
178  OsiCuts &cs,
179  const CglTreeInfo &info,
180  Bonmin::BabInfo * babInfo,
181  t_chg_bounds *chg_bds) {
182 
183  // TODO: set up list of hopeless variables and do different OBBT
184  // saving lps
185 
186  // Check if cs contains only one cut and if it is of the form 1 <=
187  // x0 <= -1. That means a previous cut generator has determined that
188  // this node is infeasible and we shouldn't take the pain of running
189  // this CGL.
190 
191  if (isWiped (cs) || info.pass >= MAX_OBBT_ATTEMPTS)
192  return 0;
193 
194  int nTotImproved = 0;
195 
196  // Do OBBT if:
197  if (doOBBT_ && // flag is checked, AND
198  ((logObbtLev_ != 0) || // (parameter is nonzero OR
199  (info.level == 0)) && // we are at root node), AND
200  (info.pass == 0) && // at first round of cuts, AND
201  ((logObbtLev_ < 0) || // (logObbtLev = -1, OR
202  (info.level <= logObbtLev_) || // depth is lower than COU_OBBT_CUTOFF_LEVEL, OR
203  // probability inversely proportional to the level)
204  (CoinDrand48 () < pow (2., (double) logObbtLev_ - (info.level + 1))))) {
205 
206  if ((info.level <= 0 && !(info.inTree)) ||
207  jnlst_ -> ProduceOutput (J_STRONGWARNING, J_COUENNE)) {
208 
209  jnlst_ -> Printf (J_ERROR, J_COUENNE, "Optimality Based BT: ");
210  //nVars () > THRESH_OBBT_AUX ? nOrigVars_ : nVars (), info.pass);
211  fflush (stdout);
212  }
213 
214  jnlst_ -> Printf (J_ITERSUMMARY, J_BOUNDTIGHTENING, "----- OBBT\n");
215 
216  // TODO: why check info.pass==0? Why not more than one pass? It
217  // should be anyway checked that info.level be >= 0 as <0 means
218  // first call at root node
219 
220  OsiSolverInterface *csi = si.clone (true);
221 
222  csi -> messageHandler () -> setLogLevel (0);
223  //dynamic_cast <CouenneSolverInterface<T> *>
224 
225  OsiClpSolverInterface *clpcsi = dynamic_cast <OsiClpSolverInterface *> (csi);
226 
227  if (clpcsi)
228  clpcsi -> setupForRepeatedUse ();
229  //csi -> doingResolve () = false;
230 
231  //csi -> setHintParam (OsiDoDualInResolve, false);
232 
233  int nImprov, nIter = 0;
234 
235  bool notImproved = false;
236 
237  while (!notImproved &&
238  (nIter++ < MAX_OBBT_ITER) &&
239  ((nImprov = obbtInner (csi, cs, chg_bds, babInfo)) > 0) &&
240  (CoinCpuTime () < maxCpuTime_)) {
241 
242  int nchanged, *changed = NULL;
243 
245  sparse2dense (nVars (), chg_bds, changed, nchanged);
246  cg -> genColCuts (*csi, cs, nchanged, changed);
247 
248  nTotImproved += nImprov;
249 
250  if ((nIter < MAX_OBBT_ITER) &&
251  (nImprov >= THRES_NBD_CHANGED)) {
252 
253  // only generate new row cuts if improvents are enough
254  int nCurCuts = cs.sizeRowCuts ();
255  cg -> genRowCuts (*csi, cs, nchanged, changed, chg_bds);
256 
257  if (nCurCuts == cs.sizeRowCuts ())
258  notImproved = true; // repeat only if new cuts available
259 
260  } else notImproved = true;
261 
262  if (changed)
263  free (changed);
264  }
265 
266  //csi -> doingResolve () = true;
267 
268  delete csi;
269 
270  if ((info.level <= 0 && !(info.inTree)) ||
271  jnlst_ -> ProduceOutput (J_STRONGWARNING, J_COUENNE))
272  jnlst_ -> Printf (J_ERROR, J_COUENNE, "%d improved bounds\n", nTotImproved);
273 
274  if (nImprov < 0) {
275  jnlst_->Printf(J_ITERSUMMARY, J_BOUNDTIGHTENING, " Couenne: infeasible node after OBBT\n");
276  return -1;
277  }
278  }
279 
280  return 0;
281 }
Cut Generator for linear convexifications.
#define MAX_OBBT_ITER
Definition: obbt.cpp:35
#define MAX_OBBT_LP_ITERATION
Definition: obbt.cpp:26
bool isWiped(OsiCuts &cs)
Check whether the previous cut generators have added an infeasible cut.
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
#define MAX_OBBT_ATTEMPTS
Definition: obbt.cpp:27
status of lower/upper bound of a variable, to be checked/modified in bound tightening ...
void sparse2dense(int ncols, t_chg_bounds *chg_bds, int *&changed, int &nchanged)
translate sparse to dense vector (should be replaced)
auxSign
&quot;sign&quot; of the constraint defining an auxiliary.
#define THRESH_OBBT_AUX
Definition: obbt.cpp:24
const Ipopt::EJournalCategory J_BOUNDTIGHTENING(Ipopt::J_USER2)
#define THRES_NBD_CHANGED
Definition: obbt.cpp:32
nodeType
type of a node in an expression tree
#define COUENNE_INFINITY
const Ipopt::EJournalCategory J_COUENNE(Ipopt::J_USER8)
Bonmin class for passing info between components of branch-and-cuts.
Definition: BonBabInfos.hpp:19