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